diff options
author | Dale Rahn <drahn@cvs.openbsd.org> | 2004-08-11 19:14:57 +0000 |
---|---|---|
committer | Dale Rahn <drahn@cvs.openbsd.org> | 2004-08-11 19:14:57 +0000 |
commit | 790e0511d8b6629c85ac959d63eac9fe4c7caacc (patch) | |
tree | a773d637df2fb3bf669f7ff2c16983fc3344e417 /libexec/ld.so | |
parent | fec58ae8c4bc4002d602ce897ee4f88767e23679 (diff) |
add dladdr() support and add some 'standard' dlsym() support.
ok millert miod pval, grumble deraadt
Diffstat (limited to 'libexec/ld.so')
-rw-r--r-- | libexec/ld.so/dlfcn.c | 167 | ||||
-rw-r--r-- | libexec/ld.so/resolve.h | 4 |
2 files changed, 161 insertions, 10 deletions
diff --git a/libexec/ld.so/dlfcn.c b/libexec/ld.so/dlfcn.c index ede4a44aa5a..9f4ce2c614b 100644 --- a/libexec/ld.so/dlfcn.c +++ b/libexec/ld.so/dlfcn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dlfcn.c,v 1.38 2004/06/07 15:18:19 mickey Exp $ */ +/* $OpenBSD: dlfcn.c,v 1.39 2004/08/11 19:14:56 drahn Exp $ */ /* * Copyright (c) 1998 Per Fogelstrom, Opsycon AB @@ -45,6 +45,7 @@ static void _dl_unload_deps(elf_object_t *object); static void _dl_thread_kern_stop(void); static void _dl_thread_kern_go(void); static void (*_dl_thread_fnc)(int) = NULL; +static elf_object_t *obj_from_addr(const void *addr); void * dlopen(const char *libname, int flags) @@ -129,19 +130,47 @@ dlsym(void *handle, const char *name) elf_object_t *dynobj; void *retval; const Elf_Sym *sym = NULL; + int flags; - object = (elf_object_t *)handle; - dynobj = _dl_objects; - while (dynobj && dynobj != object) - dynobj = dynobj->next; + if (handle == NULL || handle == RTLD_NEXT || + handle == RTLD_SELF) { + void *retaddr; - if (!dynobj || object != dynobj) { - _dl_errno = DL_INVALID_HANDLE; - return(0); + retaddr = __builtin_return_address(0); /* __GNUC__ only */ + + if ((object = obj_from_addr(retaddr)) == NULL) { + _dl_errno = DL_CANT_FIND_OBJ; + return(0); + } + + if (handle == RTLD_NEXT) + object = object->next; + + if (handle == NULL) + flags = SYM_SEARCH_SELF|SYM_PLT; + else + flags = SYM_SEARCH_ALL|SYM_PLT; + + } else if (handle == RTLD_DEFAULT) { + object = _dl_objects; + flags = SYM_SEARCH_ALL|SYM_PLT; + } else { + object = (elf_object_t *)handle; + flags = SYM_SEARCH_SELF|SYM_NOTPLT; + + dynobj = _dl_objects; + while (dynobj && dynobj != object) + dynobj = dynobj->next; + + if (!dynobj || object != dynobj) { + _dl_errno = DL_INVALID_HANDLE; + return(0); + } } retval = (void *)_dl_find_symbol(name, object, &sym, NULL, - SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_NOTPLT, 0, object); + flags|SYM_NOWARNNOTFOUND, 0, object); + if (sym != NULL) { retval += sym->st_value; #ifdef __hppa__ @@ -274,6 +303,12 @@ dlerror(void) case DL_INVALID_CTL: errmsg = "Invalid dlctl() command"; break; + case DL_NO_OBJECT: + errmsg = "No shared object contains address"; + break; + case DL_CANT_FIND_OBJ: + errmsg = "Cannot determine caller's shared object"; + break; default: errmsg = "Unknown error"; } @@ -351,3 +386,117 @@ _dl_thread_kern_go(void) (*_dl_thread_fnc)(1); } +static elf_object_t * +obj_from_addr(const void *addr) +{ + elf_object_t *dynobj; + Elf_Ehdr *ehdr; + Elf_Phdr *phdr; + Elf_Addr start; + Elf_Addr end; + u_int32_t symoffset; + const Elf_Sym *sym; + int i; + + for (dynobj = _dl_objects; dynobj != NULL; dynobj = dynobj->next) { + ehdr = (Elf_Ehdr *)dynobj->load_addr; + if (ehdr == NULL) + continue; + + phdr = (Elf_Phdr *)((char *)dynobj->load_addr + ehdr->e_phoff); + + for (i = 0; i < ehdr->e_phnum; i++) { + switch (phdr[i].p_type) { + case PT_LOAD: + start = phdr[i].p_vaddr + dynobj->load_addr; + if ((Elf_Addr)addr >= start && + (Elf_Addr)addr < start + phdr[i].p_memsz) + return dynobj; + break; + default: + break; + } + } + } + + /* find the lowest & highest symbol address in the main exe */ + start = -1; + end = 0; + + for (symoffset = 0; symoffset < _dl_objects->nchains; symoffset++) { + sym = _dl_objects->dyn.symtab + symoffset; + + /* + * For skip the symbol if st_shndx is either SHN_UNDEF or + * SHN_COMMON. + */ + if (sym->st_shndx == SHN_UNDEF || sym->st_shndx == SHN_COMMON) + continue; + + if (sym->st_value < start) + start = sym->st_value; + + if (sym->st_value > end) + end = sym->st_value; + } + + if (end && addr >= start && addr <= end) + return _dl_objects; + else + return NULL; +} + +int +dladdr(const void *addr, Dl_info *info) +{ + const elf_object_t *object; + const Elf_Sym *sym; + void *symbol_addr; + u_int32_t symoffset; + + object = obj_from_addr(addr); + + if (object == NULL) { + _dl_errno = DL_NO_OBJECT; + return 0; + } + + info->dli_fname = (char *)object->load_name; + info->dli_fbase = (void *)object->load_addr; + info->dli_sname = NULL; + info->dli_saddr = (void *)0; + + /* + * Walk the symbol list looking for the symbol whose address is + * closest to the address sent in. + */ + for (symoffset = 0; symoffset < object->nchains; symoffset++) { + sym = object->dyn.symtab + symoffset; + + /* + * For skip the symbol if st_shndx is either SHN_UNDEF or + * SHN_COMMON. + */ + if (sym->st_shndx == SHN_UNDEF || sym->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 = (void *)(object->load_addr + sym->st_value); + if (symbol_addr > addr || symbol_addr < info->dli_saddr) + continue; + + /* Update our idea of the nearest symbol. */ + info->dli_sname = object->dyn.strtab + sym->st_name; + info->dli_saddr = symbol_addr; + + /* Exact match? */ + if (info->dli_saddr == addr) + break; + } + + return 1; +} diff --git a/libexec/ld.so/resolve.h b/libexec/ld.so/resolve.h index 1f949b7e7e3..32bc4fa06ba 100644 --- a/libexec/ld.so/resolve.h +++ b/libexec/ld.so/resolve.h @@ -1,4 +1,4 @@ -/* $OpenBSD: resolve.h,v 1.31 2004/07/05 00:47:40 kjell Exp $ */ +/* $OpenBSD: resolve.h,v 1.32 2004/08/11 19:14:56 drahn Exp $ */ /* * Copyright (c) 1998 Per Fogelstrom, Opsycon AB @@ -213,6 +213,8 @@ extern char *_dl_debug; #define DL_NO_SYMBOL 6 #define DL_INVALID_HANDLE 7 #define DL_INVALID_CTL 8 +#define DL_NO_OBJECT 9 +#define DL_CANT_FIND_OBJ 10 #define ELF_ROUND(x,malign) (((x) + (malign)-1) & ~((malign)-1)) #define ELF_TRUNC(x,malign) ((x) & ~((malign)-1)) |