diff options
-rw-r--r-- | Makerules | 9 | ||||
-rw-r--r-- | extra/Configs/Config.in | 28 | ||||
-rw-r--r-- | ldso/include/dl-defs.h | 2 | ||||
-rw-r--r-- | ldso/include/dl-elf.h | 50 | ||||
-rw-r--r-- | ldso/include/dl-hash.h | 33 | ||||
-rw-r--r-- | ldso/include/ldso.h | 9 | ||||
-rw-r--r-- | ldso/ldso/Makefile.in | 8 | ||||
-rw-r--r-- | ldso/ldso/dl-debug.c | 57 | ||||
-rw-r--r-- | ldso/ldso/dl-elf.c | 81 | ||||
-rw-r--r-- | ldso/ldso/dl-hash.c | 90 | ||||
-rw-r--r-- | ldso/ldso/dl-startup.c | 41 | ||||
-rw-r--r-- | ldso/ldso/ldso.c | 452 | ||||
-rw-r--r-- | ldso/ldso/sh/elfinterp.c | 27 | ||||
-rw-r--r-- | ldso/libdl/libdl.c | 59 | ||||
-rw-r--r-- | libc/misc/internals/__uClibc_main.c | 18 |
15 files changed, 786 insertions, 178 deletions
@@ -294,6 +294,15 @@ endef cmd_hcompile.u = $(HOSTCC) $(filter-out $(PHONY),$^) $(DEPS-$(notdir $@)) -o $@ $(BUILD_LDFLAGS) $(BUILD_LDFLAGS-$(notdir $(^D))) $(BUILD_LDFLAGS-$(notdir $@)) $(BUILD_CFLAGS) $(BUILD_CFLAGS-$(notdir $(^D))) $(BUILD_CFLAGS-$(notdir $@)) cmd_hcompile.o = $(HOSTCC) $(filter-out $(PHONY),$<) $(DEPS-$(notdir $@)) -c -o $@ $(BUILD_CFLAGS) $(BUILD_CFLAGS-$(notdir $(^D))) $(BUILD_CFLAGS-$(notdir $@)) +define create-lds + $(Q)$(RM) $@.lds + $(Q)$(CC) -nostdlib -nostartfiles -shared -Wl,-z,combreloc \ + -Wl,-z,relro -Wl,--hash-style=gnu -Wl,-z,defs \ + -Wl,--verbose 2>&1 | LC_ALL=C \ + sed -e '/^=========/,/^=========/!d;/^=========/d' \ + -e 's/\. = .* + SIZEOF_HEADERS;/& _begin = . - SIZEOF_HEADERS;/' > $@.lds +endef + define link.so $(Q)$(RM) $@ $@.$(2) $(dir $@)$(1) @$(disp_ld) diff --git a/extra/Configs/Config.in b/extra/Configs/Config.in index e8d522d71..801669a17 100644 --- a/extra/Configs/Config.in +++ b/extra/Configs/Config.in @@ -342,6 +342,34 @@ config LDSO_BASE_FILENAME WARNING: Changing the default prefix could cause problems with binutils' ld ! +config LDSO_STANDALONE_SUPPORT + bool "Dynamic linker stand-alone mode support" + depends on HAVE_SHARED + default n + help + The dynamic linker can be run either indirectly through running some + dynamically linked program or library (in which case no command line + options to the dynamic linker can be passed and, in the ELF case, the + dynamic linker which is stored in the .interp section of the program + is executed) or directly by running: + + /lib/ld-uClibc.so.* [OPTIONS] [PROGRAM [ARGUMENTS]] + + Stand-alone execution is a prerequisite for adding prelink + capabilities to uClibc dynamic linker, as well useful for testing an + updated version of the dynamic linker without breaking the system. + +config LDSO_PRELINK_SUPPORT + bool "Dynamic linker prelink support" + depends on HAVE_SHARED + default n + select LDSO_STANDALONE_SUPPORT + help + The dynamic linker can be used in stand-alone mode by the prelink tool + for prelinking ELF shared libraries and binaries to speed up startup + time. It also is able to load and handle prelinked libraries and + binaries at runtime. + config UCLIBC_STATIC_LDCONFIG bool "Link ldconfig statically" depends on HAVE_SHARED diff --git a/ldso/include/dl-defs.h b/ldso/include/dl-defs.h index 2d6303cfe..cbbaa3cea 100644 --- a/ldso/include/dl-defs.h +++ b/ldso/include/dl-defs.h @@ -225,7 +225,7 @@ typedef struct { /* Similar to DL_LOADADDR_UNMAP, but used for libraries that have been dlopen()ed successfully, when they're dlclose()d. */ #ifndef DL_LIB_UNMAP -# define DL_LIB_UNMAP(LIB, LEN) (DL_LOADADDR_UNMAP ((LIB)->loadaddr, (LEN))) +# define DL_LIB_UNMAP(LIB, LEN) (DL_LOADADDR_UNMAP ((LIB)->mapaddr, (LEN))) #endif /* Define this to verify that a library named LIBNAME, whose ELF diff --git a/ldso/include/dl-elf.h b/ldso/include/dl-elf.h index cbb2100b1..40c88b9da 100644 --- a/ldso/include/dl-elf.h +++ b/ldso/include/dl-elf.h @@ -15,6 +15,7 @@ /* Forward declarations for stuff defined in ld_hash.h */ struct dyn_elf; struct elf_resolve; +struct r_scope_elem; #include <dl-defs.h> #ifdef __LDSO_CACHE_SUPPORT__ @@ -30,16 +31,16 @@ static __inline__ void _dl_unmap_cache(void) { } extern void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size); extern int _dl_parse_relocation_information(struct dyn_elf *rpnt, - unsigned long rel_addr, unsigned long rel_size); + struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size); extern struct elf_resolve * _dl_load_shared_library(int secure, struct dyn_elf **rpnt, struct elf_resolve *tpnt, char *full_libname, int trace_loaded_objects); extern struct elf_resolve * _dl_load_elf_shared_library(int secure, - struct dyn_elf **rpnt, char *libname); + struct dyn_elf **rpnt, const char *libname); extern struct elf_resolve *_dl_check_if_named_library_is_loaded(const char *full_libname, int trace_loaded_objects); extern int _dl_linux_resolve(void); -extern int _dl_fixup(struct dyn_elf *rpnt, int flag); +extern int _dl_fixup(struct dyn_elf *rpnt, struct r_scope_elem *scope, int flag); extern void _dl_protect_relro (struct elf_resolve *l); /* @@ -84,24 +85,47 @@ extern void _dl_protect_relro (struct elf_resolve *l); #endif /* OS and/or GNU dynamic extensions */ + +#define OS_NUM_BASE 1 /* for DT_RELOCCOUNT */ + #ifdef __LDSO_GNU_HASH_SUPPORT__ -# define OS_NUM 2 /* for DT_RELOCCOUNT and DT_GNU_HASH entries */ +# define OS_NUM_GNU_HASH 1 /* for DT_GNU_HASH entry */ +#else +# define OS_NUM_GNU_HASH 0 +#endif + +#ifdef __LDSO_PRELINK_SUPPORT__ +# define OS_NUM_PRELINK 6 /* for DT_GNU_PRELINKED entry */ #else -# define OS_NUM 1 /* for DT_RELOCCOUNT entry */ +# define OS_NUM_PRELINK 0 #endif +#define OS_NUM (OS_NUM_BASE + OS_NUM_GNU_HASH + OS_NUM_PRELINK) + #ifndef ARCH_DYNAMIC_INFO /* define in arch specific code, if needed */ # define ARCH_NUM 0 #endif -#define DYNAMIC_SIZE (DT_NUM+OS_NUM+ARCH_NUM) +#define DYNAMIC_SIZE (DT_NUM + OS_NUM + ARCH_NUM) /* Keep ARCH specific entries into dynamic section at the end of the array */ #define DT_RELCONT_IDX (DYNAMIC_SIZE - OS_NUM - ARCH_NUM) #ifdef __LDSO_GNU_HASH_SUPPORT__ /* GNU hash comes just after the relocation count */ # define DT_GNU_HASH_IDX (DT_RELCONT_IDX + 1) +#else +# define DT_GNU_HASH_IDX DT_RELCONT_IDX +#endif + +#ifdef __LDSO_PRELINK_SUPPORT__ +/* GNU prelink comes just after the GNU hash if present */ +#define DT_GNU_PRELINKED_IDX (DT_GNU_HASH_IDX + 1) +#define DT_GNU_CONFLICT_IDX (DT_GNU_HASH_IDX + 2) +#define DT_GNU_CONFLICTSZ_IDX (DT_GNU_HASH_IDX + 3) +#define DT_GNU_LIBLIST_IDX (DT_GNU_HASH_IDX + 4) +#define DT_GNU_LIBLISTSZ_IDX (DT_GNU_HASH_IDX + 5) +#define DT_CHECKSUM_IDX (DT_GNU_HASH_IDX + 6) #endif extern unsigned int _dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[], @@ -150,6 +174,20 @@ unsigned int __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info if (dpnt->d_tag == DT_GNU_HASH) dynamic_info[DT_GNU_HASH_IDX] = dpnt->d_un.d_ptr; #endif +#ifdef __LDSO_PRELINK_SUPPORT__ + if (dpnt->d_tag == DT_GNU_PRELINKED) + dynamic_info[DT_GNU_PRELINKED_IDX] = dpnt->d_un.d_val; + if (dpnt->d_tag == DT_GNU_CONFLICT) + dynamic_info[DT_GNU_CONFLICT_IDX] = dpnt->d_un.d_ptr; + if (dpnt->d_tag == DT_GNU_CONFLICTSZ) + dynamic_info[DT_GNU_CONFLICTSZ_IDX] = dpnt->d_un.d_val; + if (dpnt->d_tag == DT_GNU_LIBLIST) + dynamic_info[DT_GNU_LIBLIST_IDX] = dpnt->d_un.d_ptr; + if (dpnt->d_tag == DT_GNU_LIBLISTSZ) + dynamic_info[DT_GNU_LIBLISTSZ_IDX] = dpnt->d_un.d_val; + if (dpnt->d_tag == DT_CHECKSUM) + dynamic_info[DT_CHECKSUM_IDX] = dpnt->d_un.d_val; +#endif } #ifdef ARCH_DYNAMIC_INFO else { diff --git a/ldso/include/dl-hash.h b/ldso/include/dl-hash.h index 34333f40f..e138e1d4b 100644 --- a/ldso/include/dl-hash.h +++ b/ldso/include/dl-hash.h @@ -25,6 +25,19 @@ struct dyn_elf { struct dyn_elf * prev; }; +struct sym_val { + const ElfW(Sym) *s; + struct elf_resolve *m; +}; + +/* Structure to describe a single list of scope elements. The lookup + functions get passed an array of pointers to such structures. */ +struct r_scope_elem { + struct elf_resolve **r_list; /* Array of maps for the scope. */ + unsigned int r_nlist; /* Number of entries in the scope. */ + struct r_scope_elem *next; +}; + struct elf_resolve { /* These entries must be in this order to be compatible with the interface used by gdb to obtain the list of symbols. */ @@ -60,8 +73,13 @@ struct elf_resolve { #endif ElfW(Addr) mapaddr; +#ifdef __LDSO_STANDALONE_SUPPORT__ + /* Store the entry point from the ELF header (e_entry) */ + ElfW(Addr) l_entry; +#endif enum {elf_lib, elf_executable,program_interpreter, loaded_file} libtype; - struct dyn_elf * symbol_scope; + /* This is the local scope of the shared object */ + struct r_scope_elem symbol_scope; unsigned short usage_count; unsigned short int init_flag; unsigned long rtld_flags; /* RTLD_GLOBAL, RTLD_NOW etc. */ @@ -128,6 +146,7 @@ struct elf_resolve { #define INIT_FUNCS_CALLED 0x000004 #define FINI_FUNCS_CALLED 0x000008 #define DL_OPENED 0x000010 +#define DL_RESERVED 0x000020 extern struct dyn_elf * _dl_symbol_tables; extern struct elf_resolve * _dl_loaded_modules; @@ -139,17 +158,17 @@ extern struct elf_resolve * _dl_add_elf_hash_table(const char * libname, /* Only need extra arg with some configurations */ #if !((defined(USE_TLS) && USE_TLS) || defined __FDPIC__) -# define _dl_lookup_hash(n, r, m, c, t) _dl_lookup_hash(n, r, m, c) +# define _dl_lookup_hash(n, r, m, s, c, t) _dl_lookup_hash(n, r, m, s, c) #endif -extern char *_dl_lookup_hash(const char *name, struct dyn_elf *rpnt, - struct elf_resolve *mytpnt, int type_class, +extern char *_dl_lookup_hash(const char *name, struct r_scope_elem *scope, + struct elf_resolve *mytpnt, struct sym_val *symbol, int type_class, struct elf_resolve **tpntp); -static __always_inline char *_dl_find_hash(const char *name, struct dyn_elf *rpnt, - struct elf_resolve *mytpnt, int type_class, +static __always_inline char *_dl_find_hash(const char *name, struct r_scope_elem *scope, + struct elf_resolve *mytpnt, struct sym_val *symbol, int type_class, struct elf_resolve **tpntp) { - return _dl_lookup_hash(name, rpnt, mytpnt, type_class, tpntp); + return _dl_lookup_hash(name, scope, mytpnt, symbol, type_class, tpntp); } extern int _dl_linux_dynamic_link(void); diff --git a/ldso/include/ldso.h b/ldso/include/ldso.h index 69b5dd75a..9aa610e7b 100644 --- a/ldso/include/ldso.h +++ b/ldso/include/ldso.h @@ -27,6 +27,7 @@ /* Pull in compiler and arch stuff */ #include <stdlib.h> #include <stdarg.h> +#include <stddef.h> /* for ptrdiff_t */ #define _FCNTL_H #include <bits/fcntl.h> #include <bits/wordsize.h> @@ -72,6 +73,12 @@ extern char *_dl_preload; /* Things to be loaded before the libs */ extern char *_dl_ldsopath; /* Where the shared lib loader was found */ extern const char *_dl_progname; /* The name of the executable being run */ extern size_t _dl_pagesize; /* Store the page size for use later */ +#ifdef __LDSO_PRELINK_SUPPORT__ +extern char *_dl_trace_prelink; /* Library for prelinking trace */ +extern struct elf_resolve *_dl_trace_prelink_map; /* Library map for prelinking trace */ +#else +#define _dl_trace_prelink 0 +#endif #if defined(USE_TLS) && USE_TLS extern void _dl_add_to_slotinfo (struct link_map *l); @@ -144,7 +151,7 @@ extern void _dl_dprintf(int, const char *, ...); # define DL_GET_READY_TO_RUN_EXTRA_ARGS #endif -extern void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, +extern void *_dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, ElfW(auxv_t) auxvt[AT_EGID + 1], char **envp, char **argv DL_GET_READY_TO_RUN_EXTRA_PARMS); diff --git a/ldso/ldso/Makefile.in b/ldso/ldso/Makefile.in index e71ae1563..c9dcebd60 100644 --- a/ldso/ldso/Makefile.in +++ b/ldso/ldso/Makefile.in @@ -62,8 +62,16 @@ ldso-y := $($(UCLIBC_LDSO_NAME)_OBJS:.o=.oS) lib-so-y += $(ldso) objclean-y += CLEAN_ldso/ldso +ifeq ($(LDSO_PRELINK_SUPPORT),y) +# Use a specific linker script for ld.so +LDFLAGS-$(UCLIBC_LDSO_NAME).so += -T $(ldso:.$(ABI_VERSION)=).lds +endif + $(ldso): $(ldso:.$(ABI_VERSION)=) $(ldso:.$(ABI_VERSION)=): $($(UCLIBC_LDSO_NAME)_OUT)/$(UCLIBC_LDSO_NAME)_so.a +ifeq ($(LDSO_PRELINK_SUPPORT),y) + $(call create-lds) +endif $(call link.so,$(ldso_FULL_NAME),$(ABI_VERSION)) $($(UCLIBC_LDSO_NAME)_OUT)/$(UCLIBC_LDSO_NAME)_so.a: $(ldso-y) diff --git a/ldso/ldso/dl-debug.c b/ldso/ldso/dl-debug.c index 7ce8bfbce..47b32316e 100644 --- a/ldso/ldso/dl-debug.c +++ b/ldso/ldso/dl-debug.c @@ -104,3 +104,60 @@ static void debug_reloc(ElfW(Sym) *symtab, char *strtab, ELF_RELOC *rpnt) #define debug_reloc(symtab, strtab, rpnt) #endif /* __SUPPORT_LD_DEBUG__ */ + +#ifdef __LDSO_PRELINK_SUPPORT__ +static void +internal_function +_dl_debug_lookup (const char *undef_name, struct elf_resolve *undef_map, + const ElfW(Sym) *ref, struct sym_val *value, int type_class) +{ +#ifdef SHARED + unsigned long symbol_addr; + + if (_dl_trace_prelink) + { + int conflict = 0; + struct elf_resolve *tls_tpnt = NULL; + struct sym_val val = { NULL, NULL }; + + if ((_dl_trace_prelink_map == NULL + || _dl_trace_prelink_map == _dl_loaded_modules) + && undef_map != _dl_loaded_modules) + { + symbol_addr = (unsigned long) + _dl_find_hash(undef_name, &undef_map->symbol_scope, + undef_map, &val, type_class, &tls_tpnt); + + if (val.s != value->s || val.m != value->m) + conflict = 1; + } + + if (value->s + && (__builtin_expect (ELF_ST_TYPE(value->s->st_info) + == STT_TLS, 0))) + type_class = 4; + + if (conflict + || _dl_trace_prelink_map == undef_map + || _dl_trace_prelink_map == NULL + || type_class == 4) + { + _dl_dprintf (1, "%s %x %x -> %x %x ", + conflict ? "conflict" : "lookup", + (size_t) undef_map->mapaddr, + (size_t) (((ElfW(Addr)) ref) - undef_map->mapaddr), + (size_t) (value->m ? value->m->mapaddr : 0), + (size_t) (value->s ? value->s->st_value : 0)); + if (conflict) + _dl_dprintf (1, "x %x %x ", + (size_t) (val.m ? val.m->mapaddr : 0), + (size_t) (val.s ? val.s->st_value : 0)); + _dl_dprintf (1, "/%x %s\n", type_class, undef_name); + } +} +#endif +} + +#else +#define _dl_debug_lookup(undef_name, undef_map, ref, value, type_class) +#endif diff --git a/ldso/ldso/dl-elf.c b/ldso/ldso/dl-elf.c index 5562e0784..d72dd5ae2 100644 --- a/ldso/ldso/dl-elf.c +++ b/ldso/ldso/dl-elf.c @@ -322,7 +322,7 @@ goof: */ struct elf_resolve *_dl_load_elf_shared_library(int secure, - struct dyn_elf **rpnt, char *libname) + struct dyn_elf **rpnt, const char *libname) { ElfW(Ehdr) *epnt; unsigned long dynamic_addr = 0; @@ -343,7 +343,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, size_t relro_size = 0; struct stat st; uint32_t *p32; - DL_LOADADDR_TYPE lib_loadaddr; + DL_LOADADDR_TYPE lib_loadaddr = 0; DL_INIT_LOADADDR_EXTRA_DECLS libaddr = 0; @@ -397,11 +397,15 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, return NULL; } - if ((epnt->e_type != ET_DYN) || (epnt->e_machine != MAGIC1 + if ((epnt->e_type != ET_DYN +#ifdef __LDSO_STANDALONE_SUPPORT__ + && epnt->e_type != ET_EXEC +#endif + ) || (epnt->e_machine != MAGIC1 #ifdef MAGIC2 && epnt->e_machine != MAGIC2 #endif - )) + )) { _dl_internal_error_number = (epnt->e_type != ET_DYN ? LD_ERROR_NOTDYN : LD_ERROR_NOTMAGIC); @@ -426,7 +430,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, if (ppnt->p_type == PT_LOAD) { /* See if this is a PIC library. */ - if (i == 0 && ppnt->p_vaddr > 0x1000000) { + if (minvma == 0xffffffff && ppnt->p_vaddr > 0x1000000) { piclib = 0; minvma = ppnt->p_vaddr; } @@ -462,14 +466,17 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, ppnt++; } +#ifdef __LDSO_STANDALONE_SUPPORT__ + if (epnt->e_type == ET_EXEC) + piclib = 0; +#endif + DL_CHECK_LIB_TYPE (epnt, piclib, _dl_progname, libname); maxvma = (maxvma + ADDR_ALIGN) & PAGE_ALIGN; minvma = minvma & ~0xffffU; flags = MAP_PRIVATE /*| MAP_DENYWRITE */ ; - if (!piclib) - flags |= MAP_FIXED; if (piclib == 0 || piclib == 1) { status = (char *) _dl_mmap((char *) (piclib ? 0 : minvma), @@ -488,7 +495,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, /* Get the memory to store the library */ ppnt = (ElfW(Phdr) *)(intptr_t) & header[epnt->e_phoff]; - DL_INIT_LOADADDR(lib_loadaddr, libaddr, ppnt, epnt->e_phnum); + DL_INIT_LOADADDR(lib_loadaddr, libaddr - minvma, ppnt, epnt->e_phnum); for (i = 0; i < epnt->e_phnum; i++) { if (DL_IS_SPECIAL_SEGMENT (epnt, ppnt)) { @@ -510,12 +517,6 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, char *tryaddr; ssize_t size; - /* See if this is a PIC library. */ - if (i == 0 && ppnt->p_vaddr > 0x1000000) { - piclib = 0; - /* flags |= MAP_FIXED; */ - } - if (ppnt->p_flags & PF_W) { unsigned long map_size; char *cpnt; @@ -559,7 +560,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, } tryaddr = piclib == 2 ? piclib2map - : ((char*) (piclib ? libaddr : 0) + + : ((char*) (piclib ? libaddr : lib_loadaddr) + (ppnt->p_vaddr & PAGE_ALIGN)); size = (ppnt->p_vaddr & ADDR_ALIGN) @@ -644,7 +645,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, if (map_size < ppnt->p_vaddr + ppnt->p_memsz && !piclib2map) { - tryaddr = map_size + (char*)(piclib ? libaddr : 0); + tryaddr = map_size + (char*)(piclib ? libaddr : lib_loadaddr); status = (char *) _dl_mmap(tryaddr, ppnt->p_vaddr + ppnt->p_memsz - map_size, LXFLAGS(ppnt->p_flags), flags | MAP_ANONYMOUS | MAP_FIXED, -1, 0); @@ -655,7 +656,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, } else { tryaddr = (piclib == 2 ? 0 : (char *) (ppnt->p_vaddr & PAGE_ALIGN) - + (piclib ? libaddr : 0)); + + (piclib ? libaddr : lib_loadaddr)); size = (ppnt->p_vaddr & ADDR_ALIGN) + ppnt->p_filesz; status = (char *) _dl_mmap (tryaddr, size, LXFLAGS(ppnt->p_flags), @@ -679,8 +680,11 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, } _dl_close(infile); - /* For a non-PIC library, the addresses are all absolute */ - if (piclib) { + /* + * The dynamic_addr must be take into acount lib_loadaddr value, to note + * it is zero when the SO has been mapped to the elf's physical addr + */ + if (lib_loadaddr) { dynamic_addr = (unsigned long) DL_RELOC_ADDR(lib_loadaddr, dynamic_addr); } @@ -711,7 +715,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, ppnt = (ElfW(Phdr) *)(intptr_t) & header[epnt->e_phoff]; for (i = 0; i < epnt->e_phnum; i++, ppnt++) { if (ppnt->p_type == PT_LOAD && !(ppnt->p_flags & PF_W)) { - _dl_mprotect((void *) ((piclib ? libaddr : 0) + + _dl_mprotect((void *) ((piclib ? libaddr : lib_loadaddr) + (ppnt->p_vaddr & PAGE_ALIGN)), (ppnt->p_vaddr & ADDR_ALIGN) + (unsigned long) ppnt->p_filesz, PROT_READ | PROT_WRITE | PROT_EXEC); @@ -727,13 +731,17 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, tpnt = _dl_add_elf_hash_table(libname, lib_loadaddr, dynamic_info, dynamic_addr, 0); + tpnt->mapaddr = libaddr; tpnt->relro_addr = relro_addr; tpnt->relro_size = relro_size; tpnt->st_dev = st.st_dev; tpnt->st_ino = st.st_ino; - tpnt->ppnt = (ElfW(Phdr) *) DL_RELOC_ADDR(tpnt->loadaddr, epnt->e_phoff); + tpnt->ppnt = (ElfW(Phdr) *) DL_RELOC_ADDR(tpnt->mapaddr, epnt->e_phoff); tpnt->n_phent = epnt->e_phnum; tpnt->rtld_flags |= rtld_flags; +#ifdef __LDSO_STANDALONE_SUPPORT__ + tpnt->l_entry = epnt->e_entry; +#endif #if defined(USE_TLS) && USE_TLS if (tlsppnt) { @@ -755,7 +763,11 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, tpnt->l_tls_modid = _dl_next_tls_modid (); /* We know the load address, so add it to the offset. */ +#ifdef __LDSO_STANDALONE_SUPPORT__ + if ((tpnt->l_tls_initimage != NULL) && piclib) +#else if (tpnt->l_tls_initimage != NULL) +#endif { # ifdef __SUPPORT_LD_DEBUG_EARLY__ unsigned int tmp = (unsigned int) tpnt->l_tls_initimage; @@ -772,7 +784,12 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, /* * Add this object into the symbol chain */ - if (*rpnt) { + if (*rpnt +#ifdef __LDSO_STANDALONE_SUPPORT__ + /* Do not create a new chain entry for the main executable */ + && (*rpnt)->dyn +#endif + ) { (*rpnt)->next = _dl_malloc(sizeof(struct dyn_elf)); _dl_memset((*rpnt)->next, 0, sizeof(struct dyn_elf)); (*rpnt)->next->prev = (*rpnt); @@ -789,9 +806,12 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, } #endif (*rpnt)->dyn = tpnt; - tpnt->symbol_scope = _dl_symbol_tables; tpnt->usage_count++; +#ifdef __LDSO_STANDALONE_SUPPORT__ + tpnt->libtype = (epnt->e_type == ET_DYN) ? elf_lib : elf_executable; +#else tpnt->libtype = elf_lib; +#endif /* * OK, the next thing we need to do is to insert the dynamic linker into @@ -817,7 +837,7 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, } /* now_flag must be RTLD_NOW or zero */ -int _dl_fixup(struct dyn_elf *rpnt, int now_flag) +int _dl_fixup(struct dyn_elf *rpnt, struct r_scope_elem *scope, int now_flag) { int goof = 0; struct elf_resolve *tpnt; @@ -825,7 +845,7 @@ int _dl_fixup(struct dyn_elf *rpnt, int now_flag) ElfW(Addr) reloc_addr; if (rpnt->next) - goof = _dl_fixup(rpnt->next, now_flag); + goof = _dl_fixup(rpnt->next, scope, now_flag); if (goof) return goof; tpnt = rpnt->dyn; @@ -852,10 +872,15 @@ int _dl_fixup(struct dyn_elf *rpnt, int now_flag) relative_count = tpnt->dynamic_info[DT_RELCONT_IDX]; if (relative_count) { /* Optimize the XX_RELATIVE relocations if possible */ reloc_size -= relative_count * sizeof(ELF_RELOC); - elf_machine_relative(tpnt->loadaddr, reloc_addr, relative_count); + if (tpnt->loadaddr +#ifdef __LDSO_PRELINK_SUPPORT__ + || (!tpnt->dynamic_info[DT_GNU_PRELINKED_IDX]) +#endif + ) + elf_machine_relative(tpnt->loadaddr, reloc_addr, relative_count); reloc_addr += relative_count * sizeof(ELF_RELOC); } - goof += _dl_parse_relocation_information(rpnt, + goof += _dl_parse_relocation_information(rpnt, scope, reloc_addr, reloc_size); tpnt->init_flag |= RELOCS_DONE; @@ -871,7 +896,7 @@ int _dl_fixup(struct dyn_elf *rpnt, int now_flag) tpnt->dynamic_info[DT_JMPREL], tpnt->dynamic_info [DT_PLTRELSZ]); } else { - goof += _dl_parse_relocation_information(rpnt, + goof += _dl_parse_relocation_information(rpnt, scope, tpnt->dynamic_info[DT_JMPREL], tpnt->dynamic_info[DT_PLTRELSZ]); } diff --git a/ldso/ldso/dl-hash.c b/ldso/ldso/dl-hash.c index 0048734ba..bb4c56b04 100644 --- a/ldso/ldso/dl-hash.c +++ b/ldso/ldso/dl-hash.c @@ -146,7 +146,6 @@ struct elf_resolve *_dl_add_elf_hash_table(const char *libname, tpnt->chains = hash_addr; } tpnt->loadaddr = loadaddr; - tpnt->mapaddr = DL_RELOC_ADDR(loadaddr, 0); for (i = 0; i < DYNAMIC_SIZE; i++) tpnt->dynamic_info[i] = dynamic_info[i]; return tpnt; @@ -268,72 +267,81 @@ _dl_lookup_sysv_hash(struct elf_resolve *tpnt, ElfW(Sym) *symtab, unsigned long * This function resolves externals, and this is either called when we process * relocations or when we call an entry in the PLT table for the first time. */ -char *_dl_lookup_hash(const char *name, struct dyn_elf *rpnt, struct elf_resolve *mytpnt, - int type_class, struct elf_resolve **tpntp) +char *_dl_lookup_hash(const char *name, struct r_scope_elem *scope, struct elf_resolve *mytpnt, + struct sym_val *symbol, int type_class, struct elf_resolve **tpntp) { struct elf_resolve *tpnt = NULL; ElfW(Sym) *symtab; + int i = 0; unsigned long elf_hash_number = 0xffffffff; const ElfW(Sym) *sym = NULL; char *weak_result = NULL; + struct r_scope_elem *loop_scope; #ifdef __LDSO_GNU_HASH_SUPPORT__ unsigned long gnu_hash_number = _dl_gnu_hash((const unsigned char *)name); #endif - for (; rpnt; rpnt = rpnt->next) { - tpnt = rpnt->dyn; - - if (!(tpnt->rtld_flags & RTLD_GLOBAL) && mytpnt) { - if (mytpnt == tpnt) - ; - else { - struct init_fini_list *tmp; - - for (tmp = mytpnt->rtld_local; tmp; tmp = tmp->next) { - if (tmp->tpnt == tpnt) - break; + for (loop_scope = scope; loop_scope && !sym; loop_scope = loop_scope->next) { + for (i = 0; i < loop_scope->r_nlist; i++) { + tpnt = loop_scope->r_list[i]; + + if (!(tpnt->rtld_flags & RTLD_GLOBAL) && mytpnt) { + if (mytpnt == tpnt) + ; + else { + struct init_fini_list *tmp; + + for (tmp = mytpnt->rtld_local; tmp; tmp = tmp->next) { + if (tmp->tpnt == tpnt) + break; + } + if (!tmp) + continue; } - if (!tmp) - continue; } - } - /* Don't search the executable when resolving a copy reloc. */ - if ((type_class & ELF_RTYPE_CLASS_COPY) && tpnt->libtype == elf_executable) - continue; + /* Don't search the executable when resolving a copy reloc. */ + if ((type_class & ELF_RTYPE_CLASS_COPY) && tpnt->libtype == elf_executable) + continue; - /* If the hash table is empty there is nothing to do here. */ - if (tpnt->nbucket == 0) - continue; + /* If the hash table is empty there is nothing to do here. */ + if (tpnt->nbucket == 0) + continue; - symtab = (ElfW(Sym) *) (intptr_t) (tpnt->dynamic_info[DT_SYMTAB]); + symtab = (ElfW(Sym) *) (intptr_t) (tpnt->dynamic_info[DT_SYMTAB]); #ifdef __LDSO_GNU_HASH_SUPPORT__ - /* Prefer GNU hash style, if any */ - if (tpnt->l_gnu_bitmask) { - sym = _dl_lookup_gnu_hash(tpnt, symtab, gnu_hash_number, name, type_class); - if (sym != NULL) - /* If sym has been found, do not search further */ - break; - } else { + /* Prefer GNU hash style, if any */ + if (tpnt->l_gnu_bitmask) { + sym = _dl_lookup_gnu_hash(tpnt, symtab, gnu_hash_number, name, type_class); + if (sym != NULL) + /* If sym has been found, do not search further */ + break; + } else { #endif - /* Use the old SysV-style hash table */ + /* Use the old SysV-style hash table */ - /* Calculate the old sysv hash number only once */ - if (elf_hash_number == 0xffffffff) - elf_hash_number = _dl_elf_hash((const unsigned char *)name); + /* Calculate the old sysv hash number only once */ + if (elf_hash_number == 0xffffffff) + elf_hash_number = _dl_elf_hash((const unsigned char *)name); - sym = _dl_lookup_sysv_hash(tpnt, symtab, elf_hash_number, name, type_class); - if (sym != NULL) - break; + sym = _dl_lookup_sysv_hash(tpnt, symtab, elf_hash_number, name, type_class); + if (sym != NULL) + /* If sym has been found, do not search further */ + break; #ifdef __LDSO_GNU_HASH_SUPPORT__ - } + } #endif - } /* end of for (; rpnt; rpnt = rpnt->next) { */ + } /* End of inner for */ + } if (sym) { + if (symbol) { + symbol->s = sym; + symbol->m = tpnt; + } /* At this point we have found the requested symbol, do binding */ #if defined(USE_TLS) && USE_TLS if (ELF_ST_TYPE(sym->st_info) == STT_TLS) { diff --git a/ldso/ldso/dl-startup.c b/ldso/ldso/dl-startup.c index a51b583a4..4799846ee 100644 --- a/ldso/ldso/dl-startup.c +++ b/ldso/ldso/dl-startup.c @@ -94,6 +94,11 @@ /* Pull in all the arch specific stuff */ #include "dl-startup.h" +#ifdef __LDSO_PRELINK_SUPPORT__ +/* These defined magically in the linker script. */ +extern char _begin[] attribute_hidden; +#endif + /* Static declarations */ static int (*_dl_elf_main) (int, char **, char **); @@ -164,11 +169,26 @@ DL_START(unsigned long args) aux_dat += 2; } - /* locate the ELF header. We need this done as soon as possible - * (esp since SEND_STDERR() needs this on some platforms... */ + /* + * Locate the dynamic linker ELF header. We need this done as soon as + * possible (esp since SEND_STDERR() needs this on some platforms... + */ + +#ifdef __LDSO_PRELINK_SUPPORT__ + /* + * The `_begin' symbol created by the linker script points to ld.so ELF + * We use it if the kernel is not passing a valid address through the auxvt. + */ + + if (!auxvt[AT_BASE].a_un.a_val) + auxvt[AT_BASE].a_un.a_val = (Elf32_Addr) &_begin; + /* Note: if the dynamic linker itself is prelinked, the load_addr is 0 */ + DL_INIT_LOADADDR_BOOT(load_addr, elf_machine_load_address()); +#else if (!auxvt[AT_BASE].a_un.a_val) auxvt[AT_BASE].a_un.a_val = elf_machine_load_address(); DL_INIT_LOADADDR_BOOT(load_addr, auxvt[AT_BASE].a_un.a_val); +#endif header = (ElfW(Ehdr) *) auxvt[AT_BASE].a_un.a_val; /* Check the ELF header to make sure everything looks ok. */ @@ -183,7 +203,7 @@ DL_START(unsigned long args) _dl_exit(0); } SEND_EARLY_STDERR_DEBUG("ELF header="); - SEND_ADDRESS_STDERR_DEBUG(DL_LOADADDR_BASE(load_addr), 1); + SEND_ADDRESS_STDERR_DEBUG(DL_LOADADDR_BASE(header), 1); /* Locate the global offset table. Since this code must be PIC * we can take advantage of the magic offset register, if we @@ -192,7 +212,7 @@ DL_START(unsigned long args) DL_BOOT_COMPUTE_GOT(got); /* Now, finally, fix up the location of the dynamic stuff */ - DL_BOOT_COMPUTE_DYN(dpnt, got, load_addr); + DL_BOOT_COMPUTE_DYN(dpnt, got, (DL_LOADADDR_TYPE)header); SEND_EARLY_STDERR_DEBUG("First Dynamic section entry="); SEND_ADDRESS_STDERR_DEBUG(dpnt, 1); @@ -258,7 +278,12 @@ DL_START(unsigned long args) if (!indx && relative_count) { rel_size -= relative_count * sizeof(ELF_RELOC); - elf_machine_relative(load_addr, rel_addr, relative_count); + if (load_addr +#ifdef __LDSO_PRELINK_SUPPORT__ + || !tpnt->dynamic_info[DT_GNU_PRELINKED_IDX] +#endif + ) + elf_machine_relative(load_addr, rel_addr, relative_count); rel_addr += relative_count * sizeof(ELF_RELOC); } @@ -321,12 +346,12 @@ DL_START(unsigned long args) __rtld_stack_end = (void *)(argv - 1); - _dl_get_ready_to_run(tpnt, load_addr, auxvt, envp, argv - DL_GET_READY_TO_RUN_EXTRA_ARGS); + _dl_elf_main = (int (*)(int, char **, char **)) + _dl_get_ready_to_run(tpnt, (DL_LOADADDR_TYPE) header, auxvt, envp, argv + DL_GET_READY_TO_RUN_EXTRA_ARGS); /* Transfer control to the application. */ SEND_STDERR_DEBUG("transfering control to application @ "); - _dl_elf_main = (int (*)(int, char **, char **)) auxvt[AT_ENTRY].a_un.a_val; SEND_ADDRESS_STDERR_DEBUG(_dl_elf_main, 1); #if !defined(START) diff --git a/ldso/ldso/ldso.c b/ldso/ldso/ldso.c index ea4ad0f1c..e0d323a9f 100644 --- a/ldso/ldso/ldso.c +++ b/ldso/ldso/ldso.c @@ -57,6 +57,12 @@ struct r_debug *_dl_debug_addr = NULL; /* Used to communicate with the gdb debug void *(*_dl_malloc_function) (size_t size) = NULL; void (*_dl_free_function) (void *p) = NULL; +#ifdef __LDSO_PRELINK_SUPPORT__ +char *_dl_trace_prelink = NULL; /* Library for prelinking trace */ +struct elf_resolve *_dl_trace_prelink_map = NULL; /* Library module for prelinking trace */ +bool _dl_verbose = true; /* On by default */ +bool prelinked = false; +#endif static int _dl_secure = 1; /* Are we dealing with setuid stuff? */ #ifdef __SUPPORT_LD_DEBUG__ @@ -70,8 +76,18 @@ char *_dl_debug_bindings = NULL; int _dl_debug_file = 2; #endif -/* Needed for standalone execution. */ +#if defined (__LDSO_STANDALONE_SUPPORT__) && defined (__sh__) +/* Not hidden, needed for standalone execution. */ +/* + * FIXME: align dl_start for SH to other archs so that we can keep this symbol + * hidden and we don't need to handle in __uClibc_main + */ + +unsigned long _dl_skip_args = 0; +#else unsigned long attribute_hidden _dl_skip_args = 0; +#endif + const char *_dl_progname = UCLIBC_LDSO; /* The name of the executable being run */ #include "dl-startup.c" #include "dl-symbols.c" @@ -97,6 +113,7 @@ static unsigned char *_dl_malloc_addr = NULL; /* Lets _dl_malloc use the already static unsigned char *_dl_mmap_zero = NULL; /* Also used by _dl_malloc */ static struct elf_resolve **init_fini_list; +static struct elf_resolve **scope_elem_list; static unsigned int nlist; /* # items in init_fini_list */ extern void _start(void); @@ -274,20 +291,101 @@ static void __attribute__ ((destructor)) __attribute_used__ _dl_fini(void) } } -void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, - ElfW(auxv_t) auxvt[AT_EGID + 1], char **envp, - char **argv +#ifdef __LDSO_PRELINK_SUPPORT__ +static void trace_objects(struct elf_resolve *tpnt, char *str_name) +{ + if (_dl_strcmp(_dl_trace_prelink, tpnt->libname) == 0) + _dl_trace_prelink_map = tpnt; + if (tpnt->libtype == elf_executable) { +/* Main executeble */ + _dl_dprintf(1, "\t%s => %s (%x, %x)", tpnt->libname, tpnt->libname, + tpnt->mapaddr, DL_LOADADDR_BASE(tpnt->loadaddr)); + } else { +/* Preloaded, Needed or interpreter */ + _dl_dprintf(1, "\t%s => %s (%x, %x)", str_name, tpnt->libname, + tpnt->mapaddr, DL_LOADADDR_BASE(tpnt->loadaddr)); + } + + if ((tpnt->libtype != program_interpreter) && (tpnt->l_tls_modid)) + _dl_dprintf (1, " TLS(%x, %x)\n", tpnt->l_tls_modid, + (size_t) tpnt->l_tls_offset); + else + _dl_dprintf (1, "\n"); +} +#endif + +static struct elf_resolve * add_ldso(struct elf_resolve *tpnt, + DL_LOADADDR_TYPE load_addr, + ElfW(auxv_t) auxvt[AT_EGID + 1], + struct dyn_elf *rpnt) +{ + ElfW(Ehdr) *epnt = (ElfW(Ehdr) *) auxvt[AT_BASE].a_un.a_val; + ElfW(Phdr) *myppnt = (ElfW(Phdr) *) + DL_RELOC_ADDR(load_addr, epnt->e_phoff); + int j; + struct stat st; + + tpnt = _dl_add_elf_hash_table(tpnt->libname, tpnt->loadaddr, + tpnt->dynamic_info, (unsigned long)tpnt->dynamic_addr, + 0); + + tpnt->mapaddr = load_addr; + if (_dl_stat(tpnt->libname, &st) >= 0) { + tpnt->st_dev = st.st_dev; + tpnt->st_ino = st.st_ino; + } + tpnt->n_phent = epnt->e_phnum; + tpnt->ppnt = myppnt; + for (j = 0; j < epnt->e_phnum; j++, myppnt++) { + if (myppnt->p_type == PT_GNU_RELRO) { + tpnt->relro_addr = myppnt->p_vaddr; + tpnt->relro_size = myppnt->p_memsz; + break; + } + } + tpnt->libtype = program_interpreter; + if (rpnt) { + rpnt->next = _dl_zalloc(sizeof(struct dyn_elf)); + rpnt->next->prev = rpnt; + rpnt = rpnt->next; + } else { + rpnt = _dl_zalloc(sizeof(struct dyn_elf)); + } + rpnt->dyn = tpnt; + tpnt->rtld_flags = RTLD_NOW | RTLD_GLOBAL; /* Must not be LAZY */ + + return tpnt; +} + +static ptrdiff_t _dl_build_local_scope (struct elf_resolve **list, + struct elf_resolve *map) +{ + struct elf_resolve **p = list; + struct init_fini_list *q; + + *p++ = map; + map->init_flag |= DL_RESERVED; + if (map->init_fini) + for (q = map->init_fini; q; q = q->next) + if (! (q->tpnt->init_flag & DL_RESERVED)) + p += _dl_build_local_scope (p, q->tpnt); + return p - list; +} + +void *_dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, + ElfW(auxv_t) auxvt[AT_EGID + 1], char **envp, char **argv DL_GET_READY_TO_RUN_EXTRA_PARMS) { ElfW(Addr) app_mapaddr = 0; ElfW(Phdr) *ppnt; ElfW(Dyn) *dpnt; char *lpntstr; - unsigned int i; + unsigned int i, cnt, k, nscope_elem; int unlazy = 0, trace_loaded_objects = 0; struct dyn_elf *rpnt; struct elf_resolve *tcurr; struct elf_resolve *tpnt1; + struct elf_resolve *ldso_tpnt = NULL; struct elf_resolve app_tpnt_tmp; struct elf_resolve *app_tpnt = &app_tpnt_tmp; struct r_debug *debug_addr; @@ -295,7 +393,9 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, unsigned long *_dl_envp; /* The environment address */ ElfW(Addr) relro_addr = 0; size_t relro_size = 0; - struct stat st; + struct r_scope_elem *global_scope; + struct elf_resolve **local_scope; + #if defined(USE_TLS) && USE_TLS void *tcbp = NULL; #endif @@ -327,10 +427,12 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, _dl_progname = argv[0]; } +#ifndef __LDSO_STANDALONE_SUPPORT__ if (_start == (void *) auxvt[AT_ENTRY].a_un.a_val) { - _dl_dprintf(_dl_debug_file, "Standalone execution is not supported yet\n"); + _dl_dprintf(_dl_debug_file, "Standalone execution is not enabled\n"); _dl_exit(1); } +#endif /* Start to build the tables of the modules that are required for * this beast to run. We start with the basic executable, and then @@ -382,6 +484,99 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, _dl_init_static_tls = &_dl_nothread_init_static_tls; #endif +#ifdef __LDSO_STANDALONE_SUPPORT__ + if (_start == (void *) auxvt[AT_ENTRY].a_un.a_val) { + char *ptmp; + unsigned int *aux_dat = (unsigned int *) argv; + int argc = aux_dat[-1]; + + tpnt->libname = argv[0]; + while (argc > 1) + if (! _dl_strcmp (argv[1], "--library-path") && argc > 2) { + _dl_library_path = argv[2]; + _dl_skip_args += 2; + argc -= 2; + argv += 2; + } else + break; + + /* + * If we have no further argument the program was called incorrectly. + * Grant the user some education. + */ + + if (argc < 2) { + _dl_dprintf(1, "\ +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\ + --library-path PATH use given PATH instead of content of the environment\n\ + variable LD_LIBRARY_PATH\n"); + _dl_exit(1); + } + + ++_dl_skip_args; + ++argv; + _dl_progname = argv[0]; + + _dl_symbol_tables = rpnt = _dl_zalloc(sizeof(struct dyn_elf)); + /* + * It needs to load the _dl_progname and to map it + * Usually it is the main application launched by means of the ld.so + * but it could be also a shared object (when ld.so used for tracing) + * We keep the misleading app_tpnt name to avoid variable pollution + */ + app_tpnt = _dl_load_elf_shared_library(_dl_secure, &rpnt, _dl_progname); + if (!app_tpnt) { + _dl_dprintf(_dl_debug_file, "can't load '%s'\n", _dl_progname); + _dl_exit(16); + } + /* + * FIXME: it needs to properly handle a PIE executable + * Usually for a main application, loadaddr is computed as difference + * between auxvt entry points and phdr, so if it is not 0, that it is a + * PIE executable. In this case instead we need to set the loadaddr to 0 + * because we are actually mapping the ELF for the main application by + * ourselves. So the PIE case must be checked. + */ + + app_tpnt->rtld_flags = unlazy | RTLD_GLOBAL; + + /* + * This is used by gdb to locate the chain of shared libraries that are + * currently loaded. + */ + debug_addr = _dl_zalloc(sizeof(struct r_debug)); + ppnt = (ElfW(Phdr) *)app_tpnt->ppnt; + for (i = 0; i < app_tpnt->n_phent; i++, ppnt++) { + if (ppnt->p_type == PT_DYNAMIC) { + dpnt = (ElfW(Dyn) *) DL_RELOC_ADDR(app_tpnt->loadaddr, ppnt->p_vaddr); + _dl_parse_dynamic_info(dpnt, app_tpnt->dynamic_info, debug_addr, app_tpnt->loadaddr); + } + } + + /* Store the path where the shared lib loader was found + * for later use + */ + _dl_ldsopath = _dl_strdup(tpnt->libname); + ptmp = _dl_strrchr(_dl_ldsopath, '/'); + if (ptmp != _dl_ldsopath) + *ptmp = '\0'; + + _dl_debug_early("Lib Loader: (%x) %s\n", (unsigned) DL_LOADADDR_BASE(tpnt->loadaddr), tpnt->libname); + } else { +#endif + /* At this point we are now free to examine the user application, * and figure out which libraries are supposed to be called. Until * we have this list, we will not be completely ready for dynamic @@ -469,7 +664,6 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, app_tpnt->mapaddr = app_mapaddr; app_tpnt->rtld_flags = unlazy | RTLD_GLOBAL; app_tpnt->usage_count++; - app_tpnt->symbol_scope = _dl_symbol_tables; lpnt = (unsigned long *) (app_tpnt->dynamic_info[DT_PLTGOT]); #ifdef ALLOW_ZERO_PLTGOT if (lpnt) @@ -530,14 +724,18 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, * case the executable is actually an ET_DYN object. */ if (app_tpnt->l_tls_initimage != NULL) { + unsigned int tmp = (unsigned int) app_tpnt->l_tls_initimage; app_tpnt->l_tls_initimage = (char *) app_tpnt->l_tls_initimage + app_tpnt->loadaddr; _dl_debug_early("Relocated TLS initial image from %x to %x (size = %x)\n", - (unsigned int)app_tpnt->l_tls_initimage, - app_tpnt->l_tls_initimage, app_tpnt->l_tls_initimage_size); + tmp, app_tpnt->l_tls_initimage, app_tpnt->l_tls_initimage_size); } #endif +#ifdef __LDSO_STANDALONE_SUPPORT__ + } /* ! ldso standalone mode */ +#endif + #ifdef __SUPPORT_LD_DEBUG__ _dl_debug = _dl_getenv("LD_DEBUG", envp); if (_dl_debug) { @@ -586,6 +784,16 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, } #endif +#ifdef __LDSO_PRELINK_SUPPORT__ +{ + char *ld_warn = _dl_getenv ("LD_WARN", envp); + + if (ld_warn && *ld_warn == '\0') + _dl_verbose = false; +} + _dl_trace_prelink = _dl_getenv("LD_TRACE_PRELINKING", envp); +#endif + if (_dl_getenv("LD_TRACE_LOADED_OBJECTS", envp) != NULL) { trace_loaded_objects++; } @@ -639,7 +847,7 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, tpnt1 = _dl_load_shared_library(_dl_secure, &rpnt, NULL, str, trace_loaded_objects); if (!tpnt1) { #ifdef __LDSO_LDD_SUPPORT__ - if (trace_loaded_objects) + if (trace_loaded_objects || _dl_trace_prelink) _dl_dprintf(1, "\t%s => not found\n", str); else #endif @@ -653,7 +861,7 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, _dl_debug_early("Loading: (%x) %s\n", DL_LOADADDR_BASE(tpnt1->loadaddr), tpnt1->libname); #ifdef __LDSO_LDD_SUPPORT__ - if (trace_loaded_objects && + if (trace_loaded_objects && !_dl_trace_prelink && tpnt1->usage_count == 1) { /* This is a real hack to make * ldd not print the library @@ -730,7 +938,7 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, tpnt1 = _dl_load_shared_library(0, &rpnt, NULL, cp2, trace_loaded_objects); if (!tpnt1) { # ifdef __LDSO_LDD_SUPPORT__ - if (trace_loaded_objects) + if (trace_loaded_objects || _dl_trace_prelink) _dl_dprintf(1, "\t%s => not found\n", cp2); else # endif @@ -744,7 +952,7 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, _dl_debug_early("Loading: (%x) %s\n", DL_LOADADDR_BASE(tpnt1->loadaddr), tpnt1->libname); # ifdef __LDSO_LDD_SUPPORT__ - if (trace_loaded_objects && + if (trace_loaded_objects && !_dl_trace_prelink && tpnt1->usage_count == 1) { _dl_dprintf(1, "\t%s => %s (%x)\n", cp2, tpnt1->libname, @@ -775,14 +983,21 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, lpntstr = (char*) (tcurr->dynamic_info[DT_STRTAB] + this_dpnt->d_un.d_val); name = _dl_get_last_path_component(lpntstr); - if (_dl_strcmp(name, UCLIBC_LDSO) == 0) - continue; - _dl_if_debug_dprint("\tfile='%s'; needed by '%s'\n", lpntstr, _dl_progname); - if (!(tpnt1 = _dl_load_shared_library(0, &rpnt, tcurr, lpntstr, trace_loaded_objects))) { + if (_dl_strcmp(name, UCLIBC_LDSO) == 0) { + if (!ldso_tpnt) { + /* Insert the ld.so only once */ + ldso_tpnt = add_ldso(tpnt, load_addr, auxvt, rpnt); + } + ldso_tpnt->usage_count++; + tpnt1 = ldso_tpnt; + } else + tpnt1 = _dl_load_shared_library(0, &rpnt, tcurr, lpntstr, trace_loaded_objects); + + if (!tpnt1) { #ifdef __LDSO_LDD_SUPPORT__ - if (trace_loaded_objects) { + if (trace_loaded_objects || _dl_trace_prelink) { _dl_dprintf(1, "\t%s => not found\n", lpntstr); continue; } else @@ -803,7 +1018,7 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, _dl_debug_early("Loading: (%x) %s\n", DL_LOADADDR_BASE(tpnt1->loadaddr), tpnt1->libname); #ifdef __LDSO_LDD_SUPPORT__ - if (trace_loaded_objects && + if (trace_loaded_objects && !_dl_trace_prelink && tpnt1->usage_count == 1) { _dl_dprintf(1, "\t%s => %s (%x)\n", lpntstr, tpnt1->libname, @@ -815,12 +1030,18 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, } _dl_unmap_cache(); - --nlist; /* Exclude the application. */ + /* Keep track of the number of elements in the global scope */ + nscope_elem = nlist; + + if (_dl_loaded_modules->libtype == elf_executable) { + --nlist; /* Exclude the application. */ + tcurr = _dl_loaded_modules->next; + } else + tcurr = _dl_loaded_modules; init_fini_list = _dl_malloc(nlist * sizeof(struct elf_resolve *)); i = 0; - for (tcurr = _dl_loaded_modules->next; tcurr; tcurr = tcurr->next) { + for (; tcurr; tcurr = tcurr->next) init_fini_list[i++] = tcurr; - } /* Sort the INIT/FINI list in dependency order. */ for (tcurr = _dl_loaded_modules->next; tcurr; tcurr = tcurr->next) { @@ -866,41 +1087,13 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, * functions in the dynamic linker and to relocate the interpreter * again once all libs are loaded. */ - if (tpnt) { - ElfW(Ehdr) *epnt = (ElfW(Ehdr) *) auxvt[AT_BASE].a_un.a_val; - ElfW(Phdr) *myppnt = (ElfW(Phdr) *) DL_RELOC_ADDR(load_addr, epnt->e_phoff); - int j; - - tpnt = _dl_add_elf_hash_table(tpnt->libname, load_addr, - tpnt->dynamic_info, - (unsigned long)tpnt->dynamic_addr, - 0); - - if (_dl_stat(tpnt->libname, &st) >= 0) { - tpnt->st_dev = st.st_dev; - tpnt->st_ino = st.st_ino; - } - tpnt->n_phent = epnt->e_phnum; - tpnt->ppnt = myppnt; - for (j = 0; j < epnt->e_phnum; j++, myppnt++) { - if (myppnt->p_type == PT_GNU_RELRO) { - tpnt->relro_addr = myppnt->p_vaddr; - tpnt->relro_size = myppnt->p_memsz; - break; - } - } - tpnt->libtype = program_interpreter; + if (!ldso_tpnt) { + tpnt = add_ldso(tpnt, load_addr, auxvt, rpnt); tpnt->usage_count++; - tpnt->symbol_scope = _dl_symbol_tables; - if (rpnt) { - rpnt->next = _dl_zalloc(sizeof(struct dyn_elf)); - rpnt->next->prev = rpnt; - rpnt = rpnt->next; - } else { - rpnt = _dl_zalloc(sizeof(struct dyn_elf)); - } - rpnt->dyn = tpnt; - tpnt->rtld_flags = RTLD_NOW | RTLD_GLOBAL; /* Must not be LAZY */ + nscope_elem++; + } else + tpnt = ldso_tpnt; + #ifdef RERELOCATE_LDSO /* Only rerelocate functions for now. */ tpnt->init_flag = RELOCS_DONE; @@ -913,16 +1106,43 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, tpnt->init_flag = RELOCS_DONE | JMP_RELOCS_DONE; #endif tpnt = NULL; + + /* + * Allocate the global scope array. + */ + scope_elem_list = (struct elf_resolve **) _dl_malloc(nscope_elem * sizeof(struct elf_resolve *)); + + for (i = 0, tcurr = _dl_loaded_modules; tcurr; tcurr = tcurr->next) + scope_elem_list[i++] = tcurr; + + _dl_loaded_modules->symbol_scope.r_list = scope_elem_list; + _dl_loaded_modules->symbol_scope.r_nlist = nscope_elem; + /* + * The symbol scope of the application, that is the first entry of the + * _dl_loaded_modules list, is just the global scope to be used for the + * symbol lookup. + */ + global_scope = &_dl_loaded_modules->symbol_scope; + + /* Build the local scope for each loaded modules. */ + local_scope = _dl_malloc(nscope_elem * sizeof(struct elf_resolve *)); + i = 1; + for (tcurr = _dl_loaded_modules->next; tcurr; tcurr = tcurr->next) { + cnt = _dl_build_local_scope(local_scope, scope_elem_list[i++]); + tcurr->symbol_scope.r_list = _dl_malloc(cnt * sizeof(struct elf_resolve *)); + tcurr->symbol_scope.r_nlist = cnt; + _dl_memcpy (tcurr->symbol_scope.r_list, local_scope, cnt * sizeof (struct elf_resolve *)); + /* Restoring the init_flag.*/ + for (k = 1; k < nscope_elem; k++) + scope_elem_list[k]->init_flag &= ~DL_RESERVED; } + _dl_free(local_scope); + #ifdef __LDSO_LDD_SUPPORT__ - /* End of the line for ldd.... */ - if (trace_loaded_objects) { - _dl_dprintf(1, "\t%s => %s (%x)\n", - rpnt->dyn->libname + _dl_strlen(_dl_ldsopath) + 1, - rpnt->dyn->libname, DL_LOADADDR_BASE(rpnt->dyn->loadaddr)); + /* Exit if LD_TRACE_LOADED_OBJECTS is on. */ + if (trace_loaded_objects && !_dl_trace_prelink) _dl_exit(0); - } #endif #if defined(USE_TLS) && USE_TLS @@ -952,6 +1172,90 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, # endif #endif +#ifdef __LDSO_PRELINK_SUPPORT__ + if (_dl_trace_prelink) { + + unsigned int nscope_trace = ldso_tpnt ? nscope_elem : (nscope_elem - 1); + + for (i = 0; i < nscope_trace; i++) + trace_objects(scope_elem_list[i], + _dl_get_last_path_component(scope_elem_list[i]->libname)); + + if (_dl_verbose) + /* Warn about undefined symbols. */ + if (_dl_symbol_tables) + if (_dl_fixup(_dl_symbol_tables, global_scope, unlazy)) + _dl_exit(-1); + _dl_exit(0); + } + + if (_dl_loaded_modules->dynamic_info[DT_GNU_LIBLIST_IDX]) { + ElfW(Lib) *liblist, *liblistend; + struct elf_resolve **r_list, **r_listend, *l; + const char *strtab = (const char *)_dl_loaded_modules->dynamic_info[DT_STRTAB]; + + _dl_assert (_dl_loaded_modules->dynamic_info[DT_GNU_LIBLISTSZ_IDX] != 0); + liblist = (ElfW(Lib) *) _dl_loaded_modules->dynamic_info[DT_GNU_LIBLIST_IDX]; + liblistend = (ElfW(Lib) *) + ((char *) liblist + _dl_loaded_modules->dynamic_info[DT_GNU_LIBLISTSZ_IDX]); + r_list = _dl_loaded_modules->symbol_scope.r_list; + r_listend = r_list + nscope_elem; + + for (; r_list < r_listend && liblist < liblistend; r_list++) { + l = *r_list; + + if (l == _dl_loaded_modules) + continue; + + /* If the library is not mapped where it should, fail. */ + if (l->loadaddr) + break; + + /* Next, check if checksum matches. */ + if (l->dynamic_info[DT_CHECKSUM_IDX] == 0 || + l->dynamic_info[DT_CHECKSUM_IDX] != liblist->l_checksum) + break; + + if (l->dynamic_info[DT_GNU_PRELINKED_IDX] == 0 || + (l->dynamic_info[DT_GNU_PRELINKED_IDX] != liblist->l_time_stamp)) + break; + + if (_dl_strcmp(strtab + liblist->l_name, _dl_get_last_path_component(l->libname)) != 0) + break; + + ++liblist; + } + + + if (r_list == r_listend && liblist == liblistend) + prelinked = true; + + } + + _dl_debug_early ("\nprelink checking: %s\n", prelinked ? "ok" : "failed"); + + if (prelinked) { + if (_dl_loaded_modules->dynamic_info[DT_GNU_CONFLICT_IDX]) { + ELF_RELOC *conflict; + unsigned long conflict_size; + + _dl_assert (_dl_loaded_modules->dynamic_info[DT_GNU_CONFLICTSZ_IDX] != 0); + conflict = (ELF_RELOC *) _dl_loaded_modules->dynamic_info[DT_GNU_CONFLICT_IDX]; + conflict_size = _dl_loaded_modules->dynamic_info[DT_GNU_CONFLICTSZ_IDX]; + _dl_parse_relocation_information(_dl_symbol_tables, global_scope, + (unsigned long) conflict, conflict_size); + } + + /* Mark all the objects so we know they have been already relocated. */ + for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) { + tpnt->init_flag |= RELOCS_DONE; + if (tpnt->relro_size) + _dl_protect_relro (tpnt); + } + } else +#endif + + { _dl_debug_early("Beginning relocation fixups\n"); @@ -970,13 +1274,14 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, * order so that COPY directives work correctly. */ if (_dl_symbol_tables) - if (_dl_fixup(_dl_symbol_tables, unlazy)) + if (_dl_fixup(_dl_symbol_tables, global_scope, unlazy)) _dl_exit(-1); for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) { if (tpnt->relro_size) _dl_protect_relro (tpnt); } + } /* not prelinked */ #if defined(USE_TLS) && USE_TLS if (!was_tls_init_tp_called && _dl_tls_max_dtv_idx > 0) @@ -1007,7 +1312,7 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, * ld.so.1, so we have to look up each symbol individually. */ - _dl_envp = (unsigned long *) (intptr_t) _dl_find_hash(__C_SYMBOL_PREFIX__ "__environ", _dl_symbol_tables, NULL, 0, NULL); + _dl_envp = (unsigned long *) (intptr_t) _dl_find_hash(__C_SYMBOL_PREFIX__ "__environ", global_scope, NULL, NULL, 0, NULL); if (_dl_envp) *_dl_envp = (unsigned long) envp; @@ -1063,27 +1368,34 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, /* Find the real malloc function and make ldso functions use that from now on */ _dl_malloc_function = (void* (*)(size_t)) (intptr_t) _dl_find_hash(__C_SYMBOL_PREFIX__ "malloc", - _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT, NULL); + global_scope, NULL, NULL, ELF_RTYPE_CLASS_PLT, NULL); #if defined(USE_TLS) && USE_TLS /* Find the real functions and make ldso functions use them from now on */ _dl_calloc_function = (void* (*)(size_t, size_t)) (intptr_t) - _dl_find_hash(__C_SYMBOL_PREFIX__ "calloc", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT, NULL); + _dl_find_hash(__C_SYMBOL_PREFIX__ "calloc", global_scope, NULL, NULL, ELF_RTYPE_CLASS_PLT, NULL); _dl_realloc_function = (void* (*)(void *, size_t)) (intptr_t) - _dl_find_hash(__C_SYMBOL_PREFIX__ "realloc", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT, NULL); + _dl_find_hash(__C_SYMBOL_PREFIX__ "realloc", global_scope, NULL, NULL, ELF_RTYPE_CLASS_PLT, NULL); _dl_free_function = (void (*)(void *)) (intptr_t) - _dl_find_hash(__C_SYMBOL_PREFIX__ "free", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT, NULL); + _dl_find_hash(__C_SYMBOL_PREFIX__ "free", global_scope, NULL, NULL, ELF_RTYPE_CLASS_PLT, NULL); _dl_memalign_function = (void* (*)(size_t, size_t)) (intptr_t) - _dl_find_hash(__C_SYMBOL_PREFIX__ "memalign", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT, NULL); + _dl_find_hash(__C_SYMBOL_PREFIX__ "memalign", global_scope, NULL, NULL, ELF_RTYPE_CLASS_PLT, NULL); #endif /* Notify the debugger that all objects are now mapped in. */ _dl_debug_addr->r_state = RT_CONSISTENT; _dl_debug_state(); + +#ifdef __LDSO_STANDALONE_SUPPORT__ + if (_start == (void *) auxvt[AT_ENTRY].a_un.a_val) + return (void *) app_tpnt->l_entry; + else +#endif + return (void *) auxvt[AT_ENTRY].a_un.a_val; } #include "dl-hash.c" diff --git a/ldso/ldso/sh/elfinterp.c b/ldso/ldso/sh/elfinterp.c index 756f6c443..928a84b40 100644 --- a/ldso/ldso/sh/elfinterp.c +++ b/ldso/ldso/sh/elfinterp.c @@ -69,7 +69,7 @@ unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry) got_addr = (char **) instr_addr; /* Get the address of the GOT entry */ - new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL); + new_addr = _dl_find_hash(symname, &_dl_loaded_modules->symbol_scope, tpnt, NULL, ELF_RTYPE_CLASS_PLT, NULL); if (unlikely(!new_addr)) { _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname); @@ -95,9 +95,9 @@ unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry) static int -_dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope, +_dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size, - int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope, + int (*reloc_fnc) (struct elf_resolve *tpnt, struct r_scope_elem *scope, ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)) { unsigned int i; @@ -148,7 +148,7 @@ _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope, static int -_dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, +_dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope, ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab) { int reloc_type; @@ -161,6 +161,7 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, #endif struct elf_resolve *tls_tpnt = NULL; + struct sym_val current_value = { NULL, NULL }; reloc_addr = (unsigned long *)(intptr_t) (tpnt->loadaddr + (unsigned long) rpnt->r_offset); reloc_type = ELF32_R_TYPE(rpnt->r_info); @@ -170,9 +171,9 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, if (symtab_index) { symname = strtab + symtab[symtab_index].st_name; if (ELF32_ST_VISIBILITY(symtab[symtab_index].st_other) - != STV_PROTECTED) { - symbol_addr = (unsigned long) _dl_find_hash(symname, scope, tpnt, - elf_machine_type_class(reloc_type), &tls_tpnt); + != STV_PROTECTED) { + symbol_addr = (unsigned long) _dl_find_hash(symname, scope, tpnt, ¤t_value, + elf_machine_type_class(reloc_type), &tls_tpnt); /* * We want to allow undefined references to weak symbols - this might * have been intentional. We should not be linking local symbols @@ -183,7 +184,7 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, && (ELF_ST_TYPE(symtab[symtab_index].st_info) != STT_TLS) && (ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK)) { _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", - _dl_progname, strtab + symtab[symtab_index].st_name); + _dl_progname, strtab + symtab[symtab_index].st_name); /* Let the caller to handle the error: it may be non fatal if called from dlopen */ return 1; @@ -192,6 +193,10 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, /* Resolve protected symbols locally */ symbol_addr = DL_FIND_HASH_VALUE(tpnt, elf_machine_type_class(reloc_type), &symtab[symtab_index]); + + if (_dl_trace_prelink) + _dl_debug_lookup (symname, tpnt, &symtab[symtab_index], + ¤t_value, elf_machine_type_class(reloc_type)); } #if defined (__SUPPORT_LD_DEBUG__) @@ -257,7 +262,7 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, static int -_dl_do_lazy_reloc (struct elf_resolve *tpnt, struct dyn_elf *scope, +_dl_do_lazy_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope, ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab) { int reloc_type; @@ -299,7 +304,7 @@ void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt, } int _dl_parse_relocation_information(struct dyn_elf *rpnt, - unsigned long rel_addr, unsigned long rel_size) + struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size) { - return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc); + return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, _dl_do_reloc); } diff --git a/ldso/libdl/libdl.c b/ldso/libdl/libdl.c index 3957e846f..3b49216d4 100644 --- a/ldso/libdl/libdl.c +++ b/ldso/libdl/libdl.c @@ -55,7 +55,7 @@ extern struct link_map *_dl_update_slotinfo(unsigned long int req_modid); extern struct elf_resolve * _dl_load_shared_library(int, struct dyn_elf **, struct elf_resolve *, char *, int); -extern int _dl_fixup(struct dyn_elf *rpnt, int lazy); +extern int _dl_fixup(struct dyn_elf *rpnt, struct r_scope_elem *scope, int lazy); extern void _dl_protect_relro(struct elf_resolve * tpnt); extern int _dl_errno; extern struct dyn_elf *_dl_symbol_tables; @@ -271,6 +271,21 @@ void dl_cleanup(void) } } +static ptrdiff_t _dl_build_local_scope (struct elf_resolve **list, + struct elf_resolve *map) +{ + struct elf_resolve **p = list; + struct init_fini_list *q; + + *p++ = map; + map->init_flag |= DL_RESERVED; + if (map->init_fini) + for (q = map->init_fini; q; q = q->next) + if (! (q->tpnt->init_flag & DL_RESERVED)) + p += _dl_build_local_scope (p, q->tpnt); + return p - list; +} + void *dlopen(const char *libname, int flag) { struct elf_resolve *tpnt, *tfrom; @@ -283,6 +298,8 @@ void *dlopen(const char *libname, int flag) unsigned int nlist, i; struct elf_resolve **init_fini_list; static bool _dl_init; + struct elf_resolve **local_scope; + struct r_scope_elem *ls; #if defined(USE_TLS) && USE_TLS bool any_tls = false; #endif @@ -458,6 +475,23 @@ void *dlopen(const char *libname, int flag) } } + /* Build the local scope for the dynamically loaded modules. */ + local_scope = _dl_malloc(nlist * sizeof(struct elf_resolve *)); /* Could it allocated on stack? */ + for (i = 0; i < nlist; i++) + if (init_fini_list[i]->symbol_scope.r_nlist == 0) { + int k, cnt; + cnt = _dl_build_local_scope(local_scope, init_fini_list[i]); + init_fini_list[i]->symbol_scope.r_list = _dl_malloc(cnt * sizeof(struct elf_resolve *)); + init_fini_list[i]->symbol_scope.r_nlist = cnt; + _dl_memcpy (init_fini_list[i]->symbol_scope.r_list, local_scope, + cnt * sizeof (struct elf_resolve *)); + /* Restoring the init_flag.*/ + for (k = 0; k < nlist; k++) + init_fini_list[k]->init_flag &= ~DL_RESERVED; + } + + _dl_free(local_scope); + /* Sort the INIT/FINI list in dependency order. */ for (runp2 = dep_list; runp2; runp2 = runp2->next) { unsigned int j, k; @@ -505,8 +539,13 @@ void *dlopen(const char *libname, int flag) */ _dl_perform_mips_global_got_relocations(tpnt, !now_flag); #endif + /* Get the tail of the list */ + for (ls = &_dl_loaded_modules->symbol_scope; ls && ls->next; ls = ls->next); - if (_dl_fixup(dyn_chain, now_flag)) + /* Extend the global scope by adding the local scope of the dlopened DSO. */ + ls->next = &dyn_chain->dyn->symbol_scope; + + if (_dl_fixup(dyn_chain, &_dl_loaded_modules->symbol_scope, now_flag)) goto oops; if (relro_ptr) { @@ -667,7 +706,7 @@ void *dlsym(void *vhandle, const char *name) tpnt = NULL; if (handle == _dl_symbol_tables) tpnt = handle->dyn; /* Only search RTLD_GLOBAL objs if global object */ - ret = _dl_find_hash(name2, handle, NULL, 0, &tls_tpnt); + ret = _dl_find_hash(name2, &handle->dyn->symbol_scope, NULL, NULL, 0, &tls_tpnt); #if defined(USE_TLS) && USE_TLS && defined SHARED if (tls_tpnt) { @@ -708,6 +747,7 @@ static int do_dlclose(void *vhandle, int need_fini) struct dyn_elf *handle; unsigned int end; unsigned int i, j; + struct r_scope_elem *ls; #if defined(USE_TLS) && USE_TLS bool any_tls = false; size_t tls_free_start = NO_TLS_OFFSET; @@ -873,7 +913,7 @@ static int do_dlclose(void *vhandle, int need_fini) } #endif - DL_LIB_UNMAP (tpnt, end); + DL_LIB_UNMAP (tpnt, end - tpnt->mapaddr); /* Free elements in RTLD_LOCAL scope list */ for (runp = tpnt->rtld_local; runp; runp = tmp) { tmp = runp->next; @@ -897,6 +937,16 @@ static int do_dlclose(void *vhandle, int need_fini) } } + if (handle->dyn == tpnt) { + /* Unlink the local scope from global one */ + for (ls = &_dl_loaded_modules->symbol_scope; ls; ls = ls->next) + if (ls->next->r_list[0] == tpnt) { + _dl_if_debug_print("removing symbol_scope: %s\n", tpnt->libname); + break; + } + ls->next = ls->next->next; + } + /* Next, remove tpnt from the global symbol table list */ if (_dl_symbol_tables) { if (_dl_symbol_tables->dyn == tpnt) { @@ -918,6 +968,7 @@ static int do_dlclose(void *vhandle, int need_fini) } } free(tpnt->libname); + free(tpnt->symbol_scope.r_list); free(tpnt); } } diff --git a/libc/misc/internals/__uClibc_main.c b/libc/misc/internals/__uClibc_main.c index 58f6643b2..36c0c6c63 100644 --- a/libc/misc/internals/__uClibc_main.c +++ b/libc/misc/internals/__uClibc_main.c @@ -153,6 +153,10 @@ extern void (*__fini_array_end []) (void) attribute_hidden; # endif #endif +#if defined (__LDSO_STANDALONE_SUPPORT__) && defined (SHARED) && defined __sh__ +extern unsigned long _dl_skip_args; +#endif + attribute_hidden const char *__uclibc_progname = ""; #ifdef __UCLIBC_HAS_PROGRAM_INVOCATION_NAME__ const char *program_invocation_short_name = ""; @@ -341,11 +345,23 @@ void __uClibc_main(int (*main)(int, char **, char **), int argc, __rtld_fini = rtld_fini; +#if defined __LDSO_STANDALONE_SUPPORT__ && defined SHARED && defined __sh__ + /* + * Skip ld.so and its arguments + * Other archs except for SH do this in _dl_start before passing + * control to the application. + * FIXME: align SH _dl_start to other archs and remove this from here, + * so that we can keep the visibility hidden. + */ + argc -= _dl_skip_args; + argv += _dl_skip_args; +#endif + /* The environment begins right after argv. */ __environ = &argv[argc + 1]; /* If the first thing after argv is the arguments - * the the environment is empty. */ + * then the environment is empty. */ if ((char *) __environ == *argv) { /* Make __environ point to the NULL at argv[argc] */ __environ = &argv[argc]; |