diff options
Diffstat (limited to 'REORG.TODO/elf/rtld.c')
-rw-r--r-- | REORG.TODO/elf/rtld.c | 2652 |
1 files changed, 2652 insertions, 0 deletions
diff --git a/REORG.TODO/elf/rtld.c b/REORG.TODO/elf/rtld.c new file mode 100644 index 0000000000..3746653afb --- /dev/null +++ b/REORG.TODO/elf/rtld.c @@ -0,0 +1,2652 @@ +/* Run time dynamic linker. + Copyright (C) 1995-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <dlfcn.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <ldsodefs.h> +#include <_itoa.h> +#include <entry.h> +#include <fpu_control.h> +#include <hp-timing.h> +#include <libc-lock.h> +#include "dynamic-link.h" +#include <dl-librecon.h> +#include <unsecvars.h> +#include <dl-cache.h> +#include <dl-osinfo.h> +#include <dl-procinfo.h> +#include <tls.h> +#include <stap-probe.h> +#include <stackinfo.h> + +#include <assert.h> + +/* Avoid PLT use for our local calls at startup. */ +extern __typeof (__mempcpy) __mempcpy attribute_hidden; + +/* GCC has mental blocks about _exit. */ +extern __typeof (_exit) exit_internal asm ("_exit") attribute_hidden; +#define _exit exit_internal + +/* Helper function to handle errors while resolving symbols. */ +static void print_unresolved (int errcode, const char *objname, + const char *errsting); + +/* Helper function to handle errors when a version is missing. */ +static void print_missing_version (int errcode, const char *objname, + const char *errsting); + +/* Print the various times we collected. */ +static void print_statistics (hp_timing_t *total_timep); + +/* Add audit objects. */ +static void process_dl_audit (char *str); + +/* This is a list of all the modes the dynamic loader can be in. */ +enum mode { normal, list, verify, trace }; + +/* Process all environments variables the dynamic linker must recognize. + Since all of them start with `LD_' we are a bit smarter while finding + all the entries. */ +static void process_envvars (enum mode *modep); + +#ifdef DL_ARGV_NOT_RELRO +int _dl_argc attribute_hidden; +char **_dl_argv = NULL; +/* Nonzero if we were run directly. */ +unsigned int _dl_skip_args attribute_hidden; +#else +int _dl_argc attribute_relro attribute_hidden; +char **_dl_argv attribute_relro = NULL; +unsigned int _dl_skip_args attribute_relro attribute_hidden; +#endif +rtld_hidden_data_def (_dl_argv) + +#ifndef THREAD_SET_STACK_GUARD +/* Only exported for architectures that don't store the stack guard canary + in thread local area. */ +uintptr_t __stack_chk_guard attribute_relro; +#endif + +/* Only exported for architectures that don't store the pointer guard + value in thread local area. */ +uintptr_t __pointer_chk_guard_local + attribute_relro attribute_hidden __attribute__ ((nocommon)); +#ifndef THREAD_SET_POINTER_GUARD +strong_alias (__pointer_chk_guard_local, __pointer_chk_guard) +#endif + + +/* List of auditing DSOs. */ +static struct audit_list +{ + const char *name; + struct audit_list *next; +} *audit_list; + +#ifndef HAVE_INLINED_SYSCALLS +/* 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 = 0; +rtld_hidden_def (_dl_starting_up) +#endif + +/* This is the structure which defines all variables global to ld.so + (except those which cannot be added for some reason). */ +struct rtld_global _rtld_global = + { + /* Generally the default presumption without further information is an + * executable stack but this is not true for all platforms. */ + ._dl_stack_flags = DEFAULT_STACK_PERMS, +#ifdef _LIBC_REENTRANT + ._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER, + ._dl_load_write_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER, +#endif + ._dl_nns = 1, + ._dl_ns = + { +#ifdef _LIBC_REENTRANT + [LM_ID_BASE] = { ._ns_unique_sym_table + = { .lock = _RTLD_LOCK_RECURSIVE_INITIALIZER } } +#endif + } + }; +/* If we would use strong_alias here the compiler would see a + non-hidden definition. This would undo the effect of the previous + declaration. So spell out was strong_alias does plus add the + visibility attribute. */ +extern struct rtld_global _rtld_local + __attribute__ ((alias ("_rtld_global"), visibility ("hidden"))); + + +/* This variable is similar to _rtld_local, but all values are + read-only after relocation. */ +struct rtld_global_ro _rtld_global_ro attribute_relro = + { + /* Get architecture specific initializer. */ +#include <dl-procinfo.c> +#ifdef NEED_DL_SYSINFO + ._dl_sysinfo = DL_SYSINFO_DEFAULT, +#endif + ._dl_debug_fd = STDERR_FILENO, + ._dl_use_load_bias = -2, + ._dl_correct_cache_id = _DL_CACHE_DEFAULT_ID, +#if !HAVE_TUNABLES + ._dl_hwcap_mask = HWCAP_IMPORTANT, +#endif + ._dl_lazy = 1, + ._dl_fpu_control = _FPU_DEFAULT, + ._dl_pagesize = EXEC_PAGESIZE, + ._dl_inhibit_cache = 0, + + /* Function pointers. */ + ._dl_debug_printf = _dl_debug_printf, + ._dl_mcount = _dl_mcount, + ._dl_lookup_symbol_x = _dl_lookup_symbol_x, + ._dl_check_caller = _dl_check_caller, + ._dl_open = _dl_open, + ._dl_close = _dl_close, + ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft, +#ifdef HAVE_DL_DISCOVER_OSVERSION + ._dl_discover_osversion = _dl_discover_osversion +#endif + }; +/* If we would use strong_alias here the compiler would see a + non-hidden definition. This would undo the effect of the previous + declaration. So spell out was strong_alias does plus add the + visibility attribute. */ +extern struct rtld_global_ro _rtld_local_ro + __attribute__ ((alias ("_rtld_global_ro"), visibility ("hidden"))); + + +static void dl_main (const ElfW(Phdr) *phdr, ElfW(Word) phnum, + ElfW(Addr) *user_entry, ElfW(auxv_t) *auxv); + +/* These two variables cannot be moved into .data.rel.ro. */ +static struct libname_list _dl_rtld_libname; +static struct libname_list _dl_rtld_libname2; + +/* Variable for statistics. */ +#ifndef HP_TIMING_NONAVAIL +static hp_timing_t relocate_time; +static hp_timing_t load_time attribute_relro; +static hp_timing_t start_time attribute_relro; +#endif + +/* Additional definitions needed by TLS initialization. */ +#ifdef TLS_INIT_HELPER +TLS_INIT_HELPER +#endif + +/* Helper function for syscall implementation. */ +#ifdef DL_SYSINFO_IMPLEMENTATION +DL_SYSINFO_IMPLEMENTATION +#endif + +/* Before ld.so is relocated we must not access variables which need + relocations. This means variables which are exported. Variables + declared as static are fine. If we can mark a variable hidden this + is fine, too. The latter is important here. We can avoid setting + up a temporary link map for ld.so if we can mark _rtld_global as + hidden. */ +#ifdef PI_STATIC_AND_HIDDEN +# define DONT_USE_BOOTSTRAP_MAP 1 +#endif + +#ifdef DONT_USE_BOOTSTRAP_MAP +static ElfW(Addr) _dl_start_final (void *arg); +#else +struct dl_start_final_info +{ + struct link_map l; +#if !defined HP_TIMING_NONAVAIL && HP_TIMING_INLINE + hp_timing_t start_time; +#endif +}; +static ElfW(Addr) _dl_start_final (void *arg, + struct dl_start_final_info *info); +#endif + +/* These defined magically in the linker script. */ +extern char _begin[] attribute_hidden; +extern char _etext[] attribute_hidden; +extern char _end[] attribute_hidden; + + +#ifdef RTLD_START +RTLD_START +#else +# error "sysdeps/MACHINE/dl-machine.h fails to define RTLD_START" +#endif + +/* This is the second half of _dl_start (below). It can be inlined safely + under DONT_USE_BOOTSTRAP_MAP, where it is careful not to make any GOT + references. When the tools don't permit us to avoid using a GOT entry + for _dl_rtld_global (no attribute_hidden support), we must make sure + this function is not inlined (see below). */ + +#ifdef DONT_USE_BOOTSTRAP_MAP +static inline ElfW(Addr) __attribute__ ((always_inline)) +_dl_start_final (void *arg) +#else +static ElfW(Addr) __attribute__ ((noinline)) +_dl_start_final (void *arg, struct dl_start_final_info *info) +#endif +{ + ElfW(Addr) start_addr; + + if (HP_SMALL_TIMING_AVAIL) + { + /* If it hasn't happen yet record the startup time. */ + if (! HP_TIMING_INLINE) + HP_TIMING_NOW (start_time); +#if !defined DONT_USE_BOOTSTRAP_MAP && !defined HP_TIMING_NONAVAIL + else + start_time = info->start_time; +#endif + } + + /* Transfer data about ourselves to the permanent link_map structure. */ +#ifndef DONT_USE_BOOTSTRAP_MAP + GL(dl_rtld_map).l_addr = info->l.l_addr; + GL(dl_rtld_map).l_ld = info->l.l_ld; + memcpy (GL(dl_rtld_map).l_info, info->l.l_info, + sizeof GL(dl_rtld_map).l_info); + GL(dl_rtld_map).l_mach = info->l.l_mach; + GL(dl_rtld_map).l_relocated = 1; +#endif + _dl_setup_hash (&GL(dl_rtld_map)); + GL(dl_rtld_map).l_real = &GL(dl_rtld_map); + GL(dl_rtld_map).l_map_start = (ElfW(Addr)) _begin; + GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end; + GL(dl_rtld_map).l_text_end = (ElfW(Addr)) _etext; + /* Copy the TLS related data if necessary. */ +#ifndef DONT_USE_BOOTSTRAP_MAP +# if NO_TLS_OFFSET != 0 + GL(dl_rtld_map).l_tls_offset = NO_TLS_OFFSET; +# endif +#endif + + HP_TIMING_NOW (GL(dl_cpuclock_offset)); + + /* Initialize the stack end variable. */ + __libc_stack_end = __builtin_frame_address (0); + + /* Call the OS-dependent function to set up life so we can do things like + 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. */ + start_addr = _dl_sysdep_start (arg, &dl_main); + +#ifndef HP_TIMING_NONAVAIL + hp_timing_t rtld_total_time; + if (HP_SMALL_TIMING_AVAIL) + { + hp_timing_t end_time; + + /* Get the current time. */ + HP_TIMING_NOW (end_time); + + /* Compute the difference. */ + HP_TIMING_DIFF (rtld_total_time, start_time, end_time); + } +#endif + + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS)) + { +#ifndef HP_TIMING_NONAVAIL + print_statistics (&rtld_total_time); +#else + print_statistics (NULL); +#endif + } + + return start_addr; +} + +static ElfW(Addr) __attribute_used__ internal_function +_dl_start (void *arg) +{ +#ifdef DONT_USE_BOOTSTRAP_MAP +# define bootstrap_map GL(dl_rtld_map) +#else + struct dl_start_final_info info; +# define bootstrap_map info.l +#endif + + /* This #define produces dynamic linking inline functions for + bootstrap relocation instead of general-purpose relocation. + Since ld.so must not have any undefined symbols the result + is trivial: always the map of ld.so itself. */ +#define RTLD_BOOTSTRAP +#define RESOLVE_MAP(sym, version, flags) (&bootstrap_map) +#include "dynamic-link.h" + + if (HP_TIMING_INLINE && HP_SMALL_TIMING_AVAIL) +#ifdef DONT_USE_BOOTSTRAP_MAP + HP_TIMING_NOW (start_time); +#else + HP_TIMING_NOW (info.start_time); +#endif + + /* Partly clean the `bootstrap_map' structure up. Don't use + `memset' since it might not be built in or inlined and we cannot + make function calls at this point. Use '__builtin_memset' if we + know it is available. We do not have to clear the memory if we + do not have to use the temporary bootstrap_map. Global variables + are initialized to zero by default. */ +#ifndef DONT_USE_BOOTSTRAP_MAP +# ifdef HAVE_BUILTIN_MEMSET + __builtin_memset (bootstrap_map.l_info, '\0', sizeof (bootstrap_map.l_info)); +# else + for (size_t cnt = 0; + cnt < sizeof (bootstrap_map.l_info) / sizeof (bootstrap_map.l_info[0]); + ++cnt) + bootstrap_map.l_info[cnt] = 0; +# endif +#endif + + /* Figure out the run-time load address of the dynamic linker itself. */ + bootstrap_map.l_addr = elf_machine_load_address (); + + /* Read our own dynamic section and fill in the info array. */ + bootstrap_map.l_ld = (void *) bootstrap_map.l_addr + elf_machine_dynamic (); + elf_get_dynamic_info (&bootstrap_map, NULL); + +#if NO_TLS_OFFSET != 0 + bootstrap_map.l_tls_offset = NO_TLS_OFFSET; +#endif + +#ifdef ELF_MACHINE_BEFORE_RTLD_RELOC + ELF_MACHINE_BEFORE_RTLD_RELOC (bootstrap_map.l_info); +#endif + + if (bootstrap_map.l_addr || ! bootstrap_map.l_info[VALIDX(DT_GNU_PRELINKED)]) + { + /* Relocate ourselves so we can do normal function calls and + data access using the global offset table. */ + + ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, 0, 0); + } + bootstrap_map.l_relocated = 1; + + /* Please note that we don't allow profiling of this object and + therefore need not test whether we have to allocate the array + for the relocation results (as done in dl-reloc.c). */ + + /* Now life is sane; we can call functions and access global data. + Set up to use the operating system facilities, and find out from + the operating system's program loader where to find the program + header table in core. Put the rest of _dl_start into a separate + function, that way the compiler cannot put accesses to the GOT + before ELF_DYNAMIC_RELOCATE. */ + { +#ifdef DONT_USE_BOOTSTRAP_MAP + ElfW(Addr) entry = _dl_start_final (arg); +#else + ElfW(Addr) entry = _dl_start_final (arg, &info); +#endif + +#ifndef ELF_MACHINE_START_ADDRESS +# define ELF_MACHINE_START_ADDRESS(map, start) (start) +#endif + + return ELF_MACHINE_START_ADDRESS (GL(dl_ns)[LM_ID_BASE]._ns_loaded, entry); + } +} + + + +/* Now life is peachy; we can do all normal operations. + On to the real work. */ + +/* Some helper functions. */ + +/* Arguments to relocate_doit. */ +struct relocate_args +{ + struct link_map *l; + int reloc_mode; +}; + +struct map_args +{ + /* Argument to map_doit. */ + const char *str; + struct link_map *loader; + int mode; + /* Return value of map_doit. */ + struct link_map *map; +}; + +struct dlmopen_args +{ + const char *fname; + struct link_map *map; +}; + +struct lookup_args +{ + const char *name; + struct link_map *map; + void *result; +}; + +/* Arguments to version_check_doit. */ +struct version_check_args +{ + int doexit; + int dotrace; +}; + +static void +relocate_doit (void *a) +{ + struct relocate_args *args = (struct relocate_args *) a; + + _dl_relocate_object (args->l, args->l->l_scope, args->reloc_mode, 0); +} + +static void +map_doit (void *a) +{ + struct map_args *args = (struct map_args *) a; + int type = (args->mode == __RTLD_OPENEXEC) ? lt_executable : lt_library; + args->map = _dl_map_object (args->loader, args->str, type, 0, + args->mode, LM_ID_BASE); +} + +static void +dlmopen_doit (void *a) +{ + struct dlmopen_args *args = (struct dlmopen_args *) a; + args->map = _dl_open (args->fname, + (RTLD_LAZY | __RTLD_DLOPEN | __RTLD_AUDIT + | __RTLD_SECURE), + dl_main, LM_ID_NEWLM, _dl_argc, _dl_argv, + __environ); +} + +static void +lookup_doit (void *a) +{ + struct lookup_args *args = (struct lookup_args *) a; + const ElfW(Sym) *ref = NULL; + args->result = NULL; + lookup_t l = _dl_lookup_symbol_x (args->name, args->map, &ref, + args->map->l_local_scope, NULL, 0, + DL_LOOKUP_RETURN_NEWEST, NULL); + if (ref != NULL) + args->result = DL_SYMBOL_ADDRESS (l, ref); +} + +static void +version_check_doit (void *a) +{ + struct version_check_args *args = (struct version_check_args *) a; + if (_dl_check_all_versions (GL(dl_ns)[LM_ID_BASE]._ns_loaded, 1, + args->dotrace) && args->doexit) + /* We cannot start the application. Abort now. */ + _exit (1); +} + + +static inline struct link_map * +find_needed (const char *name) +{ + struct r_scope_elem *scope = &GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_searchlist; + unsigned int n = scope->r_nlist; + + while (n-- > 0) + if (_dl_name_match_p (name, scope->r_list[n])) + return scope->r_list[n]; + + /* Should never happen. */ + return NULL; +} + +static int +match_version (const char *string, struct link_map *map) +{ + const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + ElfW(Verdef) *def; + +#define VERDEFTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (DT_VERDEF)) + if (map->l_info[VERDEFTAG] == NULL) + /* The file has no symbol versioning. */ + return 0; + + def = (ElfW(Verdef) *) ((char *) map->l_addr + + map->l_info[VERDEFTAG]->d_un.d_ptr); + while (1) + { + ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) def + def->vd_aux); + + /* Compare the version strings. */ + if (strcmp (string, strtab + aux->vda_name) == 0) + /* Bingo! */ + return 1; + + /* If no more definitions we failed to find what we want. */ + if (def->vd_next == 0) + break; + + /* Next definition. */ + def = (ElfW(Verdef) *) ((char *) def + def->vd_next); + } + + return 0; +} + +static bool tls_init_tp_called; + +static void * +init_tls (void) +{ + /* Number of elements in the static TLS block. */ + GL(dl_tls_static_nelem) = GL(dl_tls_max_dtv_idx); + + /* Do not do this twice. The audit interface might have required + the DTV interfaces to be set up early. */ + if (GL(dl_initial_dtv) != NULL) + return NULL; + + /* Allocate the array which contains the information about the + dtv slots. We allocate a few entries more than needed to + avoid the need for reallocation. */ + size_t nelem = GL(dl_tls_max_dtv_idx) + 1 + TLS_SLOTINFO_SURPLUS; + + /* Allocate. */ + GL(dl_tls_dtv_slotinfo_list) = (struct dtv_slotinfo_list *) + calloc (sizeof (struct dtv_slotinfo_list) + + nelem * sizeof (struct dtv_slotinfo), 1); + /* No need to check the return value. If memory allocation failed + the program would have been terminated. */ + + struct dtv_slotinfo *slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo; + GL(dl_tls_dtv_slotinfo_list)->len = nelem; + GL(dl_tls_dtv_slotinfo_list)->next = NULL; + + /* Fill in the information from the loaded modules. No namespace + but the base one can be filled at this time. */ + assert (GL(dl_ns)[LM_ID_BASE + 1]._ns_loaded == NULL); + int i = 0; + for (struct link_map *l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l != NULL; + l = l->l_next) + if (l->l_tls_blocksize != 0) + { + /* This is a module with TLS data. Store the map reference. + The generation counter is zero. */ + slotinfo[i].map = l; + /* slotinfo[i].gen = 0; */ + ++i; + } + assert (i == GL(dl_tls_max_dtv_idx)); + + /* Compute the TLS offsets for the various blocks. */ + _dl_determine_tlsoffset (); + + /* Construct the static TLS block and the dtv for the initial + thread. For some platforms this will include allocating memory + for the thread descriptor. The memory for the TLS block will + never be freed. It should be allocated accordingly. The dtv + array can be changed if dynamic loading requires it. */ + void *tcbp = _dl_allocate_tls_storage (); + if (tcbp == NULL) + _dl_fatal_printf ("\ +cannot allocate TLS data structures for initial thread"); + + /* Store for detection of the special case by __tls_get_addr + so it knows not to pass this dtv to the normal realloc. */ + GL(dl_initial_dtv) = GET_DTV (tcbp); + + /* And finally install it for the main thread. */ + const char *lossage = TLS_INIT_TP (tcbp); + if (__glibc_unlikely (lossage != NULL)) + _dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage); + tls_init_tp_called = true; + + return tcbp; +} + +static unsigned int +do_preload (const char *fname, struct link_map *main_map, const char *where) +{ + const char *objname; + const char *err_str = NULL; + struct map_args args; + bool malloced; + + args.str = fname; + args.loader = main_map; + args.mode = __RTLD_SECURE; + + unsigned int old_nloaded = GL(dl_ns)[LM_ID_BASE]._ns_nloaded; + + (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit, &args); + if (__glibc_unlikely (err_str != NULL)) + { + _dl_error_printf ("\ +ERROR: ld.so: object '%s' from %s cannot be preloaded (%s): ignored.\n", + fname, where, err_str); + /* No need to call free, this is still before + the libc's malloc is used. */ + } + else if (GL(dl_ns)[LM_ID_BASE]._ns_nloaded != old_nloaded) + /* It is no duplicate. */ + return 1; + + /* Nothing loaded. */ + return 0; +} + +#if defined SHARED && defined _LIBC_REENTRANT \ + && defined __rtld_lock_default_lock_recursive +static void +rtld_lock_default_lock_recursive (void *lock) +{ + __rtld_lock_default_lock_recursive (lock); +} + +static void +rtld_lock_default_unlock_recursive (void *lock) +{ + __rtld_lock_default_unlock_recursive (lock); +} +#endif + + +static void +security_init (void) +{ + /* Set up the stack checker's canary. */ + uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random); +#ifdef THREAD_SET_STACK_GUARD + THREAD_SET_STACK_GUARD (stack_chk_guard); +#else + __stack_chk_guard = stack_chk_guard; +#endif + + /* Set up the pointer guard as well, if necessary. */ + uintptr_t pointer_chk_guard + = _dl_setup_pointer_guard (_dl_random, stack_chk_guard); +#ifdef THREAD_SET_POINTER_GUARD + THREAD_SET_POINTER_GUARD (pointer_chk_guard); +#endif + __pointer_chk_guard_local = pointer_chk_guard; + + /* We do not need the _dl_random value anymore. The less + information we leave behind, the better, so clear the + variable. */ + _dl_random = NULL; +} + +#include "setup-vdso.h" + +/* The library search path. */ +static const char *library_path attribute_relro; +/* The list preloaded objects. */ +static const char *preloadlist attribute_relro; +/* Nonzero if information about versions has to be printed. */ +static int version_info attribute_relro; + +static void +dl_main (const ElfW(Phdr) *phdr, + ElfW(Word) phnum, + ElfW(Addr) *user_entry, + ElfW(auxv_t) *auxv) +{ + const ElfW(Phdr) *ph; + enum mode mode; + struct link_map *main_map; + size_t file_size; + char *file; + bool has_interp = false; + unsigned int i; + bool prelinked = false; + bool rtld_is_main = false; +#ifndef HP_TIMING_NONAVAIL + hp_timing_t start; + hp_timing_t stop; + hp_timing_t diff; +#endif + void *tcbp = NULL; + + GL(dl_init_static_tls) = &_dl_nothread_init_static_tls; + +#if defined SHARED && defined _LIBC_REENTRANT \ + && defined __rtld_lock_default_lock_recursive + GL(dl_rtld_lock_recursive) = rtld_lock_default_lock_recursive; + GL(dl_rtld_unlock_recursive) = rtld_lock_default_unlock_recursive; +#endif + + /* The explicit initialization here is cheaper than processing the reloc + in the _rtld_local definition's initializer. */ + GL(dl_make_stack_executable_hook) = &_dl_make_stack_executable; + + /* Process the environment variable which control the behaviour. */ + process_envvars (&mode); + +#ifndef HAVE_INLINED_SYSCALLS + /* Set up a flag which tells we are just starting. */ + _dl_starting_up = 1; +#endif + + if (*user_entry == (ElfW(Addr)) ENTRY_POINT) + { + /* Ho ho. We are not the program interpreter! We are the program + itself! This means someone ran ld.so as a command. Well, that + might be convenient to do sometimes. We support it by + interpreting the args like this: + + ld.so PROGRAM ARGS... + + The first argument is the name of a file containing an ELF + executable we will load and run with the following arguments. + To simplify life here, PROGRAM is searched for using the + normal rules for shared objects, rather than $PATH or anything + like that. We just load it and use its entry point; we don't + pay attention to its PT_INTERP command (we are the interpreter + ourselves). This is an easy way to test a new ld.so before + installing it. */ + rtld_is_main = true; + + /* Note the place where the dynamic linker actually came from. */ + GL(dl_rtld_map).l_name = rtld_progname; + + while (_dl_argc > 1) + if (! strcmp (_dl_argv[1], "--list")) + { + mode = list; + GLRO(dl_lazy) = -1; /* This means do no dependency analysis. */ + + ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } + else if (! strcmp (_dl_argv[1], "--verify")) + { + mode = verify; + + ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } + else if (! strcmp (_dl_argv[1], "--inhibit-cache")) + { + GLRO(dl_inhibit_cache) = 1; + ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } + else if (! strcmp (_dl_argv[1], "--library-path") + && _dl_argc > 2) + { + library_path = _dl_argv[2]; + + _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } + else if (! strcmp (_dl_argv[1], "--inhibit-rpath") + && _dl_argc > 2) + { + GLRO(dl_inhibit_rpath) = _dl_argv[2]; + + _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } + else if (! strcmp (_dl_argv[1], "--audit") && _dl_argc > 2) + { + process_dl_audit (_dl_argv[2]); + + _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } + else + break; + + /* If we have no further argument the program was called incorrectly. + Grant the user some education. */ + if (_dl_argc < 2) + _dl_fatal_printf ("\ +Usage: ld.so [OPTION]... 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\ +loader to load the helper program from this file. This helper program loads\n\ +the shared libraries needed by the program executable, prepares the program\n\ +to run, and runs it. You may invoke this helper program directly from the\n\ +command line to load and run an ELF executable file; this is like executing\n\ +that file itself, but always uses this helper program from the file you\n\ +specified, instead of the helper program file specified in the executable\n\ +file you run. This is mostly of use for maintainers to test new versions\n\ +of this helper program; chances are you did not intend to run this program.\n\ +\n\ + --list list all dependencies and how they are resolved\n\ + --verify verify that given object really is a dynamically linked\n\ + object we can handle\n\ + --inhibit-cache Do not use " LD_SO_CACHE "\n\ + --library-path PATH use given PATH instead of content of the environment\n\ + variable LD_LIBRARY_PATH\n\ + --inhibit-rpath LIST ignore RUNPATH and RPATH information in object names\n\ + in LIST\n\ + --audit LIST use objects named in LIST as auditors\n"); + + ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + + /* The initialization of _dl_stack_flags done below assumes the + executable's PT_GNU_STACK may have been honored by the kernel, and + so a PT_GNU_STACK with PF_X set means the stack started out with + execute permission. However, this is not really true if the + dynamic linker is the executable the kernel loaded. For this + case, we must reinitialize _dl_stack_flags to match the dynamic + linker itself. If the dynamic linker was built with a + PT_GNU_STACK, then the kernel may have loaded us with a + nonexecutable stack that we will have to make executable when we + load the program below unless it has a PT_GNU_STACK indicating + nonexecutable stack is ok. */ + + for (ph = phdr; ph < &phdr[phnum]; ++ph) + if (ph->p_type == PT_GNU_STACK) + { + GL(dl_stack_flags) = ph->p_flags; + break; + } + + if (__builtin_expect (mode, normal) == verify) + { + const char *objname; + const char *err_str = NULL; + struct map_args args; + bool malloced; + + args.str = rtld_progname; + args.loader = NULL; + args.mode = __RTLD_OPENEXEC; + (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit, + &args); + if (__glibc_unlikely (err_str != NULL)) + /* We don't free the returned string, the programs stops + anyway. */ + _exit (EXIT_FAILURE); + } + else + { + HP_TIMING_NOW (start); + _dl_map_object (NULL, rtld_progname, lt_executable, 0, + __RTLD_OPENEXEC, LM_ID_BASE); + HP_TIMING_NOW (stop); + + HP_TIMING_DIFF (load_time, start, stop); + } + + /* Now the map for the main executable is available. */ + main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + + if (__builtin_expect (mode, normal) == normal + && GL(dl_rtld_map).l_info[DT_SONAME] != NULL + && main_map->l_info[DT_SONAME] != NULL + && strcmp ((const char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB]) + + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_val, + (const char *) D_PTR (main_map, l_info[DT_STRTAB]) + + main_map->l_info[DT_SONAME]->d_un.d_val) == 0) + _dl_fatal_printf ("loader cannot load itself\n"); + + phdr = main_map->l_phdr; + phnum = main_map->l_phnum; + /* We overwrite here a pointer to a malloc()ed string. But since + the malloc() implementation used at this point is the dummy + implementations which has no real free() function it does not + makes sense to free the old string first. */ + main_map->l_name = (char *) ""; + *user_entry = main_map->l_entry; + +#ifdef HAVE_AUX_VECTOR + /* Adjust the on-stack auxiliary vector so that it looks like the + binary was executed directly. */ + for (ElfW(auxv_t) *av = auxv; av->a_type != AT_NULL; av++) + switch (av->a_type) + { + case AT_PHDR: + av->a_un.a_val = (uintptr_t) phdr; + break; + case AT_PHNUM: + av->a_un.a_val = phnum; + break; + case AT_ENTRY: + av->a_un.a_val = *user_entry; + break; + case AT_EXECFN: + av->a_un.a_val = (uintptr_t) _dl_argv[0]; + break; + } +#endif + } + else + { + /* Create a link_map for the executable itself. + This will be what dlopen on "" returns. */ + main_map = _dl_new_object ((char *) "", "", lt_executable, NULL, + __RTLD_OPENEXEC, LM_ID_BASE); + assert (main_map != NULL); + main_map->l_phdr = phdr; + main_map->l_phnum = phnum; + main_map->l_entry = *user_entry; + + /* Even though the link map is not yet fully initialized we can add + it to the map list since there are no possible users running yet. */ + _dl_add_to_namespace_list (main_map, LM_ID_BASE); + assert (main_map == GL(dl_ns)[LM_ID_BASE]._ns_loaded); + + /* At this point we are in a bit of trouble. We would have to + fill in the values for l_dev and l_ino. But in general we + do not know where the file is. We also do not handle AT_EXECFD + even if it would be passed up. + + We leave the values here defined to 0. This is normally no + problem as the program code itself is normally no shared + object and therefore cannot be loaded dynamically. Nothing + prevent the use of dynamic binaries and in these situations + we might get problems. We might not be able to find out + whether the object is already loaded. But since there is no + easy way out and because the dynamic binary must also not + have an SONAME we ignore this program for now. If it becomes + a problem we can force people using SONAMEs. */ + + /* We delay initializing the path structure until we got the dynamic + information for the program. */ + } + + main_map->l_map_end = 0; + main_map->l_text_end = 0; + /* Perhaps the executable has no PT_LOAD header entries at all. */ + main_map->l_map_start = ~0; + /* And it was opened directly. */ + ++main_map->l_direct_opencount; + + /* Scan the program header table for the dynamic section. */ + for (ph = phdr; ph < &phdr[phnum]; ++ph) + switch (ph->p_type) + { + case PT_PHDR: + /* Find out the load address. */ + main_map->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr; + break; + case PT_DYNAMIC: + /* This tells us where to find the dynamic section, + which tells us everything we need to do. */ + main_map->l_ld = (void *) main_map->l_addr + ph->p_vaddr; + break; + case PT_INTERP: + /* This "interpreter segment" was used by the program loader to + find the program interpreter, which is this program itself, the + dynamic linker. We note what name finds us, so that a future + 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. */ + _dl_rtld_libname.name = ((const char *) main_map->l_addr + + ph->p_vaddr); + /* _dl_rtld_libname.next = NULL; Already zero. */ + GL(dl_rtld_map).l_libname = &_dl_rtld_libname; + + /* Ordinarilly, we would get additional names for the loader from + our DT_SONAME. This can't happen if we were actually linked as + a static executable (detect this case when we have no DYNAMIC). + If so, assume the filename component of the interpreter path to + be our SONAME, and add it to our name list. */ + if (GL(dl_rtld_map).l_ld == NULL) + { + const char *p = NULL; + const char *cp = _dl_rtld_libname.name; + + /* Find the filename part of the path. */ + while (*cp != '\0') + if (*cp++ == '/') + p = cp; + + if (p != NULL) + { + _dl_rtld_libname2.name = p; + /* _dl_rtld_libname2.next = NULL; Already zero. */ + _dl_rtld_libname.next = &_dl_rtld_libname2; + } + } + + has_interp = true; + break; + case PT_LOAD: + { + ElfW(Addr) mapstart; + ElfW(Addr) allocend; + + /* Remember where the main program starts in memory. */ + mapstart = (main_map->l_addr + + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1))); + if (main_map->l_map_start > mapstart) + main_map->l_map_start = mapstart; + + /* Also where it ends. */ + allocend = main_map->l_addr + ph->p_vaddr + ph->p_memsz; + if (main_map->l_map_end < allocend) + main_map->l_map_end = allocend; + if ((ph->p_flags & PF_X) && allocend > main_map->l_text_end) + main_map->l_text_end = allocend; + } + break; + + case PT_TLS: + if (ph->p_memsz > 0) + { + /* Note that in the case the dynamic linker we duplicate work + here since we read the PT_TLS entry already in + _dl_start_final. But the result is repeatable so do not + check for this special but unimportant case. */ + main_map->l_tls_blocksize = ph->p_memsz; + main_map->l_tls_align = ph->p_align; + if (ph->p_align == 0) + main_map->l_tls_firstbyte_offset = 0; + else + main_map->l_tls_firstbyte_offset = (ph->p_vaddr + & (ph->p_align - 1)); + main_map->l_tls_initimage_size = ph->p_filesz; + main_map->l_tls_initimage = (void *) ph->p_vaddr; + + /* This image gets the ID one. */ + GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1; + } + break; + + case PT_GNU_STACK: + GL(dl_stack_flags) = ph->p_flags; + break; + + case PT_GNU_RELRO: + main_map->l_relro_addr = ph->p_vaddr; + main_map->l_relro_size = ph->p_memsz; + break; + } + + /* Adjust the address of the TLS initialization image in case + the executable is actually an ET_DYN object. */ + if (main_map->l_tls_initimage != NULL) + main_map->l_tls_initimage + = (char *) main_map->l_tls_initimage + main_map->l_addr; + if (! main_map->l_map_end) + main_map->l_map_end = ~0; + if (! main_map->l_text_end) + main_map->l_text_end = ~0; + if (! GL(dl_rtld_map).l_libname && GL(dl_rtld_map).l_name) + { + /* We were invoked directly, so the program might not have a + PT_INTERP. */ + _dl_rtld_libname.name = GL(dl_rtld_map).l_name; + /* _dl_rtld_libname.next = NULL; Already zero. */ + GL(dl_rtld_map).l_libname = &_dl_rtld_libname; + } + else + assert (GL(dl_rtld_map).l_libname); /* How else did we get here? */ + + /* If the current libname is different from the SONAME, add the + latter as well. */ + if (GL(dl_rtld_map).l_info[DT_SONAME] != NULL + && strcmp (GL(dl_rtld_map).l_libname->name, + (const char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB]) + + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_val) != 0) + { + static struct libname_list newname; + newname.name = ((char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB]) + + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_ptr); + newname.next = NULL; + newname.dont_free = 1; + + assert (GL(dl_rtld_map).l_libname->next == NULL); + GL(dl_rtld_map).l_libname->next = &newname; + } + /* The ld.so must be relocated since otherwise loading audit modules + will fail since they reuse the very same ld.so. */ + assert (GL(dl_rtld_map).l_relocated); + + if (! rtld_is_main) + { + /* Extract the contents of the dynamic section for easy access. */ + elf_get_dynamic_info (main_map, NULL); + /* Set up our cache of pointers into the hash table. */ + _dl_setup_hash (main_map); + } + + if (__builtin_expect (mode, normal) == verify) + { + /* We were called just to verify that this is a dynamic + executable using us as the program interpreter. Exit with an + error if we were not able to load the binary or no interpreter + is specified (i.e., this is no dynamically linked binary. */ + if (main_map->l_ld == NULL) + _exit (1); + + /* We allow here some platform specific code. */ +#ifdef DISTINGUISH_LIB_VERSIONS + DISTINGUISH_LIB_VERSIONS; +#endif + _exit (has_interp ? 0 : 2); + } + + struct link_map **first_preload = &GL(dl_rtld_map).l_next; + /* Set up the data structures for the system-supplied DSO early, + so they can influence _dl_init_paths. */ + setup_vdso (main_map, &first_preload); + +#ifdef DL_SYSDEP_OSCHECK + DL_SYSDEP_OSCHECK (_dl_fatal_printf); +#endif + + /* Initialize the data structures for the search paths for shared + objects. */ + _dl_init_paths (library_path); + + /* Initialize _r_debug. */ + struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr, + LM_ID_BASE); + r->r_state = RT_CONSISTENT; + + /* Put the link_map for ourselves on the chain so it can be found by + name. Note that at this point the global chain of link maps contains + exactly one element, which is pointed to by dl_loaded. */ + if (! GL(dl_rtld_map).l_name) + /* If not invoked directly, the dynamic linker shared object file was + found by the PT_INTERP name. */ + GL(dl_rtld_map).l_name = (char *) GL(dl_rtld_map).l_libname->name; + GL(dl_rtld_map).l_type = lt_library; + main_map->l_next = &GL(dl_rtld_map); + GL(dl_rtld_map).l_prev = main_map; + ++GL(dl_ns)[LM_ID_BASE]._ns_nloaded; + ++GL(dl_load_adds); + + /* If LD_USE_LOAD_BIAS env variable has not been seen, default + to not using bias for non-prelinked PIEs and libraries + and using it for executables or prelinked PIEs or libraries. */ + if (GLRO(dl_use_load_bias) == (ElfW(Addr)) -2) + GLRO(dl_use_load_bias) = main_map->l_addr == 0 ? -1 : 0; + + /* Set up the program header information for the dynamic linker + itself. It is needed in the dl_iterate_phdr callbacks. */ + const ElfW(Ehdr) *rtld_ehdr; + + /* Starting from binutils-2.23, the linker will define the magic symbol + __ehdr_start to point to our own ELF header if it is visible in a + segment that also includes the phdrs. If that's not available, we use + the old method that assumes the beginning of the file is part of the + lowest-addressed PT_LOAD segment. */ +#ifdef HAVE_EHDR_START + extern const ElfW(Ehdr) __ehdr_start __attribute__ ((visibility ("hidden"))); + rtld_ehdr = &__ehdr_start; +#else + rtld_ehdr = (void *) GL(dl_rtld_map).l_map_start; +#endif + assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr); + assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr))); + + const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff; + + GL(dl_rtld_map).l_phdr = rtld_phdr; + GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum; + + + /* PT_GNU_RELRO is usually the last phdr. */ + size_t cnt = rtld_ehdr->e_phnum; + while (cnt-- > 0) + if (rtld_phdr[cnt].p_type == PT_GNU_RELRO) + { + GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr; + GL(dl_rtld_map).l_relro_size = rtld_phdr[cnt].p_memsz; + break; + } + + /* Add the dynamic linker to the TLS list if it also uses TLS. */ + if (GL(dl_rtld_map).l_tls_blocksize != 0) + /* Assign a module ID. Do this before loading any audit modules. */ + GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid (); + + /* If we have auditing DSOs to load, do it now. */ + if (__glibc_unlikely (audit_list != NULL)) + { + /* Iterate over all entries in the list. The order is important. */ + struct audit_ifaces *last_audit = NULL; + struct audit_list *al = audit_list->next; + + /* Since we start using the auditing DSOs right away we need to + initialize the data structures now. */ + tcbp = init_tls (); + + /* Initialize security features. We need to do it this early + since otherwise the constructors of the audit libraries will + use different values (especially the pointer guard) and will + fail later on. */ + security_init (); + + do + { + int tls_idx = GL(dl_tls_max_dtv_idx); + + /* Now it is time to determine the layout of the static TLS + block and allocate it for the initial thread. Note that we + always allocate the static block, we never defer it even if + no DF_STATIC_TLS bit is set. The reason is that we know + glibc will use the static model. */ + struct dlmopen_args dlmargs; + dlmargs.fname = al->name; + dlmargs.map = NULL; + + const char *objname; + const char *err_str = NULL; + bool malloced; + (void) _dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit, + &dlmargs); + if (__glibc_unlikely (err_str != NULL)) + { + not_loaded: + _dl_error_printf ("\ +ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", + al->name, err_str); + if (malloced) + free ((char *) err_str); + } + else + { + struct lookup_args largs; + largs.name = "la_version"; + largs.map = dlmargs.map; + + /* Check whether the interface version matches. */ + (void) _dl_catch_error (&objname, &err_str, &malloced, + lookup_doit, &largs); + + unsigned int (*laversion) (unsigned int); + unsigned int lav; + if (err_str == NULL + && (laversion = largs.result) != NULL + && (lav = laversion (LAV_CURRENT)) > 0 + && lav <= LAV_CURRENT) + { + /* Allocate structure for the callback function pointers. + This call can never fail. */ + union + { + struct audit_ifaces ifaces; +#define naudit_ifaces 8 + void (*fptr[naudit_ifaces]) (void); + } *newp = malloc (sizeof (*newp)); + + /* Names of the auditing interfaces. All in one + long string. */ + static const char audit_iface_names[] = + "la_activity\0" + "la_objsearch\0" + "la_objopen\0" + "la_preinit\0" +#if __ELF_NATIVE_CLASS == 32 + "la_symbind32\0" +#elif __ELF_NATIVE_CLASS == 64 + "la_symbind64\0" +#else +# error "__ELF_NATIVE_CLASS must be defined" +#endif +#define STRING(s) __STRING (s) + "la_" STRING (ARCH_LA_PLTENTER) "\0" + "la_" STRING (ARCH_LA_PLTEXIT) "\0" + "la_objclose\0"; + unsigned int cnt = 0; + const char *cp = audit_iface_names; + do + { + largs.name = cp; + (void) _dl_catch_error (&objname, &err_str, &malloced, + lookup_doit, &largs); + + /* Store the pointer. */ + if (err_str == NULL && largs.result != NULL) + { + newp->fptr[cnt] = largs.result; + + /* The dynamic linker link map is statically + allocated, initialize the data now. */ + GL(dl_rtld_map).l_audit[cnt].cookie + = (intptr_t) &GL(dl_rtld_map); + } + else + newp->fptr[cnt] = NULL; + ++cnt; + + cp = (char *) rawmemchr (cp, '\0') + 1; + } + while (*cp != '\0'); + assert (cnt == naudit_ifaces); + + /* Now append the new auditing interface to the list. */ + newp->ifaces.next = NULL; + if (last_audit == NULL) + last_audit = GLRO(dl_audit) = &newp->ifaces; + else + last_audit = last_audit->next = &newp->ifaces; + ++GLRO(dl_naudit); + + /* Mark the DSO as being used for auditing. */ + dlmargs.map->l_auditing = 1; + } + else + { + /* We cannot use the DSO, it does not have the + appropriate interfaces or it expects something + more recent. */ +#ifndef NDEBUG + Lmid_t ns = dlmargs.map->l_ns; +#endif + _dl_close (dlmargs.map); + + /* Make sure the namespace has been cleared entirely. */ + assert (GL(dl_ns)[ns]._ns_loaded == NULL); + assert (GL(dl_ns)[ns]._ns_nloaded == 0); + + GL(dl_tls_max_dtv_idx) = tls_idx; + goto not_loaded; + } + } + + al = al->next; + } + while (al != audit_list->next); + + /* If we have any auditing modules, announce that we already + have two objects loaded. */ + if (__glibc_unlikely (GLRO(dl_naudit) > 0)) + { + struct link_map *ls[2] = { main_map, &GL(dl_rtld_map) }; + + for (unsigned int outer = 0; outer < 2; ++outer) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->objopen != NULL) + { + ls[outer]->l_audit[cnt].bindflags + = afct->objopen (ls[outer], LM_ID_BASE, + &ls[outer]->l_audit[cnt].cookie); + + ls[outer]->l_audit_any_plt + |= ls[outer]->l_audit[cnt].bindflags != 0; + } + + afct = afct->next; + } + } + } + } + + /* Keep track of the currently loaded modules to count how many + non-audit modules which use TLS are loaded. */ + size_t count_modids = _dl_count_modids (); + + /* Set up debugging before the debugger is notified for the first time. */ +#ifdef ELF_MACHINE_DEBUG_SETUP + /* Some machines (e.g. MIPS) don't use DT_DEBUG in this way. */ + ELF_MACHINE_DEBUG_SETUP (main_map, r); + ELF_MACHINE_DEBUG_SETUP (&GL(dl_rtld_map), r); +#else + if (main_map->l_info[DT_DEBUG] != NULL) + /* There is a DT_DEBUG entry in the dynamic section. Fill it in + with the run-time address of the r_debug structure */ + main_map->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 (GL(dl_rtld_map).l_info[DT_DEBUG] != NULL) + GL(dl_rtld_map).l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r; +#endif + + /* We start adding objects. */ + r->r_state = RT_ADD; + _dl_debug_state (); + LIBC_PROBE (init_start, 2, LM_ID_BASE, r); + + /* Auditing checkpoint: we are ready to signal that the initial map + is being constructed. */ + if (__glibc_unlikely (GLRO(dl_naudit) > 0)) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&main_map->l_audit[cnt].cookie, LA_ACT_ADD); + + afct = afct->next; + } + } + + /* We have two ways to specify objects to preload: via environment + variable and via the file /etc/ld.so.preload. The latter can also + be used when security is enabled. */ + assert (*first_preload == NULL); + struct link_map **preloads = NULL; + unsigned int npreloads = 0; + + if (__glibc_unlikely (preloadlist != NULL)) + { + /* The LD_PRELOAD environment variable gives list of libraries + separated by white space or colons that are loaded before the + executable's dependencies and prepended to the global scope + list. If the binary is running setuid all elements + containing a '/' are ignored since it is insecure. */ + char *list = strdupa (preloadlist); + char *p; + + HP_TIMING_NOW (start); + + /* Prevent optimizing strsep. Speed is not important here. */ + while ((p = (strsep) (&list, " :")) != NULL) + if (p[0] != '\0' + && (__builtin_expect (! __libc_enable_secure, 1) + || strchr (p, '/') == NULL)) + npreloads += do_preload (p, main_map, "LD_PRELOAD"); + + HP_TIMING_NOW (stop); + HP_TIMING_DIFF (diff, start, stop); + HP_TIMING_ACCUM_NT (load_time, diff); + } + + /* There usually is no ld.so.preload file, it should only be used + for emergencies and testing. So the open call etc should usually + fail. Using access() on a non-existing file is faster than using + open(). So we do this first. If it succeeds we do almost twice + the work but this does not matter, since it is not for production + use. */ + static const char preload_file[] = "/etc/ld.so.preload"; + if (__glibc_unlikely (__access (preload_file, R_OK) == 0)) + { + /* Read the contents of the file. */ + file = _dl_sysdep_read_whole_file (preload_file, &file_size, + PROT_READ | PROT_WRITE); + if (__glibc_unlikely (file != MAP_FAILED)) + { + /* Parse the file. It contains names of libraries to be loaded, + separated by white spaces or `:'. It may also contain + comments introduced by `#'. */ + char *problem; + char *runp; + size_t rest; + + /* Eliminate comments. */ + runp = file; + rest = file_size; + while (rest > 0) + { + char *comment = memchr (runp, '#', rest); + if (comment == NULL) + break; + + rest -= comment - runp; + do + *comment = ' '; + while (--rest > 0 && *++comment != '\n'); + } + + /* We have one problematic case: if we have a name at the end of + the file without a trailing terminating characters, we cannot + place the \0. Handle the case separately. */ + if (file[file_size - 1] != ' ' && file[file_size - 1] != '\t' + && file[file_size - 1] != '\n' && file[file_size - 1] != ':') + { + problem = &file[file_size]; + while (problem > file && problem[-1] != ' ' + && problem[-1] != '\t' + && problem[-1] != '\n' && problem[-1] != ':') + --problem; + + if (problem > file) + problem[-1] = '\0'; + } + else + { + problem = NULL; + file[file_size - 1] = '\0'; + } + + HP_TIMING_NOW (start); + + if (file != problem) + { + char *p; + runp = file; + while ((p = strsep (&runp, ": \t\n")) != NULL) + if (p[0] != '\0') + npreloads += do_preload (p, main_map, preload_file); + } + + if (problem != NULL) + { + char *p = strndupa (problem, file_size - (problem - file)); + + npreloads += do_preload (p, main_map, preload_file); + } + + HP_TIMING_NOW (stop); + HP_TIMING_DIFF (diff, start, stop); + HP_TIMING_ACCUM_NT (load_time, diff); + + /* We don't need the file anymore. */ + __munmap (file, file_size); + } + } + + if (__glibc_unlikely (*first_preload != NULL)) + { + /* Set up PRELOADS with a vector of the preloaded libraries. */ + struct link_map *l = *first_preload; + preloads = __alloca (npreloads * sizeof preloads[0]); + 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. */ + HP_TIMING_NOW (start); + _dl_map_object_deps (main_map, preloads, npreloads, mode == trace, 0); + HP_TIMING_NOW (stop); + HP_TIMING_DIFF (diff, start, stop); + HP_TIMING_ACCUM_NT (load_time, diff); + + /* Mark all objects as being in the global scope. */ + for (i = main_map->l_searchlist.r_nlist; i > 0; ) + main_map->l_searchlist.r_list[--i]->l_global = 1; + + /* Remove _dl_rtld_map from the chain. */ + GL(dl_rtld_map).l_prev->l_next = GL(dl_rtld_map).l_next; + if (GL(dl_rtld_map).l_next != NULL) + GL(dl_rtld_map).l_next->l_prev = GL(dl_rtld_map).l_prev; + + for (i = 1; i < main_map->l_searchlist.r_nlist; ++i) + if (main_map->l_searchlist.r_list[i] == &GL(dl_rtld_map)) + break; + + bool rtld_multiple_ref = false; + if (__glibc_likely (i < main_map->l_searchlist.r_nlist)) + { + /* 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. */ + rtld_multiple_ref = true; + + GL(dl_rtld_map).l_prev = main_map->l_searchlist.r_list[i - 1]; + if (__builtin_expect (mode, normal) == normal) + { + GL(dl_rtld_map).l_next = (i + 1 < main_map->l_searchlist.r_nlist + ? main_map->l_searchlist.r_list[i + 1] + : NULL); +#ifdef NEED_DL_SYSINFO_DSO + if (GLRO(dl_sysinfo_map) != NULL + && GL(dl_rtld_map).l_prev->l_next == GLRO(dl_sysinfo_map) + && GL(dl_rtld_map).l_next != GLRO(dl_sysinfo_map)) + GL(dl_rtld_map).l_prev = GLRO(dl_sysinfo_map); +#endif + } + else + /* In trace mode there might be an invisible object (which we + could not find) after the previous one in the search list. + In this case it doesn't matter much where we put the + interpreter object, so we just initialize the list pointer so + that the assertion below holds. */ + GL(dl_rtld_map).l_next = GL(dl_rtld_map).l_prev->l_next; + + assert (GL(dl_rtld_map).l_prev->l_next == GL(dl_rtld_map).l_next); + GL(dl_rtld_map).l_prev->l_next = &GL(dl_rtld_map); + if (GL(dl_rtld_map).l_next != NULL) + { + assert (GL(dl_rtld_map).l_next->l_prev == GL(dl_rtld_map).l_prev); + GL(dl_rtld_map).l_next->l_prev = &GL(dl_rtld_map); + } + } + + /* Now let us see whether all libraries are available in the + versions we need. */ + { + struct version_check_args args; + args.doexit = mode == normal; + args.dotrace = mode == trace; + _dl_receive_error (print_missing_version, version_check_doit, &args); + } + + /* We do not initialize any of the TLS functionality unless any of the + initial modules uses TLS. This makes dynamic loading of modules with + TLS impossible, but to support it requires either eagerly doing setup + now or lazily doing it later. Doing it now makes us incompatible with + an old kernel that can't perform TLS_INIT_TP, even if no TLS is ever + used. Trying to do it lazily is too hairy to try when there could be + multiple threads (from a non-TLS-using libpthread). */ + bool was_tls_init_tp_called = tls_init_tp_called; + if (tcbp == NULL) + tcbp = init_tls (); + + if (__glibc_likely (audit_list == NULL)) + /* Initialize security features. But only if we have not done it + earlier. */ + security_init (); + + if (__builtin_expect (mode, normal) != normal) + { + /* We were run just to list the shared libraries. It is + important that we do this before real relocation, because the + functions we call below for output may no longer work properly + after relocation. */ + struct link_map *l; + + if (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) + { + struct r_scope_elem *scope = &main_map->l_searchlist; + + for (i = 0; i < scope->r_nlist; i++) + { + l = scope->r_list [i]; + if (l->l_faked) + { + _dl_printf ("\t%s => not found\n", l->l_libname->name); + continue; + } + if (_dl_name_match_p (GLRO(dl_trace_prelink), l)) + GLRO(dl_trace_prelink_map) = l; + _dl_printf ("\t%s => %s (0x%0*Zx, 0x%0*Zx)", + DSO_FILENAME (l->l_libname->name), + DSO_FILENAME (l->l_name), + (int) sizeof l->l_map_start * 2, + (size_t) l->l_map_start, + (int) sizeof l->l_addr * 2, + (size_t) l->l_addr); + + if (l->l_tls_modid) + _dl_printf (" TLS(0x%Zx, 0x%0*Zx)\n", l->l_tls_modid, + (int) sizeof l->l_tls_offset * 2, + (size_t) l->l_tls_offset); + else + _dl_printf ("\n"); + } + } + else if (GLRO(dl_debug_mask) & DL_DEBUG_UNUSED) + { + /* Look through the dependencies of the main executable + and determine which of them is not actually + required. */ + struct link_map *l = main_map; + + /* Relocate the main executable. */ + struct relocate_args args = { .l = l, + .reloc_mode = ((GLRO(dl_lazy) + ? RTLD_LAZY : 0) + | __RTLD_NOIFUNC) }; + _dl_receive_error (print_unresolved, relocate_doit, &args); + + /* This loop depends on the dependencies of the executable to + correspond in number and order to the DT_NEEDED entries. */ + ElfW(Dyn) *dyn = main_map->l_ld; + bool first = true; + while (dyn->d_tag != DT_NULL) + { + if (dyn->d_tag == DT_NEEDED) + { + l = l->l_next; +#ifdef NEED_DL_SYSINFO_DSO + /* Skip the VDSO since it's not part of the list + of objects we brought in via DT_NEEDED entries. */ + if (l == GLRO(dl_sysinfo_map)) + l = l->l_next; +#endif + if (!l->l_used) + { + if (first) + { + _dl_printf ("Unused direct dependencies:\n"); + first = false; + } + + _dl_printf ("\t%s\n", l->l_name); + } + } + + ++dyn; + } + + _exit (first != true); + } + else if (! main_map->l_info[DT_NEEDED]) + _dl_printf ("\tstatically linked\n"); + else + { + for (l = main_map->l_next; l; l = l->l_next) + if (l->l_faked) + /* The library was not found. */ + _dl_printf ("\t%s => not found\n", l->l_libname->name); + else if (strcmp (l->l_libname->name, l->l_name) == 0) + _dl_printf ("\t%s (0x%0*Zx)\n", l->l_libname->name, + (int) sizeof l->l_map_start * 2, + (size_t) l->l_map_start); + else + _dl_printf ("\t%s => %s (0x%0*Zx)\n", l->l_libname->name, + l->l_name, (int) sizeof l->l_map_start * 2, + (size_t) l->l_map_start); + } + + if (__builtin_expect (mode, trace) != trace) + for (i = 1; i < (unsigned int) _dl_argc; ++i) + { + const ElfW(Sym) *ref = NULL; + ElfW(Addr) loadbase; + lookup_t result; + + result = _dl_lookup_symbol_x (_dl_argv[i], main_map, + &ref, main_map->l_scope, + NULL, ELF_RTYPE_CLASS_PLT, + DL_LOOKUP_ADD_DEPENDENCY, NULL); + + loadbase = LOOKUP_VALUE_ADDRESS (result); + + _dl_printf ("%s found at 0x%0*Zd in object at 0x%0*Zd\n", + _dl_argv[i], + (int) sizeof ref->st_value * 2, + (size_t) ref->st_value, + (int) sizeof loadbase * 2, (size_t) loadbase); + } + else + { + /* If LD_WARN is set, warn about undefined symbols. */ + if (GLRO(dl_lazy) >= 0 && GLRO(dl_verbose)) + { + /* We have to do symbol dependency testing. */ + struct relocate_args args; + unsigned int i; + + args.reloc_mode = ((GLRO(dl_lazy) ? RTLD_LAZY : 0) + | __RTLD_NOIFUNC); + + i = main_map->l_searchlist.r_nlist; + while (i-- > 0) + { + struct link_map *l = main_map->l_initfini[i]; + if (l != &GL(dl_rtld_map) && ! l->l_faked) + { + args.l = l; + _dl_receive_error (print_unresolved, relocate_doit, + &args); + } + } + + if ((GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) + && rtld_multiple_ref) + { + /* Mark the link map as not yet relocated again. */ + GL(dl_rtld_map).l_relocated = 0; + _dl_relocate_object (&GL(dl_rtld_map), + main_map->l_scope, __RTLD_NOIFUNC, 0); + } + } +#define VERNEEDTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (DT_VERNEED)) + if (version_info) + { + /* Print more information. This means here, print information + about the versions needed. */ + int first = 1; + struct link_map *map; + + for (map = main_map; map != NULL; map = map->l_next) + { + const char *strtab; + ElfW(Dyn) *dyn = map->l_info[VERNEEDTAG]; + ElfW(Verneed) *ent; + + if (dyn == NULL) + continue; + + strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); + ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr); + + if (first) + { + _dl_printf ("\n\tVersion information:\n"); + first = 0; + } + + _dl_printf ("\t%s:\n", DSO_FILENAME (map->l_name)); + + while (1) + { + ElfW(Vernaux) *aux; + struct link_map *needed; + + needed = find_needed (strtab + ent->vn_file); + aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux); + + while (1) + { + const char *fname = NULL; + + if (needed != NULL + && match_version (strtab + aux->vna_name, + needed)) + fname = needed->l_name; + + _dl_printf ("\t\t%s (%s) %s=> %s\n", + strtab + ent->vn_file, + strtab + aux->vna_name, + aux->vna_flags & VER_FLG_WEAK + ? "[WEAK] " : "", + fname ?: "not found"); + + if (aux->vna_next == 0) + /* No more symbols. */ + break; + + /* Next symbol. */ + aux = (ElfW(Vernaux) *) ((char *) aux + + aux->vna_next); + } + + if (ent->vn_next == 0) + /* No more dependencies. */ + break; + + /* Next dependency. */ + ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next); + } + } + } + } + + _exit (0); + } + + if (main_map->l_info[ADDRIDX (DT_GNU_LIBLIST)] + && ! __builtin_expect (GLRO(dl_profile) != NULL, 0) + && ! __builtin_expect (GLRO(dl_dynamic_weak), 0)) + { + ElfW(Lib) *liblist, *liblistend; + struct link_map **r_list, **r_listend, *l; + const char *strtab = (const void *) D_PTR (main_map, l_info[DT_STRTAB]); + + assert (main_map->l_info[VALIDX (DT_GNU_LIBLISTSZ)] != NULL); + liblist = (ElfW(Lib) *) + main_map->l_info[ADDRIDX (DT_GNU_LIBLIST)]->d_un.d_ptr; + liblistend = (ElfW(Lib) *) + ((char *) liblist + + main_map->l_info[VALIDX (DT_GNU_LIBLISTSZ)]->d_un.d_val); + r_list = main_map->l_searchlist.r_list; + r_listend = r_list + main_map->l_searchlist.r_nlist; + + for (; r_list < r_listend && liblist < liblistend; r_list++) + { + l = *r_list; + + if (l == main_map) + continue; + + /* If the library is not mapped where it should, fail. */ + if (l->l_addr) + break; + + /* Next, check if checksum matches. */ + if (l->l_info [VALIDX(DT_CHECKSUM)] == NULL + || l->l_info [VALIDX(DT_CHECKSUM)]->d_un.d_val + != liblist->l_checksum) + break; + + if (l->l_info [VALIDX(DT_GNU_PRELINKED)] == NULL + || l->l_info [VALIDX(DT_GNU_PRELINKED)]->d_un.d_val + != liblist->l_time_stamp) + break; + + if (! _dl_name_match_p (strtab + liblist->l_name, l)) + break; + + ++liblist; + } + + + if (r_list == r_listend && liblist == liblistend) + prelinked = true; + + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) + _dl_debug_printf ("\nprelink checking: %s\n", + prelinked ? "ok" : "failed"); + } + + + /* Now set up the variable which helps the assembler startup code. */ + GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist = &main_map->l_searchlist; + + /* Save the information about the original global scope list since + we need it in the memory handling later. */ + GLRO(dl_initial_searchlist) = *GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist; + + /* Remember the last search directory added at startup, now that + malloc will no longer be the one from dl-minimal.c. */ + GLRO(dl_init_all_dirs) = GL(dl_all_dirs); + + /* Print scope information. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES)) + { + _dl_debug_printf ("\nInitial object scopes\n"); + + for (struct link_map *l = main_map; l != NULL; l = l->l_next) + _dl_show_scope (l, 0); + } + + if (prelinked) + { + if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL) + { + ElfW(Rela) *conflict, *conflictend; +#ifndef HP_TIMING_NONAVAIL + hp_timing_t start; + hp_timing_t stop; +#endif + + HP_TIMING_NOW (start); + assert (main_map->l_info [VALIDX (DT_GNU_CONFLICTSZ)] != NULL); + conflict = (ElfW(Rela) *) + main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)]->d_un.d_ptr; + conflictend = (ElfW(Rela) *) + ((char *) conflict + + main_map->l_info [VALIDX (DT_GNU_CONFLICTSZ)]->d_un.d_val); + _dl_resolve_conflicts (main_map, conflict, conflictend); + HP_TIMING_NOW (stop); + HP_TIMING_DIFF (relocate_time, start, stop); + } + + + /* Mark all the objects so we know they have been already relocated. */ + for (struct link_map *l = main_map; l != NULL; l = l->l_next) + { + l->l_relocated = 1; + if (l->l_relro_size) + _dl_protect_relro (l); + + /* Add object to slot information data if necessasy. */ + if (l->l_tls_blocksize != 0 && tls_init_tp_called) + _dl_add_to_slotinfo (l); + } + } + else + { + /* 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). */ + + int consider_profiling = GLRO(dl_profile) != NULL; +#ifndef HP_TIMING_NONAVAIL + hp_timing_t start; + hp_timing_t stop; +#endif + + /* If we are profiling we also must do lazy reloaction. */ + GLRO(dl_lazy) |= consider_profiling; + + HP_TIMING_NOW (start); + unsigned i = main_map->l_searchlist.r_nlist; + while (i-- > 0) + { + struct link_map *l = main_map->l_initfini[i]; + + /* While we are at it, help the memory handling a bit. We have to + mark some data structures as allocated with the fake malloc() + implementation in ld.so. */ + struct libname_list *lnp = l->l_libname->next; + + while (__builtin_expect (lnp != NULL, 0)) + { + lnp->dont_free = 1; + lnp = lnp->next; + } + /* Also allocated with the fake malloc(). */ + l->l_free_initfini = 0; + + if (l != &GL(dl_rtld_map)) + _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0, + consider_profiling); + + /* Add object to slot information data if necessasy. */ + if (l->l_tls_blocksize != 0 && tls_init_tp_called) + _dl_add_to_slotinfo (l); + } + HP_TIMING_NOW (stop); + + HP_TIMING_DIFF (relocate_time, start, stop); + + /* Now enable profiling if needed. Like the previous call, + this has to go here because the calls it makes should use the + rtld versions of the functions (particularly calloc()), but it + needs to have _dl_profile_map set up by the relocator. */ + if (__glibc_unlikely (GL(dl_profile_map) != NULL)) + /* We must prepare the profiling. */ + _dl_start_profile (); + } + + if ((!was_tls_init_tp_called && GL(dl_tls_max_dtv_idx) > 0) + || count_modids != _dl_count_modids ()) + ++GL(dl_tls_generation); + + /* Now that we have completed relocation, the initializer data + for the TLS blocks has its final values and we can copy them + into the main thread's TLS area, which we allocated above. */ + _dl_allocate_tls_init (tcbp); + + /* And finally install it for the main thread. */ + if (! tls_init_tp_called) + { + const char *lossage = TLS_INIT_TP (tcbp); + if (__glibc_unlikely (lossage != NULL)) + _dl_fatal_printf ("cannot set up thread-local storage: %s\n", + lossage); + } + + /* Make sure no new search directories have been added. */ + assert (GLRO(dl_init_all_dirs) == GL(dl_all_dirs)); + + if (! prelinked && rtld_multiple_ref) + { + /* There was an explicit ref to the dynamic linker as a shared lib. + Re-relocate ourselves with user-controlled symbol definitions. + + We must do this after TLS initialization in case after this + re-relocation, we might call a user-supplied function + (e.g. calloc from _dl_relocate_object) that uses TLS data. */ + +#ifndef HP_TIMING_NONAVAIL + hp_timing_t start; + hp_timing_t stop; + hp_timing_t add; +#endif + + HP_TIMING_NOW (start); + /* Mark the link map as not yet relocated again. */ + GL(dl_rtld_map).l_relocated = 0; + _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0); + HP_TIMING_NOW (stop); + HP_TIMING_DIFF (add, start, stop); + HP_TIMING_ACCUM_NT (relocate_time, add); + } + + /* 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 (); + +#ifdef SHARED + /* Auditing checkpoint: we have added all objects. */ + if (__glibc_unlikely (GLRO(dl_naudit) > 0)) + { + struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + /* Do not call the functions for any auditing object. */ + if (head->l_auditing == 0) + { + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->activity != NULL) + afct->activity (&head->l_audit[cnt].cookie, LA_ACT_CONSISTENT); + + afct = afct->next; + } + } + } +#endif + + /* Notify the debugger all new objects are now ready to go. We must re-get + the address since by now the variable might be in another object. */ + r = _dl_debug_initialize (0, LM_ID_BASE); + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + LIBC_PROBE (init_complete, 2, LM_ID_BASE, r); + +#if defined USE_LDCONFIG && !defined MAP_COPY + /* We must munmap() the cache file. */ + _dl_unload_cache (); +#endif + + /* Once we return, _dl_sysdep_start will invoke + the DT_INIT functions and then *USER_ENTRY. */ +} + +/* This is a little helper function for resolving symbols while + tracing the binary. */ +static void +print_unresolved (int errcode __attribute__ ((unused)), const char *objname, + const char *errstring) +{ + if (objname[0] == '\0') + objname = RTLD_PROGNAME; + _dl_error_printf ("%s (%s)\n", errstring, objname); +} + +/* This is a little helper function for resolving symbols while + tracing the binary. */ +static void +print_missing_version (int errcode __attribute__ ((unused)), + const char *objname, const char *errstring) +{ + _dl_error_printf ("%s: %s: %s\n", RTLD_PROGNAME, + objname, errstring); +} + +/* Nonzero if any of the debugging options is enabled. */ +static int any_debug attribute_relro; + +/* Process the string given as the parameter which explains which debugging + options are enabled. */ +static void +process_dl_debug (const char *dl_debug) +{ + /* When adding new entries make sure that the maximal length of a name + is correctly handled in the LD_DEBUG_HELP code below. */ + static const struct + { + unsigned char len; + const char name[10]; + const char helptext[41]; + unsigned short int mask; + } debopts[] = + { +#define LEN_AND_STR(str) sizeof (str) - 1, str + { LEN_AND_STR ("libs"), "display library search paths", + DL_DEBUG_LIBS | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("reloc"), "display relocation processing", + DL_DEBUG_RELOC | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("files"), "display progress for input file", + DL_DEBUG_FILES | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("symbols"), "display symbol table processing", + DL_DEBUG_SYMBOLS | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("bindings"), "display information about symbol binding", + DL_DEBUG_BINDINGS | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("versions"), "display version dependencies", + DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS }, + { LEN_AND_STR ("scopes"), "display scope information", + DL_DEBUG_SCOPES }, + { LEN_AND_STR ("all"), "all previous options combined", + DL_DEBUG_LIBS | DL_DEBUG_RELOC | DL_DEBUG_FILES | DL_DEBUG_SYMBOLS + | DL_DEBUG_BINDINGS | DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS + | DL_DEBUG_SCOPES }, + { LEN_AND_STR ("statistics"), "display relocation statistics", + DL_DEBUG_STATISTICS }, + { LEN_AND_STR ("unused"), "determined unused DSOs", + DL_DEBUG_UNUSED }, + { LEN_AND_STR ("help"), "display this help message and exit", + DL_DEBUG_HELP }, + }; +#define ndebopts (sizeof (debopts) / sizeof (debopts[0])) + + /* Skip separating white spaces and commas. */ + while (*dl_debug != '\0') + { + if (*dl_debug != ' ' && *dl_debug != ',' && *dl_debug != ':') + { + size_t cnt; + size_t len = 1; + + while (dl_debug[len] != '\0' && dl_debug[len] != ' ' + && dl_debug[len] != ',' && dl_debug[len] != ':') + ++len; + + for (cnt = 0; cnt < ndebopts; ++cnt) + if (debopts[cnt].len == len + && memcmp (dl_debug, debopts[cnt].name, len) == 0) + { + GLRO(dl_debug_mask) |= debopts[cnt].mask; + any_debug = 1; + break; + } + + if (cnt == ndebopts) + { + /* Display a warning and skip everything until next + separator. */ + char *copy = strndupa (dl_debug, len); + _dl_error_printf ("\ +warning: debug option `%s' unknown; try LD_DEBUG=help\n", copy); + } + + dl_debug += len; + continue; + } + + ++dl_debug; + } + + if (GLRO(dl_debug_mask) & DL_DEBUG_UNUSED) + { + /* In order to get an accurate picture of whether a particular + DT_NEEDED entry is actually used we have to process both + the PLT and non-PLT relocation entries. */ + GLRO(dl_lazy) = 0; + } + + if (GLRO(dl_debug_mask) & DL_DEBUG_HELP) + { + size_t cnt; + + _dl_printf ("\ +Valid options for the LD_DEBUG environment variable are:\n\n"); + + for (cnt = 0; cnt < ndebopts; ++cnt) + _dl_printf (" %.*s%s%s\n", debopts[cnt].len, debopts[cnt].name, + " " + debopts[cnt].len - 3, + debopts[cnt].helptext); + + _dl_printf ("\n\ +To direct the debugging output into a file instead of standard output\n\ +a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n"); + _exit (0); + } +} + +static void +process_dl_audit (char *str) +{ + /* The parameter is a colon separated list of DSO names. */ + char *p; + + while ((p = (strsep) (&str, ":")) != NULL) + if (p[0] != '\0' + && (__builtin_expect (! __libc_enable_secure, 1) + || strchr (p, '/') == NULL)) + { + /* This is using the local malloc, not the system malloc. The + memory can never be freed. */ + struct audit_list *newp = malloc (sizeof (*newp)); + newp->name = p; + + if (audit_list == NULL) + audit_list = newp->next = newp; + else + { + newp->next = audit_list->next; + audit_list = audit_list->next = newp; + } + } +} + +/* Process all environments variables the dynamic linker must recognize. + Since all of them start with `LD_' we are a bit smarter while finding + all the entries. */ +extern char **_environ attribute_hidden; + + +static void +process_envvars (enum mode *modep) +{ + char **runp = _environ; + char *envline; + enum mode mode = normal; + char *debug_output = NULL; + + /* This is the default place for profiling data file. */ + GLRO(dl_profile_output) + = &"/var/tmp\0/var/profile"[__libc_enable_secure ? 9 : 0]; + + while ((envline = _dl_next_ld_env_entry (&runp)) != NULL) + { + size_t len = 0; + + while (envline[len] != '\0' && envline[len] != '=') + ++len; + + if (envline[len] != '=') + /* This is a "LD_" variable at the end of the string without + a '=' character. Ignore it since otherwise we will access + invalid memory below. */ + continue; + + switch (len) + { + case 4: + /* Warning level, verbose or not. */ + if (memcmp (envline, "WARN", 4) == 0) + GLRO(dl_verbose) = envline[5] != '\0'; + break; + + case 5: + /* Debugging of the dynamic linker? */ + if (memcmp (envline, "DEBUG", 5) == 0) + { + process_dl_debug (&envline[6]); + break; + } + if (memcmp (envline, "AUDIT", 5) == 0) + process_dl_audit (&envline[6]); + break; + + case 7: + /* Print information about versions. */ + if (memcmp (envline, "VERBOSE", 7) == 0) + { + version_info = envline[8] != '\0'; + break; + } + + /* List of objects to be preloaded. */ + if (memcmp (envline, "PRELOAD", 7) == 0) + { + preloadlist = &envline[8]; + break; + } + + /* Which shared object shall be profiled. */ + if (memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0') + GLRO(dl_profile) = &envline[8]; + break; + + case 8: + /* Do we bind early? */ + if (memcmp (envline, "BIND_NOW", 8) == 0) + { + GLRO(dl_lazy) = envline[9] == '\0'; + break; + } + if (memcmp (envline, "BIND_NOT", 8) == 0) + GLRO(dl_bind_not) = envline[9] != '\0'; + break; + + case 9: + /* Test whether we want to see the content of the auxiliary + array passed up from the kernel. */ + if (!__libc_enable_secure + && memcmp (envline, "SHOW_AUXV", 9) == 0) + _dl_show_auxv (); + break; + +#if !HAVE_TUNABLES + case 10: + /* Mask for the important hardware capabilities. */ + if (!__libc_enable_secure + && memcmp (envline, "HWCAP_MASK", 10) == 0) + GLRO(dl_hwcap_mask) = __strtoul_internal (&envline[11], NULL, + 0, 0); + break; +#endif + + case 11: + /* Path where the binary is found. */ + if (!__libc_enable_secure + && memcmp (envline, "ORIGIN_PATH", 11) == 0) + GLRO(dl_origin_path) = &envline[12]; + break; + + case 12: + /* The library search path. */ + if (memcmp (envline, "LIBRARY_PATH", 12) == 0) + { + library_path = &envline[13]; + break; + } + + /* Where to place the profiling data file. */ + if (memcmp (envline, "DEBUG_OUTPUT", 12) == 0) + { + debug_output = &envline[13]; + break; + } + + if (!__libc_enable_secure + && memcmp (envline, "DYNAMIC_WEAK", 12) == 0) + GLRO(dl_dynamic_weak) = 1; + break; + + case 13: + /* We might have some extra environment variable with length 13 + to handle. */ +#ifdef EXTRA_LD_ENVVARS_13 + EXTRA_LD_ENVVARS_13 +#endif + if (!__libc_enable_secure + && memcmp (envline, "USE_LOAD_BIAS", 13) == 0) + { + GLRO(dl_use_load_bias) = envline[14] == '1' ? -1 : 0; + break; + } + break; + + case 14: + /* Where to place the profiling data file. */ + if (!__libc_enable_secure + && memcmp (envline, "PROFILE_OUTPUT", 14) == 0 + && envline[15] != '\0') + GLRO(dl_profile_output) = &envline[15]; + break; + + case 16: + /* The mode of the dynamic linker can be set. */ + if (memcmp (envline, "TRACE_PRELINKING", 16) == 0) + { + mode = trace; + GLRO(dl_verbose) = 1; + GLRO(dl_debug_mask) |= DL_DEBUG_PRELINK; + GLRO(dl_trace_prelink) = &envline[17]; + } + break; + + case 20: + /* The mode of the dynamic linker can be set. */ + if (memcmp (envline, "TRACE_LOADED_OBJECTS", 20) == 0) + mode = trace; + break; + + /* We might have some extra environment variable to handle. This + is tricky due to the pre-processing of the length of the name + in the switch statement here. The code here assumes that added + environment variables have a different length. */ +#ifdef EXTRA_LD_ENVVARS + EXTRA_LD_ENVVARS +#endif + } + } + + /* The caller wants this information. */ + *modep = mode; + + /* Extra security for SUID binaries. Remove all dangerous environment + variables. */ + if (__builtin_expect (__libc_enable_secure, 0)) + { + static const char unsecure_envvars[] = +#ifdef EXTRA_UNSECURE_ENVVARS + EXTRA_UNSECURE_ENVVARS +#endif + UNSECURE_ENVVARS; + const char *nextp; + + nextp = unsecure_envvars; + do + { + unsetenv (nextp); + /* We could use rawmemchr but this need not be fast. */ + nextp = (char *) (strchr) (nextp, '\0') + 1; + } + while (*nextp != '\0'); + + if (__access ("/etc/suid-debug", F_OK) != 0) + { +#if !HAVE_TUNABLES + unsetenv ("MALLOC_CHECK_"); +#endif + GLRO(dl_debug_mask) = 0; + } + + if (mode != normal) + _exit (5); + } + /* If we have to run the dynamic linker in debugging mode and the + LD_DEBUG_OUTPUT environment variable is given, we write the debug + messages to this file. */ + else if (any_debug && debug_output != NULL) + { + const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW; + size_t name_len = strlen (debug_output); + char buf[name_len + 12]; + char *startp; + + buf[name_len + 11] = '\0'; + startp = _itoa (__getpid (), &buf[name_len + 11], 10, 0); + *--startp = '.'; + startp = memcpy (startp - name_len, debug_output, name_len); + + GLRO(dl_debug_fd) = __open (startp, flags, DEFFILEMODE); + if (GLRO(dl_debug_fd) == -1) + /* We use standard output if opening the file failed. */ + GLRO(dl_debug_fd) = STDOUT_FILENO; + } +} + + +/* Print the various times we collected. */ +static void +__attribute ((noinline)) +print_statistics (hp_timing_t *rtld_total_timep) +{ +#ifndef HP_TIMING_NONAVAIL + char buf[200]; + char *cp; + char *wp; + + /* Total time rtld used. */ + if (HP_SMALL_TIMING_AVAIL) + { + HP_TIMING_PRINT (buf, sizeof (buf), *rtld_total_timep); + _dl_debug_printf ("\nruntime linker statistics:\n" + " total startup time in dynamic loader: %s\n", buf); + + /* Print relocation statistics. */ + char pbuf[30]; + HP_TIMING_PRINT (buf, sizeof (buf), relocate_time); + cp = _itoa ((1000ULL * relocate_time) / *rtld_total_timep, + pbuf + sizeof (pbuf), 10, 0); + wp = pbuf; + switch (pbuf + sizeof (pbuf) - cp) + { + case 3: + *wp++ = *cp++; + case 2: + *wp++ = *cp++; + case 1: + *wp++ = '.'; + *wp++ = *cp++; + } + *wp = '\0'; + _dl_debug_printf ("\ + time needed for relocation: %s (%s%%)\n", buf, pbuf); + } +#endif + + unsigned long int num_relative_relocations = 0; + for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns) + { + if (GL(dl_ns)[ns]._ns_loaded == NULL) + continue; + + struct r_scope_elem *scope = &GL(dl_ns)[ns]._ns_loaded->l_searchlist; + + for (unsigned int i = 0; i < scope->r_nlist; i++) + { + struct link_map *l = scope->r_list [i]; + + if (l->l_addr != 0 && l->l_info[VERSYMIDX (DT_RELCOUNT)]) + num_relative_relocations + += l->l_info[VERSYMIDX (DT_RELCOUNT)]->d_un.d_val; +#ifndef ELF_MACHINE_REL_RELATIVE + /* Relative relocations are processed on these architectures if + library is loaded to different address than p_vaddr or + if not prelinked. */ + if ((l->l_addr != 0 || !l->l_info[VALIDX(DT_GNU_PRELINKED)]) + && l->l_info[VERSYMIDX (DT_RELACOUNT)]) +#else + /* On e.g. IA-64 or Alpha, relative relocations are processed + only if library is loaded to different address than p_vaddr. */ + if (l->l_addr != 0 && l->l_info[VERSYMIDX (DT_RELACOUNT)]) +#endif + num_relative_relocations + += l->l_info[VERSYMIDX (DT_RELACOUNT)]->d_un.d_val; + } + } + + _dl_debug_printf (" number of relocations: %lu\n" + " number of relocations from cache: %lu\n" + " number of relative relocations: %lu\n", + GL(dl_num_relocations), + GL(dl_num_cache_relocations), + num_relative_relocations); + +#ifndef HP_TIMING_NONAVAIL + /* Time spend while loading the object and the dependencies. */ + if (HP_SMALL_TIMING_AVAIL) + { + char pbuf[30]; + HP_TIMING_PRINT (buf, sizeof (buf), load_time); + cp = _itoa ((1000ULL * load_time) / *rtld_total_timep, + pbuf + sizeof (pbuf), 10, 0); + wp = pbuf; + switch (pbuf + sizeof (pbuf) - cp) + { + case 3: + *wp++ = *cp++; + case 2: + *wp++ = *cp++; + case 1: + *wp++ = '.'; + *wp++ = *cp++; + } + *wp = '\0'; + _dl_debug_printf ("\ + time needed to load objects: %s (%s%%)\n", + buf, pbuf); + } +#endif +} |