summaryrefslogtreecommitdiff
path: root/libexec
diff options
context:
space:
mode:
authorAlexander Kabaev <kan@FreeBSD.org>2003-02-20 20:42:46 +0000
committerAlexander Kabaev <kan@FreeBSD.org>2003-02-20 20:42:46 +0000
commitf864ae268ecf8afbe72747597635fd2624509cf8 (patch)
tree9ec7318ce1d41b5210f77de60d3e305880cf1a0a /libexec
parent75c7c718637e845322503bcf6e0cacf84b55f640 (diff)
Notes
Diffstat (limited to 'libexec')
-rw-r--r--libexec/rtld-elf/malloc.c19
-rw-r--r--libexec/rtld-elf/rtld.c342
-rw-r--r--libexec/rtld-elf/rtld.h4
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 */