From 94cc6edb78a12655c0602a246fa1cbdc8c6d0ad9 Mon Sep 17 00:00:00 2001 From: Filippo Arcidiacono Date: Thu, 29 Jul 2010 11:35:05 +0200 Subject: ldso: Rework global scope handling and symbol lookup mechanism Global symbol scope is implemented as a linked list of local scope, that dynamically grows and shrinks when dlopen/ dlclose are called. Each local scope is implemented as an array of pointer to struct elf_resolve. This will help to detect conflict when LD_TRACE_PRELINKING option will be implemented. Signed-off-by: Filippo Arcidiacono Signed-off-by: Carmelo Amoroso --- ldso/include/dl-defs.h | 2 +- ldso/include/dl-elf.h | 5 +-- ldso/include/dl-hash.h | 19 ++++++++--- ldso/include/ldso.h | 1 + ldso/ldso/dl-elf.c | 9 +++--- ldso/ldso/dl-hash.c | 83 +++++++++++++++++++++++++----------------------- ldso/ldso/dl-startup.c | 2 +- ldso/ldso/ldso.c | 78 ++++++++++++++++++++++++++++++++++++++------- ldso/ldso/sh/elfinterp.c | 14 ++++---- ldso/libdl/libdl.c | 59 +++++++++++++++++++++++++++++++--- 10 files changed, 197 insertions(+), 75 deletions(-) (limited to 'ldso') 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 dc4af7bce..3e8586444 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 #ifdef __LDSO_CACHE_SUPPORT__ @@ -30,7 +31,7 @@ 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); @@ -39,7 +40,7 @@ extern struct elf_resolve * _dl_load_elf_shared_library(int secure, 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); /* diff --git a/ldso/include/dl-hash.h b/ldso/include/dl-hash.h index edef9d81b..f47384c31 100644 --- a/ldso/include/dl-hash.h +++ b/ldso/include/dl-hash.h @@ -25,6 +25,15 @@ struct dyn_elf { struct dyn_elf * prev; }; + +/* 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. */ @@ -65,7 +74,8 @@ struct elf_resolve { 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. */ @@ -132,6 +142,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; @@ -145,15 +156,15 @@ extern struct elf_resolve * _dl_add_elf_hash_table(const char * libname, #if !((defined(USE_TLS) && USE_TLS) || defined __FDPIC__) # define _dl_lookup_hash(n, r, m, c, t) _dl_lookup_hash(n, r, m, c) #endif -extern char *_dl_lookup_hash(const char *name, struct dyn_elf *rpnt, +extern char *_dl_lookup_hash(const char *name, struct r_scope_elem *scope, struct elf_resolve *mytpnt, int type_class, struct elf_resolve **tpntp); -static __always_inline char *_dl_find_hash(const char *name, struct dyn_elf *rpnt, +static __always_inline char *_dl_find_hash(const char *name, struct r_scope_elem *scope, struct elf_resolve *mytpnt, int type_class, struct elf_resolve **tpntp) { - return _dl_lookup_hash(name, rpnt, mytpnt, type_class, tpntp); + return _dl_lookup_hash(name, scope, mytpnt, type_class, tpntp); } extern int _dl_linux_dynamic_link(void); diff --git a/ldso/include/ldso.h b/ldso/include/ldso.h index 120889216..536f7d266 100644 --- a/ldso/include/ldso.h +++ b/ldso/include/ldso.h @@ -27,6 +27,7 @@ /* Pull in compiler and arch stuff */ #include #include +#include /* for ptrdiff_t */ #define _FCNTL_H #include #include diff --git a/ldso/ldso/dl-elf.c b/ldso/ldso/dl-elf.c index 2a77587db..a8ccc5e91 100644 --- a/ldso/ldso/dl-elf.c +++ b/ldso/ldso/dl-elf.c @@ -814,7 +814,6 @@ 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; @@ -846,7 +845,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; @@ -854,7 +853,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; @@ -884,7 +883,7 @@ int _dl_fixup(struct dyn_elf *rpnt, int now_flag) 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; @@ -900,7 +899,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..2c4571f20 100644 --- a/ldso/ldso/dl-hash.c +++ b/ldso/ldso/dl-hash.c @@ -268,70 +268,75 @@ _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, +char *_dl_lookup_hash(const char *name, struct r_scope_elem *scope, struct elf_resolve *mytpnt, 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) { /* At this point we have found the requested symbol, do binding */ diff --git a/ldso/ldso/dl-startup.c b/ldso/ldso/dl-startup.c index 2aefa6d74..feffa787b 100644 --- a/ldso/ldso/dl-startup.c +++ b/ldso/ldso/dl-startup.c @@ -192,7 +192,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); diff --git a/ldso/ldso/ldso.c b/ldso/ldso/ldso.c index bb394714f..ff3519f7c 100644 --- a/ldso/ldso/ldso.c +++ b/ldso/ldso/ldso.c @@ -107,6 +107,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); @@ -284,6 +285,21 @@ static void __attribute__ ((destructor)) __attribute_used__ _dl_fini(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 *_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) @@ -292,7 +308,7 @@ void *_dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr, 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; @@ -304,6 +320,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 r_scope_elem *global_scope; + struct elf_resolve **local_scope; + struct stat st; #if defined(USE_TLS) && USE_TLS void *tcbp = NULL; @@ -576,7 +595,6 @@ of this helper program; chances are you did not intend to run this program.\n\ 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) @@ -637,11 +655,11 @@ of this helper program; chances are you did not intend to run this program.\n\ * 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 @@ -926,6 +944,9 @@ of this helper program; chances are you did not intend to run this program.\n\ } _dl_unmap_cache(); + /* Keep track of the number of elements in the global scope */ + nscope_elem = nlist; + --nlist; /* Exclude the application. */ init_fini_list = _dl_malloc(nlist * sizeof(struct elf_resolve *)); i = 0; @@ -1002,7 +1023,7 @@ of this helper program; chances are you did not intend to run this program.\n\ } tpnt->libtype = program_interpreter; tpnt->usage_count++; - tpnt->symbol_scope = _dl_symbol_tables; + nscope_elem++; if (rpnt) { rpnt->next = _dl_zalloc(sizeof(struct dyn_elf)); rpnt->next->prev = rpnt; @@ -1012,6 +1033,7 @@ of this helper program; chances are you did not intend to run this program.\n\ } rpnt->dyn = tpnt; tpnt->rtld_flags = RTLD_NOW | RTLD_GLOBAL; /* Must not be LAZY */ + #ifdef RERELOCATE_LDSO /* Only rerelocate functions for now. */ tpnt->init_flag = RELOCS_DONE; @@ -1026,6 +1048,38 @@ of this helper program; chances are you did not intend to run this program.\n\ 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 the 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) { @@ -1081,7 +1135,7 @@ of this helper program; chances are you did not intend to run this program.\n\ * 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) { @@ -1118,7 +1172,7 @@ of this helper program; chances are you did not intend to run this program.\n\ * 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, 0, NULL); if (_dl_envp) *_dl_envp = (unsigned long) envp; @@ -1174,21 +1228,21 @@ of this helper program; chances are you did not intend to run this program.\n\ /* 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, 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, 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, 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, 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, ELF_RTYPE_CLASS_PLT, NULL); #endif diff --git a/ldso/ldso/sh/elfinterp.c b/ldso/ldso/sh/elfinterp.c index 715eadc15..d3465bcd5 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, 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; @@ -251,7 +251,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; @@ -293,7 +293,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..b641fe366 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, 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); } } -- cgit v1.2.3