* 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
This commit is contained in:
Ingo Weinhold 2008-11-03 13:27:14 +00:00
parent 56eb43e3ca
commit 10b4b5d175
2 changed files with 377 additions and 45 deletions

View File

@ -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

View File

@ -8,7 +8,6 @@
* Distributed under the terms of the NewOS License.
*/
#include "runtime_loader_private.h"
#include <ctype.h>
@ -22,6 +21,8 @@
#include <runtime_loader.h>
#include <syscalls.h>
#include <user_runtime.h>
#include <util/DoublyLinkedList.h>
#include <util/kernel_cpp.h>
#include <util/KMessage.h>
#include <vm_defs.h>
@ -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<RuntimeLoaderAddOn> {
image_t* image;
runtime_loader_add_on* addOn;
RuntimeLoaderAddOn(image_t* image, runtime_loader_add_on* addOn)
:
image(image),
addOn(addOn)
{
}
};
typedef DoublyLinkedList<RuntimeLoaderAddOn> 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;