* Reworked undefined symbol resolution in the runtime loader. Got rid of

the per-root-image breadth-first sorted image array. Instead we have a
  per-image hook function to resolve the symbols. The default function
  uses the sLoadedImages list directly, which is breadth-first sorted
  anyway. There's also a BeOS function for old-style symbol resolution
  and one for add-ons, which lacks a proper implementation yet (just
  uses old-style ATM).
* Made the dl*() functions POSIX compliant:
  - dlopen() does no longer use load_add_on(), but loads the object as a
    library. It also properly supports a NULL name, now -- the previous
    "_APP_" work-around did only work, if this soname was set on the
    program (unlikely for programs using this API).
  - Implemented RTLD_{GLOBAL,LOCAL}.
  - dlsym() looks up symbols properly now, i.e. not just in the given
    image, but breadth-first for an actual image or in load order for
    the global scope. It also supports the not-quite POSIX RTLD_DEFAULT
    and RTLD_NEXT extensions. Our RTLD_NEXT finds more symbols than in
    Linux (also in later dlopen()ed libraries), but that should be fine.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28568 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-11-08 22:40:56 +00:00
parent e10b4cace5
commit 0c85bd054e
8 changed files with 412 additions and 213 deletions
+5
View File
@@ -14,6 +14,11 @@
#define RTLD_LOCAL 0 /* symbols are not available for relocating any other object */ #define RTLD_LOCAL 0 /* symbols are not available for relocating any other object */
#define RTLD_GLOBAL 2 /* all symbols are available */ #define RTLD_GLOBAL 2 /* all symbols are available */
/* not-yet-POSIX extensions (dlsym() handles) */
#define RTLD_DEFAULT ((void*)0)
/* find the symbol in the global scope */
#define RTLD_NEXT ((void*)-1L)
/* find the next definition of the symbol */
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -25,8 +25,12 @@ struct rld_export {
// runtime loader API export // runtime loader API export
image_id (*load_add_on)(char const *path, uint32 flags); image_id (*load_add_on)(char const *path, uint32 flags);
status_t (*unload_add_on)(image_id imageID); status_t (*unload_add_on)(image_id imageID);
image_id (*load_library)(char const *path, uint32 flags, void **_handle);
status_t (*unload_library)(void* handle);
status_t (*get_image_symbol)(image_id imageID, char const *symbolName, status_t (*get_image_symbol)(image_id imageID, char const *symbolName,
int32 symbolType, void **_location); int32 symbolType, void **_location);
status_t (*get_library_symbol)(void* handle, void* caller,
const char* symbolName, void **_location);
status_t (*get_nth_image_symbol)(image_id imageID, int32 num, char *symbolName, status_t (*get_nth_image_symbol)(image_id imageID, int32 num, char *symbolName,
int32 *nameLength, int32 *symbolType, void **_location); int32 *nameLength, int32 *symbolType, void **_location);
status_t (*test_executable)(const char *path, char *interpreter); status_t (*test_executable)(const char *path, char *interpreter);
@@ -100,10 +104,9 @@ typedef struct image_t {
uint32 num_needed; uint32 num_needed;
struct image_t **needed; struct image_t **needed;
// For "root" images (the program, add-ons): Symbols are searched in the struct Elf32_Sym* (*find_undefined_symbol)(struct image_t* rootImage,
// images in array order. struct image_t* image, const char* name,
uint32 symbol_resolution_image_count; struct image_t** foundInImage);
struct image_t **symbol_resolution_images;
// Singly-linked list of symbol patchers for symbols defined respectively // Singly-linked list of symbol patchers for symbols defined respectively
// referenced by this image. // referenced by this image.
@@ -123,7 +126,6 @@ typedef struct image_queue_t {
// image_t::flags // image_t::flags
#define IMAGE_FLAG_RTLD_MASK 0x03 #define IMAGE_FLAG_RTLD_MASK 0x03
// RTLD_{LAZY,NOW} | RTLD_{LOCAL,GLOBAL} // RTLD_{LAZY,NOW} | RTLD_{LOCAL,GLOBAL}
#define IMAGE_FLAG_R5_SYMBOL_RESOLUTION 0x04
#define STRING(image, offset) ((char *)(&(image)->strtab[(offset)])) #define STRING(image, offset) ((char *)(&(image)->strtab[(offset)]))
#define SYMNAME(image, sym) STRING(image, (sym)->st_name) #define SYMNAME(image, sym) STRING(image, (sym)->st_name)
+3
View File
@@ -66,6 +66,9 @@ load_image(int32 argCount, const char **args, const char **environ)
image_id image_id
load_add_on(char const *name) load_add_on(char const *name)
{ {
if (name == NULL)
return B_BAD_VALUE;
return __gRuntimeLoader->load_add_on(name, 0); return __gRuntimeLoader->load_add_on(name, 0);
} }
+15 -16
View File
@@ -1,4 +1,5 @@
/* /*
* Copyright 2008, Ingo Weinhold, [email protected].
* Copyright 2003-2008, Axel Dörfler, [email protected]. * Copyright 2003-2008, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
@@ -23,31 +24,27 @@ static status_t sStatus;
void * void *
dlopen(char const *name, int mode) dlopen(char const *name, int mode)
{ {
// TODO: According to the standard multiple dlopen() invocations for the same void* handle;
// file will cause the object to be loaded once only. That is we should load image_id imageID = __gRuntimeLoader->load_library(name, mode, &handle);
// the object as a library, not an add-on.
status_t status;
if (name == NULL) sStatus = imageID >= 0 ? B_OK : imageID;
name = MAGIC_APP_NAME;
status = __gRuntimeLoader->load_add_on(name, mode); return imageID >= 0 ? handle : NULL;
sStatus = status;
if (status < B_OK)
return NULL;
return (void *)status;
} }
void * void *
dlsym(void *handle, char const *name) dlsym(void *handle, char const *name)
{ {
status_t status;
void* location; void* location;
status_t status;
void* caller = NULL;
status = get_image_symbol((image_id)handle, name, B_SYMBOL_TYPE_ANY, &location); if (handle == RTLD_NEXT)
caller = __arch_get_caller();
status = __gRuntimeLoader->get_library_symbol(handle, caller, name,
&location);
sStatus = status; sStatus = status;
if (status < B_OK) if (status < B_OK)
@@ -60,7 +57,7 @@ dlsym(void *handle, char const *name)
int int
dlclose(void *handle) dlclose(void *handle)
{ {
return unload_add_on((image_id)handle); return __gRuntimeLoader->unload_library(handle);
} }
@@ -77,6 +74,8 @@ dlerror(void)
int int
dladdr(void *addr, Dl_info *info) dladdr(void *addr, Dl_info *info)
{ {
// TODO: This can be implemented more efficiently in the runtime loader.
// get_library_symbol() already has the code doing that.
char curSymName[NAME_MAX]; char curSymName[NAME_MAX];
static char symName[NAME_MAX]; static char symName[NAME_MAX];
static char imageName[MAXPATHLEN]; static char imageName[MAXPATHLEN];
+327 -156
View File
@@ -11,6 +11,7 @@
#include "runtime_loader_private.h" #include "runtime_loader_private.h"
#include <ctype.h> #include <ctype.h>
#include <dlfcn.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -49,9 +50,14 @@
#define RLD_PROGRAM_BASE 0x00200000 #define RLD_PROGRAM_BASE 0x00200000
/* keep in sync with app ldscript */ /* keep in sync with app ldscript */
// a handle returned by load_library() (dlopen())
#define RLD_GLOBAL_SCOPE ((void*)-2l)
enum { enum {
RFLAG_RW = 0x0001, // the lower two bits are reserved for RTLD_NOW and RTLD_GLOBAL
RFLAG_ANON = 0x0002,
RFLAG_RW = 0x0010,
RFLAG_ANON = 0x0020,
RFLAG_TERMINATED = 0x0200, RFLAG_TERMINATED = 0x0200,
RFLAG_INITIALIZED = 0x0400, RFLAG_INITIALIZED = 0x0400,
@@ -61,7 +67,8 @@ enum {
RFLAG_DEPENDENCIES_LOADED = 0x4000, RFLAG_DEPENDENCIES_LOADED = 0x4000,
RFLAG_REMAPPED = 0x8000, RFLAG_REMAPPED = 0x8000,
RFLAG_VISITED = 0x10000 RFLAG_VISITED = 0x10000,
RFLAG_USE_FOR_RESOLVING = 0x20000
// temporarily set in the symbol resolution code // temporarily set in the symbol resolution code
}; };
@@ -501,7 +508,6 @@ delete_image_struct(image_t *image)
memset(image->needed, 0xa5, sizeof(image->needed[0]) * image->num_needed); memset(image->needed, 0xa5, sizeof(image->needed[0]) * image->num_needed);
#endif #endif
free(image->needed); free(image->needed);
free(image->symbol_resolution_images);
while (RuntimeLoaderSymbolPatcher* patcher while (RuntimeLoaderSymbolPatcher* patcher
= image->defined_symbol_patchers) { = image->defined_symbol_patchers) {
@@ -535,6 +541,52 @@ delete_image(image_t *image)
} }
static void
update_image_flags_recursively(image_t* image, uint32 flagsToSet,
uint32 flagsToClear)
{
image_t* queue[sLoadedImageCount];
uint32 count = 0;
uint32 index = 0;
queue[count++] = image;
image->flags |= RFLAG_VISITED;
while (index < count) {
// pop next image
image = queue[index++];
// push dependencies
for (uint32 i = 0; i < image->num_needed; i++) {
image_t* needed = image->needed[i];
if ((needed->flags & RFLAG_VISITED) == 0) {
queue[count++] = needed;
needed->flags |= RFLAG_VISITED;
}
}
}
// update flags
for (uint32 i = 0; i < count; i++) {
queue[i]->flags = (queue[i]->flags | flagsToSet)
& ~(flagsToClear | RFLAG_VISITED);
}
}
static void
set_image_flags_recursively(image_t* image, uint32 flags)
{
update_image_flags_recursively(image, flags, 0);
}
static void
clear_image_flags_recursively(image_t* image, uint32 flags)
{
update_image_flags_recursively(image, 0, flags);
}
static status_t static status_t
parse_program_headers(image_t *image, char *buff, int phnum, int phentsize) parse_program_headers(image_t *image, char *buff, int phnum, int phentsize)
{ {
@@ -1183,36 +1235,48 @@ find_symbol(image_t* image, char const* symbolName, int32 symbolType,
} }
static struct Elf32_Sym* static status_t
find_symbol_in_root_image_list(image_t* rootImage, const char* name, find_symbol_breadth_first(image_t* image, const char* name, int32 type,
image_t** foundInImage) image_t** _foundInImage, void** _location)
{ {
int32 count = rootImage->symbol_resolution_image_count; image_t* queue[sLoadedImageCount];
image_t** images = rootImage->symbol_resolution_images; uint32 count = 0;
for (int32 i = 0; i < count; i++) { uint32 index = 0;
image_t* image = images[i]; queue[count++] = image;
if (image->dynamic_ptr) { image->flags |= RFLAG_VISITED;
Elf32_Sym* symbol = find_symbol(image, name, B_SYMBOL_TYPE_ANY);
if (symbol) { bool found = false;
*foundInImage = image; while (index < count) {
return symbol; // pop next image
image = queue[index++];
if (find_symbol(image, name, type, _location) == B_OK) {
found = true;
break;
}
// push needed images
for (uint32 i = 0; i < image->num_needed; i++) {
image_t* needed = image->needed[i];
if ((needed->flags & RFLAG_VISITED) == 0) {
queue[count++] = needed;
needed->flags |= RFLAG_VISITED;
} }
} }
} }
return NULL; // clear visited flags
for (uint32 i = 0; i < count; i++)
queue[i]->flags &= ~RFLAG_VISITED;
return found ? B_OK : B_ENTRY_NOT_FOUND;
} }
static struct Elf32_Sym* static struct Elf32_Sym*
find_undefined_symbol(image_t* rootImage, image_t* image, const char* name, find_undefined_symbol_beos(image_t* rootImage, image_t* image, const char* name,
image_t** foundInImage) image_t** foundInImage)
{ {
// If not simulating BeOS style symbol resolution, undefined symbols are
// searched recursively starting from the root image.
if ((rootImage->flags & IMAGE_FLAG_R5_SYMBOL_RESOLUTION) == 0)
return find_symbol_in_root_image_list(rootImage, name, foundInImage);
// BeOS style symbol resolution: It is sufficient to check the direct // BeOS style symbol resolution: It is sufficient to check the direct
// dependencies. The linker would have complained, if the symbol wasn't // dependencies. The linker would have complained, if the symbol wasn't
// there. // there.
@@ -1231,6 +1295,48 @@ find_undefined_symbol(image_t* rootImage, image_t* image, const char* name,
} }
static struct Elf32_Sym*
find_undefined_symbol_global(image_t* rootImage, image_t* image,
const char* name, image_t** foundInImage)
{
// Global load order symbol resolution: All loaded images are searched for
// the symbol in the order they have been loaded. We skip add-on images and
// RTLD_LOCAL images though.
image_t* otherImage = sLoadedImages.head;
while (otherImage != NULL) {
if (otherImage == rootImage
|| otherImage->type != B_ADD_ON_IMAGE
&& (otherImage->flags
& (RTLD_GLOBAL | RFLAG_USE_FOR_RESOLVING)) != 0) {
struct Elf32_Sym *symbol = find_symbol(otherImage, name,
B_SYMBOL_TYPE_ANY);
if (symbol) {
*foundInImage = otherImage;
return symbol;
}
}
otherImage = otherImage->next;
}
return NULL;
}
static struct Elf32_Sym*
find_undefined_symbol_add_on(image_t* rootImage, image_t* image,
const char* name, image_t** foundInImage)
{
// TODO: How do we want to implement this one? Using global scope resolution
// might be undesired as it is now, since libraries could refer to symbols in
// the add-on, which would result in add-on symbols implicitely becoming used
// outside of the add-on. So the options would be to use the global scope but
// skip the add-on, or to do breadth-first resolution in the add-on dependency
// scope, also skipping the add-on itself. BeOS style resolution is safe, too,
// but we miss out features like undefined symbols and preloading.
return find_undefined_symbol_beos(rootImage, image, name, foundInImage);
}
/*! This function is called when we run BeOS images on Haiku. /*! This function is called when we run BeOS images on Haiku.
It allows us to redirect functions to ensure compatibility. It allows us to redirect functions to ensure compatibility.
*/ */
@@ -1289,8 +1395,8 @@ resolve_symbol(image_t *rootImage, image_t *image, struct Elf32_Sym *sym,
type = B_SYMBOL_TYPE_DATA; type = B_SYMBOL_TYPE_DATA;
// it's undefined, must be outside this image, try the other images // it's undefined, must be outside this image, try the other images
sharedSym = find_undefined_symbol(rootImage, image, symName, sharedSym = rootImage->find_undefined_symbol(rootImage, image,
&sharedImage); symName, &sharedImage);
void* location = NULL; void* location = NULL;
enum { enum {
@@ -1447,7 +1553,8 @@ relocate_image(image_t *rootImage, image_t *image)
static status_t static status_t
load_container(char const *name, image_type type, const char *rpath, image_t **_image) load_container(char const *name, image_type type, const char *rpath,
image_t **_image)
{ {
int32 pheaderSize, sheaderSize; int32 pheaderSize, sheaderSize;
char path[PATH_MAX]; char path[PATH_MAX];
@@ -1593,7 +1700,7 @@ load_container(char const *name, image_type type, const char *rpath, image_t **_
// unavailable) // unavailable)
if (image->gcc_version.major == 0 if (image->gcc_version.major == 0
|| image->gcc_version.major == 2 && image->gcc_version.middle < 95) { || image->gcc_version.major == 2 && image->gcc_version.middle < 95) {
image->flags |= IMAGE_FLAG_R5_SYMBOL_RESOLUTION; image->find_undefined_symbol = find_undefined_symbol_beos;
} }
status = map_image(fd, path, image, type == B_APP_IMAGE); status = map_image(fd, path, image, type == B_APP_IMAGE);
@@ -1659,7 +1766,7 @@ find_dt_rpath(image_t *image)
static status_t static status_t
load_dependencies(image_t *image) load_immediate_dependencies(image_t *image)
{ {
struct Elf32_Dyn *d = (struct Elf32_Dyn *)image->dynamic_ptr; struct Elf32_Dyn *d = (struct Elf32_Dyn *)image->dynamic_ptr;
bool reportErrors = report_errors(); bool reportErrors = report_errors();
@@ -1748,6 +1855,20 @@ load_dependencies(image_t *image)
} }
static status_t
load_dependencies(image_t* image)
{
for (image_t* otherImage = image; otherImage != NULL;
otherImage = otherImage->next) {
status_t status = load_immediate_dependencies(otherImage);
if (status != B_OK)
return status;
}
return B_OK;
}
static uint32 static uint32
topological_sort(image_t *image, uint32 slot, image_t **initList, topological_sort(image_t *image, uint32 slot, image_t **initList,
uint32 sortFlag) uint32 sortFlag)
@@ -1766,36 +1887,6 @@ topological_sort(image_t *image, uint32 slot, image_t **initList,
} }
static uint32
topological_sort_breadth_first(image_t* image, image_t** list, uint32 sortFlag)
{
if ((image->flags & sortFlag) != 0)
return 0;
// We directly use the result list as queue for the breadth first search.
list[0] = image;
image->flags |= sortFlag;
int32 index = 0;
int32 count = 1;
while (index < count) {
image = list[index++];
for (uint32 i = 0; i < image->num_needed; i++) {
image_t* neededImage = image->needed[i];
if ((neededImage->flags & sortFlag) == 0) {
// dependency not yet visited -- add to queue
list[count++] = neededImage;
neededImage->flags |= sortFlag;
}
}
}
return count;
}
static ssize_t static ssize_t
get_sorted_image_list(image_t *image, image_t ***_list, uint32 sortFlag) get_sorted_image_list(image_t *image, image_t ***_list, uint32 sortFlag)
{ {
@@ -1815,85 +1906,23 @@ get_sorted_image_list(image_t *image, image_t ***_list, uint32 sortFlag)
} }
static ssize_t
get_sorted_image_list_breadth_first(image_t* image, image_t*** _list,
uint32 sortFlag)
{
image_t** list = (image_t**)malloc(sLoadedImageCount * sizeof(image_t*));
if (list == NULL) {
FATAL("memory shortage in get_sorted_image_list()");
*_list = NULL;
return B_NO_MEMORY;
}
memset(list, 0, sLoadedImageCount * sizeof(image_t *));
*_list = list;
return topological_sort_breadth_first(image, list, sortFlag);
}
static status_t static status_t
relocate_dependencies(image_t *image) relocate_dependencies(image_t *image)
{ {
// build an array of images in the order we want to lookup symbols
image_t** lookupList;
ssize_t lookupCount = get_sorted_image_list_breadth_first(image,
&lookupList, RFLAG_VISITED);
if (lookupCount < 0)
return lookupCount;
// If the image is not the program image, add it and its dependencies, too.
if (sProgramImage != NULL && image != sProgramImage) {
lookupCount += topological_sort_breadth_first(sProgramImage,
lookupList + lookupCount, RFLAG_VISITED);
}
// If we have pre-loaded images, prepend them.
if (sPreloadedImageCount > 0) {
// Count the ones that have to be added -- normally all, but one never
// knows.
uint32 preloadedCount = 0;
for (uint32 i = 0; i < sPreloadedImageCount; i++) {
image_t* preloadedImage = sPreloadedImages[i];
if ((preloadedImage->flags & RFLAG_VISITED) == 0)
preloadedCount++;
}
// add them
if (preloadedCount > 0) {
memmove(lookupList + preloadedCount, lookupList,
lookupCount * sizeof(image_t*));
lookupCount += preloadedCount;
preloadedCount = 0;
for (uint32 i = 0; i < sPreloadedImageCount; i++) {
image_t* preloadedImage = sPreloadedImages[i];
if ((preloadedImage->flags & RFLAG_VISITED) == 0)
lookupList[preloadedCount++] = preloadedImage;
}
}
}
// clear the "visited" flag
for (int32 i = 0; i < lookupCount; i++)
lookupList[i]->flags &= ~RFLAG_VISITED;
image->symbol_resolution_images = lookupList;
image->symbol_resolution_image_count = lookupCount;
// get the images that still have to be relocated // get the images that still have to be relocated
image_t **list; image_t **list;
ssize_t count = get_sorted_image_list(image, &list, RFLAG_RELOCATED); ssize_t count = get_sorted_image_list(image, &list, RFLAG_RELOCATED);
if (count < B_OK) if (count < B_OK)
return count; return count;
// relocate
for (ssize_t i = 0; i < count; i++) { for (ssize_t i = 0; i < count; i++) {
status_t status = relocate_image(image, list[i]); status_t status = relocate_image(image, list[i]);
if (status < B_OK) if (status < B_OK) {
free(list);
return status; return status;
} }
}
free(list); free(list);
return B_OK; return B_OK;
@@ -1959,12 +1988,10 @@ inject_runtime_loader_api(image_t* rootImage)
// We patch any exported __gRuntimeLoader symbols to point to our private // We patch any exported __gRuntimeLoader symbols to point to our private
// API. // API.
image_t* image; image_t* image;
Elf32_Sym* symbol = find_symbol_in_root_image_list(rootImage, void* _export;
"__gRuntimeLoader", &image); if (find_symbol_breadth_first(rootImage, "__gRuntimeLoader",
B_SYMBOL_TYPE_DATA, &image, &_export) == B_OK) {
if (symbol != NULL) { *(void**)_export = &gRuntimeLoader;
void** _export = (void**)(symbol->st_value + image->regions[0].delta);
*_export = &gRuntimeLoader;
} }
} }
@@ -2003,12 +2030,14 @@ preload_image(char const* path)
return status; return status;
} }
for (image_t* otherImage = sLoadedImages.head; otherImage != NULL; if (image->find_undefined_symbol == NULL)
otherImage = otherImage->next) { image->find_undefined_symbol = find_undefined_symbol_global;
status = load_dependencies(otherImage);
status = load_dependencies(image);
if (status < B_OK) if (status < B_OK)
goto err; goto err;
}
set_image_flags_recursively(image, RTLD_GLOBAL);
status = relocate_dependencies(image); status = relocate_dependencies(image);
if (status < B_OK) if (status < B_OK)
@@ -2105,11 +2134,17 @@ load_program(char const *path, void **_entry)
if (status < B_OK) if (status < B_OK)
goto err; goto err;
for (image = sLoadedImages.head; image != NULL; image = image->next) { if (sProgramImage->find_undefined_symbol == NULL)
status = load_dependencies(image); sProgramImage->find_undefined_symbol = find_undefined_symbol_global;
status = load_dependencies(sProgramImage);
if (status < B_OK) if (status < B_OK)
goto err; goto err;
}
// Set RTLD_GLOBAL on all libraries, but clear it on the program image.
// This results in the desired symbol resolution for dlopen()ed libraries.
set_image_flags_recursively(sProgramImage, RTLD_GLOBAL);
sProgramImage->flags &= ~RTLD_GLOBAL;
status = relocate_dependencies(sProgramImage); status = relocate_dependencies(sProgramImage);
if (status < B_OK) if (status < B_OK)
@@ -2122,14 +2157,8 @@ load_program(char const *path, void **_entry)
// Since the images are initialized now, we no longer should use our // Since the images are initialized now, we no longer should use our
// getenv(), but use the one from libroot.so // getenv(), but use the one from libroot.so
{ find_symbol_breadth_first(sProgramImage, "getenv", B_SYMBOL_TYPE_TEXT,
struct Elf32_Sym *symbol = find_symbol_in_root_image_list(sProgramImage, &image, (void**)&gGetEnv);
"getenv", &image);
if (symbol != NULL)
gGetEnv = (char* (*)(const char*))
(symbol->st_value + image->regions[0].delta);
}
if (sProgramImage->entry_point == 0) { if (sProgramImage->entry_point == 0) {
status = B_NOT_AN_EXECUTABLE; status = B_NOT_AN_EXECUTABLE;
@@ -2169,19 +2198,15 @@ err:
image_id image_id
load_library(char const *path, uint32 flags, bool addOn) load_library(char const *path, uint32 flags, bool addOn, void** _handle)
{ {
image_t *image = NULL; image_t *image = NULL;
image_t *iter;
image_type type = (addOn ? B_ADD_ON_IMAGE : B_LIBRARY_IMAGE); image_type type = (addOn ? B_ADD_ON_IMAGE : B_LIBRARY_IMAGE);
status_t status; status_t status;
if (path == NULL) if (path == NULL && addOn)
return B_BAD_VALUE; return B_BAD_VALUE;
// ToDo: implement flags
(void)flags;
KTRACE("rld: load_library(\"%s\", 0x%lx, %d)", path, flags, addOn); KTRACE("rld: load_library(\"%s\", 0x%lx, %d)", path, flags, addOn);
rld_lock(); rld_lock();
@@ -2190,12 +2215,23 @@ load_library(char const *path, uint32 flags, bool addOn)
// have we already loaded this library? // have we already loaded this library?
// Checking it at this stage saves loading its dependencies again // Checking it at this stage saves loading its dependencies again
if (!addOn) { if (!addOn) {
// a NULL path is fine -- it means the global scope shall be opened
if (path == NULL) {
*_handle = RLD_GLOBAL_SCOPE;
rld_unlock();
return 0;
}
image = find_image(path, APP_OR_LIBRARY_TYPE); image = find_image(path, APP_OR_LIBRARY_TYPE);
if (image != NULL && (flags & RTLD_GLOBAL) != 0)
set_image_flags_recursively(image, RTLD_GLOBAL);
if (image) { if (image) {
atomic_add(&image->ref_count, 1); atomic_add(&image->ref_count, 1);
rld_unlock(); rld_unlock();
KTRACE("rld: load_library(\"%s\"): already loaded: %ld", path, KTRACE("rld: load_library(\"%s\"): already loaded: %ld", path,
image->id); image->id);
*_handle = image;
return image->id; return image->id;
} }
} }
@@ -2208,16 +2244,33 @@ load_library(char const *path, uint32 flags, bool addOn)
return status; return status;
} }
for (iter = sLoadedImages.head; iter; iter = iter->next) { if (image->find_undefined_symbol == NULL) {
status = load_dependencies(iter); if (addOn)
image->find_undefined_symbol = find_undefined_symbol_add_on;
else
image->find_undefined_symbol = find_undefined_symbol_global;
}
status = load_dependencies(image);
if (status < B_OK) if (status < B_OK)
goto err; goto err;
}
// If specified, set the RTLD_GLOBAL flag recursively on this image and all
// dependencies. If not specified, we temporarily set
// RFLAG_USE_FOR_RESOLVING so that the dependencies will correctly be used
// for undefined symbol resolution.
if ((flags & RTLD_GLOBAL) != 0)
set_image_flags_recursively(image, RTLD_GLOBAL);
else
set_image_flags_recursively(image, RFLAG_USE_FOR_RESOLVING);
status = relocate_dependencies(image); status = relocate_dependencies(image);
if (status < B_OK) if (status < B_OK)
goto err; goto err;
if ((flags & RTLD_GLOBAL) == 0)
clear_image_flags_recursively(image, RFLAG_USE_FOR_RESOLVING);
remap_images(); remap_images();
init_dependencies(image, true); init_dependencies(image, true);
@@ -2225,6 +2278,7 @@ load_library(char const *path, uint32 flags, bool addOn)
KTRACE("rld: load_library(\"%s\") done: id: %ld", path, image->id); KTRACE("rld: load_library(\"%s\") done: id: %ld", path, image->id);
*_handle = image;
return image->id; return image->id;
err: err:
@@ -2239,15 +2293,18 @@ err:
status_t status_t
unload_library(image_id imageID, bool addOn) unload_library(void* handle, image_id imageID, bool addOn)
{ {
status_t status = B_BAD_IMAGE_ID; status_t status = B_BAD_IMAGE_ID;
image_t *image; image_t *image;
image_type type = addOn ? B_ADD_ON_IMAGE : B_LIBRARY_IMAGE; image_type type = addOn ? B_ADD_ON_IMAGE : B_LIBRARY_IMAGE;
if (imageID < B_OK) if (handle == NULL && imageID < 0)
return B_BAD_IMAGE_ID; return B_BAD_IMAGE_ID;
if (handle == RLD_GLOBAL_SCOPE)
return B_OK;
rld_lock(); rld_lock();
// for now, just do stupid simple global locking // for now, just do stupid simple global locking
@@ -2258,6 +2315,10 @@ unload_library(image_id imageID, bool addOn)
// we only check images that have been already initialized // we only check images that have been already initialized
if (handle != NULL) {
image = (image_t*)handle;
put_image(image);
} else {
for (image = sLoadedImages.head; image; image = image->next) { for (image = sLoadedImages.head; image; image = image->next) {
if (image->id == imageID) { if (image->id == imageID) {
// unload image // unload image
@@ -2269,6 +2330,7 @@ unload_library(image_id imageID, bool addOn)
break; break;
} }
} }
}
if (status == B_OK) { if (status == B_OK) {
while ((image = sDisposableImages.head) != NULL) { while ((image = sDisposableImages.head) != NULL) {
@@ -2383,6 +2445,115 @@ get_symbol(image_id imageID, char const *symbolName, int32 symbolType,
} }
status_t
get_library_symbol(void* handle, void* caller, const char* symbolName,
void **_location)
{
status_t status = B_ENTRY_NOT_FOUND;
if (symbolName == NULL)
return B_BAD_VALUE;
rld_lock();
// for now, just do stupid simple global locking
if (handle == RTLD_DEFAULT || handle == RLD_GLOBAL_SCOPE) {
// look in the default scope
image_t* image;
Elf32_Sym* symbol = find_undefined_symbol_global(sProgramImage,
sProgramImage, symbolName, &image);
if (symbol != NULL) {
*_location = (void*)(symbol->st_value + image->regions[0].delta);
int32 symbolType = ELF32_ST_TYPE(symbol->st_info) == STT_FUNC
? B_SYMBOL_TYPE_TEXT : B_SYMBOL_TYPE_DATA;
patch_defined_symbol(image, symbolName, _location, &symbolType);
status = B_OK;
}
} else if (handle == RTLD_NEXT) {
// Look in the default scope, but also in the dependencies of the
// calling image. Return the next after the caller symbol.
// First of all, find the caller symbol and its image.
Elf32_Sym* callerSymbol = NULL;
image_t* callerImage = sLoadedImages.head;
for (; callerImage != NULL; callerImage = callerImage->next) {
elf_region_t& text = callerImage->regions[0];
if ((addr_t)caller < text.vmstart
|| (addr_t)caller >= text.vmstart + text.vmsize) {
continue;
}
// found the image -- now find the symbol
for (uint32 i = 0; i < callerImage->symhash[1]; i++) {
Elf32_Sym& symbol = callerImage->syms[i];
if ((ELF32_ST_TYPE(symbol.st_info) != STT_FUNC)
|| symbol.st_value == 0) {
continue;
}
addr_t address = symbol.st_value
+ callerImage->regions[0].delta;
if ((addr_t)caller >= address
&& (addr_t)caller < address + symbol.st_size) {
callerSymbol = &symbol;
break;
}
}
break;
}
if (callerSymbol != NULL) {
// found the caller -- now search the global scope until we find
// the next symbol
set_image_flags_recursively(callerImage, RFLAG_USE_FOR_RESOLVING);
image_t* image = sLoadedImages.head;
for (; image != NULL; image = image->next) {
if (image != callerImage
&& (image->type == B_ADD_ON_IMAGE
|| (image->flags
& (RTLD_GLOBAL | RFLAG_USE_FOR_RESOLVING)) == 0)) {
continue;
}
struct Elf32_Sym *symbol = find_symbol(image, symbolName,
B_SYMBOL_TYPE_TEXT);
if (symbol == NULL)
continue;
if (callerSymbol == NULL) {
// already skipped the caller symbol -- so this is
// the one we're looking for
*_location = (void*)(symbol->st_value
+ image->regions[0].delta);
int32 symbolType = B_SYMBOL_TYPE_TEXT;
patch_defined_symbol(image, symbolName, _location,
&symbolType);
status = B_OK;
break;
}
if (symbol == callerSymbol) {
// found the caller symbol
callerSymbol = NULL;
}
}
clear_image_flags_recursively(callerImage, RFLAG_USE_FOR_RESOLVING);
}
} else {
// breadth-first search in the given image and its dependencies
image_t* inImage;
status = find_symbol_breadth_first((image_t*)handle, symbolName,
B_SYMBOL_TYPE_ANY, &inImage, _location);
}
rld_unlock();
return status;
}
status_t status_t
get_next_image_dependency(image_id id, uint32 *cookie, const char **_name) get_next_image_dependency(image_id id, uint32 *cookie, const char **_name)
{ {
+20 -2
View File
@@ -16,14 +16,29 @@
static image_id static image_id
export_load_add_on(char const *name, uint32 flags) export_load_add_on(char const *name, uint32 flags)
{ {
return load_library(name, flags, true); void* handle;
return load_library(name, flags, true, &handle);
} }
static status_t static status_t
export_unload_add_on(image_id id) export_unload_add_on(image_id id)
{ {
return unload_library(id, true); return unload_library(NULL, id, true);
}
static image_id
export_load_library(char const *name, uint32 flags, void **_handle)
{
return load_library(name, flags, false, _handle);
}
static status_t
export_unload_library(void* handle)
{
return unload_library(handle, -1, false);
} }
@@ -31,7 +46,10 @@ struct rld_export gRuntimeLoader = {
// dynamic loading support API // dynamic loading support API
export_load_add_on, export_load_add_on,
export_unload_add_on, export_unload_add_on,
export_load_library,
export_unload_library,
get_symbol, get_symbol,
get_library_symbol,
get_nth_symbol, get_nth_symbol,
test_executable, test_executable,
get_next_image_dependency, get_next_image_dependency,
+16 -18
View File
@@ -240,8 +240,22 @@ open_executable(char *name, image_type type, const char *rpath,
memmove(name, paths, strlen(paths) + 1); memmove(name, paths, strlen(paths) + 1);
} }
// first try rpath (DT_RPATH) // let's evaluate the system path variables to find the container
if (rpath) { paths = search_path_for_type(type);
if (paths) {
fd = search_executable_in_path_list(name, paths, strlen(paths),
programPath, compatibilitySubDir, buffer, sizeof(buffer));
// If not found and a compatibility sub directory has been
// specified, look again in the standard search paths.
if (fd == B_ENTRY_NOT_FOUND && compatibilitySubDir != NULL) {
fd = search_executable_in_path_list(name, paths, strlen(paths),
programPath, NULL, buffer, sizeof(buffer));
}
}
// try rpath (DT_RPATH), if not found yet
if (fd < 0 && rpath != NULL) {
// It consists of a colon-separated search path list. Optionally it // It consists of a colon-separated search path list. Optionally it
// follows a second search path list, separated from the first by a // follows a second search path list, separated from the first by a
// semicolon. // semicolon.
@@ -260,22 +274,6 @@ open_executable(char *name, image_type type, const char *rpath,
} }
} }
// let's evaluate the system path variables to find the container
if (fd < 0) {
paths = search_path_for_type(type);
if (paths) {
fd = search_executable_in_path_list(name, paths, strlen(paths),
programPath, compatibilitySubDir, buffer, sizeof(buffer));
// If not found and a compatibility sub directory has been
// specified, look again in the standard search paths.
if (fd == B_ENTRY_NOT_FOUND && compatibilitySubDir != NULL) {
fd = search_executable_in_path_list(name, paths, strlen(paths),
programPath, NULL, buffer, sizeof(buffer));
}
}
}
if (fd >= 0) { if (fd >= 0) {
// we found it, copy path! // we found it, copy path!
TRACE(("runtime_loader: open_executable(%s): found at %s\n", name, buffer)); TRACE(("runtime_loader: open_executable(%s): found at %s\n", name, buffer));
@@ -29,12 +29,15 @@ status_t test_executable(const char *path, char *interpreter);
void terminate_program(void); void terminate_program(void);
image_id load_program(char const *path, void **entry); image_id load_program(char const *path, void **entry);
status_t unload_library(image_id imageID, bool addOn); image_id load_library(char const *path, uint32 flags, bool addOn,
image_id load_library(char const *path, uint32 flags, bool addOn); void** _handle);
status_t unload_library(void* handle, image_id imageID, bool addOn);
status_t get_nth_symbol(image_id imageID, int32 num, char *nameBuffer, status_t get_nth_symbol(image_id imageID, int32 num, char *nameBuffer,
int32 *_nameLength, int32 *_type, void **_location); int32 *_nameLength, int32 *_type, void **_location);
status_t get_symbol(image_id imageID, char const *symbolName, int32 symbolType, status_t get_symbol(image_id imageID, char const *symbolName, int32 symbolType,
void **_location); void **_location);
status_t get_library_symbol(void* handle, void* caller, const char* symbolName,
void **_location);
status_t get_next_image_dependency(image_id id, uint32 *cookie, status_t get_next_image_dependency(image_id id, uint32 *cookie,
const char **_name); const char **_name);
int resolve_symbol(image_t *rootImage, image_t *image, struct Elf32_Sym *sym, int resolve_symbol(image_t *rootImage, image_t *image, struct Elf32_Sym *sym,