diff options
| author | Alexander Kabaev <kan@FreeBSD.org> | 2003-02-20 20:42:46 +0000 |
|---|---|---|
| committer | Alexander Kabaev <kan@FreeBSD.org> | 2003-02-20 20:42:46 +0000 |
| commit | f864ae268ecf8afbe72747597635fd2624509cf8 (patch) | |
| tree | 9ec7318ce1d41b5210f77de60d3e305880cf1a0a /libexec | |
| parent | 75c7c718637e845322503bcf6e0cacf84b55f640 (diff) | |
Notes
Diffstat (limited to 'libexec')
| -rw-r--r-- | libexec/rtld-elf/malloc.c | 19 | ||||
| -rw-r--r-- | libexec/rtld-elf/rtld.c | 342 | ||||
| -rw-r--r-- | libexec/rtld-elf/rtld.h | 4 |
3 files changed, 307 insertions, 58 deletions
diff --git a/libexec/rtld-elf/malloc.c b/libexec/rtld-elf/malloc.c index 1e0664ce3195..2872f0fe70c1 100644 --- a/libexec/rtld-elf/malloc.c +++ b/libexec/rtld-elf/malloc.c @@ -50,6 +50,8 @@ static char *rcsid = "$FreeBSD$"; #include <sys/types.h> #include <err.h> #include <paths.h> +#include <stdarg.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> @@ -148,7 +150,7 @@ botch(s) #endif /* Debugging stuff */ -extern void xprintf(const char *, ...); +static void xprintf(const char *, ...); #define TRACE() xprintf("TRACE %s:%d\n", __FILE__, __LINE__) void * @@ -484,3 +486,18 @@ int n; #endif return n; } + +/* + * Non-mallocing printf, for use by malloc itself. + */ +static void +xprintf(const char *fmt, ...) +{ + char buf[256]; + va_list ap; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + (void)write(STDOUT_FILENO, buf, strlen(buf)); + va_end(ap); +} diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index f991c9c8c0b8..863a6d27a312 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1,5 +1,6 @@ /*- * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra. + * Copyright 2003 Alexander Kabaev <kan@FreeBSD.ORG>. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -57,6 +58,7 @@ /* Types. */ typedef void (*func_ptr_type)(); +typedef void * (*path_enum_proc) (const char *path, size_t len, void *arg); /* * This structure provides a reentrant way to keep a list of objects and @@ -76,9 +78,11 @@ static void die(void); static void digest_dynamic(Obj_Entry *); static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *); static Obj_Entry *dlcheck(void *); +static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *); static bool donelist_check(DoneList *, const Obj_Entry *); static void errmsg_restore(char *); static char *errmsg_save(void); +static void *fill_search_info(const char *, size_t, void *); static char *find_library(const char *, const Obj_Entry *); static const char *gethints(void); static void init_dag(Obj_Entry *); @@ -104,7 +108,9 @@ static void objlist_push_head(Objlist *, Obj_Entry *); static void objlist_push_tail(Objlist *, Obj_Entry *); static void objlist_remove(Objlist *, Obj_Entry *); static void objlist_remove_unref(Objlist *); +static void *path_enumerate(const char *, path_enum_proc, void *); static int relocate_objects(Obj_Entry *, bool); +static int rtld_dirname(const char *, char *); static void rtld_exit(void); static char *search_library_path(const char *, const char *); static const void **get_program_var_addr(const char *name); @@ -114,17 +120,17 @@ static const Elf_Sym *symlook_default(const char *, unsigned long hash, static const Elf_Sym *symlook_list(const char *, unsigned long, Objlist *, const Obj_Entry **, bool in_plt, DoneList *); static void trace_loaded_objects(Obj_Entry *obj); +static void unlink_object(Obj_Entry *); static void unload_object(Obj_Entry *); static void unref_dag(Obj_Entry *); void r_debug_state(struct r_debug*, struct link_map*); -void xprintf(const char *, ...); /* * Data declarations. */ static char *error_message; /* Message for dlerror(), or NULL */ -struct r_debug r_debug; /* for GDB; */ +struct r_debug r_debug; /* for GDB; */ static bool trust; /* False for setuid and setgid programs */ static char *ld_bind_now; /* Environment variable for immediate binding */ static char *ld_debug; /* Environment variable for debugging */ @@ -167,6 +173,7 @@ static func_ptr_type exports[] = { (func_ptr_type) &dlsym, (func_ptr_type) &dladdr, (func_ptr_type) &dllockinit, + (func_ptr_type) &dlinfo, NULL }; @@ -1161,7 +1168,7 @@ load_object(char *path) *obj_tail = obj; obj_tail = &obj->next; obj_count++; - linkmap_add(obj); /* for GDB */ + linkmap_add(obj); /* for GDB & dlinfo() */ dbg(" %p .. %p: %s", obj->mapbase, obj->mapbase + obj->mapsize - 1, obj->path); @@ -1428,40 +1435,83 @@ rtld_exit(void) /* No need to remove the items from the list, since we are exiting. */ } -static char * -search_library_path(const char *name, const char *path) +static void * +path_enumerate(const char *path, path_enum_proc callback, void *arg) { - size_t namelen = strlen(name); - const char *p = path; + if (path == NULL) + return (NULL); - if (p == NULL) - return NULL; + path += strspn(path, ":;"); + while (*path != '\0') { + size_t len; + char *res; - p += strspn(p, ":;"); - while (*p != '\0') { - size_t len = strcspn(p, ":;"); + len = strcspn(path, ":;"); + res = callback(path, len, arg); - if (*p == '/' || trust) { - char *pathname; - const char *dir = p; - size_t dirlen = len; + if (res != NULL) + return (res); - pathname = xmalloc(dirlen + 1 + namelen + 1); - strncpy(pathname, dir, dirlen); - pathname[dirlen] = '/'; - strcpy(pathname + dirlen + 1, name); + path += len; + path += strspn(path, ":;"); + } - dbg(" Trying \"%s\"", pathname); - if (access(pathname, F_OK) == 0) /* We found it */ - return pathname; + return (NULL); +} - free(pathname); +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); } - p += len; - p += strspn(p, ":;"); } + return (NULL); +} - 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 @@ -1612,7 +1662,8 @@ dlsym(void *handle, const char *name) defobj = NULL; rlock_acquire(); - if (handle == NULL || handle == RTLD_NEXT || handle == RTLD_DEFAULT) { + if (handle == NULL || handle == RTLD_NEXT || + handle == RTLD_DEFAULT || handle == RTLD_SELF) { void *retaddr; retaddr = __builtin_return_address(0); /* __GNUC__ only */ @@ -1624,8 +1675,11 @@ dlsym(void *handle, const char *name) 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 */ - while ((obj = obj->next) != NULL) { + } 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; @@ -1675,7 +1729,7 @@ dladdr(const void *addr, Dl_info *info) const Elf_Sym *def; void *symbol_addr; unsigned long symoffset; - + rlock_acquire(); obj = obj_from_addr(addr); if (obj == NULL) { @@ -1723,6 +1777,182 @@ dladdr(const void *addr, Dl_info *info) return 1; } +int +dlinfo(void *handle, int request, void *p) +{ + const Obj_Entry *obj; + int error; + + rlock_acquire(); + + if (handle == NULL || 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"); + } else + obj = dlcheck(handle); + + if (obj == NULL) { + rlock_release(); + return (-1); + } + + error = 0; + switch (request) { + case RTLD_DI_LINKMAP: + *((struct link_map const **)p) = &obj->linkmap; + break; + case RTLD_DI_ORIGIN: + error = rtld_dirname(obj->path, p); + break; + + case RTLD_DI_SERINFOSIZE: + case RTLD_DI_SERINFO: + error = do_search_info(obj, request, (struct dl_serinfo *)p); + break; + + default: + _rtld_error("Invalid request %d passed to dlinfo()", request); + error = -1; + } + + rlock_release(); + + return (error); +} + +struct fill_search_info_args { + int request; + unsigned int flags; + Dl_serinfo *serinfo; + Dl_serpath *serpath; + char *strspace; +}; + +static void * +fill_search_info(const char *dir, size_t dirlen, void *param) +{ + struct fill_search_info_args *arg; + + arg = param; + + if (arg->request == RTLD_DI_SERINFOSIZE) { + arg->serinfo->dls_cnt ++; + arg->serinfo->dls_size += dirlen + 1; + } else { + struct dl_serpath *s_entry; + + s_entry = arg->serpath; + s_entry->dls_name = arg->strspace; + s_entry->dls_flags = arg->flags; + + strncpy(arg->strspace, dir, dirlen); + arg->strspace[dirlen] = '\0'; + + arg->strspace += dirlen + 1; + arg->serpath++; + } + + return (NULL); +} + +static int +do_search_info(const Obj_Entry *obj, int request, struct dl_serinfo *info) +{ + struct dl_serinfo _info; + struct fill_search_info_args args; + + args.request = RTLD_DI_SERINFOSIZE; + args.serinfo = &_info; + + _info.dls_size = __offsetof(struct dl_serinfo, dls_serpath); + _info.dls_cnt = 0; + + path_enumerate(ld_library_path, fill_search_info, &args); + path_enumerate(obj->rpath, fill_search_info, &args); + path_enumerate(gethints(), fill_search_info, &args); + path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &args); + + + if (request == RTLD_DI_SERINFOSIZE) { + info->dls_size = _info.dls_size; + info->dls_cnt = _info.dls_cnt; + return (0); + } + + if (info->dls_cnt != _info.dls_cnt || info->dls_size != _info.dls_size) { + _rtld_error("Uninitialized Dl_serinfo struct passed to dlinfo()"); + return (-1); + } + + args.request = RTLD_DI_SERINFO; + args.serinfo = info; + args.serpath = &info->dls_serpath[0]; + args.strspace = (char *)&info->dls_serpath[_info.dls_cnt]; + + args.flags = LA_SER_LIBPATH; + if (path_enumerate(ld_library_path, fill_search_info, &args) != NULL) + return (-1); + + args.flags = LA_SER_RUNPATH; + if (path_enumerate(obj->rpath, fill_search_info, &args) != NULL) + return (-1); + + args.flags = LA_SER_CONFIG; + if (path_enumerate(gethints(), fill_search_info, &args) != NULL) + return (-1); + + args.flags = LA_SER_DEFAULT; + if (path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &args) != NULL) + return (-1); + return (0); +} + +static int +rtld_dirname(const char *path, char *bname) +{ + const char *endp; + + /* Empty or NULL string gets treated as "." */ + if (path == NULL || *path == '\0') { + bname[0] = '.'; + bname[1] = '\0'; + return (0); + } + + /* Strip trailing slashes */ + endp = path + strlen(path) - 1; + while (endp > path && *endp == '/') + endp--; + + /* Find the start of the dir */ + while (endp > path && *endp != '/') + endp--; + + /* Either the dir is "/" or there are no slashes */ + if (endp == path) { + bname[0] = *endp == '/' ? '/' : '.'; + bname[1] = '\0'; + return (0); + } else { + do { + endp--; + } while (endp > path && *endp == '/'); + } + + if (endp - path + 2 > PATH_MAX) + { + _rtld_error("Filename is too long: %s", path); + return(-1); + } + + strncpy(bname, path, endp - path + 1); + bname[endp - path + 1] = '\0'; + return (0); +} + static void linkmap_add(Obj_Entry *obj) { @@ -1741,7 +1971,7 @@ linkmap_add(Obj_Entry *obj) r_debug.r_map = l; return; } - + /* * Scan to the end of the list, but not past the entry for the * dynamic linker, which we want to keep at the very end. @@ -2079,16 +2309,14 @@ unload_object(Obj_Entry *root) { Obj_Entry *obj; Obj_Entry **linkp; - Objlist_Entry *elm; assert(root->refcount == 0); - /* Remove the DAG from all objects' DAG lists. */ - STAILQ_FOREACH(elm, &root->dagmembers , link) - objlist_remove(&elm->obj->dldags, root); - - /* Remove the DAG from the RTLD_GLOBAL list. */ - objlist_remove(&list_global, root); + /* + * Pass over the DAG removing unreferenced objects from + * appropriate lists. + */ + unlink_object(root); /* Unmap all objects that are no longer referenced. */ linkp = &obj_list->next; @@ -2107,6 +2335,26 @@ unload_object(Obj_Entry *root) } static void +unlink_object(Obj_Entry *root) +{ + const Needed_Entry *needed; + Objlist_Entry *elm; + + if (root->refcount == 0) { + /* Remove the object from the RTLD_GLOBAL list. */ + objlist_remove(&list_global, root); + + /* Remove the object from all objects' DAG lists. */ + STAILQ_FOREACH(elm, &root->dagmembers , link) + objlist_remove(&elm->obj->dldags, root); + } + + for (needed = root->needed; needed != NULL; needed = needed->next) + if (needed->obj != NULL) + unlink_object(needed->obj); +} + +static void unref_dag(Obj_Entry *root) { const Needed_Entry *needed; @@ -2119,19 +2367,3 @@ unref_dag(Obj_Entry *root) if (needed->obj != NULL) unref_dag(needed->obj); } - -/* - * Non-mallocing printf, for use by malloc itself. - * XXX - This doesn't belong in this module. - */ -void -xprintf(const char *fmt, ...) -{ - char buf[256]; - va_list ap; - - va_start(ap, fmt); - vsprintf(buf, fmt, ap); - (void)write(1, buf, strlen(buf)); - va_end(ap); -} diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 347682666ee4..660bdbe2e388 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -39,7 +39,7 @@ #include "rtld_machdep.h" #ifndef STANDARD_LIBRARY_PATH -#define STANDARD_LIBRARY_PATH "/usr/lib/elf:/usr/lib" +#define STANDARD_LIBRARY_PATH "/usr/lib" #endif #define NEW(type) ((type *) xmalloc(sizeof(type))) @@ -156,7 +156,7 @@ typedef struct Struct_Obj_Entry { bool jmpslots_done; /* Already have relocated the jump slots */ bool init_done; /* Already have added object to init list */ - struct link_map linkmap; /* for GDB */ + struct link_map linkmap; /* for GDB and dlinfo() */ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */ Objlist dagmembers; /* DAG has these members (%) */ dev_t dev; /* Object's filesystem's device */ |
