diff options
-rw-r--r-- | ChangeLog | 101 | ||||
-rw-r--r-- | elf/Makefile | 36 | ||||
-rw-r--r-- | elf/dl-conflict.c | 66 | ||||
-rw-r--r-- | elf/dl-deps.c | 61 | ||||
-rw-r--r-- | elf/dl-load.c | 3 | ||||
-rw-r--r-- | elf/dl-lookup.c | 229 | ||||
-rw-r--r-- | elf/dl-runtime.c | 3 | ||||
-rw-r--r-- | elf/do-rel.h | 11 | ||||
-rw-r--r-- | elf/dynamic-link.h | 60 | ||||
-rw-r--r-- | elf/elf.h | 4 | ||||
-rw-r--r-- | elf/rtld.c | 167 | ||||
-rw-r--r-- | include/link.h | 16 | ||||
-rw-r--r-- | sysdeps/alpha/dl-machine.h | 43 | ||||
-rw-r--r-- | sysdeps/arm/bits/link.h | 4 | ||||
-rw-r--r-- | sysdeps/arm/dl-machine.h | 90 | ||||
-rw-r--r-- | sysdeps/generic/ldsodefs.h | 8 | ||||
-rw-r--r-- | sysdeps/i386/bits/link.h | 5 | ||||
-rw-r--r-- | sysdeps/i386/dl-machine.h | 82 | ||||
-rw-r--r-- | sysdeps/powerpc/dl-machine.h | 6 | ||||
-rw-r--r-- | sysdeps/s390/s390-32/bits/link.h | 5 | ||||
-rw-r--r-- | sysdeps/s390/s390-32/dl-machine.h | 17 | ||||
-rw-r--r-- | sysdeps/s390/s390-64/bits/link.h | 5 | ||||
-rw-r--r-- | sysdeps/s390/s390-64/dl-machine.h | 17 | ||||
-rw-r--r-- | sysdeps/sh/bits/link.h | 5 | ||||
-rw-r--r-- | sysdeps/sh/dl-machine.h | 17 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/dl-machine.h | 99 | ||||
-rw-r--r-- | sysdeps/sparc/sparc64/dl-machine.h | 66 | ||||
-rw-r--r-- | sysdeps/x86_64/bits/link.h | 5 | ||||
-rw-r--r-- | sysdeps/x86_64/dl-machine.h | 17 |
29 files changed, 1045 insertions, 203 deletions
@@ -1,3 +1,104 @@ +2001-12-11 Jakub Jelinek <jakub@redhat.com> + + * elf/Makefile (dl-routines): Add conflict. + (rtld-ldscript-in, rtld-ldscript, rtld-parms): Remove. + (ld.so): Add _begin local symbol. + * elf/elf.h (DT_VALTAGIDX, DT_VALNUM, DT_ADDRTAGIDX, DT_ADDRNUM): + Define. + * elf/dl-deps.c (_dl_build_local_scope): New. + (_dl_map_object_deps): If LD_TRACE_PRELINKING, compute local scopes + of all libraries. + * elf/do-rel.h (VALIDX): Define. + (elf_dynamic_do_rel): If ELF_MACHINE_PLT_REL is defined, don't do + lazy binding for RELA. If DT_GNU_PRELINKED, DT_RELACOUNT relocations + can be skipped. + * elf/dl-conflict.c: New file. + * elf/dl-lookup.c (_dl_debug_bindings): New. + (_dl_lookup_symbol): Use _dl_debug_bindings. Reference_name is always + non-NULL. + (_dl_lookup_symbol_skip): Likewise. + (_dl_lookup_versioned_symbol): Likewise. + (_dl_lookup_versioned_symbol_skip): Likewise. + * elf/dl-runtime.c (PLTREL): If ELF_MACHINE_PLT_REL is defined, + define to ElfW(Rel). + * elf/dynamic-link.h (elf_get_dynamic_info): Record selected dynamic + tags in the DT_VALRNGLO..DT_VALRNGHI and DT_ADDRRNGLO..DT_ADDRRNGHI + ranges. + Don't adjust address dynamic tags if l_addr is 0. + * elf/rtld.c (_dl_trace_prelink, _dl_trace_prelink_map): New variables. + (_dl_start): Skip ELF_DYNAMIC_RELOCATE if ld.so is prelinked. + (VALIDX, ADDRIDX): Define. + (_dl_start_final): Initialize _dl_rtld_map's l_map_start and l_map_end. + (dl_main): Print library list for LD_TRACE_PRELINKING. + If prelinking information can be used, skip relocating libraries and + call _dl_resolve_conflicts instead. + (process_envvars): Handle LD_TRACE_PRELINKING envvar. + * elf/dl-load.c (_dl_map_object): Don't create fake libs + if LD_TRACE_PRELINKING. + * include/link.h (struct link_map) [l_info]: Add DT_VALNUM + + DT_ADDRNUM. + * sysdeps/generic/ldsodefs.h (_dl_trace_prelink_map): New declaration. + (DL_DEBUG_PRELINK): Define. + (_dl_resolve_conflicts): Add prototype. + + * sysdeps/alpha/dl-machine.h (elf_machine_runtime_setup): Reinitialize + .plt for prelinked libraries where prelinking info cannot be used. + (elf_machine_rela): If relocating R_ALPHA_JMP_SLOT in .gnu.conflict + section, use RESOLVE_CONFLICT_FIND_MAP to find out reloc's link_map. + * sysdeps/arm/bits/link.h: New file. + * sysdeps/arm/dl-machine.h (elf_machine_runtime_setup): Save original + content of .got[1]. + (ELF_MACHINE_NO_RELA): Only define if RTLD_BOOTSTRAP. + (ELF_MACHINE_PLT_REL): Define. + (elf_machine_rela, elf_machine_rela_relative): New. + (elf_machine_lazy_rel): Reinitialize R_ARM_JUMP_SLOT address instead + of adjusting it if prelinked and prelinking cannot be used. + * sysdeps/i386/bits/link.h: New file. + * sysdeps/i386/dl-machine.h (elf_machine_runtime_setup): Save original + content of .got[1]. + (ELF_MACHINE_NO_RELA): Only define if RTLD_BOOTSTRAP. + (ELF_MACHINE_PLT_REL): Define. + (elf_machine_rela, elf_machine_rela_relative): New. + (elf_machine_lazy_rel): Reinitialize R_386_JUMP_SLOT address instead + of adjusting it if prelinked and prelinking cannot be used. + * sysdeps/powerpc/dl-machine.h (elf_machine_rela): If relocating + conflicts, skip finaladdr computation. Use RESOLVE_CONFLICT_FIND_MAP + to find out map for R_PPC_JMP_SLOT relocs. + * sysdeps/sparc/sparc32/dl-machine.h (VALIDX): Define. + (OPCODE_BA): Define. + (elf_machine_runtime_setup): Reinitialize .plt for prelinked + libraries where prelinking info cannot be used. + (sparc_fixup_plt): Renamed from elf_machine_fixup_plt. + (elf_machine_fixup_plt): Call sparc_fixup_plt. + (elf_machine_rela): Set value to 0 if relocating conflicts. + Call sparc_fixup_plt for R_SPARC_JMP_SLOT. + * sysdeps/sparc/sparc64/dl-machine.h (VALIDX): Define. + (sparc64_fixup_plt): Fix a typo. + (elf_machine_rela): Set value to 0 if relocating conflicts. + Handle R_SPARC_JMP_SLOT relocs when relocating conflicts. + (elf_machine_runtime_setup): Reinitialize .plt for prelinked + libraries where prelinking info cannot be used. + * sysdeps/sh/bits/link.h: New file. + * sysdeps/sh/dl-machine.h (elf_machine_runtime_setup): Save original + content of .got[1]. + (elf_machine_lazy_rel): Reinitialize R_SH_JMP_SLOT address instead + of adjusting it if prelinked and prelinking cannot be used. + * sysdeps/s390/s390-32/bits/link.h: New file. + * sysdeps/s390/s390-32/dl-machine.h (elf_machine_runtime_setup): + Save original content of .got[1]. + (elf_machine_lazy_rel): Reinitialize R_390_JMP_SLOT address instead + of adjusting it if prelinked and prelinking cannot be used. + * sysdeps/s390/s390-64/bits/link.h: New file. + * sysdeps/s390/s390-64/dl-machine.h (elf_machine_runtime_setup): + Save original content of .got[1]. + (elf_machine_lazy_rel): Reinitialize R_390_JMP_SLOT address instead + of adjusting it if prelinked and prelinking cannot be used. + * sysdeps/x86_64/bits/link.h: New file. + * sysdeps/x86_64/dl-machine.h (elf_machine_runtime_setup): + Save original content of .got[1]. + (elf_machine_lazy_rel): Reinitialize R_X86_64_JMP_SLOT address instead + of adjusting it if prelinked and prelinking cannot be used. + 2001-12-11 Ulrich Drepper <drepper@redhat.com> * sysdeps/unix/sysv/linux/ptsname.c (__ptsname_r): Use sizeof diff --git a/elf/Makefile b/elf/Makefile index b39034c7bf..f1245328f5 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -29,7 +29,7 @@ routines = $(dl-routines) dl-open dl-close dl-support dl-iteratephdr \ # profiled libraries. dl-routines = $(addprefix dl-,load cache lookup object reloc deps \ runtime error init fini debug misc \ - version profile) + version profile conflict) all-dl-routines = $(dl-routines) $(sysdep-dl-routines) # But they are absent from the shared libc, because that code is in ld.so. elide-routines.os = $(all-dl-routines) dl-support enbl-secure \ @@ -154,30 +154,16 @@ $(objpfx)dl-allobjs.os: $(all-rtld-routines:%=$(objpfx)%.os) $(objpfx)librtld.os: $(objpfx)dl-allobjs.os $(common-objpfx)libc_pic.a $(reloc-link) '-Wl,-(' $^ -lgcc '-Wl,-)' -# Do we need a linker script? -rtld-ldscript-in := $(firstword $(wildcard $(+sysdep_dirs:%=%/rtld-ldscript.in))) - -ifneq (,$(rtld-ldscript-in)) -rtld-ldscript = $(objpfx)rtld-ldscript -generated += rtld-ldscript - -LDFLAGS-rtld = -T $(rtld-ldscript) -before-compile += $(rtld-ldscript) - -rtld-parms = $(wildcard $(+sysdep_dirs:%=%/rtld-parms)) -include $(rtld-parms) - -$(rtld-ldscript): $(rtld-ldscript-in) $(rtld-parms) - sed -e 's#@@rtld-oformat@@#$(rtld-oformat)#' \ - -e 's#@@rtld-arch@@#$(rtld-arch)#' \ - -e 's#@@rtld-entry@@#$(rtld-entry)#' \ - -e 's#@@rtld-base@@#$(rtld-base)#' $< >$@ -endif - -$(objpfx)ld.so: $(objpfx)librtld.os $(rtld-ldscript) $(ld-map) - $(LINK.o) -nostdlib -nostartfiles -shared -o $@ $(LDFLAGS-rtld) \ - $(filter-out $(rtld-ldscript) $(map-file),$^) \ - $(load-map-file) -Wl,-soname=$(rtld-installed-name) +$(objpfx)ld.so: $(objpfx)librtld.os $(ld-map) + $(LINK.o) -nostdlib -nostartfiles -shared \ + $(LDFLAGS-rtld) -Wl,--verbose 2>&1 | \ + sed -e '/^=========/,/^=========/!d;/^=========/d' \ + -e 's/\. = 0 + SIZEOF_HEADERS;/& _begin = . - SIZEOF_HEADERS;/' \ + > $@.lds; \ + $(LINK.o) -nostdlib -nostartfiles -shared -o $@ $(LDFLAGS-rtld) \ + $(filter-out $(map-file),$^) $(load-map-file) \ + -Wl,-soname=$(rtld-installed-name) -T $@.lds; \ + rm -f $@.lds # interp.c exists just to get this string into the libraries. CFLAGS-interp.c = -D'RUNTIME_LINKER="$(slibdir)/$(rtld-installed-name)"' diff --git a/elf/dl-conflict.c b/elf/dl-conflict.c new file mode 100644 index 0000000000..5426a5ad44 --- /dev/null +++ b/elf/dl-conflict.c @@ -0,0 +1,66 @@ +/* Resolve conflicts against already prelinked libraries. + Copyright (C) 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2001. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <libintl.h> +#include <stdlib.h> +#include <unistd.h> +#include <ldsodefs.h> +#include <sys/mman.h> +#include <sys/types.h> +#include "dynamic-link.h" + +extern unsigned long int _dl_num_cache_relocations; /* in dl-lookup.c */ + +void +_dl_resolve_conflicts (struct link_map *l, ElfW(Rela) *conflict, + ElfW(Rela) *conflictend) +{ + if (__builtin_expect (_dl_debug_mask & DL_DEBUG_RELOC, 0)) + _dl_printf ("\nconflict processing: %s\n", + l->l_name[0] ? l->l_name : _dl_argv[0]); + + { + /* Do the conflict relocation of the object and library GOT and other + data. */ + + /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code. */ +#define RESOLVE_MAP(ref, version, flags) (*ref = NULL, 0) +#define RESOLVE(ref, version, flags) (*ref = NULL, 0) +#define RESOLVE_CONFLICT_FIND_MAP(map, r_offset) \ +do \ + { \ + while (resolve_conflict_map->l_map_end < (r_offset) \ + || resolve_conflict_map->l_map_start > (r_offset)) \ + resolve_conflict_map \ + = resolve_conflict_map->l_next; \ + (map) = resolve_conflict_map; \ + } while (0) + + struct link_map *resolve_conflict_map = _dl_loaded; + +#include "dynamic-link.h" + + _dl_num_cache_relocations += conflictend - conflict; + + for (; conflict < conflictend; ++conflict) + elf_machine_rela (l, conflict, NULL, NULL, (void *) conflict->r_offset); + } +} diff --git a/elf/dl-deps.c b/elf/dl-deps.c index ec61326614..af39de1023 100644 --- a/elf/dl-deps.c +++ b/elf/dl-deps.c @@ -21,6 +21,7 @@ #include <dlfcn.h> #include <errno.h> #include <libintl.h> +#include <stddef.h> #include <stdlib.h> #include <string.h> #include <unistd.h> @@ -70,6 +71,21 @@ openaux (void *a) args->trace_mode, 0); } +static ptrdiff_t +internal_function +_dl_build_local_scope (struct link_map **list, struct link_map *map) +{ + struct link_map **p = list; + struct link_map **q; + + *p++ = map; + map->l_reserved = 1; + if (map->l_initfini) + for (q = map->l_initfini + 1; *q; ++q) + if (! (*q)->l_reserved) + p += _dl_build_local_scope (p, *q); + return p - list; +} /* We use a very special kind of list to track the path @@ -491,6 +507,51 @@ _dl_map_object_deps (struct link_map *map, runp->map->l_reserved = 0; } + if (__builtin_expect(_dl_debug_mask & DL_DEBUG_PRELINK, 0) != 0 + && map == _dl_loaded) + { + /* If we are to compute conflicts, we have to build local scope + for each library, not just the ultimate loader. */ + for (i = 0; i < nlist; ++i) + { + struct link_map *l = map->l_searchlist.r_list[i]; + unsigned int j, cnt; + + /* The local scope has been already computed. */ + if (l == map + || (l->l_local_scope[0] + && l->l_local_scope[0]->r_nlist) != 0) + continue; + + if (l->l_info[AUXTAG] || l->l_info[FILTERTAG]) + { + /* As current DT_AUXILIARY/DT_FILTER implementation needs to be + rewritten, no need to bother with prelinking the old + implementation. */ + _dl_signal_error (EINVAL, l->l_name, NULL, N_("\ +Filters not supported with LD_TRACE_PRELINKING")); + } + + cnt = _dl_build_local_scope (map->l_initfini, l); + assert (cnt <= nlist); + for (j = 0; j < cnt; j++) + map->l_initfini[j]->l_reserved = 0; + + l->l_local_scope[0] = + (struct r_scope_elem *) malloc (sizeof (struct r_scope_elem) + + (cnt + * sizeof (struct link_map *))); + if (l->l_local_scope[0] == NULL) + _dl_signal_error (ENOMEM, map->l_name, NULL, + N_("cannot allocate symbol search list")); + l->l_local_scope[0]->r_nlist = cnt; + l->l_local_scope[0]->r_list = + (struct link_map **) (l->l_local_scope[0] + 1); + memcpy (l->l_local_scope[0]->r_list, map->l_initfini, + cnt * sizeof (struct link_map *)); + } + } + /* Maybe we can remove some relocation dependencies now. */ assert (map->l_searchlist.r_list[0] == map); for (i = 0; i < map->l_reldepsact; ++i) diff --git a/elf/dl-load.c b/elf/dl-load.c index 0bc01eca20..4b9af501c1 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1782,7 +1782,8 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, if (__builtin_expect (fd, 0) == -1) { - if (trace_mode) + if (trace_mode + && __builtin_expect (_dl_debug_mask & DL_DEBUG_PRELINK, 0) == 0) { /* We haven't found an appropriate library. But since we are only interested in the list of libraries this isn't diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index 5fa50fc457..ae3f0b1e34 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -194,6 +194,12 @@ _dl_do_lookup_versioned (const char *undef_name, unsigned long int hash, const struct r_found_version *const version, struct link_map *skip, int type_class); +static void +internal_function +_dl_debug_bindings (const char *undef_name, struct link_map *undef_map, + const ElfW(Sym) **ref, struct r_scope_elem *symbol_scope[], + struct sym_val *value, const struct r_found_version *version, + int type_class, int protected); /* Search loaded objects' symbol tables for a definition of the symbol UNDEF_NAME. */ @@ -204,7 +210,7 @@ _dl_lookup_symbol (const char *undef_name, struct link_map *undef_map, const ElfW(Sym) **ref, struct r_scope_elem *symbol_scope[], int type_class, int explicit) { - unsigned long int hash = _dl_elf_hash (undef_name); + const unsigned long int hash = _dl_elf_hash (undef_name); struct sym_val current_value = { NULL, NULL }; struct r_scope_elem **scope; int protected; @@ -241,7 +247,7 @@ _dl_lookup_symbol (const char *undef_name, struct link_map *undef_map, if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK) /* We could find no value for a strong reference. */ /* XXX We cannot translate the messages. */ - _dl_signal_cerror (0, (reference_name && reference_name[0] + _dl_signal_cerror (0, (reference_name[0] ? reference_name : (_dl_argv[0] ?: "<main program>")), N_("relocation error"), @@ -251,25 +257,7 @@ _dl_lookup_symbol (const char *undef_name, struct link_map *undef_map, } protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED; - - if (__builtin_expect (_dl_debug_mask & DL_DEBUG_BINDINGS, 0)) - { - const char *reference_name = undef_map ? undef_map->l_name : NULL; - - _dl_debug_printf ("binding file %s to %s: %s symbol `%s'\n", - (reference_name && reference_name[0] - ? reference_name : (_dl_argv[0] ?: "<main program>")), - current_value.m->l_name[0] - ? current_value.m->l_name : _dl_argv[0], - protected ? "protected" : "normal", undef_name); - } - - if (__builtin_expect (protected == 0, 1)) - { - *ref = current_value.s; - return LOOKUP_VALUE (current_value.m); - } - else + if (__builtin_expect (protected != 0, 0)) { /* It is very tricky. We need to figure out what value to return for the protected symbol */ @@ -280,14 +268,20 @@ _dl_lookup_symbol (const char *undef_name, struct link_map *undef_map, 0, NULL, ELF_RTYPE_CLASS_PLT)) break; - if (protected_value.s == NULL || protected_value.m == undef_map) + if (protected_value.s != NULL && protected_value.m != undef_map) { - *ref = current_value.s; - return LOOKUP_VALUE (current_value.m); + current_value.s = *ref; + current_value.m = undef_map; } - - return LOOKUP_VALUE (undef_map); } + + if (__builtin_expect (_dl_debug_mask + & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK), 0)) + _dl_debug_bindings (undef_name, undef_map, ref, symbol_scope, + ¤t_value, NULL, type_class, protected); + + *ref = current_value.s; + return LOOKUP_VALUE (current_value.m); } @@ -303,7 +297,6 @@ _dl_lookup_symbol_skip (const char *undef_name, struct r_scope_elem *symbol_scope[], struct link_map *skip_map) { - const char *reference_name = undef_map ? undef_map->l_name : NULL; const unsigned long int hash = _dl_elf_hash (undef_name); struct sym_val current_value = { NULL, NULL }; struct r_scope_elem **scope; @@ -332,20 +325,7 @@ _dl_lookup_symbol_skip (const char *undef_name, protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED; - if (__builtin_expect (_dl_debug_mask & DL_DEBUG_BINDINGS, 0)) - _dl_debug_printf ("binding file %s to %s: %s symbol `%s'\n", - (reference_name && reference_name[0] - ? reference_name : (_dl_argv[0] ?: "<main program>")), - current_value.m->l_name[0] - ? current_value.m->l_name : _dl_argv[0], - protected ? "protected" : "normal", undef_name); - - if (__builtin_expect (protected == 0, 1)) - { - *ref = current_value.s; - return LOOKUP_VALUE (current_value.m); - } - else + if (__builtin_expect (protected != 0, 0)) { /* It is very tricky. We need to figure out what value to return for the protected symbol. */ @@ -359,14 +339,20 @@ _dl_lookup_symbol_skip (const char *undef_name, 0, skip_map, ELF_RTYPE_CLASS_PLT)) break; - if (protected_value.s == NULL || protected_value.m == undef_map) + if (protected_value.s != NULL && protected_value.m != undef_map) { - *ref = current_value.s; - return LOOKUP_VALUE (current_value.m); + current_value.s = *ref; + current_value.m = undef_map; } - - return LOOKUP_VALUE (undef_map); } + + if (__builtin_expect (_dl_debug_mask + & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK), 0)) + _dl_debug_bindings (undef_name, undef_map, ref, symbol_scope, + ¤t_value, NULL, 0, protected); + + *ref = current_value.s; + return LOOKUP_VALUE (current_value.m); } @@ -383,7 +369,7 @@ _dl_lookup_versioned_symbol (const char *undef_name, const struct r_found_version *version, int type_class, int explicit) { - unsigned long int hash = _dl_elf_hash (undef_name); + const unsigned long int hash = _dl_elf_hash (undef_name); struct sym_val current_value = { NULL, NULL }; struct r_scope_elem **scope; int protected; @@ -423,7 +409,7 @@ _dl_lookup_versioned_symbol (const char *undef_name, const char *reference_name = undef_map ? undef_map->l_name : NULL; /* XXX We cannot translate the message. */ - _dl_signal_cerror (0, (reference_name && reference_name[0] + _dl_signal_cerror (0, (reference_name[0] ? reference_name : (_dl_argv[0] ?: "<main program>")), N_("relocation error"), @@ -447,7 +433,7 @@ _dl_lookup_versioned_symbol (const char *undef_name, const char *reference_name = undef_map ? undef_map->l_name : NULL; /* XXX We cannot translate the message. */ - _dl_signal_cerror (0, (reference_name && reference_name[0] + _dl_signal_cerror (0, (reference_name[0] ? reference_name : (_dl_argv[0] ?: "<main program>")), NULL, make_string (undefined_msg, undef_name, @@ -460,25 +446,7 @@ _dl_lookup_versioned_symbol (const char *undef_name, protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED; - if (__builtin_expect (_dl_debug_mask & DL_DEBUG_BINDINGS, 0)) - { - const char *reference_name = undef_map ? undef_map->l_name : NULL; - - _dl_debug_printf ("binding file %s to %s: %s symbol `%s' [%s]\n", - (reference_name && reference_name[0] - ? reference_name : (_dl_argv[0] ?: "<main program>")), - current_value.m->l_name[0] - ? current_value.m->l_name : _dl_argv[0], - protected ? "protected" : "normal", - undef_name, version->name); - } - - if (__builtin_expect (protected == 0, 1)) - { - *ref = current_value.s; - return LOOKUP_VALUE (current_value.m); - } - else + if (__builtin_expect (protected != 0, 0)) { /* It is very tricky. We need to figure out what value to return for the protected symbol */ @@ -490,14 +458,20 @@ _dl_lookup_versioned_symbol (const char *undef_name, ELF_RTYPE_CLASS_PLT)) break; - if (protected_value.s == NULL || protected_value.m == undef_map) + if (protected_value.s != NULL && protected_value.m != undef_map) { - *ref = current_value.s; - return LOOKUP_VALUE (current_value.m); + current_value.s = *ref; + current_value.m = undef_map; } - - return LOOKUP_VALUE (undef_map); } + + if (__builtin_expect (_dl_debug_mask + & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK), 0)) + _dl_debug_bindings (undef_name, undef_map, ref, symbol_scope, + ¤t_value, version, type_class, protected); + + *ref = current_value.s; + return LOOKUP_VALUE (current_value.m); } @@ -512,7 +486,7 @@ _dl_lookup_versioned_symbol_skip (const char *undef_name, const struct r_found_version *version, struct link_map *skip_map) { - const char *reference_name = undef_map ? undef_map->l_name : NULL; + const char *reference_name = undef_map->l_name; const unsigned long int hash = _dl_elf_hash (undef_name); struct sym_val current_value = { NULL, NULL }; struct r_scope_elem **scope; @@ -543,7 +517,7 @@ _dl_lookup_versioned_symbol_skip (const char *undef_name, __mempcpy (__mempcpy (buf, undefined_msg, sizeof undefined_msg - 1), undef_name, len + 1); /* XXX We cannot translate the messages. */ - _dl_signal_cerror (0, (reference_name && reference_name[0] + _dl_signal_cerror (0, (reference_name[0] ? reference_name : (_dl_argv[0] ?: "<main program>")), NULL, buf); @@ -554,21 +528,7 @@ _dl_lookup_versioned_symbol_skip (const char *undef_name, protected = *ref && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED; - if (__builtin_expect (_dl_debug_mask & DL_DEBUG_BINDINGS, 0)) - _dl_debug_printf ("binding file %s to %s: %s symbol `%s' [%s]\n", - (reference_name && reference_name[0] - ? reference_name : (_dl_argv[0] ?: "<main program>")), - current_value.m->l_name[0] - ? current_value.m->l_name : _dl_argv[0], - protected ? "protected" : "normal", - undef_name, version->name); - - if (__builtin_expect (protected == 0, 1)) - { - *ref = current_value.s; - return LOOKUP_VALUE (current_value.m); - } - else + if (__builtin_expect (protected != 0, 0)) { /* It is very tricky. We need to figure out what value to return for the protected symbol */ @@ -584,14 +544,20 @@ _dl_lookup_versioned_symbol_skip (const char *undef_name, skip_map, ELF_RTYPE_CLASS_PLT)) break; - if (protected_value.s == NULL || protected_value.m == undef_map) + if (protected_value.s != NULL && protected_value.m != undef_map) { - *ref = current_value.s; - return LOOKUP_VALUE (current_value.m); + current_value.s = *ref; + current_value.m = undef_map; } - - return LOOKUP_VALUE (undef_map); } + + if (__builtin_expect (_dl_debug_mask + & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK), 0)) + _dl_debug_bindings (undef_name, undef_map, ref, symbol_scope, + ¤t_value, version, 0, protected); + + *ref = current_value.s; + return LOOKUP_VALUE (current_value.m); } @@ -615,6 +581,79 @@ _dl_setup_hash (struct link_map *map) map->l_chain = hash; } +static void +internal_function +_dl_debug_bindings (const char *undef_name, struct link_map *undef_map, + const ElfW(Sym) **ref, struct r_scope_elem *symbol_scope[], + struct sym_val *value, const struct r_found_version *version, + int type_class, int protected) +{ + const char *reference_name = undef_map->l_name; + + if (_dl_debug_mask & DL_DEBUG_BINDINGS) + { + _dl_debug_printf ("binding file %s to %s: %s symbol `%s'", + (reference_name[0] + ? reference_name : (_dl_argv[0] ?: "<main program>")), + value->m->l_name[0] ? value->m->l_name : _dl_argv[0], + protected ? "protected" : "normal", + undef_name); + if (version) + _dl_debug_printf_c (" [%s]\n", version->name); + else + _dl_debug_printf_c ("\n"); + } +#ifdef SHARED + if (_dl_debug_mask & DL_DEBUG_PRELINK) + { + int conflict = 0; + struct sym_val val = { NULL, NULL }; + + if ((_dl_trace_prelink_map == NULL + || _dl_trace_prelink_map == _dl_loaded) + && undef_map != _dl_loaded) + { + const unsigned long int hash = _dl_elf_hash (undef_name); + + if (version == 0) + _dl_do_lookup (undef_name, hash, *ref, &val, + undef_map->l_local_scope[0], 0, NULL, type_class); + else + _dl_do_lookup_versioned (undef_name, hash, *ref, &val, + undef_map->l_local_scope[0], 0, version, + NULL, type_class); + + if (val.s != value->s || val.m != value->m) + conflict = 1; + } + + if (conflict + || _dl_trace_prelink_map == undef_map + || _dl_trace_prelink_map == NULL) + { + _dl_printf ("%s 0x%0*Zx 0x%0*Zx -> 0x%0*Zx 0x%0*Zx ", + conflict ? "conflict" : "lookup", + (int) sizeof (ElfW(Addr)) * 2, undef_map->l_map_start, + (int) sizeof (ElfW(Addr)) * 2, + ((ElfW(Addr)) *ref) - undef_map->l_map_start, + (int) sizeof (ElfW(Addr)) * 2, + (ElfW(Addr)) (value->s ? value->m->l_map_start : 0), + (int) sizeof (ElfW(Addr)) * 2, + (ElfW(Addr)) (value->s ? value->s->st_value : 0)); + + if (conflict) + _dl_printf ("x 0x%0*Zx 0x%0*Zx ", + (int) sizeof (ElfW(Addr)) * 2, + (ElfW(Addr)) (val.s ? val.m->l_map_start : 0), + (int) sizeof (ElfW(Addr)) * 2, + (ElfW(Addr)) (val.s ? val.s->st_value : 0)); + + _dl_printf ("/%x %s\n", type_class, undef_name); + } + } +#endif +} + /* These are here so that we only inline do_lookup{,_versioned} in the common case, not everywhere. */ static int diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c index 2f5832426b..27d99fcc66 100644 --- a/elf/dl-runtime.c +++ b/elf/dl-runtime.c @@ -23,7 +23,8 @@ #include <ldsodefs.h> #include "dynamic-link.h" -#if !defined ELF_MACHINE_NO_RELA || ELF_MACHINE_NO_REL +#if (!defined ELF_MACHINE_NO_RELA && !defined ELF_MACHINE_PLT_REL) \ + || ELF_MACHINE_NO_REL # define PLTREL ElfW(Rela) #else # define PLTREL ElfW(Rel) diff --git a/elf/do-rel.h b/elf/do-rel.h index 8b9bdf2da7..d08f655815 100644 --- a/elf/do-rel.h +++ b/elf/do-rel.h @@ -33,6 +33,10 @@ #ifndef VERSYMIDX # define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym)) #endif +#ifndef VALIDX +# define VALIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \ + + DT_EXTRANUM + DT_VALTAGIDX (tag)) +#endif /* Perform the relocations in MAP on the running program image as specified by RELTAG, SZTAG. If LAZY is nonzero, this is the first pass on PLT @@ -48,7 +52,7 @@ elf_dynamic_do_rel (struct link_map *map, const ElfW(Rel) *end = (const void *) (reladdr + relsize); ElfW(Addr) l_addr = map->l_addr; -#ifndef RTLD_BOOTSTRAP +#if (!defined DO_RELA || !defined ELF_MACHINE_PLT_REL) && !defined RTLD_BOOTSTRAP /* We never bind lazily during ld.so bootstrap. Unfortunately gcc is not clever enough to see through all the function calls to realize that. */ @@ -81,8 +85,11 @@ elf_dynamic_do_rel (struct link_map *map, /* Rela platforms get the offset from r_addend and this must be copied in the relocation address. Therefore we can skip the relative relocations only if this is for rel - relocations. */ + relocations... */ if (l_addr != 0) +# else + /* ...or we know the object has been prelinked. */ + if (l_addr != 0 || ! map->l_info[VALIDX(DT_GNU_PRELINKED)]) # endif #endif for (; relative < r; ++relative) diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h index 8e70a7eaec..bbfd301231 100644 --- a/elf/dynamic-link.h +++ b/elf/dynamic-link.h @@ -1,5 +1,5 @@ /* Inline functions for dynamic linking. - Copyright (C) 1995,96,97,98,99,2000 Free Software Foundation, Inc. + Copyright (C) 1995,96,97,98,99,2000,2001 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 @@ -58,31 +58,39 @@ elf_get_dynamic_info (struct link_map *l) else if ((Elf32_Word) DT_EXTRATAGIDX (dyn->d_tag) < DT_EXTRANUM) info[DT_EXTRATAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM] = dyn; + else if ((Elf32_Word) DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM) + info[DT_VALTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM + + DT_VERSIONTAGNUM + DT_EXTRANUM] = dyn; + else if ((Elf32_Word) DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM) + info[DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM + + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn; else assert (! "bad dynamic tag"); ++dyn; } #ifndef DL_RO_DYN_SECTION - if (info[DT_PLTGOT] != NULL) - info[DT_PLTGOT]->d_un.d_ptr += l_addr; - if (info[DT_STRTAB] != NULL) - info[DT_STRTAB]->d_un.d_ptr += l_addr; - if (info[DT_SYMTAB] != NULL) - info[DT_SYMTAB]->d_un.d_ptr += l_addr; -# if ! ELF_MACHINE_NO_RELA - if (info[DT_RELA] != NULL) + /* Don't adjust .dynamic unnecessarily. */ + if (l_addr) { - assert (info[DT_RELAENT]->d_un.d_val == sizeof (ElfW(Rela))); - info[DT_RELA]->d_un.d_ptr += l_addr; - } + if (info[DT_PLTGOT] != NULL) + info[DT_PLTGOT]->d_un.d_ptr += l_addr; + if (info[DT_STRTAB] != NULL) + info[DT_STRTAB]->d_un.d_ptr += l_addr; + if (info[DT_SYMTAB] != NULL) + info[DT_SYMTAB]->d_un.d_ptr += l_addr; +# if ! ELF_MACHINE_NO_RELA + if (info[DT_RELA] != NULL) + info[DT_RELA]->d_un.d_ptr += l_addr; # endif # if ! ELF_MACHINE_NO_REL - if (info[DT_REL] != NULL) - { - assert (info[DT_RELENT]->d_un.d_val == sizeof (ElfW(Rel))); - info[DT_REL]->d_un.d_ptr += l_addr; - } + if (info[DT_REL] != NULL) + info[DT_REL]->d_un.d_ptr += l_addr; # endif + if (info[DT_JMPREL] != NULL) + info[DT_JMPREL]->d_un.d_ptr += l_addr; + if (info[VERSYMIDX (DT_VERSYM)] != NULL) + info[VERSYMIDX (DT_VERSYM)]->d_un.d_ptr += l_addr; + } #endif if (info[DT_PLTREL] != NULL) { @@ -95,12 +103,14 @@ elf_get_dynamic_info (struct link_map *l) || info[DT_PLTREL]->d_un.d_val == DT_RELA); # endif } -#ifndef DL_RO_DYN_SECTION - if (info[DT_JMPREL] != NULL) - info[DT_JMPREL]->d_un.d_ptr += l_addr; - if (info[VERSYMIDX (DT_VERSYM)] != NULL) - info[VERSYMIDX (DT_VERSYM)]->d_un.d_ptr += l_addr; -#endif +# if ! ELF_MACHINE_NO_RELA + if (info[DT_RELA] != NULL) + assert (info[DT_RELAENT]->d_un.d_val == sizeof (ElfW(Rela))); +# endif +# if ! ELF_MACHINE_NO_REL + if (info[DT_REL] != NULL) + assert (info[DT_RELENT]->d_un.d_val == sizeof (ElfW(Rel))); +# endif if (info[DT_FLAGS] != NULL) { /* Flags are used. Translate to the old form where available. @@ -177,8 +187,8 @@ elf_get_dynamic_info (struct link_map *l) \ if ((map)->l_info[DT_##RELOC]) \ { \ - ranges[0].start = D_PTR ((map), l_info[DT_##RELOC]); \ - ranges[0].size = (map)->l_info[DT_##RELOC##SZ]->d_un.d_val; \ + ranges[0].start = D_PTR ((map), l_info[DT_##RELOC]); \ + ranges[0].size = (map)->l_info[DT_##RELOC##SZ]->d_un.d_val; \ } \ if ((map)->l_info[DT_PLTREL] \ && (!test_rel || (map)->l_info[DT_PLTREL]->d_un.d_val == DT_##RELOC)) \ @@ -676,6 +676,8 @@ typedef struct #define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ #define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ #define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 /* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the Dyn.d_un.d_ptr field of the Elf*_Dyn structure. @@ -692,6 +694,8 @@ typedef struct #define DT_MOVETAB 0x6ffffefe /* Move table. */ #define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ #define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 10 /* The versioning entry types. The next are defined as part of the GNU extension. */ diff --git a/elf/rtld.c b/elf/rtld.c index 8ed86eaedb..a05bbe9a62 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) diff --git a/include/link.h b/include/link.h index 77c1851736..2442350457 100644 --- a/include/link.h +++ b/include/link.h @@ -132,14 +132,20 @@ struct link_map /* Indexed pointers to dynamic section. [0,DT_NUM) are indexed by the processor-independent tags. [DT_NUM,DT_NUM+DT_THISPROCNUM) are indexed by the tag minus DT_LOPROC. - [DT_NUM+DT_THISPROCNUM,DT_NUM+DT_THISPROCNUM+DT_EXTRANUM) are indexed - by DT_EXTRATAGIDX(tagvalue) and + [DT_NUM+DT_THISPROCNUM,DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM) are + indexed by DT_VERSIONTAGIDX(tagvalue). [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM, - DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM) - are indexed by DT_EXTRATAGIDX(tagvalue) (see <elf.h>). */ + DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM) are indexed by + DT_EXTRATAGIDX(tagvalue). + [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM, + DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM) are + indexed by DT_VALTAGIDX(tagvalue) and + [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM, + DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM+DT_ADDRNUM) + are indexed by DT_ADDRTAGIDX(tagvalue), see <elf.h>. */ ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM - + DT_EXTRANUM]; + + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM]; const ElfW(Phdr) *l_phdr; /* Pointer to program header table in core. */ ElfW(Addr) l_entry; /* Entry point location. */ ElfW(Half) l_phnum; /* Number of program header entries. */ diff --git a/sysdeps/alpha/dl-machine.h b/sysdeps/alpha/dl-machine.h index a039f245db..c93da661bf 100644 --- a/sysdeps/alpha/dl-machine.h +++ b/sysdeps/alpha/dl-machine.h @@ -122,8 +122,30 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) *(Elf64_Addr *)(plt + 24) = (Elf64_Addr) l; /* If the first instruction of the plt entry is not - "br $28, plt0", we cannot do lazy relocation. */ - lazy = (*(unsigned int *)(plt + 32) == 0xc39ffff7); + "br $28, plt0", we have to reinitialize .plt for lazy relocation. */ + if (*(unsigned int *)(plt + 32) != 0xc39ffff7) + { + unsigned int val = 0xc39ffff7; + unsigned int *slot, *end; + const Elf64_Rela *rela = D_PTR (l, l_info[DT_JMPREL]); + Elf64_Addr l_addr = l->l_addr; + + /* br t12,.+4; ldq t12,12(t12); nop; jmp t12,(t12),.+4 */ + *(unsigned long *)plt = 0xa77b000cc3600000; + *(unsigned long *)(plt + 8) = 0x6b7b000047ff041f; + slot = (unsigned int *)(plt + 32); + end = (unsigned int *)(plt + 32 + + l->l_info[DT_PLTRELSZ]->d_un.d_val / 2); + while (slot < end) + { + /* br at,.plt+0 */ + *slot = val; + *(Elf64_Addr *) rela->r_offset = (Elf64_Addr) slot - l_addr; + val -= 3; + slot += 3; + ++rela; + } + } } return lazy; @@ -520,8 +542,23 @@ elf_machine_rela (struct link_map *map, if (r_type == R_ALPHA_GLOB_DAT) *reloc_addr = sym_value; - else if (r_type == R_ALPHA_JMP_SLOT) +#ifdef RESOLVE_CONFLICT_FIND_MAP + /* In .gnu.conflict section, R_ALPHA_JMP_SLOT relocations have + R_ALPHA_JMP_SLOT in lower 8 bits and the remaining 24 bits + are .rela.plt index. */ + else if ((r_type & 0xff) == R_ALPHA_JMP_SLOT) + { + /* elf_machine_fixup_plt needs the map reloc_addr points into, + while in _dl_resolve_conflicts map is _dl_loaded. */ + RESOLVE_CONFLICT_FIND_MAP (map, reloc_addr); + reloc = ((const Elf64_Rela *) D_PTR (map, l_info[DT_JMPREL])) + + (r_type >> 8); + elf_machine_fixup_plt (map, 0, reloc, reloc_addr, sym_value); + } +#else + else if (r_type == R_ALPHA_JMP_SLOT) elf_machine_fixup_plt (map, 0, reloc, reloc_addr, sym_value); +#endif #ifndef RTLD_BOOTSTRAP else if (r_type == R_ALPHA_REFQUAD) { diff --git a/sysdeps/arm/bits/link.h b/sysdeps/arm/bits/link.h new file mode 100644 index 0000000000..648976d7d2 --- /dev/null +++ b/sysdeps/arm/bits/link.h @@ -0,0 +1,4 @@ +struct link_map_machine + { + Elf32_Addr plt; /* Address of .plt */ + }; diff --git a/sysdeps/arm/dl-machine.h b/sysdeps/arm/dl-machine.h index 2d802b7e9b..cda424757b 100644 --- a/sysdeps/arm/dl-machine.h +++ b/sysdeps/arm/dl-machine.h @@ -92,6 +92,11 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) index into the .got section, load ip with &_GLOBAL_OFFSET_TABLE_[3], and then jump to _GLOBAL_OFFSET_TABLE[2]. */ got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]); + /* If a library is prelinked but we have to relocate anyway, + we have to be able to undo the prelinking of .got.plt. + The prelinker saved us here address of .plt. */ + if (got[1]) + l->l_mach.plt = got[1] + l->l_addr; got[1] = (Elf32_Addr) l; /* Identify this shared object. */ /* The got[2] entry contains the address of a function which gets @@ -334,8 +339,9 @@ _dl_start_user: /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */ #define ELF_MACHINE_JMP_SLOT R_ARM_JUMP_SLOT -/* The ARM never uses Elf32_Rela relocations. */ -#define ELF_MACHINE_NO_RELA 1 +/* ARM never uses Elf32_Rela relocations for the dynamic linker. + Prelinked libraries may use Elf32_Rela though. */ +#define ELF_MACHINE_PLT_REL 1 /* We define an initialization functions. This is called very early in _dl_sysdep_start. */ @@ -371,6 +377,12 @@ elf_machine_plt_value (struct link_map *map, const Elf32_Rel *reloc, #ifdef RESOLVE +/* ARM never uses Elf32_Rela relocations for the dynamic linker. + Prelinked libraries may use Elf32_Rela though. */ +#ifdef RTLD_BOOTSTRAP +#define ELF_MACHINE_NO_RELA 1 +#endif + extern char **_dl_argv; /* Deal with an out-of-range PC24 reloc. */ @@ -517,6 +529,64 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc, } } +#ifndef RTLD_BOOTSTRAP +static inline void +elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, + const Elf32_Sym *sym, const struct r_found_version *version, + Elf32_Addr *const reloc_addr) +{ + const unsigned int r_type = ELF32_R_TYPE (reloc->r_info); + + if (__builtin_expect (r_type == R_ARM_RELATIVE, 0)) + *reloc_addr = map->l_addr + reloc->r_addend; +#ifndef RTLD_BOOTSTRAP + else if (__builtin_expect (r_type == R_ARM_NONE, 0)) + return; +#endif + else + { + const Elf32_Sym *const refsym = sym; + Elf32_Addr value = RESOLVE (&sym, version, r_type); + if (sym) + value += sym->st_value; + + switch (r_type) + { + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + case R_ARM_ABS32: + *reloc_addr = value + reloc->r_addend; + break; + case R_ARM_PC24: + { + Elf32_Addr newvalue, topbits; + + newvalue = value + reloc->r_addend - (Elf32_Addr)reloc_addr; + topbits = newvalue & 0xfe000000; + if (topbits != 0xfe000000 && topbits != 0x00000000) + { + newvalue = fix_bad_pc24(reloc_addr, value) + - (Elf32_Addr)reloc_addr + (addend << 2); + topbits = newvalue & 0xfe000000; + if (topbits != 0xfe000000 && topbits != 0x00000000) + { + _dl_signal_error (0, map->l_name, NULL, + "R_ARM_PC24 relocation out of range"); + } + } + newvalue >>= 2; + value = (*reloc_addr & 0xff000000) | (newvalue & 0x00ffffff); + *reloc_addr = value; + } + break; + default: + _dl_reloc_bad_type (map, r_type, 0); + break; + } + } +} +#endif + static inline void elf_machine_rel_relative (Elf32_Addr l_addr, const Elf32_Rel *reloc, Elf32_Addr *const reloc_addr) @@ -524,6 +594,15 @@ elf_machine_rel_relative (Elf32_Addr l_addr, const Elf32_Rel *reloc, *reloc_addr += l_addr; } +#ifndef RTLD_BOOTSTRAP +static inline void +elf_machine_rela_relative (Elf32_Addr l_addr, const Elf32_Rela *reloc, + Elf32_Addr *const reloc_addr) +{ + *reloc_addr = l_addr + reloc->r_addend; +} +#endif + static inline void elf_machine_lazy_rel (struct link_map *map, Elf32_Addr l_addr, const Elf32_Rel *reloc) @@ -532,7 +611,12 @@ elf_machine_lazy_rel (struct link_map *map, const unsigned int r_type = ELF32_R_TYPE (reloc->r_info); /* Check for unexpected PLT reloc type. */ if (__builtin_expect (r_type == R_ARM_JUMP_SLOT, 1)) - *reloc_addr += l_addr; + { + if (__builtin_expect (map->l_mach.plt, 0) == 0) + *reloc_addr += l_addr; + else + *reloc_addr = map->l_mach.plt; + } else _dl_reloc_bad_type (map, r_type, 1); } diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 8c2f160160..837d32d0ca 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -207,6 +207,8 @@ extern const char *_dl_profile; extern struct link_map *_dl_profile_map; /* Filename of the output file. */ extern const char *_dl_profile_output; +/* Map of shared object to be prelink traced. */ +extern struct link_map *_dl_trace_prelink_map; /* If nonzero the appropriate debug information is printed. */ extern int _dl_debug_mask; @@ -220,6 +222,7 @@ extern int _dl_debug_mask; #define DL_DEBUG_STATISTICS (1 << 7) /* This one is used only internally. */ #define DL_DEBUG_HELP (1 << 8) +#define DL_DEBUG_PRELINK (1 << 9) /* Expect cache ID. */ extern int _dl_correct_cache_id; @@ -435,6 +438,11 @@ extern void _dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt) internal_function __attribute__ ((__noreturn__)); +/* Resolve conflicts if prelinking. */ +extern void _dl_resolve_conflicts (struct link_map *l, + ElfW(Rela) *conflict, + ElfW(Rela) *conflictend); + /* Check the version dependencies of all objects available through MAP. If VERBOSE print some more diagnostics. */ extern int _dl_check_all_versions (struct link_map *map, int verbose, diff --git a/sysdeps/i386/bits/link.h b/sysdeps/i386/bits/link.h new file mode 100644 index 0000000000..3be9b7eae8 --- /dev/null +++ b/sysdeps/i386/bits/link.h @@ -0,0 +1,5 @@ +struct link_map_machine + { + Elf32_Addr plt; /* Address of .plt + 0x16 */ + Elf32_Addr gotplt; /* Address of .got + 0x0c */ + }; diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h index 2f7f96d487..b86f11724b 100644 --- a/sysdeps/i386/dl-machine.h +++ b/sysdeps/i386/dl-machine.h @@ -87,6 +87,14 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) offset into the .rel.plt section, push _GLOBAL_OFFSET_TABLE_[1], and then jump to _GLOBAL_OFFSET_TABLE[2]. */ got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]); + /* If a library is prelinked but we have to relocate anyway, + we have to be able to undo the prelinking of .got.plt. + The prelinker saved us here address of .plt + 0x16. */ + if (got[1]) + { + l->l_mach.plt = got[1] + l->l_addr; + l->l_mach.gotplt = (Elf32_Addr) &got[3]; + } got[1] = (Elf32_Addr) l; /* Identify this shared object. */ /* The got[2] entry contains the address of a function which gets @@ -258,8 +266,9 @@ _dl_start_user:\n\ /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries. */ #define ELF_MACHINE_JMP_SLOT R_386_JMP_SLOT -/* The i386 never uses Elf32_Rela relocations. */ -#define ELF_MACHINE_NO_RELA 1 +/* The i386 never uses Elf32_Rela relocations for the dynamic linker. + Prelinked libraries may use Elf32_Rela though. */ +#define ELF_MACHINE_PLT_REL 1 /* We define an initialization functions. This is called very early in _dl_sysdep_start. */ @@ -295,6 +304,12 @@ elf_machine_plt_value (struct link_map *map, const Elf32_Rel *reloc, #ifdef RESOLVE +/* The i386 never uses Elf32_Rela relocations for the dynamic linker. + Prelinked libraries may use Elf32_Rela though. */ +#ifdef RTLD_BOOTSTRAP +#define ELF_MACHINE_NO_RELA 1 +#endif + /* Perform the relocation specified by RELOC and SYM (which is fully resolved). MAP is the object containing the reloc. */ @@ -378,6 +393,41 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc, } } +#ifndef RTLD_BOOTSTRAP +static inline void +elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, + const Elf32_Sym *sym, const struct r_found_version *version, + Elf32_Addr *const reloc_addr) +{ + if (ELF32_R_TYPE (reloc->r_info) == R_386_RELATIVE) + *reloc_addr = map->l_addr + reloc->r_addend; + else if (ELF32_R_TYPE (reloc->r_info) != R_386_NONE) + { +/* const Elf32_Sym *const refsym = sym; */ + Elf32_Addr value = RESOLVE (&sym, version, ELF32_R_TYPE (reloc->r_info)); + if (sym) + value += sym->st_value; + + switch (ELF32_R_TYPE (reloc->r_info)) + { + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + case R_386_32: + *reloc_addr = value + reloc->r_addend; + break; + case R_386_PC32: + *reloc_addr = (value + reloc->r_addend - (Elf32_Addr) reloc_addr); + break; + default: + /* We add these checks in the version to relocate ld.so only + if we are still debugging. */ + _dl_reloc_bad_type (map, ELFW(R_TYPE) (reloc->r_info), 0); + break; + } + } +} +#endif + static inline void elf_machine_rel_relative (Elf32_Addr l_addr, const Elf32_Rel *reloc, Elf32_Addr *const reloc_addr) @@ -386,6 +436,15 @@ elf_machine_rel_relative (Elf32_Addr l_addr, const Elf32_Rel *reloc, *reloc_addr += l_addr; } +#ifndef RTLD_BOOTSTRAP +static inline void +elf_machine_rela_relative (Elf32_Addr l_addr, const Elf32_Rela *reloc, + Elf32_Addr *const reloc_addr) +{ + *reloc_addr = l_addr + reloc->r_addend; +} +#endif + static inline void elf_machine_lazy_rel (struct link_map *map, Elf32_Addr l_addr, const Elf32_Rel *reloc) @@ -394,9 +453,26 @@ elf_machine_lazy_rel (struct link_map *map, const unsigned int r_type = ELF32_R_TYPE (reloc->r_info); /* Check for unexpected PLT reloc type. */ if (__builtin_expect (r_type == R_386_JMP_SLOT, 1)) - *reloc_addr += l_addr; + { + if (__builtin_expect (map->l_mach.plt, 0) == 0) + *reloc_addr += l_addr; + else + *reloc_addr = + map->l_mach.plt + + (((Elf32_Addr) reloc_addr) - map->l_mach.gotplt) * 4; + } else _dl_reloc_bad_type (map, r_type, 1); } +#ifndef RTLD_BOOTSTRAP + +static inline void +elf_machine_lazy_rela (struct link_map *map, + Elf32_Addr l_addr, const Elf32_Rela *reloc) +{ +} + +#endif + #endif /* RESOLVE */ diff --git a/sysdeps/powerpc/dl-machine.h b/sysdeps/powerpc/dl-machine.h index e8b5446875..35b7e55e99 100644 --- a/sysdeps/powerpc/dl-machine.h +++ b/sysdeps/powerpc/dl-machine.h @@ -347,6 +347,7 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, Elf32_Word loadbase, finaladdr; const int rinfo = ELF32_R_TYPE (reloc->r_info); +#ifndef RESOLVE_CONFLICT_FIND_MAP if (__builtin_expect (rinfo == R_PPC_NONE, 0)) return; @@ -375,6 +376,11 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, finaladdr = (loadbase + (Elf32_Word) (char *) sym->st_value + reloc->r_addend); } +#else + finaladdr = reloc->r_addend; + if (rinfo == R_PPC_JMP_SLOT) + RESOLVE_CONFLICT_FIND_MAP (map, reloc_addr); +#endif /* A small amount of code is duplicated here for speed. In libc, more than 90% of the relocs are R_PPC_RELATIVE; in the X11 shared diff --git a/sysdeps/s390/s390-32/bits/link.h b/sysdeps/s390/s390-32/bits/link.h new file mode 100644 index 0000000000..962cf56851 --- /dev/null +++ b/sysdeps/s390/s390-32/bits/link.h @@ -0,0 +1,5 @@ +struct link_map_machine + { + Elf32_Addr plt; /* Address of .plt + 0x2c */ + Elf32_Addr gotplt; /* Address of .got + 0x0c */ + }; diff --git a/sysdeps/s390/s390-32/dl-machine.h b/sysdeps/s390/s390-32/dl-machine.h index f72651fba0..fc80877428 100644 --- a/sysdeps/s390/s390-32/dl-machine.h +++ b/sysdeps/s390/s390-32/dl-machine.h @@ -92,6 +92,14 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) and then jump to _GLOBAL_OFFSET_TABLE[2]. */ Elf32_Addr *got; got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]); + /* If a library is prelinked but we have to relocate anyway, + we have to be able to undo the prelinking of .got.plt. + The prelinker saved us here address of .plt + 0x2c. */ + if (got[1]) + { + l->l_mach.plt = got[1] + l->l_addr; + l->l_mach.gotplt = (Elf32_Addr) &got[3]; + } got[1] = (Elf32_Addr) l; /* Identify this shared object. */ /* The got[2] entry contains the address of a function which gets @@ -454,7 +462,14 @@ elf_machine_lazy_rel (struct link_map *map, const unsigned int r_type = ELF32_R_TYPE (reloc->r_info); /* Check for unexpected PLT reloc type. */ if (__builtin_expect (r_type == R_390_JMP_SLOT, 1)) - *reloc_addr += l_addr; + { + if (__builtin_expect (map->l_mach.plt, 0) == 0) + *reloc_addr += l_addr; + else + *reloc_addr = + map->l_mach.plt + + (((Elf32_Addr) reloc_addr) - map->l_mach.gotplt) * 8; + } else _dl_reloc_bad_type (map, r_type, 1); } diff --git a/sysdeps/s390/s390-64/bits/link.h b/sysdeps/s390/s390-64/bits/link.h new file mode 100644 index 0000000000..34add4ffaa --- /dev/null +++ b/sysdeps/s390/s390-64/bits/link.h @@ -0,0 +1,5 @@ +struct link_map_machine + { + Elf64_Addr plt; /* Address of .plt + 0x2e */ + Elf64_Addr gotplt; /* Address of .got + 0x18 */ + }; diff --git a/sysdeps/s390/s390-64/dl-machine.h b/sysdeps/s390/s390-64/dl-machine.h index e77017ab1a..4d4c344ea0 100644 --- a/sysdeps/s390/s390-64/dl-machine.h +++ b/sysdeps/s390/s390-64/dl-machine.h @@ -85,6 +85,14 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) and then jump to _GLOBAL_OFFSET_TABLE[2]. */ Elf64_Addr *got; got = (Elf64_Addr *) D_PTR (l, l_info[DT_PLTGOT]); + /* If a library is prelinked but we have to relocate anyway, + we have to be able to undo the prelinking of .got.plt. + The prelinker saved us here address of .plt + 0x2e. */ + if (got[1]) + { + l->l_mach.plt = got[1] + l->l_addr; + l->l_mach.gotplt = (Elf64_Addr) &got[3]; + } got[1] = (Elf64_Addr) l; /* Identify this shared object. */ /* The got[2] entry contains the address of a function which gets @@ -434,7 +442,14 @@ elf_machine_lazy_rel (struct link_map *map, const unsigned int r_type = ELF32_R_TYPE (reloc->r_info); /* Check for unexpected PLT reloc type. */ if (__builtin_expect (r_type == R_390_JMP_SLOT, 1)) - *reloc_addr += l_addr; + { + if (__builtin_expect (map->l_mach.plt, 0) == 0) + *reloc_addr += l_addr; + else + *reloc_addr = + map->l_mach.plt + + (((Elf64_Addr) reloc_addr) - map->l_mach.gotplt) * 4; + } else _dl_reloc_bad_type (map, r_type, 1); } diff --git a/sysdeps/sh/bits/link.h b/sysdeps/sh/bits/link.h new file mode 100644 index 0000000000..bb2fbb5f16 --- /dev/null +++ b/sysdeps/sh/bits/link.h @@ -0,0 +1,5 @@ +struct link_map_machine + { + Elf32_Addr plt; /* Address of .plt + 36 */ + Elf32_Addr gotplt; /* Address of .got + 0x0c */ + }; diff --git a/sysdeps/sh/dl-machine.h b/sysdeps/sh/dl-machine.h index dc53c652d0..b303756e44 100644 --- a/sysdeps/sh/dl-machine.h +++ b/sysdeps/sh/dl-machine.h @@ -85,6 +85,14 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) offset into the .rela.plt section and _GLOBAL_OFFSET_TABLE_[1], and then jump to _GLOBAL_OFFSET_TABLE[2]. */ got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]); + /* If a library is prelinked but we have to relocate anyway, + we have to be able to undo the prelinking of .got.plt. + The prelinker saved us here address of .plt + 36. */ + if (got[1]) + { + l->l_mach.plt = got[1] + l->l_addr; + l->l_mach.gotplt = (Elf32_Addr) &got[3]; + } got[1] = (Elf32_Addr) l; /* Identify this shared object. */ /* The got[2] entry contains the address of a function which gets @@ -582,7 +590,14 @@ elf_machine_lazy_rel (struct link_map *map, Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset); /* Check for unexpected PLT reloc type. */ if (ELF32_R_TYPE (reloc->r_info) == R_SH_JMP_SLOT) - *reloc_addr += l_addr; + { + if (__builtin_expect (map->l_mach.plt, 0) == 0) + *reloc_addr += l_addr; + else + *reloc_addr = + map->l_mach.plt + + (((Elf32_Addr) reloc_addr) - map->l_mach.gotplt) * 7; + } else _dl_reloc_bad_type (map, ELF32_R_TYPE (reloc->r_info), 1); } diff --git a/sysdeps/sparc/sparc32/dl-machine.h b/sysdeps/sparc/sparc32/dl-machine.h index d98848b5dd..19a3897edb 100644 --- a/sysdeps/sparc/sparc32/dl-machine.h +++ b/sysdeps/sparc/sparc32/dl-machine.h @@ -23,6 +23,10 @@ #include <sys/param.h> #include <ldsodefs.h> +#ifndef VALIDX +# define VALIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \ + + DT_EXTRANUM + DT_VALTAGIDX (tag)) +#endif /* Some SPARC opcodes we need to use for self-modifying code. */ #define OPCODE_NOP 0x01000000 /* nop */ @@ -30,6 +34,7 @@ #define OPCODE_SETHI_G1 0x03000000 /* sethi ?, %g1; add value>>10 */ #define OPCODE_JMP_G1 0x81c06000 /* jmp %g1+?; add lo 10 bits of value */ #define OPCODE_SAVE_SP 0x9de3bfa8 /* save %sp, -(16+6)*4, %sp */ +#define OPCODE_BA 0x30800000 /* b,a ?; add PC-rel word address */ /* Protect some broken versions of gcc from misinterpreting weak addresses. */ #define WEAKADDR(x) ({ __typeof(x) *_px = &x; \ @@ -139,6 +144,37 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) plt[1] = OPCODE_CALL | ((rfunc - (Elf32_Addr) &plt[1]) >> 2); plt[2] = OPCODE_NOP; /* Fill call delay slot. */ plt[3] = (Elf32_Addr) l; + if (__builtin_expect (l->l_info[VALIDX(DT_GNU_PRELINKED)] != NULL, 0) + || __builtin_expect (l->l_info [VALIDX (DT_GNU_LIBLISTSZ)] != NULL, 0)) + { + /* Need to reinitialize .plt to undo prelinking. */ + unsigned long *hwcap; + int do_flush; + Elf32_Rela *rela = (Elf32_Rela *) D_PTR (l, l_info[DT_JMPREL]); + Elf32_Rela *relaend + = (Elf32_Rela *) ((char *) rela + + l->l_info[DT_PLTRELSZ]->d_un.d_val); + weak_extern (_dl_hwcap); + hwcap = WEAKADDR(_dl_hwcap); + do_flush = (!hwcap || (*hwcap & HWCAP_SPARC_FLUSH)); + + /* prelink must ensure there are no R_SPARC_NONE relocs left + in .rela.plt. */ + while (rela < relaend) + { + *(unsigned int *) rela->r_offset + = OPCODE_SETHI_G1 | (rela->r_offset - (Elf32_Addr) plt); + *(unsigned int *) (rela->r_offset + 4) + = OPCODE_BA | ((((Elf32_Addr) plt + - rela->r_offset - 4) >> 2) & 0x3fffff); + if (do_flush) + { + __asm __volatile ("flush %0" : : "r"(rela->r_offset)); + __asm __volatile ("flush %0+4" : : "r"(rela->r_offset)); + } + ++rela; + } + } } return lazy; @@ -292,10 +328,10 @@ _dl_start_user: .previous"); static inline Elf32_Addr -elf_machine_fixup_plt (struct link_map *map, lookup_t t, - const Elf32_Rela *reloc, - Elf32_Addr *reloc_addr, Elf32_Addr value) +sparc_fixup_plt (const Elf32_Rela *reloc, Elf32_Addr *reloc_addr, + Elf32_Addr value, int t) { + Elf32_Sword disp = value - (Elf32_Addr) reloc_addr; #ifndef RTLD_BOOTSTRAP /* Note that we don't mask the hwcap here, as the flush is essential to functionality on those cpu's that implement it. */ @@ -309,23 +345,44 @@ elf_machine_fixup_plt (struct link_map *map, lookup_t t, ld.so will not execute corrupt PLT entry instructions. */ const int do_flush = 1; #endif + + if (0 && disp >= -0x800000 && disp < 0x800000) + { + /* Don't need to worry about thread safety. We're writing just one + instruction. */ - /* For thread safety, write the instructions from the bottom and - flush before we overwrite the critical "b,a". This of course - need not be done during bootstrapping, since there are no threads. - But we also can't tell if we _can_ use flush, so don't. */ - - reloc_addr[2] = OPCODE_JMP_G1 | (value & 0x3ff); - if (do_flush) - __asm __volatile ("flush %0+8" : : "r"(reloc_addr)); - - reloc_addr[1] = OPCODE_SETHI_G1 | (value >> 10); - if (do_flush) - __asm __volatile ("flush %0+4" : : "r"(reloc_addr)); + reloc_addr[0] = OPCODE_BA | ((disp >> 2) & 0x3fffff); + if (do_flush) + __asm __volatile ("flush %0" : : "r"(reloc_addr)); + } + else + { + /* For thread safety, write the instructions from the bottom and + flush before we overwrite the critical "b,a". This of course + need not be done during bootstrapping, since there are no threads. + But we also can't tell if we _can_ use flush, so don't. */ + + reloc_addr += t; + reloc_addr[1] = OPCODE_JMP_G1 | (value & 0x3ff); + if (do_flush) + __asm __volatile ("flush %0+4" : : "r"(reloc_addr)); + + reloc_addr[0] = OPCODE_SETHI_G1 | (value >> 10); + if (do_flush) + __asm __volatile ("flush %0" : : "r"(reloc_addr)); + } return value; } +static inline Elf32_Addr +elf_machine_fixup_plt (struct link_map *map, lookup_t t, + const Elf32_Rela *reloc, + Elf32_Addr *reloc_addr, Elf32_Addr value) +{ + return sparc_fixup_plt (reloc, reloc_addr, value, 1); +} + /* Return the final value of a plt relocation. */ static inline Elf32_Addr elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc, @@ -366,10 +423,11 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, else #endif { -#ifndef RTLD_BOOTSTRAP +#if !defined RTLD_BOOTSTRAP && !defined RESOLVE_CONFLICT_FIND_MAP const Elf32_Sym *const refsym = sym; #endif Elf32_Addr value; +#ifndef RESOLVE_CONFLICT_FIND_MAP if (sym->st_shndx != SHN_UNDEF && ELF32_ST_BIND (sym->st_info) == STB_LOCAL) value = map->l_addr; @@ -379,11 +437,14 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, if (sym) value += sym->st_value; } +#else + value = 0; +#endif value += reloc->r_addend; /* Assume copy relocs have zero addend. */ switch (r_type) { -#ifndef RTLD_BOOTSTRAP +#if !defined RTLD_BOOTSTRAP && !defined RESOLVE_CONFLICT_FIND_MAP case R_SPARC_COPY: if (sym == NULL) /* This can happen in trace mode if an object could not be @@ -410,7 +471,9 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, *reloc_addr = value; break; case R_SPARC_JMP_SLOT: - elf_machine_fixup_plt(map, 0, reloc, reloc_addr, value); + /* At this point we don't need to bother with thread safety, + so we can optimize the first instruction of .plt out. */ + sparc_fixup_plt (reloc, reloc_addr, value, 0); break; #ifndef RTLD_BOOTSTRAP case R_SPARC_8: diff --git a/sysdeps/sparc/sparc64/dl-machine.h b/sysdeps/sparc/sparc64/dl-machine.h index 9d2f2187ae..913f98a5e1 100644 --- a/sysdeps/sparc/sparc64/dl-machine.h +++ b/sysdeps/sparc/sparc64/dl-machine.h @@ -24,6 +24,11 @@ #include <ldsodefs.h> #include <sysdep.h> +#ifndef VALIDX +# define VALIDX(tag) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM \ + + DT_EXTRANUM + DT_VALTAGIDX (tag)) +#endif + #define ELF64_R_TYPE_ID(info) ((info) & 0xff) #define ELF64_R_TYPE_DATA(info) ((info) >> 8) @@ -147,7 +152,7 @@ sparc64_fixup_plt (struct link_map *map, const Elf64_Rela *reloc, insns[1] = 0x40000000 | (displacement >> 2); __asm __volatile ("flush %0 + 4" : : "r" (insns)); - insns[t] = 0x8210000f; + insns[0] = 0x8210000f; __asm __volatile ("flush %0" : : "r" (insns)); } /* Worst case, ho hum... */ @@ -251,10 +256,11 @@ elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc, else #endif { -#ifndef RTLD_BOOTSTRAP +#if !defined RTLD_BOOTSTRAP && !defined RESOLVE_CONFLICT_FIND_MAP const Elf64_Sym *const refsym = sym; #endif Elf64_Addr value; +#ifndef RESOLVE_CONFLICT_FIND_MAP if (sym->st_shndx != SHN_UNDEF && ELF64_ST_BIND (sym->st_info) == STB_LOCAL) value = map->l_addr; @@ -264,11 +270,14 @@ elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc, if (sym) value += sym->st_value; } +#else + value = 0; +#endif value += reloc->r_addend; /* Assume copy relocs have zero addend. */ switch (r_type) { -#ifndef RTLD_BOOTSTRAP +#if !defined RTLD_BOOTSTRAP && !defined RESOLVE_CONFLICT_FIND_MAP case R_SPARC_COPY: if (sym == NULL) /* This can happen in trace mode if an object could not be @@ -371,8 +380,18 @@ elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc, break; #endif case R_SPARC_JMP_SLOT: +#ifdef RESOLVE_CONFLICT_FIND_MAP + /* R_SPARC_JMP_SLOT conflicts against .plt[32768+] + relocs should be turned into R_SPARC_64 relocs + in .gnu.conflict section. + r_addend non-zero does not mean it is a .plt[32768+] + reloc, instead it is the actual address of the function + to call. */ + sparc64_fixup_plt (NULL, reloc, reloc_addr, value, 0, 0); +#else sparc64_fixup_plt (map, reloc, reloc_addr, value, reloc->r_addend, 0); +#endif break; #ifndef RTLD_BOOTSTRAP case R_SPARC_UA16: @@ -536,6 +555,47 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) /* Now put the magic cookie at the beginning of .PLT2 Entry .PLT3 is unused by this implementation. */ *((struct link_map **)(&plt[16 + 0])) = l; + + if (__builtin_expect (l->l_info[VALIDX(DT_GNU_PRELINKED)] != NULL, 0) + || __builtin_expect (l->l_info [VALIDX (DT_GNU_LIBLISTSZ)] != NULL, 0)) + { + /* Need to reinitialize .plt to undo prelinking. */ + Elf64_Rela *rela = (Elf64_Rela *) D_PTR (l, l_info[DT_JMPREL]); + Elf64_Rela *relaend + = (Elf64_Rela *) ((char *) rela + + l->l_info[DT_PLTRELSZ]->d_un.d_val); + + /* prelink must ensure there are no R_SPARC_NONE relocs left + in .rela.plt. */ + while (rela < relaend) + { + if (__builtin_expect (rela->r_addend, 0) != 0) + { + Elf64_Addr slot = ((rela->r_offset + 0x400 + - (Elf64_Addr) plt) + / 0x1400) * 0x1400 + + (Elf64_Addr) plt - 0x400; + /* ldx [%o7 + X], %g1 */ + unsigned int first_ldx = *(unsigned int *)(slot + 12); + Elf64_Addr ptr = slot + (first_ldx & 0xfff) + 4; + + *(Elf64_Addr *) rela->r_offset + = (Elf64_Addr) plt + - (slot + ((rela->r_offset - ptr) / 8) * 24 + 4); + ++rela; + continue; + } + + *(unsigned int *) rela->r_offset + = 0x03000000 | (rela->r_offset - (Elf64_Addr) plt); + *(unsigned int *) (rela->r_offset + 4) + = 0x30680000 | ((((Elf64_Addr) plt + 32 + - rela->r_offset - 4) >> 2) & 0x7ffff); + __asm __volatile ("flush %0" : : "r" (rela->r_offset)); + __asm __volatile ("flush %0+4" : : "r" (rela->r_offset)); + ++rela; + } + } } return lazy; diff --git a/sysdeps/x86_64/bits/link.h b/sysdeps/x86_64/bits/link.h new file mode 100644 index 0000000000..21c294a73c --- /dev/null +++ b/sysdeps/x86_64/bits/link.h @@ -0,0 +1,5 @@ +struct link_map_machine + { + Elf64_Addr plt; /* Address of .plt + 0x16 */ + Elf64_Addr gotplt; /* Address of .got + 0x18 */ + }; diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h index 268c2c31d6..955239dd27 100644 --- a/sysdeps/x86_64/dl-machine.h +++ b/sysdeps/x86_64/dl-machine.h @@ -76,6 +76,14 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) offset into the .rel.plt section, push _GLOBAL_OFFSET_TABLE_[1], and then jump to _GLOBAL_OFFSET_TABLE[2]. */ got = (Elf64_Addr *) D_PTR (l, l_info[DT_PLTGOT]); + /* If a library is prelinked but we have to relocate anyway, + we have to be able to undo the prelinking of .got.plt. + The prelinker saved us here address of .plt + 0x16. */ + if (got[1]) + { + l->l_mach.plt = got[1] + l->l_addr; + l->l_mach.gotplt = (Elf64_Addr) &got[3]; + } got[1] = (Elf64_Addr) l; /* Identify this shared object. */ /* The got[2] entry contains the address of a function which gets @@ -409,7 +417,14 @@ elf_machine_lazy_rel (struct link_map *map, /* Check for unexpected PLT reloc type. */ if (__builtin_expect (r_type == R_X86_64_JUMP_SLOT, 1)) - *reloc_addr += l_addr; + { + if (__builtin_expect (map->l_mach.plt, 0) == 0) + *reloc_addr += l_addr; + else + *reloc_addr = + map->l_mach.plt + + (((Elf64_Addr) reloc_addr) - map->l_mach.gotplt) * 2; + } else _dl_reloc_bad_type (map, r_type, 1); } |