📄 elf.c
字号:
elm = STAILQ_FIRST(list);
STAILQ_REMOVE_HEAD(list, link);
if (elm->obj->refcount == 0)
free(elm);
else
STAILQ_INSERT_TAIL(&newlist, elm, link);
}
*list = newlist;
}
#endif /* 0 */
/*
* Relocate newly-loaded shared objects. The argument is a pointer to
* the Obj_Entry for the first such object. All objects from the first
* to the end of the list of objects are relocated. Returns 0 on success,
* or -1 on failure.
*/
static int
relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj)
{
Obj_Entry *obj;
for (obj = first; obj != NULL; obj = obj->next) {
if (obj != rtldobj)
dbg("relocating \"%s\"", obj->path);
if (obj->nbuckets == 0 || obj->nchains == 0 || obj->buckets == NULL ||
obj->symtab == NULL || obj->strtab == NULL) {
_rtld_error("%s: Shared object has no run-time symbol table",
obj->path);
return -1;
}
if (obj->textrel) {
/* There are relocations to the write-protected text segment. */
if (mprotect(obj->mapbase, obj->textsize,
PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
_rtld_error("%s: Cannot write-enable text segment: %s",
obj->path, strerror(errno));
return -1;
}
}
/* Process the non-PLT relocations. */
if (reloc_non_plt(obj, rtldobj))
return -1;
if (obj->textrel) { /* Re-protected the text segment. */
if (mprotect(obj->mapbase, obj->textsize,
PROT_READ|PROT_EXEC) == -1) {
_rtld_error("%s: Cannot write-protect text segment: %s",
obj->path, strerror(errno));
return -1;
}
}
/* Process the PLT relocations. */
if (reloc_plt(obj) == -1)
return -1;
/* Relocate the jump slots if we are doing immediate binding. */
if (obj->bind_now || bind_now)
if (reloc_jmpslots(obj) == -1)
return -1;
/*
* Set up the magic number and version in the Obj_Entry. These
* were checked in the crt1.o from the original ElfKit, so we
* set them for backward compatibility.
*/
obj->magic = RTLD_MAGIC;
obj->version = RTLD_VERSION;
/* Set the special PLT or GOT entries. */
init_pltgot(obj);
}
return 0;
}
#if 0
/*
* Cleanup procedure. It will be called (by the atexit mechanism) just
* before the process exits.
*/
static void
rtld_exit(void)
{
Obj_Entry *obj;
dbg("rtld_exit()");
/* Clear all the reference counts so the fini functions will be called. */
for (obj = obj_list; obj != NULL; obj = obj->next)
obj->refcount = 0;
objlist_call_fini(&list_fini);
/* No need to remove the items from the list, since we are exiting. */
if (!libmap_disable)
lm_fini();
}
static void *
path_enumerate(const char *path, path_enum_proc callback, void *arg)
{
#ifdef COMPAT_32BIT
const char *trans;
#endif
if (path == NULL)
return (NULL);
path += strspn(path, ":;");
while (*path != '\0') {
size_t len;
char *res;
len = strcspn(path, ":;");
#ifdef COMPAT_32BIT
trans = lm_findn(NULL, path, len);
if (trans)
res = callback(trans, strlen(trans), arg);
else
#endif
res = callback(path, len, arg);
if (res != NULL)
return (res);
path += len;
path += strspn(path, ":;");
}
return (NULL);
}
struct try_library_args {
const char *name;
size_t namelen;
char *buffer;
size_t buflen;
};
static void *
try_library_path(const char *dir, size_t dirlen, void *param)
{
struct try_library_args *arg;
arg = param;
if (*dir == '/' || trust) {
char *pathname;
if (dirlen + 1 + arg->namelen + 1 > arg->buflen)
return (NULL);
pathname = arg->buffer;
strncpy(pathname, dir, dirlen);
pathname[dirlen] = '/';
strcpy(pathname + dirlen + 1, arg->name);
dbg(" Trying \"%s\"", pathname);
if (access(pathname, F_OK) == 0) { /* We found it */
pathname = xmalloc(dirlen + 1 + arg->namelen + 1);
strcpy(pathname, arg->buffer);
return (pathname);
}
}
return (NULL);
}
static char *
search_library_path(const char *name, const char *path)
{
char *p;
struct try_library_args arg;
if (path == NULL)
return NULL;
arg.name = name;
arg.namelen = strlen(name);
arg.buffer = xmalloc(PATH_MAX);
arg.buflen = PATH_MAX;
p = path_enumerate(path, try_library_path, &arg);
free(arg.buffer);
return (p);
}
int
dlclose(void *handle)
{
Obj_Entry *root;
int lockstate;
lockstate = wlock_acquire(rtld_bind_lock);
root = dlcheck(handle);
if (root == NULL) {
wlock_release(rtld_bind_lock, lockstate);
return -1;
}
/* Unreference the object and its dependencies. */
root->dl_refcount--;
unref_dag(root);
if (root->refcount == 0) {
/*
* The object is no longer referenced, so we must unload it.
* First, call the fini functions with no locks held.
*/
wlock_release(rtld_bind_lock, lockstate);
objlist_call_fini(&list_fini);
lockstate = wlock_acquire(rtld_bind_lock);
objlist_remove_unref(&list_fini);
/* Finish cleaning up the newly-unreferenced objects. */
GDB_STATE(RT_DELETE,&root->linkmap);
unload_object(root);
GDB_STATE(RT_CONSISTENT,NULL);
}
wlock_release(rtld_bind_lock, lockstate);
return 0;
}
const char *
dlerror(void)
{
char *msg = error_message;
error_message = NULL;
return msg;
}
/*
* This function is deprecated and has no effect.
*/
void
dllockinit(void *context,
void *(*lock_create)(void *context),
void (*rlock_acquire)(void *lock),
void (*wlock_acquire)(void *lock),
void (*lock_release)(void *lock),
void (*lock_destroy)(void *lock),
void (*context_destroy)(void *context))
{
static void *cur_context;
static void (*cur_context_destroy)(void *);
/* Just destroy the context from the previous call, if necessary. */
if (cur_context_destroy != NULL)
cur_context_destroy(cur_context);
cur_context = context;
cur_context_destroy = context_destroy;
}
void *
dlopen(const char *name, int mode)
{
Obj_Entry **old_obj_tail;
Obj_Entry *obj;
Objlist initlist;
int result, lockstate;
ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1";
if (ld_tracing != NULL)
environ = (char **)*get_program_var_addr("environ");
objlist_init(&initlist);
lockstate = wlock_acquire(rtld_bind_lock);
GDB_STATE(RT_ADD,NULL);
old_obj_tail = obj_tail;
obj = NULL;
if (name == NULL) {
obj = obj_main;
obj->refcount++;
} else {
char *path = find_library(name, obj_main);
if (path != NULL)
obj = load_object(path);
}
if (obj) {
obj->dl_refcount++;
if (mode & RTLD_GLOBAL && objlist_find(&list_global, obj) == NULL)
objlist_push_tail(&list_global, obj);
mode &= RTLD_MODEMASK;
if (*old_obj_tail != NULL) { /* We loaded something new. */
assert(*old_obj_tail == obj);
result = load_needed_objects(obj);
if (result != -1 && ld_tracing)
goto trace;
if (result == -1 ||
(init_dag(obj), relocate_objects(obj, mode == RTLD_NOW,
&obj_rtld)) == -1) {
obj->dl_refcount--;
unref_dag(obj);
if (obj->refcount == 0)
unload_object(obj);
obj = NULL;
} else {
/* Make list of init functions to call. */
initlist_add_objects(obj, &obj->next, &initlist);
}
} else {
/* Bump the reference counts for objects on this DAG. */
ref_dag(obj);
if (ld_tracing)
goto trace;
}
}
GDB_STATE(RT_CONSISTENT,obj ? &obj->linkmap : NULL);
/* Call the init functions with no locks held. */
wlock_release(rtld_bind_lock, lockstate);
objlist_call_init(&initlist);
lockstate = wlock_acquire(rtld_bind_lock);
objlist_clear(&initlist);
wlock_release(rtld_bind_lock, lockstate);
return obj;
trace:
trace_loaded_objects(obj);
wlock_release(rtld_bind_lock, lockstate);
exit(0);
}
void *
dlsym(void *handle, const char *name)
{
const Obj_Entry *obj;
unsigned long hash;
const Elf_Sym *def;
const Obj_Entry *defobj;
int lockstate;
hash = elf_hash(name);
def = NULL;
defobj = NULL;
lockstate = rlock_acquire(rtld_bind_lock);
if (handle == NULL || handle == RTLD_NEXT ||
handle == RTLD_DEFAULT || handle == RTLD_SELF) {
void *retaddr;
retaddr = __builtin_return_address(0); /* __GNUC__ only */
if ((obj = obj_from_addr(retaddr)) == NULL) {
_rtld_error("Cannot determine caller's shared object");
rlock_release(rtld_bind_lock, lockstate);
return NULL;
}
if (handle == NULL) { /* Just the caller's shared object. */
def = symlook_obj(name, hash, obj, true);
defobj = obj;
} else if (handle == RTLD_NEXT || /* Objects after caller's */
handle == RTLD_SELF) { /* ... caller included */
if (handle == RTLD_NEXT)
obj = obj->next;
for (; obj != NULL; obj = obj->next) {
if ((def = symlook_obj(name, hash, obj, true)) != NULL) {
defobj = obj;
break;
}
}
} else {
assert(handle == RTLD_DEFAULT);
def = symlook_default(name, hash, obj, &defobj, true);
}
} else {
if ((obj = dlcheck(handle)) == NULL) {
rlock_release(rtld_bind_lock, lockstate);
return NULL;
}
if (obj->mainprog) {
DoneList donelist;
/* Search main program and all libraries loaded by it. */
donelist_init(&donelist);
def = symlook_list(name, hash, &list_main, &defobj, true,
&donelist);
} else {
/*
* XXX - This isn't correct. The search should include the whole
* DAG rooted at the given object.
*/
def = symlook_obj(name, hash, obj, true);
defobj = obj;
}
}
if (def != NULL) {
rlock_release(rtld_bind_lock, lockstate);
/*
* The value required by the caller is derived from the value
* of the symbol. For the ia64 architecture, we need to
* construct a function descriptor which the caller can use to
* call the function with the right 'gp' value. For other
* architectures and for non-functions, the value is simply
* the relocated value of the symbol.
*/
if (ELF_ST_TYPE(def->st_info) == STT_FUNC)
return make_function_pointer(def, defobj);
else
return defobj->relocbase + def->st_value;
}
_rtld_error("Undefined symbol \"%s\"", name);
rlock_release(rtld_bind_lock, lockstate);
return NULL;
}
int
dladdr(const void *addr, Dl_info *info)
{
const Obj_Entry *obj;
const Elf_Sym *def;
void *symbol_addr;
unsigned long symoffset;
int lockstate;
lockstate = rlock_acquire(rtld_bind_lock);
obj = obj_from_addr(addr);
if (obj == NULL) {
_rtld_error("No shared object contains address");
rlock_release(rtld_bind_lock, lockstate);
return 0;
}
info->dli_fname = obj->path;
info->dli_fbase = obj->mapbase;
info->dli_saddr = (void *)0;
info->dli_sname = NULL;
/*
* Walk the symbol list looking for the symbol whose address is
* closest to the address sent in.
*/
for (symoffset = 0; symoffset < obj->nchains; symoffset++) {
def = obj->symtab + symoffset;
/*
* For skip the symbol if st_shndx is either SHN_UNDEF or
* SHN_COMMON.
*/
if (def->st_shndx == SHN_UNDEF || def->st_shndx == SHN_COMMON)
continue;
/*
* If the symbol is greater than the specified address, or if it
* is further away from addr than the current nearest symbol,
* then reject it.
*/
symbol_addr = obj->relocbase + def->st_value;
if (symbol_addr > addr || symbol_addr < info->dli_saddr)
continue;
/* Update our idea of the nearest symbol. */
info->dli_sname = obj->strtab + def->st_name;
info->dli_saddr = symbol_addr;
/* Exact match? */
if (info->dli_saddr == addr)
break;
}
rlock_release(rtld_bind_lock, lockstate);
return 1;
}
int
dlinfo(void *handle, int request, void *p)
{
const Obj_Entry *obj;
int error, lockstate;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -