From 10b4b5d1755c3d2b4d405321c8c30bc609dd635d Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Mon, 3 Nov 2008 13:27:14 +0000 Subject: [PATCH] * Added an "add-on" interface for the runtime loader. Pre-loaded images can export a structure containing callback hooks invoked by the runtime loader when certain image events occur (image loaded, relocated, initialized, etc.). * Also added a mechanism to patch image symbols. For an image callback functions can be installed that patch symbols exported or imported by the image. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28475 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../private/runtime_loader/runtime_loader.h | 51 +++ src/system/runtime_loader/elf.cpp | 371 +++++++++++++++--- 2 files changed, 377 insertions(+), 45 deletions(-) diff --git a/headers/private/runtime_loader/runtime_loader.h b/headers/private/runtime_loader/runtime_loader.h index 789c243246..2b5697fe14 100644 --- a/headers/private/runtime_loader/runtime_loader.h +++ b/headers/private/runtime_loader/runtime_loader.h @@ -48,6 +48,8 @@ extern struct rld_export *__gRuntimeLoader; // #pragma mark - runtime loader debugger interface +struct RuntimeLoaderSymbolPatcher; + typedef struct elf_region_t { area_id id; addr_t start; @@ -103,6 +105,11 @@ typedef struct image_t { uint32 symbol_resolution_image_count; struct image_t **symbol_resolution_images; + // Singly-linked list of symbol patchers for symbols defined respectively + // referenced by this image. + struct RuntimeLoaderSymbolPatcher *defined_symbol_patchers; + struct RuntimeLoaderSymbolPatcher *undefined_symbol_patchers; + // describes the text and data regions uint32 num_regions; elf_region_t regions[1]; @@ -134,4 +141,48 @@ typedef struct runtime_loader_debug_area { image_queue_t *loaded_images; } runtime_loader_debug_area; + +// #pragma mark - runtime loader add-on interface + + +// symbol patcher callback +typedef void runtime_loader_symbol_patcher(void* cookie, + struct image_t* rootImage, struct image_t* image, const char* name, + struct image_t** foundInImage, void** symbol, int32* type); + +// interface provided to add-ons +struct runtime_loader_add_on_export { + status_t (*register_defined_symbol_patcher)(struct image_t* image, + runtime_loader_symbol_patcher* patcher, void* cookie); + void (*unregister_defined_symbol_patcher)(struct image_t* image, + runtime_loader_symbol_patcher* patcher, void* cookie); + status_t (*register_undefined_symbol_patcher)(struct image_t* image, + runtime_loader_symbol_patcher* patcher, void* cookie); + void (*unregister_undefined_symbol_patcher)(struct image_t* image, + runtime_loader_symbol_patcher* patcher, void* cookie); +}; + + +#define RUNTIME_LOADER_ADD_ON_VERSION 1 + +typedef struct runtime_loader_add_on { + uint32 version; + uint32 flags; + + // called after the add-on image has been loaded + void (*init)(struct rld_export* standardInterface, + struct runtime_loader_add_on_export* addOnInterface); + + // called whenever the respective image event occurs + void (*image_loaded)(struct image_t* image); + void (*image_relocated)(struct image_t* image); + void (*image_initialized)(struct image_t* image); + void (*image_uninitializing)(struct image_t* image); + void (*image_unloading)(struct image_t* image); +} runtime_loader_add_on; + +// This is the variable a preloaded shared object has to export to get picked up +// by the runtime loader as an add-on. +extern runtime_loader_add_on __gRuntimeLoaderAddOn; + #endif // _RUNTIME_LOADER_H diff --git a/src/system/runtime_loader/elf.cpp b/src/system/runtime_loader/elf.cpp index df44dee82d..070833dfe4 100644 --- a/src/system/runtime_loader/elf.cpp +++ b/src/system/runtime_loader/elf.cpp @@ -8,7 +8,6 @@ * Distributed under the terms of the NewOS License. */ - #include "runtime_loader_private.h" #include @@ -22,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -36,8 +37,8 @@ #endif -// ToDo: implement better locking strategy -// ToDo: implement lazy binding +// TODO: implement better locking strategy +// TODO: implement lazy binding #define PAGE_MASK (B_PAGE_SIZE - 1) @@ -75,6 +76,47 @@ enum { typedef void (*init_term_function)(image_id); + +// image events +enum { + IMAGE_EVENT_LOADED, + IMAGE_EVENT_RELOCATED, + IMAGE_EVENT_INITIALIZED, + IMAGE_EVENT_UNINITIALIZING, + IMAGE_EVENT_UNLOADING +}; + + +struct RuntimeLoaderAddOn + : public DoublyLinkedListLinkImpl { + image_t* image; + runtime_loader_add_on* addOn; + + RuntimeLoaderAddOn(image_t* image, runtime_loader_add_on* addOn) + : + image(image), + addOn(addOn) + { + } +}; + +typedef DoublyLinkedList AddOnList; + +struct RuntimeLoaderSymbolPatcher { + RuntimeLoaderSymbolPatcher* next; + runtime_loader_symbol_patcher* patcher; + void* cookie; + + RuntimeLoaderSymbolPatcher(runtime_loader_symbol_patcher* patcher, + void* cookie) + : + patcher(patcher), + cookie(cookie) + { + } +}; + + static image_queue_t sLoadedImages = {0, 0}; static image_queue_t sDisposableImages = {0, 0}; static uint32 sLoadedImageCount = 0; @@ -85,12 +127,15 @@ static const char *sSearchPathSubDir = NULL; static bool sInvalidImageIDs; static image_t **sPreloadedImages = NULL; static uint32 sPreloadedImageCount = 0; +static AddOnList sAddOns; // a recursive lock static sem_id sSem; static thread_id sSemOwner; static int32 sSemCount; +extern runtime_loader_add_on_export gRuntimeLoaderAddOnExport; + void dprintf(const char *format, ...) @@ -456,6 +501,18 @@ delete_image_struct(image_t *image) memset(image->needed, 0xa5, sizeof(image->needed[0]) * image->num_needed); #endif free(image->needed); + free(image->symbol_resolution_images); + + while (RuntimeLoaderSymbolPatcher* patcher + = image->defined_symbol_patchers) { + image->defined_symbol_patchers = patcher->next; + delete patcher; + } + while (RuntimeLoaderSymbolPatcher* patcher + = image->undefined_symbol_patchers) { + image->undefined_symbol_patchers = patcher->next; + delete patcher; + } #ifdef DEBUG // overwrite images to make sure they aren't accidently reused anywhere @@ -970,6 +1027,110 @@ parse_dynamic_segment(image_t *image) } +static void +patch_defined_symbol(image_t* image, const char* name, void** symbol, + int32* type) +{ + RuntimeLoaderSymbolPatcher* patcher = image->defined_symbol_patchers; + while (patcher != NULL && *symbol != 0) { + image_t* inImage = image; + patcher->patcher(patcher->cookie, NULL, image, name, &inImage, + symbol, type); + patcher = patcher->next; + } +} + + +static void +patch_undefined_symbol(image_t* rootImage, image_t* image, const char* name, + image_t** foundInImage, void** symbol, int32* type) +{ + if (*foundInImage != NULL) + patch_defined_symbol(*foundInImage, name, symbol, type); + + RuntimeLoaderSymbolPatcher* patcher = image->undefined_symbol_patchers; + while (patcher != NULL) { + patcher->patcher(patcher->cookie, rootImage, image, name, foundInImage, + symbol, type); + patcher = patcher->next; + } +} + + +status_t +register_defined_symbol_patcher(struct image_t* image, + runtime_loader_symbol_patcher* _patcher, void* cookie) +{ + RuntimeLoaderSymbolPatcher* patcher + = new(mynothrow) RuntimeLoaderSymbolPatcher(_patcher, cookie); + if (patcher == NULL) + return B_NO_MEMORY; + + patcher->next = image->defined_symbol_patchers; + image->defined_symbol_patchers = patcher; + + return B_OK; +} + + +void +unregister_defined_symbol_patcher(struct image_t* image, + runtime_loader_symbol_patcher* _patcher, void* cookie) +{ + RuntimeLoaderSymbolPatcher** patcher = &image->defined_symbol_patchers; + while (*patcher != NULL) { + if ((*patcher)->patcher == _patcher && (*patcher)->cookie == cookie) { + RuntimeLoaderSymbolPatcher* toDelete = *patcher; + *patcher = (*patcher)->next; + delete toDelete; + return; + } + patcher = &(*patcher)->next; + } +} + + +status_t +register_undefined_symbol_patcher(struct image_t* image, + runtime_loader_symbol_patcher* _patcher, void* cookie) +{ + RuntimeLoaderSymbolPatcher* patcher + = new(mynothrow) RuntimeLoaderSymbolPatcher(_patcher, cookie); + if (patcher == NULL) + return B_NO_MEMORY; + + patcher->next = image->undefined_symbol_patchers; + image->undefined_symbol_patchers = patcher; + + return B_OK; +} + + +void +unregister_undefined_symbol_patcher(struct image_t* image, + runtime_loader_symbol_patcher* _patcher, void* cookie) +{ + RuntimeLoaderSymbolPatcher** patcher = &image->undefined_symbol_patchers; + while (*patcher != NULL) { + if ((*patcher)->patcher == _patcher && (*patcher)->cookie == cookie) { + RuntimeLoaderSymbolPatcher* toDelete = *patcher; + *patcher = (*patcher)->next; + delete toDelete; + return; + } + patcher = &(*patcher)->next; + } +} + + +runtime_loader_add_on_export gRuntimeLoaderAddOnExport = { + register_defined_symbol_patcher, + unregister_defined_symbol_patcher, + register_undefined_symbol_patcher, + unregister_undefined_symbol_patcher +}; + + static struct Elf32_Sym * find_symbol(image_t *image, const char *name, int32 type) { @@ -1003,6 +1164,25 @@ find_symbol(image_t *image, const char *name, int32 type) } +static status_t +find_symbol(image_t* image, char const* symbolName, int32 symbolType, + void **_location) +{ + // get the symbol in the image + struct Elf32_Sym* symbol = find_symbol(image, symbolName, symbolType); + if (symbol == NULL) + return B_ENTRY_NOT_FOUND; + + void* location = (void*)(symbol->st_value + image->regions[0].delta); + patch_defined_symbol(image, symbolName, &location, &symbolType); + + if (_location != NULL) + *_location = location; + + return B_OK; +} + + static struct Elf32_Sym* find_symbol_in_root_image_list(image_t* rootImage, const char* name, image_t** foundInImage) @@ -1051,7 +1231,7 @@ find_undefined_symbol(image_t* rootImage, image_t* image, const char* name, } -/*! This functions 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. */ static const char* @@ -1062,7 +1242,12 @@ beos_compatibility_map_symbol(const char* symbolName) const char* to; }; static const struct symbol_mapping kMappings[] = { - // TODO: improve this, and also use it for libnet.so compatibility! + // TODO: Improve this, and also use it for libnet.so compatibility! + // Allow an image to provide a function that will be invoked for every + // (transitively) depending image. The function can return a table to + // remap symbols (probably better address to address). All the tables + // for a single image would be combined into a hash table and an + // undefined symbol patcher using this hash table would be added. {"fstat", "__be_fstat"}, {"lstat", "__be_lstat"}, {"stat", "__be_stat"}, @@ -1097,33 +1282,73 @@ resolve_symbol(image_t *rootImage, image_t *image, struct Elf32_Sym *sym, symName = beos_compatibility_map_symbol(symName); } + int32 type = B_SYMBOL_TYPE_ANY; + if (ELF32_ST_TYPE(sym->st_info) == STT_FUNC) + type = B_SYMBOL_TYPE_TEXT; + else if (ELF32_ST_TYPE(sym->st_info) == STT_OBJECT) + type = B_SYMBOL_TYPE_DATA; + // it's undefined, must be outside this image, try the other images sharedSym = find_undefined_symbol(rootImage, image, symName, &sharedImage); - if (sharedSym == NULL) { - FATAL("elf_resolve_symbol: could not resolve symbol '%s'\n", - symName); - return B_MISSING_SYMBOL; - } + void* location = NULL; - // make sure they're the same type - if (ELF32_ST_TYPE(sym->st_info) != STT_NOTYPE + enum { + ERROR_NO_SYMBOL, + ERROR_WRONG_TYPE, + ERROR_NOT_EXPORTED, + ERROR_UNPATCHED + }; + uint32 lookupError = ERROR_UNPATCHED; + + if (sharedSym == NULL) { + // symbol not found at all + lookupError = ERROR_NO_SYMBOL; + sharedImage = NULL; + } else if (ELF32_ST_TYPE(sym->st_info) != STT_NOTYPE && ELF32_ST_TYPE(sym->st_info) != ELF32_ST_TYPE(sharedSym->st_info)) { - FATAL("elf_resolve_symbol: found symbol '%s' in shared image " - "but wrong type\n", symName); - return B_MISSING_SYMBOL; - } - - if (ELF32_ST_BIND(sharedSym->st_info) != STB_GLOBAL + // symbol not of the requested type + lookupError = ERROR_WRONG_TYPE; + sharedImage = NULL; + } else if (ELF32_ST_BIND(sharedSym->st_info) != STB_GLOBAL && ELF32_ST_BIND(sharedSym->st_info) != STB_WEAK) { - FATAL("elf_resolve_symbol: found symbol '%s' but not " - "exported\n", symName); + // symbol not exported + lookupError = ERROR_NOT_EXPORTED; + sharedImage = NULL; + } else { + // symbol is fine, get its location + location = (void*)(sharedSym->st_value + + sharedImage->regions[0].delta); + } + + patch_undefined_symbol(rootImage, image, symName, &sharedImage, + &location, &type); + + if (location == NULL) { + switch (lookupError) { + case ERROR_NO_SYMBOL: + FATAL("elf_resolve_symbol: could not resolve symbol " + "'%s'\n", symName); + break; + case ERROR_WRONG_TYPE: + FATAL("elf_resolve_symbol: found symbol '%s' in shared " + "image but wrong type\n", symName); + break; + case ERROR_NOT_EXPORTED: + FATAL("elf_resolve_symbol: found symbol '%s', but not " + "exported\n", symName); + break; + case ERROR_UNPATCHED: + FATAL("elf_resolve_symbol: found symbol '%s', but was " + "hidden by symbol patchers\n", symName); + break; + } return B_MISSING_SYMBOL; } - *symAddress = sharedSym->st_value + sharedImage->regions[0].delta; - return B_NO_ERROR; + *symAddress = (addr_t)location; + return B_OK; } case SHN_ABS: @@ -1143,6 +1368,37 @@ resolve_symbol(image_t *rootImage, image_t *image, struct Elf32_Sym *sym, } +static void +image_event(image_t* image, uint32 event) +{ + AddOnList::Iterator it = sAddOns.GetIterator(); + while (RuntimeLoaderAddOn* addOn = it.Next()) { + void (*function)(image_t* image) = NULL; + + switch (event) { + case IMAGE_EVENT_LOADED: + function = addOn->addOn->image_loaded; + break; + case IMAGE_EVENT_RELOCATED: + function = addOn->addOn->image_relocated; + break; + case IMAGE_EVENT_INITIALIZED: + function = addOn->addOn->image_initialized; + break; + case IMAGE_EVENT_UNINITIALIZING: + function = addOn->addOn->image_uninitializing; + break; + case IMAGE_EVENT_UNLOADING: + function = addOn->addOn->image_unloading; + break; + } + + if (function != NULL) + function(image); + } +} + + static void register_image(image_t *image, int fd, const char *path) { @@ -1185,6 +1441,7 @@ relocate_image(image_t *rootImage, image_t *image) } _kern_image_relocated(image->id); + image_event(image, IMAGE_EVENT_RELOCATED); return B_OK; } @@ -1357,6 +1614,7 @@ load_container(char const *name, image_type type, const char *rpath, image_t **_ image->type = type; register_image(image, fd, path); + image_event(image, IMAGE_EVENT_LOADED); _kern_close(fd); @@ -1666,6 +1924,8 @@ init_dependencies(image_t *image, bool initHead) if (image->init_routine != 0) ((init_term_function)image->init_routine)(image->id); + + image_event(image, IMAGE_EVENT_INITIALIZED); } TRACE(("%ld: init done.\n", find_thread(NULL))); @@ -1763,6 +2023,18 @@ preload_image(char const* path) remap_images(); init_dependencies(image, true); + // if the image contains an add-on, register it + runtime_loader_add_on* addOnStruct; + if (find_symbol(image, "__gRuntimeLoaderAddOn", B_SYMBOL_TYPE_DATA, + (void**)&addOnStruct) == B_OK) { + RuntimeLoaderAddOn* addOn = new(mynothrow) RuntimeLoaderAddOn(image, + addOnStruct); + if (addOn != NULL) { + sAddOns.Add(addOn); + addOnStruct->init(&gRuntimeLoader, &gRuntimeLoaderAddOnExport); + } + } + KTRACE("rld: preload_image(\"%s\") done: id: %ld", path, image->id); return image->id; @@ -2006,12 +2278,16 @@ unload_library(image_id imageID, bool addOn) image->regions[0].vmstart, image->regions[0].vmsize); } + image_event(image, IMAGE_EVENT_UNINITIALIZING); + if (image->term_routine) ((init_term_function)image->term_routine)(image->id); dequeue_image(&sDisposableImages, image); unmap_image(image); + image_event(image, IMAGE_EVENT_UNLOADING); + delete_image(image); } } @@ -2044,21 +2320,27 @@ get_nth_symbol(image_id imageID, int32 num, char *nameBuffer, struct Elf32_Sym *symbol = &image->syms[j]; if (count == num) { - strlcpy(nameBuffer, SYMNAME(image, symbol), *_nameLength); - *_nameLength = strlen(SYMNAME(image, symbol)); + const char* symbolName = SYMNAME(image, symbol); + strlcpy(nameBuffer, symbolName, *_nameLength); + *_nameLength = strlen(symbolName); - if (_type != NULL) { - // ToDo: check with the return types of that BeOS function - if (ELF32_ST_TYPE(symbol->st_info) == STT_FUNC) - *_type = B_SYMBOL_TYPE_TEXT; - else if (ELF32_ST_TYPE(symbol->st_info) == STT_OBJECT) - *_type = B_SYMBOL_TYPE_DATA; - else - *_type = B_SYMBOL_TYPE_ANY; - } + void* location = (void*)(symbol->st_value + + image->regions[0].delta); + int32 type; + if (ELF32_ST_TYPE(symbol->st_info) == STT_FUNC) + type = B_SYMBOL_TYPE_TEXT; + else if (ELF32_ST_TYPE(symbol->st_info) == STT_OBJECT) + type = B_SYMBOL_TYPE_DATA; + else + type = B_SYMBOL_TYPE_ANY; + // TODO: check with the return types of that BeOS function + patch_defined_symbol(image, symbolName, &location, &type); + + if (_type != NULL) + *_type = type; if (_location != NULL) - *_location = (void *)(symbol->st_value + image->regions[0].delta); + *_location = location; goto out; } count++; @@ -2091,17 +2373,9 @@ get_symbol(image_id imageID, char const *symbolName, int32 symbolType, // get the image from those who have been already initialized image = find_loaded_image_by_id(imageID); - if (image != NULL) { - struct Elf32_Sym *symbol; - - // get the symbol in the image - symbol = find_symbol(image, symbolName, symbolType); - if (symbol) { - if (_location != NULL) - *_location = (void *)(symbol->st_value + image->regions[0].delta); - } else - status = B_ENTRY_NOT_FOUND; - } else + if (image != NULL) + status = find_symbol(image, symbolName, symbolType, _location); + else status = B_BAD_IMAGE_ID; rld_unlock(); @@ -2190,8 +2464,12 @@ terminate_program(void) TRACE(("%ld: term: %s\n", find_thread(NULL), image->name)); + image_event(image, IMAGE_EVENT_UNINITIALIZING); + if (image->term_routine) ((init_term_function)image->term_routine)(image->id); + + image_event(image, IMAGE_EVENT_UNLOADING); } TRACE(("%ld: term done.\n", find_thread(NULL))); @@ -2202,6 +2480,9 @@ terminate_program(void) void rldelf_init(void) { + // invoke static constructors + new(&sAddOns) AddOnList; + sSem = create_sem(1, "runtime loader"); sSemOwner = -1; sSemCount = 0;