summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/dlfcn.h1
-rw-r--r--lib/libc/gen/dlopen.37
-rw-r--r--libexec/rtld-elf/rtld.c44
-rw-r--r--usr.bin/ldd/ldd.13
-rw-r--r--usr.bin/ldd/ldd.c14
5 files changed, 57 insertions, 12 deletions
diff --git a/include/dlfcn.h b/include/dlfcn.h
index 1669560..5034a36 100644
--- a/include/dlfcn.h
+++ b/include/dlfcn.h
@@ -45,6 +45,7 @@
#define RTLD_MODEMASK 0x3
#define RTLD_GLOBAL 0x100 /* Make symbols globally available */
#define RTLD_LOCAL 0 /* Opposite of RTLD_GLOBAL, and the default */
+#define RTLD_TRACE 0x200 /* Trace loaded objects and exit */
/*
* Special handle arguments for dlsym().
diff --git a/lib/libc/gen/dlopen.3 b/lib/libc/gen/dlopen.3
index 45f1d96..50b0599 100644
--- a/lib/libc/gen/dlopen.3
+++ b/lib/libc/gen/dlopen.3
@@ -123,6 +123,13 @@ Symbols in this shared object and its DAG of needed objects will be
available for resolving undefined references only from other objects
in the same DAG. This is the default, but it may be specified
explicitly with this flag.
+.It Dv RTLD_TRACE
+When set, causes dynamic linker to exit after loading all objects
+needed by this shared object and printing a summary which includes
+the absolute pathnames of all objects, to standard output.
+With this flag
+.Fn dlopen
+will return to the caller only in the case of error.
.El
.Pp
If
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index a083dea..100f011 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -107,6 +107,7 @@ static void objlist_remove_unref(Objlist *);
static int relocate_objects(Obj_Entry *, bool);
static void rtld_exit(void);
static char *search_library_path(const char *, const char *);
+static const void **get_program_var_addr(const char *name);
static void set_program_var(const char *, const void *);
static const Elf_Sym *symlook_default(const char *, unsigned long hash,
const Obj_Entry *refobj, const Obj_Entry **defobj_out, bool in_plt);
@@ -1556,6 +1557,11 @@ dlopen(const char *name, int mode)
Obj_Entry **old_obj_tail;
Obj_Entry *obj;
Objlist initlist;
+ int result;
+
+ ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1";
+ if (ld_tracing != NULL)
+ environ = (char **)*get_program_var_addr("environ");
objlist_init(&initlist);
@@ -1581,7 +1587,14 @@ dlopen(const char *name, int mode)
if (*old_obj_tail != NULL) { /* We loaded something new. */
assert(*old_obj_tail == obj);
- if (load_needed_objects(obj) == -1 ||
+ result = load_needed_objects(obj);
+ if (result != -1 && ld_tracing) {
+ trace_loaded_objects(obj);
+ wlock_release();
+ exit(0);
+ }
+
+ if (result == -1 ||
(init_dag(obj), relocate_objects(obj, mode == RTLD_NOW)) == -1) {
obj->dl_refcount--;
unref_dag(obj);
@@ -1812,12 +1825,10 @@ r_debug_state(struct r_debug* rd, struct link_map *m)
}
/*
- * Set a pointer variable in the main program to the given value. This
- * is used to set key variables such as "environ" before any of the
- * init functions are called.
+ * Get address of the pointer variable in the main program.
*/
-static void
-set_program_var(const char *name, const void *value)
+static const void **
+get_program_var_addr(const char *name)
{
const Obj_Entry *obj;
unsigned long hash;
@@ -1830,11 +1841,26 @@ set_program_var(const char *name, const void *value)
const void **addr;
addr = (const void **)(obj->relocbase + def->st_value);
- dbg("\"%s\": *%p <-- %p", name, addr, value);
- *addr = value;
- break;
+ return addr;
}
}
+ return NULL;
+}
+
+/*
+ * Set a pointer variable in the main program to the given value. This
+ * is used to set key variables such as "environ" before any of the
+ * init functions are called.
+ */
+static void
+set_program_var(const char *name, const void *value)
+{
+ const void **addr;
+
+ if ((addr = get_program_var_addr(name)) != NULL) {
+ dbg("\"%s\": *%p <-- %p", name, addr, value);
+ *addr = value;
+ }
}
/*
diff --git a/usr.bin/ldd/ldd.1 b/usr.bin/ldd/ldd.1
index d784751..470fd42 100644
--- a/usr.bin/ldd/ldd.1
+++ b/usr.bin/ldd/ldd.1
@@ -13,7 +13,8 @@
.Ar program ...
.Sh DESCRIPTION
.Nm
-displays all shared objects that are needed to run the given program.
+displays all shared objects that are needed to run the given program or
+to load the given shared object.
Contrary to
.Xr nm 1 ,
the list includes
diff --git a/usr.bin/ldd/ldd.c b/usr.bin/ldd/ldd.c
index 22b4072..0d86e66 100644
--- a/usr.bin/ldd/ldd.c
+++ b/usr.bin/ldd/ldd.c
@@ -36,6 +36,7 @@ static const char rcsid[] =
#include <sys/wait.h>
#include <machine/elf.h>
#include <a.out.h>
+#include <dlfcn.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
@@ -116,6 +117,7 @@ char *argv[];
int n;
int status;
int file_ok;
+ int is_shlib;
if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
warn("%s", *argv);
@@ -130,6 +132,7 @@ char *argv[];
}
file_ok = 1;
+ is_shlib = 0;
if (n >= sizeof hdr.aout && !N_BADMAG(hdr.aout)) {
/* a.out file */
if ((N_GETFLAG(hdr.aout) & EX_DPMASK) != EX_DYNAMIC
@@ -167,6 +170,8 @@ char *argv[];
if (!dynamic) {
warnx("%s: not a dynamic executable", *argv);
file_ok = 0;
+ } else if (hdr.elf.e_type == ET_DYN) {
+ is_shlib = 1;
}
} else {
warnx("%s: not a dynamic executable", *argv);
@@ -204,8 +209,13 @@ char *argv[];
}
break;
case 0:
- execl(*argv, *argv, (char *)NULL);
- warn("%s", *argv);
+ if (is_shlib == 0) {
+ execl(*argv, *argv, (char *)NULL);
+ warn("%s", *argv);
+ } else {
+ dlopen(*argv, RTLD_TRACE);
+ warnx("%s: %s", *argv, dlerror());
+ }
_exit(1);
}
}
OpenPOWER on IntegriCloud