* Introduced a new flag for preventing load_dependencies() to be run a

second time for already loaded libraries, when a library/add-on is
  loaded.
* image_t features now both, a path and a name. The name is the DT_SONAME,
  if present, the leaf name otherwise.
* Modified find_image() to compare both path and name, if necessary.
  Added a type mask parameter to specify what image types one is
  interested.
* Refactored open_container(). We now also support DT_RPATH, a dependency
  search path list that can be specified for the dependencies of a shared
  object.
* Extended [un]load_library() to support add-ons.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11685 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2005-03-11 23:36:32 +00:00
parent 0b4af91dad
commit 2d890ffaa2

View File

@ -39,8 +39,6 @@
// ToDo: implement better locking strategy
// ToDo: implement unload_program()
// ToDo: implement load_addon()/unload_addon(): at the very least, we will have to make
// sure that B_ADD_ON_IMAGE is set correctly
// ToDo: implement search paths $LIBRARY_PATH, $ADDON_PATH
// ToDo: implement lazy binding
@ -54,18 +52,27 @@
/* keep in sync with app ldscript */
enum {
RFLAG_RW = 0x0001,
RFLAG_ANON = 0x0002,
RFLAG_RW = 0x0001,
RFLAG_ANON = 0x0002,
RFLAG_SORTED = 0x0400,
RFLAG_SYMBOLIC = 0x0800,
RFLAG_RELOCATED = 0x1000,
RFLAG_PROTECTED = 0x2000,
RFLAG_INITIALIZED = 0x4000,
RFLAG_REMAPPED = 0x8000
RFLAG_SORTED = 0x0400, // also means `initialized'
RFLAG_SYMBOLIC = 0x0800,
RFLAG_RELOCATED = 0x1000,
RFLAG_PROTECTED = 0x2000,
RFLAG_DEPENDENCIES_LOADED = 0x4000,
RFLAG_REMAPPED = 0x8000
};
#define IMAGE_TYPE_TO_MASK(type) (1 << ((type) - 1))
#define ALL_IMAGE_TYPES (IMAGE_TYPE_TO_MASK(B_APP_IMAGE) \
| IMAGE_TYPE_TO_MASK(B_LIBRARY_IMAGE) \
| IMAGE_TYPE_TO_MASK(B_ADD_ON_IMAGE) \
| IMAGE_TYPE_TO_MASK(B_SYSTEM_IMAGE))
#define APP_OR_LIBRARY_TYPE (IMAGE_TYPE_TO_MASK(B_APP_IMAGE) \
| IMAGE_TYPE_TO_MASK(B_LIBRARY_IMAGE))
typedef
struct elf_region_t {
area_id id;
@ -82,6 +89,7 @@ struct elf_region_t {
typedef struct image_t {
// image identification
char path[B_OS_NAME_LENGTH];
char name[B_OS_NAME_LENGTH];
image_id id;
image_type type;
@ -265,24 +273,46 @@ elf_hash(const uchar *name)
static image_t *
find_image(char const *name)
find_image_in_queue(image_queue_t *queue, const char *name, bool isPath,
uint32 typeMask)
{
image_t *iter;
for (iter = gLoadedImages.head; iter; iter = iter->next) {
if (strncmp(iter->name, name, sizeof(iter->name)) == 0)
return iter;
}
for (iter = gLoadingImages.head; iter; iter = iter->next) {
if (strncmp(iter->name, name, sizeof(iter->name)) == 0)
return iter;
if (isPath) {
for (iter = queue->head; iter; iter = iter->next) {
if (strncmp(iter->path, name, sizeof(iter->path)) == 0
&& typeMask & IMAGE_TYPE_TO_MASK(iter->type)) {
return iter;
}
}
} else {
for (iter = queue->head; iter; iter = iter->next) {
if (strncmp(iter->name, name, sizeof(iter->path)) == 0
&& typeMask & IMAGE_TYPE_TO_MASK(iter->type)) {
return iter;
}
}
}
return NULL;
}
static image_t *
find_image(char const *name, uint32 typeMask)
{
bool isPath = (strchr(name, '/') != NULL);
image_t *image;
image = find_image_in_queue(&gLoadedImages, name, isPath, typeMask);
if (!image)
image = find_image_in_queue(&gLoadingImages, name, isPath, typeMask);
return image;
}
static image_t *
find_loaded_image_by_id(image_id id)
{
@ -376,17 +406,26 @@ count_regions(char const *buff, int phnum, int phentsize)
*/
static image_t *
create_image(char const *name, int num_regions)
create_image(const char *name, const char *path, int num_regions)
{
size_t allocSize;
image_t *image;
const char *lastSlash;
allocSize = sizeof(image_t) + (num_regions - 1) * sizeof(elf_region_t);
image = rldalloc(allocSize);
memset(image, 0, allocSize);
strlcpy(image->name, name, sizeof(image->name));
strlcpy(image->path, path, sizeof(image->path));
// Make the last component of the supplied name the image name.
// If present, DT_SONAME will replace this name.
if ((lastSlash = strrchr(name, '/')))
strlcpy(image->name, lastSlash + 1, sizeof(image->name));
else
strlcpy(image->name, name, sizeof(image->name));
image->ref_count = 1;
image->num_regions = num_regions;
@ -666,6 +705,7 @@ parse_dynamic_segment(image_t *image)
{
struct Elf32_Dyn *d;
int i;
int sonameOffset = -1;
image->symhash = 0;
image->syms = 0;
@ -714,6 +754,9 @@ parse_dynamic_segment(image_t *image)
case DT_FINI:
image->term_routine = (d[i].d_un.d_ptr + image->regions[0].delta);
break;
case DT_SONAME:
sonameOffset = d[i].d_un.d_val;
break;
default:
continue;
}
@ -723,6 +766,9 @@ parse_dynamic_segment(image_t *image)
if (!image->symhash || !image->syms || !image->strtab)
return false;
if (sonameOffset >= 0)
strlcpy(image->name, STRING(image, sonameOffset), sizeof(image->name));
return true;
}
@ -914,7 +960,9 @@ search_path_for_type(image_type type)
return "%A/lib:/boot/home/config/lib:/boot/beos/system/lib";
case B_ADD_ON_IMAGE:
return "%A/lib:/boot/home/config/lib:/boot/beos/system/lib";
return "%A/add-ons"
":/boot/home/config/add-ons"
":/boot/beos/system/add-ons";
default:
return NULL;
@ -923,73 +971,132 @@ search_path_for_type(image_type type)
static int
open_container(char *name, image_type type)
try_open_container(const char *dir, int dirLen, const char *name, char *path,
int pathLen)
{
int nameLen = strlen(name);
// construct the path
if (dirLen > 0) {
char *buffer = path;
if (dirLen >= 2 && strncmp(dir, "%A", 2) == 0) {
// Replace %A with current app folder path (of course,
// this must be the first part of the path)
// ToDo: Maybe using first image info is better suited than
// gProgamArgs->program_path here?
char *lastSlash = strrchr(gProgramArgs->program_path, '/');
int bytesCopied;
// copy what's left (when the application name is removed)
if (lastSlash != NULL) {
strlcpy(buffer, gProgramArgs->program_path,
min(pathLen, lastSlash + 1 - gProgramArgs->program_path));
} else
strlcpy(buffer, ".", pathLen);
bytesCopied = strlen(buffer);
buffer += bytesCopied;
pathLen -= bytesCopied;
dir += 2;
dirLen -= 2;
}
if (dirLen + 1 + nameLen >= pathLen)
return B_NAME_TOO_LONG;
memcpy(buffer, dir, dirLen);
buffer[dirLen] = '/';
strcpy(buffer + dirLen + 1, name);
} else {
if (nameLen >= pathLen)
return B_NAME_TOO_LONG;
strcpy(path + dirLen + 1, name);
}
TRACE(("rld.so: try_open_container(): %s\n", path));
return _kern_open(-1, path, O_RDONLY, 0);
}
static int
search_container_in_path_list(const char *name, const char *pathList,
int pathListLen, char *pathBuffer, int pathBufferLen)
{
const char *pathListEnd = pathList + pathListLen;
TRACE(("rld.so: search_container_in_path_list() %s in %.*s\n", name,
pathListLen, pathList));
while (pathListLen > 0) {
const char *pathEnd = pathList;
int fd;
// find the next ':' or run till the end of the string
while (pathEnd < pathListEnd && *pathEnd != ':')
pathEnd++;
fd = try_open_container(pathList, pathEnd - pathList, name, pathBuffer,
pathBufferLen);
if (fd >= 0)
return fd;
pathListLen = pathListEnd - pathEnd - 1;
pathList = pathEnd + 1;
}
return B_ENTRY_NOT_FOUND;
}
static int
open_container(char *name, image_type type, const char *rpath)
{
char searchPath[PATH_MAX];
const char *paths;
char *path;
char *nextPathToken = NULL;
char buffer[PATH_MAX];
int fd = -1;
if (strchr(name, '/')) {
// the name already contains a path, we don't have to search for it
return _kern_open(-1, name, O_RDONLY, 0);
}
// first try rpath (DT_RPATH)
if (rpath) {
// It consists of a colon-separated search path list. Optionally it
// follows a second search path list, separated from the first by a
// semicolon.
const char *semicolon = strchr(rpath, ';');
const char *firstList = (semicolon ? rpath : NULL);
const char *secondList = (semicolon ? semicolon + 1 : rpath);
// If there is no ';', we set only secondList to simplify things.
if (firstList) {
fd = search_container_in_path_list(name, firstList,
semicolon - firstList, buffer, sizeof(buffer));
}
if (fd < 0) {
fd = search_container_in_path_list(name, secondList,
strlen(secondList), buffer, sizeof(buffer));
}
}
// let's evaluate the system path variables to find the container
paths = search_path_for_type(type);
if (paths == NULL)
return B_ENTRY_NOT_FOUND;
// duplicate environment variable before screw it!
strlcpy(searchPath, paths, PATH_MAX);
// ToDo: PATH_MAX is definitely too short for a list of paths.
// Better roll our own non-destructive strtok_r() replacement for this
// special case, so that the string doesn't need to be copied.
// Alternatively use strdup().
TRACE(("rld.so: open_container() %s in %s\n", name, searchPath));
path = strtok_r(searchPath, ":", &nextPathToken);
while (path != NULL) {
char buffer[PATH_MAX + 1];
int fd;
if (strncmp(path, "%A", 2) == 0) {
// Replace %A with current app folder path (of course,
// this must be the first part of the path)
// ToDo: Maybe using first image info is better suited than gProgamArgs->program_path here?
char *lastSlash = strrchr(gProgramArgs->program_path, '/');
// copy what's left (when the application name is removed)
if (lastSlash != NULL) {
strlcpy(buffer, gProgramArgs->program_path,
min(PATH_MAX, lastSlash + 1 - gProgramArgs->program_path));
} else
strlcpy(buffer, ".", PATH_MAX);
strlcat(buffer, path + 2, PATH_MAX);
} else {
// Take the path as-is
strlcpy(buffer, path, PATH_MAX);
if (fd < 0) {
paths = search_path_for_type(type);
if (paths) {
fd = search_container_in_path_list(name, paths, strlen(paths),
buffer, sizeof(buffer));
}
}
strlcat(buffer, "/", PATH_MAX);
// Several slashes in sequence will be ignored, so we're playing safe and add one more
strlcat(buffer, name, PATH_MAX);
TRACE(("rld.so: open_container(%s): trying %s\n", name, buffer));
fd = _kern_open(-1, buffer, O_RDONLY, 0);
if (fd >= B_OK) {
// we found it, copy path!
TRACE(("rld.so: open_container(%s): found at %s\n", name, buffer));
strlcpy(name, buffer, PATH_MAX);
return fd;
}
// Try next search path
path = strtok_r(NULL, ":", &nextPathToken);
if (fd >= 0) {
// we found it, copy path!
TRACE(("rld.so: open_container(%s): found at %s\n", name, buffer));
strlcpy(name, buffer, PATH_MAX);
return fd;
}
return B_ENTRY_NOT_FOUND;
@ -997,7 +1104,7 @@ open_container(char *name, image_type type)
static image_t *
load_container(char const *containerPath, char const *name, image_type type)
load_container(char const *name, image_type type, const char *rpath)
{
int32 pheaderSize, sheaderSize;
char path[PATH_MAX];
@ -1012,17 +1119,20 @@ load_container(char const *containerPath, char const *name, image_type type)
struct Elf32_Ehdr eheader;
// have we already loaded that image?
found = find_image(name);
if (found) {
atomic_add(&found->ref_count, 1);
return found;
// Have we already loaded that image? Don't check for add-ons -- we always
// reload them.
if (type != B_ADD_ON_IMAGE) {
found = find_image(name, APP_OR_LIBRARY_TYPE);
if (found) {
atomic_add(&found->ref_count, 1);
return found;
}
}
strlcpy(path, containerPath, sizeof(path));
strlcpy(path, name, sizeof(path));
// Try to load explicit image path first
fd = open_container(path, type);
fd = open_container(path, type, rpath);
FATAL((fd < 0), "cannot open file %s\n", path);
len = _kern_read(fd, 0, &eheader, sizeof(eheader));
@ -1040,7 +1150,7 @@ load_container(char const *containerPath, char const *name, image_type type)
num_regions = count_regions(ph_buff, eheader.e_phnum, eheader.e_phentsize);
FATAL((num_regions <= 0), "troubles parsing Program headers, num_regions= %d\n", num_regions);
image = create_image(name, num_regions);
image = create_image(name, path, num_regions);
FATAL((!image), "failed to allocate image_t control block\n");
parse_program_headers(image, ph_buff, eheader.e_phnum, eheader.e_phentsize);
@ -1064,16 +1174,36 @@ load_container(char const *containerPath, char const *name, image_type type)
}
static const char *
find_dt_rpath(image_t *image)
{
int i;
struct Elf32_Dyn *d = (struct Elf32_Dyn *)image->dynamic_ptr;
for (i = 0; d[i].d_tag != DT_NULL; i++) {
if (d[i].d_tag == DT_RPATH)
return STRING(image, d[i].d_un.d_val);
}
return NULL;
}
static void
load_dependencies(image_t *image)
{
struct Elf32_Dyn *d = (struct Elf32_Dyn *)image->dynamic_ptr;
addr_t needed_offset;
int needed_offset;
uint32 i, j;
const char *rpath;
if (!d)
if (!d || (image->flags & RFLAG_DEPENDENCIES_LOADED))
return;
image->flags |= RFLAG_DEPENDENCIES_LOADED;
rpath = find_dt_rpath(image);
image->needed = rldalloc(image->num_needed * sizeof(image_t *));
FATAL((!image->needed), "failed to allocate needed struct\n");
memset(image->needed, 0, image->num_needed * sizeof(image_t *));
@ -1081,9 +1211,9 @@ load_dependencies(image_t *image)
for (i = 0, j = 0; d[i].d_tag != DT_NULL; i++) {
switch (d[i].d_tag) {
case DT_NEEDED:
needed_offset = d[i].d_un.d_ptr;
needed_offset = d[i].d_un.d_val;
image->needed[j] = load_container(STRING(image, needed_offset),
STRING(image, needed_offset), B_LIBRARY_IMAGE);
B_LIBRARY_IMAGE, rpath);
j += 1;
break;
@ -1197,7 +1327,7 @@ load_program(char const *path, void **_entry)
TRACE(("rld: load %s\n", path));
image = load_container(path, MAGIC_APP_NAME, B_APP_IMAGE);
image = load_container(path, B_APP_IMAGE, NULL);
for (iter = gLoadedImages.head; iter; iter = iter->next) {
load_dependencies(iter);
@ -1220,10 +1350,11 @@ load_program(char const *path, void **_entry)
image_id
load_library(char const *path, uint32 flags)
load_library(char const *path, uint32 flags, bool addOn)
{
image_t *image;
image_t *image = NULL;
image_t *iter;
image_type type = (addOn ? B_ADD_ON_IMAGE : B_LIBRARY_IMAGE);
// ToDo: implement flags
(void)flags;
@ -1233,15 +1364,16 @@ load_library(char const *path, uint32 flags)
// have we already loaded this library?
// Checking it at this stage saves loading its dependencies again
// ToDo: don't we have to increment the reference counter of the dependencies??
image = find_image(path);
if (image) {
atomic_add(&image->ref_count, 1);
rld_unlock();
return image->id;
if (!addOn) {
image = find_image(path, APP_OR_LIBRARY_TYPE);
if (image) {
atomic_add(&image->ref_count, 1);
rld_unlock();
return image->id;
}
}
image = load_container(path, path, B_LIBRARY_IMAGE);
image = load_container(path, type, NULL);
for (iter = gLoadedImages.head; iter; iter = iter->next) {
load_dependencies(iter);
@ -1261,10 +1393,11 @@ load_library(char const *path, uint32 flags)
status_t
unload_library(image_id imageID)
unload_library(image_id imageID, bool addOn)
{
status_t status;
status_t status = B_BAD_IMAGE_ID;
image_t *image;
image_type type = (addOn ? B_ADD_ON_IMAGE : B_LIBRARY_IMAGE);
rld_lock();
// for now, just do stupid simple global locking
@ -1278,22 +1411,26 @@ unload_library(image_id imageID)
/*
* do the unloading
*/
put_image(image);
if (type == image->type) {
put_image(image);
status = B_OK;
} else
status = B_BAD_VALUE;
break;
}
}
status = image ? B_OK : B_BAD_IMAGE_ID;
while ((image = gDisposableImages.head) != NULL) {
// call image fini here...
if (image->term_routine)
((libinit_f *)image->term_routine)(image->id, gProgramArgs);
dequeue_image(&gDisposableImages, image);
unmap_image(image);
delete_image(image);
if (status == B_OK) {
while ((image = gDisposableImages.head) != NULL) {
// call image fini here...
if (image->term_routine)
((libinit_f *)image->term_routine)(image->id, gProgramArgs);
dequeue_image(&gDisposableImages, image);
unmap_image(image);
delete_image(image);
}
}
rld_unlock();