Cambridge, MA 02139, USA. */
#include <link.h>
-#include "dynamic-link.h"
#include <stddef.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
+#include <sys/mman.h> /* Check if MAP_ANON is defined. */
#include "../stdio-common/_itoa.h"
+#include <assert.h>
+#include "dynamic-link.h"
-#ifdef RTLD_START
-RTLD_START
-#else
-#error "sysdeps/MACHINE/dl-machine.h fails to define RTLD_START"
-#endif
-
/* System-specific function to do initial startup for the dynamic linker.
After this, file access calls and getenv must work. This is responsible
- for setting _dl_secure if we need to be secure (e.g. setuid),
+ for setting __libc_enable_secure if we need to be secure (e.g. setuid),
and for setting _dl_argc and _dl_argv, and then calling _dl_main. */
-extern Elf32_Addr _dl_sysdep_start (void **start_argptr,
- void (*dl_main) (const Elf32_Phdr *phdr,
- Elf32_Word phent,
- Elf32_Addr *user_entry));
+extern ElfW(Addr) _dl_sysdep_start (void **start_argptr,
+ void (*dl_main) (const ElfW(Phdr) *phdr,
+ ElfW(Half) phent,
+ ElfW(Addr) *user_entry));
extern void _dl_sysdep_start_cleanup (void);
-int _dl_secure;
int _dl_argc;
char **_dl_argv;
const char *_dl_rpath;
-struct r_debug dl_r_debug;
+/* Set nonzero during loading and initialization of executable and
+ libraries, cleared before the executable's entry point runs. This
+ must not be initialized to nonzero, because the unused dynamic
+ linker loaded in for libc.so's "ld.so.1" dep will provide the
+ definition seen by libc.so's initializer; that value must be zero,
+ and will be since that dynamic linker's _dl_start and dl_main will
+ never be called. */
+int _dl_starting_up;
-static void dl_main (const Elf32_Phdr *phdr,
- Elf32_Word phent,
- Elf32_Addr *user_entry);
+static void dl_main (const ElfW(Phdr) *phdr,
+ ElfW(Half) phent,
+ ElfW(Addr) *user_entry);
struct link_map _dl_rtld_map;
-Elf32_Addr
+#ifdef RTLD_START
+RTLD_START
+#else
+#error "sysdeps/MACHINE/dl-machine.h fails to define RTLD_START"
+#endif
+
+ElfW(Addr)
_dl_start (void *arg)
{
struct link_map bootstrap_map;
+ /* This #define produces dynamic linking inline functions for
+ bootstrap relocation instead of general-purpose relocation. */
+#define RTLD_BOOTSTRAP
+#define RESOLVE(sym, flags) bootstrap_map.l_addr
+#include "dynamic-link.h"
+
/* Figure out the run-time load address of the dynamic linker itself. */
bootstrap_map.l_addr = elf_machine_load_address ();
/* Relocate ourselves so we can do normal function calls and
data access using the global offset table. */
- /* We must initialize `l_type' to make sure it is not `lt_interpreter'.
- That is the type to describe us, but not during bootstrapping--it
- indicates to elf_machine_rel{,a} that we were already relocated during
- bootstrapping, so it must anti-perform each bootstrapping relocation
- before applying the final relocation when ld.so is linked in as
- normal a shared library. */
- bootstrap_map.l_type = lt_library;
- ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, NULL);
+ ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0);
/* Now life is sane; we can call functions and access global data.
file access. It will call `dl_main' (below) to do all the real work
of the dynamic linker, and then unwind our frame and run the user
entry point on the same stack we entered on. */
- return _dl_sysdep_start (&arg, &dl_main);
+ return _dl_sysdep_start (arg, &dl_main);
}
unsigned int _dl_skip_args; /* Nonzero if we were run directly. */
static void
-dl_main (const Elf32_Phdr *phdr,
- Elf32_Word phent,
- Elf32_Addr *user_entry)
+dl_main (const ElfW(Phdr) *phdr,
+ ElfW(Half) phent,
+ ElfW(Addr) *user_entry)
{
- const Elf32_Phdr *ph;
+ const ElfW(Phdr) *ph;
struct link_map *l;
- const char *interpreter_name;
int lazy;
- int list_only = 0;
+ enum { normal, list, verify, trace } mode;
+ struct link_map **preloads;
+ unsigned int npreloads;
+
+ mode = getenv ("LD_TRACE_LOADED_OBJECTS") != NULL ? trace : normal;
- if (*user_entry == (Elf32_Addr) &_start)
+ if (*user_entry == (ElfW(Addr)) &_start)
{
/* Ho ho. We are not the program interpreter! We are the program
itself! This means someone ran ld.so as a command. Well, that
installing it. */
if (_dl_argc < 2)
_dl_sysdep_fatal ("\
-Usage: ld.so [--list] EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
+Usage: ld.so [--list|--verify] EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
You have invoked `ld.so', the helper program for shared library executables.\n\
This program usually lives in the file `/lib/ld.so', and special directives\n\
in executable files using ELF shared libraries tell the system's program\n\
of this helper program; chances are you did not intend to run this program.\n",
NULL);
- interpreter_name = _dl_argv[0];
+ /* Note the place where the dynamic linker actually came from. */
+ _dl_rtld_map.l_name = _dl_argv[0];
if (! strcmp (_dl_argv[1], "--list"))
{
- list_only = 1;
+ mode = list;
+
+ ++_dl_skip_args;
+ --_dl_argc;
+ ++_dl_argv;
+ }
+ else if (! strcmp (_dl_argv[1], "--verify"))
+ {
+ mode = verify;
++_dl_skip_args;
--_dl_argc;
--_dl_argc;
++_dl_argv;
- l = _dl_map_object (NULL, _dl_argv[0]);
+ if (mode == verify)
+ {
+ void doit (void)
+ {
+ l = _dl_map_object (NULL, _dl_argv[0], lt_library);
+ }
+ char *err_str = NULL;
+ const char *obj_name __attribute__ ((unused));
+
+ (void) _dl_catch_error (&err_str, &obj_name, doit);
+ if (err_str != NULL)
+ {
+ free (err_str);
+ _exit (EXIT_FAILURE);
+ }
+ }
+ else
+ l = _dl_map_object (NULL, _dl_argv[0], lt_library);
+
phdr = l->l_phdr;
phent = l->l_phnum;
l->l_name = (char *) "";
l = _dl_new_object ((char *) "", "", lt_executable);
l->l_phdr = phdr;
l->l_phnum = phent;
- interpreter_name = 0;
l->l_entry = *user_entry;
}
dlopen call or DT_NEEDED entry, for something that wants to link
against the dynamic linker as a shared library, will know that
the shared object is already loaded. */
- interpreter_name = (void *) l->l_addr + ph->p_vaddr;
+ _dl_rtld_map.l_libname = (const char *) l->l_addr + ph->p_vaddr;
break;
}
- assert (interpreter_name); /* How else did we get here? */
+ if (! _dl_rtld_map.l_libname && _dl_rtld_map.l_name)
+ /* We were invoked directly, so the program might not have a PT_INTERP. */
+ _dl_rtld_map.l_libname = _dl_rtld_map.l_name;
+ else
+ assert (_dl_rtld_map.l_libname); /* How else did we get here? */
+
+ if (mode == verify)
+ /* We were called just to verify that this is a dynamic executable
+ using us as the program interpreter. */
+ _exit ((strcmp (_dl_rtld_map.l_libname, _dl_rtld_map.l_name) ||
+ l->l_ld == NULL)
+ ? EXIT_FAILURE : EXIT_SUCCESS);
/* Extract the contents of the dynamic section for easy access. */
elf_get_dynamic_info (l->l_ld, l->l_info);
/* Set up our cache of pointers into the hash table. */
_dl_setup_hash (l);
- if (l->l_info[DT_DEBUG])
- /* There is a DT_DEBUG entry in the dynamic section. Fill it in
- with the run-time address of the r_debug structure, which we
- will set up later to communicate with the debugger. */
- l->l_info[DT_DEBUG]->d_un.d_ptr = (Elf32_Addr) &dl_r_debug;
-
/* Put the link_map for ourselves on the chain so it can be found by
name. */
- _dl_rtld_map.l_name = (char *) _dl_rtld_map.l_libname = interpreter_name;
- _dl_rtld_map.l_type = lt_interpreter;
+ if (! _dl_rtld_map.l_name)
+ /* If not invoked directly, the dynamic linker shared object file was
+ found by the PT_INTERP name. */
+ _dl_rtld_map.l_name = (char *) _dl_rtld_map.l_libname;
+ _dl_rtld_map.l_type = lt_library;
while (l->l_next)
l = l->l_next;
l->l_next = &_dl_rtld_map;
_dl_rtld_map.l_prev = l;
- /* Load all the libraries specified by DT_NEEDED entries. */
- _dl_map_object_deps (l);
+ preloads = NULL;
+ npreloads = 0;
+ if (! __libc_enable_secure)
+ {
+ const char *preloadlist = getenv ("LD_PRELOAD");
+ if (preloadlist)
+ {
+ /* The LD_PRELOAD environment variable gives a colon-separated
+ list of libraries that are loaded before the executable's
+ dependencies and prepended to the global scope list. */
+ char *list = strdupa (preloadlist);
+ char *p;
+ while ((p = strsep (&list, ":")) != NULL)
+ {
+ (void) _dl_map_object (NULL, p, lt_library);
+ ++npreloads;
+ }
+
+ if (npreloads != 0)
+ {
+ /* Set up PRELOADS with a vector of the preloaded libraries. */
+ struct link_map *l;
+ unsigned int i;
+ preloads = __alloca (npreloads * sizeof preloads[0]);
+ l = _dl_rtld_map.l_next; /* End of the chain before preloads. */
+ i = 0;
+ do
+ {
+ preloads[i++] = l;
+ l = l->l_next;
+ } while (l);
+ assert (i == npreloads);
+ }
+ }
+ }
+
+ /* Load all the libraries specified by DT_NEEDED entries. If LD_PRELOAD
+ specified some libraries to load, these are inserted before the actual
+ dependencies in the executable's searchlist for symbol resolution. */
+ _dl_map_object_deps (l, preloads, npreloads);
+
+#ifndef MAP_ANON
+ /* We are done mapping things, so close the zero-fill descriptor. */
+ __close (_dl_zerofd);
+ _dl_zerofd = -1;
+#endif
- /* XXX if kept, move it so l_next list is in dep order because
- it will determine gdb's search order.
- Perhaps do this always, so later dlopen by name finds it?
- XXX But then gdb always considers it present. */
- if (_dl_rtld_map.l_opencount == 0)
+ /* Remove _dl_rtld_map from the chain. */
+ _dl_rtld_map.l_prev->l_next = _dl_rtld_map.l_next;
+ if (_dl_rtld_map.l_next)
+ _dl_rtld_map.l_next->l_prev = _dl_rtld_map.l_prev;
+
+ if (_dl_rtld_map.l_opencount)
{
- /* No DT_NEEDED entry referred to the interpreter object itself,
- so remove it from the list of visible objects. */
- _dl_rtld_map.l_prev->l_next = _dl_rtld_map.l_next;
- _dl_rtld_map.l_next->l_prev = _dl_rtld_map.l_prev;
+ /* Some DT_NEEDED entry referred to the interpreter object itself, so
+ put it back in the list of visible objects. We insert it into the
+ chain in symbol search order because gdb uses the chain's order as
+ its symbol search order. */
+ unsigned int i = 1;
+ while (l->l_searchlist[i] != &_dl_rtld_map)
+ ++i;
+ _dl_rtld_map.l_prev = l->l_searchlist[i - 1];
+ _dl_rtld_map.l_next = (i + 1 < l->l_nsearchlist ?
+ l->l_searchlist[i + 1] : NULL);
+ assert (_dl_rtld_map.l_prev->l_next == _dl_rtld_map.l_next);
+ _dl_rtld_map.l_prev->l_next = &_dl_rtld_map;
+ if (_dl_rtld_map.l_next)
+ {
+ assert (_dl_rtld_map.l_next->l_prev == _dl_rtld_map.l_prev);
+ _dl_rtld_map.l_next->l_prev = &_dl_rtld_map;
+ }
}
- if (list_only)
+ if (mode != normal)
{
/* We were run just to list the shared libraries. It is
important that we do this before real relocation, because the
" (0x", bp, ")\n", NULL);
}
- for (i = 1; i < _dl_argc; ++i)
- {
- const Elf32_Sym *ref = NULL;
- struct link_map *scope[2] ={ _dl_loaded, NULL };
- Elf32_Addr loadbase
- = _dl_lookup_symbol (_dl_argv[i], &ref, scope, "argument", 0, 0);
- char buf[20], *bp;
- buf[sizeof buf - 1] = '\0';
- bp = _itoa (ref->st_value, &buf[sizeof buf - 1], 16, 0);
- while (&buf[sizeof buf - 1] - bp < sizeof loadbase * 2)
- *--bp = '0';
- _dl_sysdep_message (_dl_argv[i], " found at 0x", bp, NULL);
- buf[sizeof buf - 1] = '\0';
- bp = _itoa (loadbase, &buf[sizeof buf - 1], 16, 0);
- while (&buf[sizeof buf - 1] - bp < sizeof loadbase * 2)
- *--bp = '0';
- _dl_sysdep_message (" in object at 0x", bp, "\n", NULL);
- }
+ if (mode != trace)
+ for (i = 1; i < _dl_argc; ++i)
+ {
+ const ElfW(Sym) *ref = NULL;
+ ElfW(Addr) loadbase = _dl_lookup_symbol (_dl_argv[i], &ref,
+ &_dl_default_scope[2],
+ "argument",
+ DL_LOOKUP_NOPLT);
+ char buf[20], *bp;
+ buf[sizeof buf - 1] = '\0';
+ bp = _itoa (ref->st_value, &buf[sizeof buf - 1], 16, 0);
+ while (&buf[sizeof buf - 1] - bp < sizeof loadbase * 2)
+ *--bp = '0';
+ _dl_sysdep_message (_dl_argv[i], " found at 0x", bp, NULL);
+ buf[sizeof buf - 1] = '\0';
+ bp = _itoa (loadbase, &buf[sizeof buf - 1], 16, 0);
+ while (&buf[sizeof buf - 1] - bp < sizeof loadbase * 2)
+ *--bp = '0';
+ _dl_sysdep_message (" in object at 0x", bp, "\n", NULL);
+ }
_exit (0);
}
- lazy = !_dl_secure && *(getenv ("LD_BIND_NOW") ?: "") == '\0';
-
- /* Now we have all the objects loaded. Relocate them all except for
- the dynamic linker itself. We do this in reverse order so that
- copy relocs of earlier objects overwrite the data written by later
- objects. We do not re-relocate the dynamic linker itself in this
- loop because that could result in the GOT entries for functions we
- call being changed, and that would break us. It is safe to
- relocate the dynamic linker out of order because it has no copy
- relocs (we know that because it is self-contained). */
- l = _dl_loaded;
- while (l->l_next)
- l = l->l_next;
- do
- {
- if (l != &_dl_rtld_map)
- _dl_relocate_object (l, lazy);
- l = l->l_prev;
- } while (l);
-
- /* Do any necessary cleanups for the startup OS interface code.
- We do these now so that no calls are made after rtld re-relocation
- which might be resolved to different functions than we expect.
- We cannot do this before relocating the other objects because
- _dl_relocate_object might need to call `mprotect' for DT_TEXTREL. */
- _dl_sysdep_start_cleanup ();
-
- if (_dl_rtld_map.l_opencount > 0)
- /* There was an explicit ref to the dynamic linker as a shared lib.
- Re-relocate ourselves with user-controlled symbol definitions. */
- _dl_relocate_object (&_dl_rtld_map, lazy);
-
- /* Tell the debugger where to find the map of loaded objects. */
- dl_r_debug.r_version = 1 /* R_DEBUG_VERSION XXX */;
- dl_r_debug.r_ldbase = _dl_rtld_map.l_addr; /* Record our load address. */
- dl_r_debug.r_map = _dl_loaded;
- dl_r_debug.r_brk = (Elf32_Addr) &_dl_r_debug_state;
+ lazy = !__libc_enable_secure && *(getenv ("LD_BIND_NOW") ?: "") == '\0';
+
+ {
+ /* Now we have all the objects loaded. Relocate them all except for
+ the dynamic linker itself. We do this in reverse order so that copy
+ relocs of earlier objects overwrite the data written by later
+ objects. We do not re-relocate the dynamic linker itself in this
+ loop because that could result in the GOT entries for functions we
+ call being changed, and that would break us. It is safe to relocate
+ the dynamic linker out of order because it has no copy relocs (we
+ know that because it is self-contained). */
+
+ l = _dl_loaded;
+ while (l->l_next)
+ l = l->l_next;
+ do
+ {
+ if (l != &_dl_rtld_map)
+ {
+ _dl_relocate_object (l, _dl_object_relocation_scope (l), lazy);
+ *_dl_global_scope_end = NULL;
+ }
+ l = l->l_prev;
+ } while (l);
+
+ /* Do any necessary cleanups for the startup OS interface code.
+ We do these now so that no calls are made after rtld re-relocation
+ which might be resolved to different functions than we expect.
+ We cannot do this before relocating the other objects because
+ _dl_relocate_object might need to call `mprotect' for DT_TEXTREL. */
+ _dl_sysdep_start_cleanup ();
+
+ if (_dl_rtld_map.l_opencount > 0)
+ /* There was an explicit ref to the dynamic linker as a shared lib.
+ Re-relocate ourselves with user-controlled symbol definitions. */
+ _dl_relocate_object (&_dl_rtld_map, &_dl_default_scope[2], 0);
+ }
+
+ {
+ /* Initialize _r_debug. */
+ struct r_debug *r = _dl_debug_initialize (_dl_rtld_map.l_addr);
+
+ l = _dl_loaded;
+
+#ifdef ELF_MACHINE_DEBUG_SETUP
+
+ /* Some machines (e.g. MIPS) don't use DT_DEBUG in this way. */
+
+ ELF_MACHINE_DEBUG_SETUP (l, r);
+ ELF_MACHINE_DEBUG_SETUP (&_dl_rtld_map, r);
+
+#else
+
+ if (l->l_info[DT_DEBUG])
+ /* There is a DT_DEBUG entry in the dynamic section. Fill it in
+ with the run-time address of the r_debug structure */
+ l->l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
+
+ /* Fill in the pointer in the dynamic linker's own dynamic section, in
+ case you run gdb on the dynamic linker directly. */
+ if (_dl_rtld_map.l_info[DT_DEBUG])
+ _dl_rtld_map.l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
+
+#endif
+
+ /* Notify the debugger that all objects are now mapped in. */
+ r->r_state = RT_ADD;
+ _dl_debug_state ();
+ }
if (_dl_rtld_map.l_info[DT_INIT])
{
dynamic linker. There is no additional initialization
required for the ABI-compliant dynamic linker. */
- (*(void (*) (void)) (_dl_rtld_map.l_addr +
- _dl_rtld_map.l_info[DT_INIT]->d_un.d_ptr)) ();
+ (*(void (*) (int, char **, char**))
+ (_dl_rtld_map.l_addr + _dl_rtld_map.l_info[DT_INIT]->d_un.d_ptr))
+ (0, NULL, NULL);
/* Clear the field so a future dlopen won't run it again. */
_dl_rtld_map.l_info[DT_INIT] = NULL;
}
+ /* We finished the intialization and will start up. */
+ _dl_starting_up = 1;
+
/* Once we return, _dl_sysdep_start will invoke
the DT_INIT functions and then *USER_ENTRY. */
}
-
-/* This function exists solely to have a breakpoint set on it by the
- debugger. */
-void
-_dl_r_debug_state (void)
-{
-}