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 | |
parent | fec58ae8c4bc4002d602ce897ee4f88767e23679 (diff) |
add dladdr() support and add some 'standard' dlsym() support.
ok millert miod pval, grumble deraadt
-rw-r--r-- | include/dlfcn.h | 20 | ||||
-rw-r--r-- | lib/libc/dlfcn/dlfcn_stubs.c | 10 | ||||
-rw-r--r-- | lib/libc/shlib_version | 2 | ||||
-rw-r--r-- | libexec/ld.so/dlfcn.c | 167 | ||||
-rw-r--r-- | libexec/ld.so/resolve.h | 4 | ||||
-rw-r--r-- | share/man/man3/Makefile | 4 | ||||
-rw-r--r-- | share/man/man3/dlfcn.3 | 97 |
7 files changed, 288 insertions, 16 deletions
diff --git a/include/dlfcn.h b/include/dlfcn.h index 41da0bbda69..3a87daee962 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dlfcn.h,v 1.8 2003/09/02 15:14:54 drahn Exp $ */ +/* $OpenBSD: dlfcn.h,v 1.9 2004/08/11 19:14:56 drahn Exp $ */ /* $NetBSD: dlfcn.h,v 1.2 1995/06/05 19:38:00 pk Exp $ */ /* @@ -37,6 +37,16 @@ #include <sys/cdefs.h> /* + * Structure filled in by dladdr(). + */ +typedef struct dl_info { + const char *dli_fname; /* Pathname of shared object. */ + void *dli_fbase; /* Base address of shared object. */ + const char *dli_sname; /* Name of nearest symbol. */ + void *dli_saddr; /* Address of nearest symbol. */ +} Dl_info; + +/* * User interface to the run-time linker. */ __BEGIN_DECLS @@ -45,6 +55,7 @@ extern int dlclose(void *); extern void *dlsym(void *, const char *); extern int dlctl(void *, int, void *); extern const char *dlerror(void); +extern int dladdr(const void *, Dl_info *); __END_DECLS /* Values for dlopen `mode'. */ @@ -55,6 +66,13 @@ __END_DECLS #define DL_LAZY RTLD_LAZY /* Compat */ /* + * Special handle arguments for dlsym(). + */ +#define RTLD_NEXT ((void *) -1) /* Search subsequent objects. */ +#define RTLD_DEFAULT ((void *) -2) /* Use default search algorithm. */ +#define RTLD_SELF ((void *) -3) /* Search the caller itself. */ + +/* * dlctl() commands */ #define DL_GETERRNO 1 diff --git a/lib/libc/dlfcn/dlfcn_stubs.c b/lib/libc/dlfcn/dlfcn_stubs.c index 9891498b33d..430cd9d65fa 100644 --- a/lib/libc/dlfcn/dlfcn_stubs.c +++ b/lib/libc/dlfcn/dlfcn_stubs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dlfcn_stubs.c,v 1.7 2003/07/15 02:56:14 deraadt Exp $ */ +/* $OpenBSD: dlfcn_stubs.c,v 1.8 2004/08/11 19:14:56 drahn Exp $ */ /* * Copyright (c) 1998 Per Fogelstrom, Opsycon AB @@ -39,6 +39,7 @@ int dlclose(void *handle) __attribute__((weak)); void *dlsym(void *handle, const char *name) __attribute__((weak)); int dlctl(void *handle, int command, void *data) __attribute__((weak)); const char * dlerror(void) __attribute__((weak)); +int dladdr(const void *addr, void *info) __attribute__((weak)); #include <stdio.h> @@ -74,3 +75,10 @@ dlerror(void) { return "Wrong dl symbols!\n"; } + +int +dladdr(const void *addr, void *info) +{ + printf("Wrong dl symbols!\n"); + return -1; +} diff --git a/lib/libc/shlib_version b/lib/libc/shlib_version index c3b79bfdab3..c945bee1e61 100644 --- a/lib/libc/shlib_version +++ b/lib/libc/shlib_version @@ -1,4 +1,4 @@ major=34 -minor=0 +minor=1 # note: If changes were made to include/thread_private.h or if system # calls were added/changed then libpthread must also be updated. 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)) diff --git a/share/man/man3/Makefile b/share/man/man3/Makefile index 2b21a0ba101..dc959f06457 100644 --- a/share/man/man3/Makefile +++ b/share/man/man3/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.15 2004/02/09 19:21:51 espie Exp $ +# $OpenBSD: Makefile,v 1.16 2004/08/11 19:14:56 drahn Exp $ # @(#)Makefile 8.2 (Berkeley) 12/13/93 MAN= assert.3 bitstring.3 dlfcn.3 end.3 intro.3 queue.3 stdarg.3 \ @@ -49,7 +49,7 @@ MLINKS+=queue.3 SLIST_ENTRY.3 queue.3 SLIST_HEAD.3 \ MLINKS+=stdarg.3 varargs.3 stdarg.3 va_arg.3 stdarg.3 va_end.3 MLINKS+=stdarg.3 va_start.3 stdarg.3 va_copy.3 MLINKS+=dlfcn.3 dlopen.3 dlfcn.3 dlclose.3 dlfcn.3 dlsym.3 dlfcn.3 dlctl.3 \ - dlfcn.3 dlerror.3 + dlfcn.3 dlerror.3 dlfcn.3 dladdr.3 MLINKS+=tree.3 SPLAY_PROTOTYPE.3 tree.3 SPLAY_GENERATE.3 \ tree.3 SPLAY_ENTRY.3 tree.3 SPLAY_HEAD.3 \ tree.3 SPLAY_INITIALIZER.3 tree.3 SPLAY_ROOT.3 \ diff --git a/share/man/man3/dlfcn.3 b/share/man/man3/dlfcn.3 index 4a03872ad84..48747a3a3f2 100644 --- a/share/man/man3/dlfcn.3 +++ b/share/man/man3/dlfcn.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: dlfcn.3,v 1.15 2003/09/25 21:56:20 millert Exp $ +.\" $OpenBSD: dlfcn.3,v 1.16 2004/08/11 19:14:56 drahn Exp $ .\" $NetBSD: dlfcn.3,v 1.3 1996/01/09 19:43:34 pk Exp $ .\" .\" Copyright (c) 1995 Paul Kranenburg @@ -36,6 +36,7 @@ .Nm dlopen , .Nm dlclose , .Nm dlsym , +.Nm dladdr , .Nm dlctl , .Nm dlerror .Nd dynamic link interface @@ -48,6 +49,8 @@ .Ft "void *" .Fn dlsym "void *handle" "const char *symbol" .Ft "int" +.Fn dladdr "const void *addr" "Dl_info *info" +.Ft "int" .Fn dlctl "void *handle" "int cmd" "void *data" .Ft "const char *" .Fn dlerror "void" @@ -75,6 +78,8 @@ argument can either be an absolute pathname or it can be of the form in which case the same library search rules apply that are used for .Dq intrinsic shared library searches. +A null pointer supplied for path is interpreted as a reference to the main +executable of the process. The second argument currently has no effect, but should be set to .Dv DL_LAZY for future compatibility. @@ -111,6 +116,96 @@ If the symbol cannot be resolved, .Dv NULL is returned. .Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv NULL , +it is interpreted as a reference to the executable or shared object +from which the call is being made. +Thus a shared object can reference its own symbols. +.Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_DEFAULT , +all the shared objects will be searched in the order they were loaded. +.Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_NEXT , +then the search for the symbol is limited to the shared objects +which were loaded after the one issuing the call to +.Fn dlsym . +Thus, if the function is called from the main program, all +the shared libraries are searched. +If it is called from a shared library, all subsequent shared +libraries are searched. +.Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_SELF , +then the search for the symbol is limited to the shared object +issuing the call to +.Fn dlsym +and those shared objects which were loaded after it. +.Pp +.Fn dladdr +queries the dynamic linker for information about the shared object +containing the address +.Fa addr . +The information is returned in the structure specified by +.Fa info . +The structure contains at least the following members: +.Bl -tag -width "XXXconst char *dli_fname" +.It Li "const char *dli_fname" +The pathname of the shared object containing the address +.Fa addr . +.It Li "void *dli_fbase" +The base address at which the shared object is mapped into the +address space of the calling process. +.It Li "const char *dli_sname" +The name of the nearest run-time symbol with a address less than or +equal to +.Fa addr . +.Pp +If no symbol with a suitable address is found, both this field and +.Va dli_saddr +are set to +.Dv NULL . +.It Li "void *dli_saddr" +The address of the symbol returned in +.Va dli_sname . +.El +.Pp +If a mapped shared object containing +.Fa addr +cannot be found, +.Fn dladdr +returns 0. +In that case, a message detailing the failure can be retrieved by +calling +.Fn dlerror . +On success, a non-zero value is returned. Note: both strings pointed +at by +.Va dli_fname +and +.Va dli_sname +reside in memory private to the run-time linker module and should not +be modified by the caller. +.Pp +In dynamically linked programs, the address of a global function will +point to its program linkage table entry, rather than to the entry +point of the function itself. +This causes most global functions to appear to be defined within the +main executable, rather than in the shared libraries where the actual +code resides. +.Pp .Fn dlctl provides an interface similar to .Xr ioctl 2 |