aboutsummaryrefslogtreecommitdiff
path: root/elf/rtld.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/rtld.c')
-rw-r--r--elf/rtld.c167
1 files changed, 152 insertions, 15 deletions
diff --git a/elf/rtld.c b/elf/rtld.c
index 8ed86ea..a05bbe9 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -67,6 +67,8 @@ struct r_search_path *_dl_search_paths;
const char *_dl_profile;
const char *_dl_profile_output;
struct link_map *_dl_profile_map;
+const char *_dl_trace_prelink;
+struct link_map *_dl_trace_prelink_map;
int _dl_lazy = 1;
/* XXX I know about at least one case where we depend on the old weak
behavior (it has to do with librt). Until we get DSO groups implemented
@@ -183,10 +185,14 @@ _dl_start (void *arg)
ELF_MACHINE_BEFORE_RTLD_RELOC (bootstrap_map.l_info);
#endif
- /* Relocate ourselves so we can do normal function calls and
- data access using the global offset table. */
+ 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);
+ }
- ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, 0);
/* 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). */
@@ -209,6 +215,15 @@ _dl_start (void *arg)
}
+#ifndef VALIDX
+# define VALIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \
+ + DT_EXTRANUM + DT_VALTAGIDX (tag))
+#endif
+#ifndef ADDRIDX
+# define ADDRIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \
+ + DT_EXTRANUM + DT_VALNUM + DT_ADDRTAGIDX (tag))
+#endif
+
static ElfW(Addr)
_dl_start_final (void *arg, struct link_map *bootstrap_map_p,
hp_timing_t start_time)
@@ -218,6 +233,7 @@ _dl_start_final (void *arg, struct link_map *bootstrap_map_p,
way to do this so we use this trick. gcc never inlines functions
which use `alloca'. */
ElfW(Addr) *start_addr = alloca (sizeof (ElfW(Addr)));
+ extern char _begin[], _end[];
if (HP_TIMING_AVAIL)
{
@@ -237,10 +253,8 @@ _dl_start_final (void *arg, struct link_map *bootstrap_map_p,
sizeof _dl_rtld_map.l_info);
_dl_setup_hash (&_dl_rtld_map);
_dl_rtld_map.l_mach = bootstrap_map_p->l_mach;
-
-/* Don't bother trying to work out how ld.so is mapped in memory. */
- _dl_rtld_map.l_map_start = ~0;
- _dl_rtld_map.l_map_end = ~0;
+ _dl_rtld_map.l_map_start = (ElfW(Addr)) _begin;
+ _dl_rtld_map.l_map_end = (ElfW(Addr)) _end;
/* 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
@@ -383,6 +397,7 @@ dl_main (const ElfW(Phdr) *phdr,
char *file;
int has_interp = 0;
unsigned int i;
+ int prelinked = 0;
int rtld_is_main = 0;
#ifndef HP_TIMING_NONAVAIL
hp_timing_t start;
@@ -885,13 +900,42 @@ of this helper program; chances are you did not intend to run this program.\n\
{
struct link_map *l;
- for (l = _dl_loaded->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
- _dl_printf ("\t%s => %s (0x%0*Zx)\n", l->l_libname->name,
- l->l_name, (int) sizeof l->l_addr * 2, l->l_addr);
+ if (_dl_debug_mask & DL_DEBUG_PRELINK)
+ {
+ struct r_scope_elem *scope = &_dl_loaded->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 (_dl_trace_prelink, l))
+ _dl_trace_prelink_map = l;
+ _dl_printf ("\t%s => %s (0x%0*Zx, 0x%0*Zx)\n",
+ l->l_libname->name[0] ? l->l_libname->name
+ : _dl_argv[0] ?: "<main program>",
+ l->l_name[0] ? l->l_name
+ : _dl_argv[0] ?: "<main program>",
+ (int) sizeof l->l_map_start * 2,
+ l->l_map_start,
+ (int) sizeof l->l_addr * 2,
+ l->l_addr);
+ }
+ }
+ else
+ {
+ for (l = _dl_loaded->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
+ _dl_printf ("\t%s => %s (0x%0*Zx)\n", l->l_libname->name,
+ l->l_name, (int) sizeof l->l_map_start * 2,
+ l->l_map_start);
+ }
}
if (__builtin_expect (mode, trace) != trace)
@@ -936,6 +980,10 @@ of this helper program; chances are you did not intend to run this program.\n\
}
l = l->l_prev;
} while (l);
+
+ if ((_dl_debug_mask & DL_DEBUG_PRELINK)
+ && _dl_rtld_map.l_opencount > 1)
+ _dl_relocate_object (&_dl_rtld_map, _dl_loaded->l_scope, 0, 0);
}
#define VERNEEDTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (DT_VERNEED))
@@ -1014,6 +1062,84 @@ of this helper program; chances are you did not intend to run this program.\n\
_exit (0);
}
+ if (_dl_loaded->l_info [ADDRIDX (DT_GNU_LIBLIST)]
+ && ! __builtin_expect (_dl_profile != NULL, 0))
+ {
+ ElfW(Lib) *liblist, *liblistend;
+ struct link_map **r_list, **r_listend, *l;
+ const char *strtab = (const void *)
+ D_PTR (_dl_loaded, l_info[DT_STRTAB]);
+
+ assert (_dl_loaded->l_info [VALIDX (DT_GNU_LIBLISTSZ)] != NULL);
+ liblist = (ElfW(Lib) *)
+ _dl_loaded->l_info [ADDRIDX (DT_GNU_LIBLIST)]->d_un.d_ptr;
+ liblistend = (ElfW(Lib) *)
+ ((char *) liblist
+ + _dl_loaded->l_info [VALIDX (DT_GNU_LIBLISTSZ)]->d_un.d_val);
+ r_list = _dl_loaded->l_searchlist.r_list;
+ r_listend = r_list + _dl_loaded->l_searchlist.r_nlist;
+
+ for (; r_list < r_listend && liblist < liblistend; r_list++)
+ {
+ l = *r_list;
+
+ if (l == _dl_loaded)
+ 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 = 1;
+
+ if (__builtin_expect (_dl_debug_mask & DL_DEBUG_LIBS, 0))
+ _dl_printf ("\nprelink checking: %s\n", prelinked ? "ok" : "failed");
+ }
+
+ if (prelinked)
+ {
+ if (_dl_loaded->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 (_dl_loaded->l_info [VALIDX (DT_GNU_CONFLICTSZ)] != NULL);
+ conflict = (ElfW(Rela) *)
+ _dl_loaded->l_info [ADDRIDX (DT_GNU_CONFLICT)]->d_un.d_ptr;
+ conflictend = (ElfW(Rela) *)
+ ((char *) conflict
+ + _dl_loaded->l_info [VALIDX (DT_GNU_CONFLICTSZ)]->d_un.d_val);
+ _dl_resolve_conflicts (_dl_loaded, conflict, conflictend);
+ HP_TIMING_NOW (stop);
+ HP_TIMING_DIFF (relocate_time, start, stop);
+ }
+
+ _dl_sysdep_start_cleanup ();
+ }
+ 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
@@ -1094,7 +1220,7 @@ of this helper program; chances are you did not intend to run this program.\n\
_dl_main_searchlist = &_dl_loaded->l_searchlist;
_dl_global_scope[0] = &_dl_loaded->l_searchlist;
- /* Safe the information about the original global scope list since
+ /* Save the information about the original global scope list since
we need it in the memory handling later. */
_dl_initial_searchlist = *_dl_main_searchlist;
@@ -1371,6 +1497,17 @@ process_envvars (enum mode *modep)
_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;
+ _dl_verbose = 1;
+ _dl_debug_mask |= DL_DEBUG_PRELINK;
+ _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)