diff options
| -rw-r--r-- | ldso/include/dl-elf.h | 21 | ||||
| -rw-r--r-- | ldso/include/dl-hash.h | 45 | ||||
| -rw-r--r-- | ldso/include/ldsodefs.h | 7 | ||||
| -rw-r--r-- | ldso/ldso/Makefile.in | 7 | ||||
| -rw-r--r-- | ldso/ldso/dl-elf.c | 89 | ||||
| -rw-r--r-- | ldso/ldso/dl-hash.c | 325 | ||||
| -rw-r--r-- | ldso/ldso/dl-tls.c | 20 | ||||
| -rw-r--r-- | ldso/ldso/ldso.c | 30 | ||||
| -rw-r--r-- | ldso/ldso/sh/dl-debug.h | 2 | ||||
| -rw-r--r-- | ldso/ldso/sh/dl-startup.h | 1 | ||||
| -rw-r--r-- | ldso/ldso/sh/dl-sysdep.h | 9 | ||||
| -rw-r--r-- | ldso/ldso/sh/elfinterp.c | 48 | ||||
| -rw-r--r-- | ldso/libdl/Makefile.in | 4 | ||||
| -rw-r--r-- | ldso/libdl/libdl.c | 245 |
14 files changed, 534 insertions, 319 deletions
diff --git a/ldso/include/dl-elf.h b/ldso/include/dl-elf.h index de404aec1..e959de12a 100644 --- a/ldso/include/dl-elf.h +++ b/ldso/include/dl-elf.h @@ -84,8 +84,11 @@ extern void _dl_protect_relro (struct elf_resolve *l); #endif /* OS and/or GNU dynamic extensions */ -#define OS_NUM 1 -#define DT_RELCONT_IDX DT_NUM +#ifdef __LDSO_GNU_HASH_SUPPORT__ +# define OS_NUM 2 /* for DT_RELOCCOUNT and DT_GNU_HASH entries */ +#else +# define OS_NUM 1 /* for DT_RELOCCOUNT entry */ +#endif #ifndef ARCH_DYNAMIC_INFO /* define in arch specific code, if needed */ @@ -93,6 +96,13 @@ extern void _dl_protect_relro (struct elf_resolve *l); #endif #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) +#endif extern void _dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[], void *debug_addr, ElfW(Addr) load_off); @@ -129,6 +139,10 @@ void __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[], void if (dpnt->d_tag == DT_FLAGS_1 && (dpnt->d_un.d_val & DF_1_NOW)) dynamic_info[DT_BIND_NOW] = 1; +#ifdef __LDSO_GNU_HASH_SUPPORT__ + if (dpnt->d_tag == DT_GNU_HASH) + dynamic_info[DT_GNU_HASH_IDX] = dpnt->d_un.d_ptr; +#endif } #ifdef ARCH_DYNAMIC_INFO else { @@ -147,6 +161,9 @@ void __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[], void ADJUST_DYN_INFO(DT_SYMTAB, load_off); ADJUST_DYN_INFO(DT_RELOC_TABLE_ADDR, load_off); ADJUST_DYN_INFO(DT_JMPREL, load_off); +#ifdef __LDSO_GNU_HASH_SUPPORT__ + ADJUST_DYN_INFO(DT_GNU_HASH_IDX, load_off); +#endif #undef ADJUST_DYN_INFO } diff --git a/ldso/include/dl-hash.h b/ldso/include/dl-hash.h index f496e580d..fb75c8611 100644 --- a/ldso/include/dl-hash.h +++ b/ldso/include/dl-hash.h @@ -65,14 +65,39 @@ struct elf_resolve { unsigned short int init_flag; unsigned long rtld_flags; /* RTLD_GLOBAL, RTLD_NOW etc. */ Elf_Symndx nbucket; + +#ifdef __LDSO_GNU_HASH_SUPPORT__ + /* Data needed to support GNU hash style */ + Elf32_Word l_gnu_bitmask_idxbits; + Elf32_Word l_gnu_shift; + const ElfW(Addr) *l_gnu_bitmask; + + union + { + const Elf32_Word *l_gnu_chain_zero; + const Elf_Symndx *elf_buckets; + }; +#else Elf_Symndx *elf_buckets; +#endif + struct init_fini_list *init_fini; struct init_fini_list *rtld_local; /* keep tack of RTLD_LOCAL libs in same group */ /* * These are only used with ELF style shared libraries */ Elf_Symndx nchain; + +#ifdef __LDSO_GNU_HASH_SUPPORT__ + union + { + const Elf32_Word *l_gnu_buckets; + const Elf_Symndx *chains; + }; +#else Elf_Symndx *chains; +#endif + unsigned long dynamic_info[DYNAMIC_SIZE]; unsigned long n_phent; @@ -105,11 +130,23 @@ extern struct elf_resolve * _dl_add_elf_hash_table(const char * libname, char * loadaddr, unsigned long * dynamic_info, unsigned long dynamic_addr, unsigned long dynamic_size); -extern char * _dl_find_hash(const char * name, struct dyn_elf * rpnt1, - struct elf_resolve *mytpnt, int type_class); +extern char * _dl_lookup_hash(const char * name, struct dyn_elf * rpnt1, + struct elf_resolve *mytpnt, int type_class + #if USE_TLS + ,struct elf_resolve **tls_tpnt + #endif + ); + +static __always_inline char *_dl_find_hash(const char *name, struct dyn_elf *rpnt, struct elf_resolve *mytpnt, + int type_class, struct elf_resolve **tls_tpnt) +{ +#ifdef USE_TLS + return _dl_lookup_hash(name, rpnt, mytpnt, type_class, tls_tpnt); +#else + return _dl_lookup_hash(name, rpnt, mytpnt, type_class); +#endif +} -extern char * _dl_find_hash2(const char * name, struct dyn_elf * rpnt1, - struct elf_resolve *mytpnt, int type_class, ElfW(Sym) **sym_tls, struct elf_resolve **tpnt_tls); extern int _dl_linux_dynamic_link(void); diff --git a/ldso/include/ldsodefs.h b/ldso/include/ldsodefs.h index edc7efee3..bd3b02d47 100644 --- a/ldso/include/ldsodefs.h +++ b/ldso/include/ldsodefs.h @@ -63,6 +63,13 @@ extern void _dl_get_tls_static_info (size_t *sizep, size_t *alignp) extern void _dl_allocate_static_tls (struct link_map *map) internal_function attribute_hidden; +/* Taken from glibc/elf/dl-reloc.c */ +#define CHECK_STATIC_TLS(sym_map) \ + do { \ + if (__builtin_expect ((sym_map)->l_tls_offset == NO_TLS_OFFSET, 0)) \ + _dl_allocate_static_tls (sym_map); \ + } while (0) + /* These are internal entry points to the two halves of _dl_allocate_tls, only used within rtld.c itself at startup time. */ extern void *_dl_allocate_tls_storage (void) diff --git a/ldso/ldso/Makefile.in b/ldso/ldso/Makefile.in index a92734817..5c58afce4 100644 --- a/ldso/ldso/Makefile.in +++ b/ldso/ldso/Makefile.in @@ -13,6 +13,13 @@ CFLAGS-ldso += -fno-omit-frame-pointer CFLAGS-ldso += -I$(top_srcdir)ldso/ldso/$(TARGET_ARCH) -I$(top_srcdir)ldso/include -I$(top_srcdir)ldso/ldso CFLAGS-ldso += -DUCLIBC_RUNTIME_PREFIX=\"$(RUNTIME_PREFIX)\" -DUCLIBC_LDSO=\"$(UCLIBC_LDSO)\" +ifeq ($(DODEBUG),y) +# Not really much point in including debugging info, since gdb +# can't really debug ldso, since gdb requires help from ldso to +# debug things.... +CFLAGS-ldso += -Os -g +endif + CFLAGS-ldso/ldso/$(TARGET_ARCH)/ := $(CFLAGS-ldso) CFLAGS-ldso.c := -DLDSO_ELFINTERP=\"$(TARGET_ARCH)/elfinterp.c\" diff --git a/ldso/ldso/dl-elf.c b/ldso/ldso/dl-elf.c index 866b2560a..bf97ede7c 100644 --- a/ldso/ldso/dl-elf.c +++ b/ldso/ldso/dl-elf.c @@ -136,26 +136,13 @@ static struct elf_resolve * search_for_named_library(const char *name, int secure, const char *path_list, struct dyn_elf **rpnt) { -#ifdef USE_TLS - char *path, *path_n; - char mylibname[2050]; -#else char *path, *path_n, *mylibname; -#endif struct elf_resolve *tpnt; int done; if (path_list==NULL) return NULL; -#ifdef USE_TLS - /* We need a writable copy of this string */ - path = _dl_strdup(path_list); - if (!path) { - _dl_dprintf(2, "Out of memory!\n"); - _dl_exit(0); - } -#else /* We need a writable copy of this string, but we don't * need this allocated permanently since we don't want * to leak memory, so use alloca to put path on the stack */ @@ -166,7 +153,6 @@ search_for_named_library(const char *name, int secure, const char *path_list, mylibname = alloca(2050); _dl_memcpy(path, path_list, done+1); -#endif /* Unlike ldd.c, don't bother to eliminate double //s */ @@ -347,6 +333,9 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, ElfW(Dyn) *dpnt; struct elf_resolve *tpnt; ElfW(Phdr) *ppnt; +#if USE_TLS + ElfW(Phdr) *tlsppnt = NULL; +#endif char *status, *header; unsigned long dynamic_info[DYNAMIC_SIZE]; unsigned long *lpnt; @@ -450,6 +439,29 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, maxvma = ppnt->p_vaddr + ppnt->p_memsz; } } + if (ppnt->p_type == PT_TLS) + { +#if USE_TLS + if (ppnt->p_memsz == 0) + /* Nothing to do for an empty segment. */ + continue; + else + /* Save for after 'tpnt' is actually allocated. */ + tlsppnt = ppnt; +#else + /* + * Yup, the user was an idiot and tried to sneak in a library with + * TLS in it and we don't support it. Let's fall on our own sword + * and scream at the luser while we die. + */ + _dl_dprintf(2, "%s: '%s' library contains unsupported TLS\n", + _dl_progname, libname); + _dl_internal_error_number = LD_ERROR_TLS_FAILED; + _dl_close(infile); + _dl_munmap(header, _dl_pagesize); + return NULL; +#endif + } ppnt++; } @@ -602,39 +614,33 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, tpnt->n_phent = epnt->e_phnum; #if USE_TLS - for (i = 0, ppnt = tpnt->ppnt; i < tpnt->n_phent; i++) { - if (ppnt->p_type == PT_TLS) - { - if(ppnt->p_memsz == 0) - break; + if (tlsppnt) + { _dl_debug_early("Found TLS header for %s\n", libname); #if NO_TLS_OFFSET != 0 tpnt->l_tls_offset = NO_TLS_OFFSET; #endif - tpnt->l_tls_blocksize = ppnt->p_memsz; - tpnt->l_tls_align = ppnt->p_align; - if (ppnt->p_align == 0) + tpnt->l_tls_blocksize = tlsppnt->p_memsz; + tpnt->l_tls_align = tlsppnt->p_align; + if (tlsppnt->p_align == 0) tpnt->l_tls_firstbyte_offset = 0; else - tpnt->l_tls_firstbyte_offset = ppnt->p_vaddr & - (ppnt->p_align - 1); - tpnt->l_tls_initimage_size = ppnt->p_filesz; - tpnt->l_tls_initimage = (void *) ppnt->p_vaddr; - + tpnt->l_tls_firstbyte_offset = tlsppnt->p_vaddr & + (tlsppnt->p_align - 1); + tpnt->l_tls_initimage_size = tlsppnt->p_filesz; + tpnt->l_tls_initimage = (void *) tlsppnt->p_vaddr; + /* Assign the next available module ID. */ tpnt->l_tls_modid = _dl_next_tls_modid (); - + /* We know the load address, so add it to the offset. */ if (tpnt->l_tls_initimage != NULL) { unsigned int tmp = (unsigned int) tpnt->l_tls_initimage; - tpnt->l_tls_initimage = (char *) ppnt->p_vaddr + tpnt->loadaddr; + tpnt->l_tls_initimage = (char *) tlsppnt->p_vaddr + tpnt->loadaddr; _dl_debug_early("Relocated TLS initial image from %x to %x (size = %x)\n", tmp, tpnt->l_tls_initimage, tpnt->l_tls_initimage_size); tmp = 0; } - break; - } - ppnt++; } #endif @@ -646,9 +652,19 @@ struct elf_resolve *_dl_load_elf_shared_library(int secure, _dl_memset((*rpnt)->next, 0, sizeof(struct dyn_elf)); (*rpnt)->next->prev = (*rpnt); *rpnt = (*rpnt)->next; - (*rpnt)->dyn = tpnt; - tpnt->symbol_scope = _dl_symbol_tables; } +#ifndef SHARED + /* When statically linked, the first time we dlopen a DSO + * the *rpnt is NULL, so we need to allocate memory for it, + * and initialize the _dl_symbol_table. + */ + else { + *rpnt = _dl_symbol_tables = (struct dyn_elf *) _dl_malloc(sizeof(struct dyn_elf)); + _dl_memset(*rpnt, 0, sizeof(struct dyn_elf)); + } +#endif + (*rpnt)->dyn = tpnt; + tpnt->symbol_scope = _dl_symbol_tables; tpnt->usage_count++; tpnt->libtype = elf_lib; @@ -737,12 +753,15 @@ int _dl_fixup(struct dyn_elf *rpnt, int now_flag) tpnt->init_flag |= JMP_RELOCS_DONE; } +#if 0 +/* _dl_add_to_slotinfo is called by init_tls() for initial DSO + or by dlopen() for dynamically loaded DSO. */ #if USE_TLS /* Add object to slot information data if necessasy. */ if (tpnt->l_tls_blocksize != 0 && tls_init_tp_called) _dl_add_to_slotinfo ((struct link_map *) tpnt); #endif - +#endif return goof; } diff --git a/ldso/ldso/dl-hash.c b/ldso/ldso/dl-hash.c index e692fb6ab..971b1842c 100644 --- a/ldso/ldso/dl-hash.c +++ b/ldso/ldso/dl-hash.c @@ -53,21 +53,24 @@ struct dyn_elf *_dl_symbol_tables = NULL; */ struct dyn_elf *_dl_handles = NULL; -#if USE_TLS -/* - * These are only used when doing TLS relocations. They are referenced from - * other parts of the linker and from 'libdl.so'. This allows preservation - * of the '_dl_find_hash' interface. - */ -ElfW(Sym) *_dl_tls_reloc_sym = NULL; -struct elf_resolve *_dl_tls_reloc_tpnt = NULL; +#ifdef __LDSO_GNU_HASH_SUPPORT__ +/* This is the new hash function that is used by the ELF linker to generate the + * GNU hash table that each executable and library will have if --hash-style=[gnu,both] + * is passed to the linker. We need it to decode the GNU hash table. */ +static inline Elf_Symndx _dl_gnu_hash (const unsigned char *name) +{ + unsigned long h = 5381; + unsigned char c; + for (c = *name; c != '\0'; c = *++name) + h = h * 33 + c; + return h & 0xffffffff; +} #endif - /* This is the hash function that is used by the ELF linker to generate the * hash table that each executable and library is required to have. We need * it to decode the hash table. */ -static inline Elf_Symndx _dl_elf_hash(const char *name) +static inline Elf_Symndx _dl_elf_hash(const unsigned char *name) { unsigned long hash=0; unsigned long tmp; @@ -118,6 +121,29 @@ struct elf_resolve *_dl_add_elf_hash_table(const char *libname, tpnt->libname = _dl_strdup(libname); tpnt->dynamic_addr = (ElfW(Dyn) *)dynamic_addr; tpnt->libtype = loaded_file; + +#ifdef __LDSO_GNU_HASH_SUPPORT__ + if (dynamic_info[DT_GNU_HASH_IDX] != 0) { + + Elf32_Word *hash32 = (Elf_Symndx*)dynamic_info[DT_GNU_HASH_IDX]; + + tpnt->nbucket = *hash32++; + Elf32_Word symbias = *hash32++; + Elf32_Word bitmask_nwords = *hash32++; + /* Must be a power of two. */ + _dl_assert ((bitmask_nwords & (bitmask_nwords - 1)) == 0); + tpnt->l_gnu_bitmask_idxbits = bitmask_nwords - 1; + tpnt->l_gnu_shift = *hash32++; + + tpnt->l_gnu_bitmask = (ElfW(Addr) *) hash32; + hash32 += __ELF_NATIVE_CLASS / 32 * bitmask_nwords; + + tpnt->l_gnu_buckets = hash32; + hash32 += tpnt->nbucket; + tpnt->l_gnu_chain_zero = hash32 - symbias; + } else + /* Fall using old SysV hash table if GNU hash is not present */ +#endif if (dynamic_info[DT_HASH] != 0) { hash_addr = (Elf_Symndx*)dynamic_info[DT_HASH]; @@ -134,120 +160,134 @@ struct elf_resolve *_dl_add_elf_hash_table(const char *libname, } -/* - * 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_find_hash(const char *name, struct dyn_elf *rpnt, struct elf_resolve *mytpnt, int type_class) -{ - struct elf_resolve *tpnt; - int si; - char *strtab; - ElfW(Sym) *symtab; - unsigned long elf_hash_number, hn; - const ElfW(Sym) *sym; - char *weak_result = NULL; +/* Routine to check whether the symbol matches. */ +static __attribute_noinline__ const ElfW(Sym) * +check_match (const ElfW(Sym) *sym, char *strtab, const char* undef_name, int type_class) { + #if USE_TLS - _dl_tls_reloc_sym = NULL; - _dl_tls_reloc_tpnt = NULL; + if((sym->st_value == 0 && (ELF_ST_TYPE(sym->st_info) != STT_TLS)) + || (type_class & (sym->st_shndx == SHN_UNDEF))) + /* No value or undefined symbol itself */ + return NULL; + + if(ELF_ST_TYPE(sym->st_info) > STT_FUNC + && ELF_ST_TYPE(sym->st_info) != STT_COMMON + && ELF_ST_TYPE(sym->st_info) != STT_TLS) + /* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC and STT_COMMON + * entries (and STT_TLS if TLS is supported) since these + * are no code/data definitions. + */ + return NULL; +#else + if (type_class & (sym->st_shndx == SHN_UNDEF)) + /* undefined symbol itself */ + return NULL; + + if (sym->st_value == 0) + /* No value */ + return NULL; + + if (ELF_ST_TYPE(sym->st_info) > STT_FUNC) + /* Ignore all but STT_NOTYPE, STT_OBJECT and STT_FUNC + * entries since these are no code/data definitions. + */ + return NULL; #endif + if (_dl_strcmp(strtab + sym->st_name, undef_name) != 0) + return NULL; - elf_hash_number = _dl_elf_hash(name); - - 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; - } - 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; + /* This is the matching symbol */ + return sym; +} - /* Avoid calling .urem here. */ - do_rem(hn, elf_hash_number, tpnt->nbucket); - symtab = (ElfW(Sym) *) (intptr_t) (tpnt->dynamic_info[DT_SYMTAB]); - strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]); - for (si = tpnt->elf_buckets[hn]; si != STN_UNDEF; si = tpnt->chains[si]) - { - sym = &symtab[si]; +#ifdef __LDSO_GNU_HASH_SUPPORT__ - if ((sym->st_value == 0 -#if USE_TLS - && ELF_ST_TYPE(sym->st_info) != STT_TLS -#endif - ) - || (type_class & (sym->st_shndx == SHN_UNDEF))) - continue; +static __always_inline const ElfW(Sym) * +_dl_lookup_gnu_hash(struct elf_resolve *tpnt, ElfW(Sym) *symtab, unsigned long hash, + const char* undef_name, int type_class) { - if (ELF_ST_TYPE(sym->st_info) > STT_FUNC -#if USE_TLS - && ELF_ST_TYPE(sym->st_info) != STT_TLS -#endif - ) - continue; - - if (_dl_strcmp(strtab + sym->st_name, name) != 0) - continue; - - switch (ELF_ST_BIND(sym->st_info)) { - case STB_WEAK: -#if 0 -/* Perhaps we should support old style weak symbol handling - * per what glibc does when you export LD_DYNAMIC_WEAK */ - if (!weak_result) - weak_result = (char *)tpnt->loadaddr + sym->st_value; - break; -#endif - case STB_GLOBAL: -#if USE_TLS - /* Update these pointers if we are doing TLS relocations. */ - _dl_tls_reloc_sym = (ElfW(Sym) *) sym; - _dl_tls_reloc_tpnt = tpnt; -#endif - return (char*)tpnt->loadaddr + sym->st_value; - default: /* Local symbols not handled here */ - break; - } + Elf_Symndx symidx; + const ElfW(Sym) *sym; + char *strtab; + + const ElfW(Addr) *bitmask = tpnt->l_gnu_bitmask; + + ElfW(Addr) bitmask_word = bitmask[(hash / __ELF_NATIVE_CLASS) & tpnt->l_gnu_bitmask_idxbits]; + + unsigned int hashbit1 = hash & (__ELF_NATIVE_CLASS - 1); + unsigned int hashbit2 = ((hash >> tpnt->l_gnu_shift) & (__ELF_NATIVE_CLASS - 1)); + + _dl_assert (bitmask != NULL); + + if (__builtin_expect ((bitmask_word >> hashbit1) & (bitmask_word >> hashbit2) & 1, 0)) { + + Elf32_Word bucket = tpnt->l_gnu_buckets[hash % tpnt->nbucket]; + + if (bucket != 0) { + const Elf32_Word *hasharr = &tpnt->l_gnu_chain_zero[bucket]; + do { + if (((*hasharr ^ hash) >> 1) == 0) { + symidx = hasharr - tpnt->l_gnu_chain_zero; + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]); + sym = check_match (&symtab[symidx], strtab, undef_name, type_class); + if (sym != NULL) + return sym; + } + } while ((*hasharr++ & 1u) == 0); } } - return weak_result; + /* No symbol found. */ + return NULL; } +#endif +static __always_inline const ElfW(Sym) * +_dl_lookup_sysv_hash(struct elf_resolve *tpnt, ElfW(Sym) *symtab, unsigned long hash, const char* undef_name, int type_class) { + unsigned long hn; + char *strtab; + const ElfW(Sym) *sym; + Elf_Symndx symidx; + + /* Avoid calling .urem here. */ + do_rem(hn, hash, tpnt->nbucket); + strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]); + + _dl_assert(tpnt->elf_buckets != NULL); + + for (symidx = tpnt->elf_buckets[hn]; symidx != STN_UNDEF; symidx = tpnt->chains[symidx]) { + sym = check_match (&symtab[symidx], strtab, undef_name, type_class); + if (sym != NULL) + /* At this point the symbol is that we are looking for */ + return sym; + } + /* No symbol found into the current module*/ + return NULL; +} -/* TLS Version +/* * 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_find_hash2(const char *name, struct dyn_elf *rpnt, struct elf_resolve *mytpnt, int type_class, ElfW(Sym) **sym_tls, struct elf_resolve **tpnt_tls) +char *_dl_lookup_hash(const char *name, struct dyn_elf *rpnt, struct elf_resolve *mytpnt, int type_class +#if USE_TLS +,struct elf_resolve **tls_tpnt +#endif +) { - struct elf_resolve *tpnt; - int si; - char *strtab; + struct elf_resolve *tpnt = NULL; ElfW(Sym) *symtab; - unsigned long elf_hash_number, hn; - const ElfW(Sym) *sym; - char *weak_result = NULL; -#if USE_TLS - _dl_tls_reloc_sym = NULL; - _dl_tls_reloc_tpnt = NULL; -#endif - elf_hash_number = _dl_elf_hash(name); + unsigned long elf_hash_number = 0xffffffff; + const ElfW(Sym) *sym = NULL; + + char *weak_result = NULL; +#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; @@ -268,53 +308,60 @@ char *_dl_find_hash2(const char *name, struct dyn_elf *rpnt, struct elf_resolve /* Don't search the executable when resolving a copy reloc. */ if ((type_class & ELF_RTYPE_CLASS_COPY) && tpnt->libtype == elf_executable) continue; - - /* Avoid calling .urem here. */ - do_rem(hn, elf_hash_number, tpnt->nbucket); - symtab = (ElfW(Sym) *) (intptr_t) (tpnt->dynamic_info[DT_SYMTAB]); - strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]); - - for (si = tpnt->elf_buckets[hn]; si != STN_UNDEF; si = tpnt->chains[si]) - { - sym = &symtab[si]; - - if ((sym->st_value == 0 -#if USE_TLS - && ELF_ST_TYPE(sym->st_info) != STT_TLS -#endif - ) - || (type_class & (sym->st_shndx == SHN_UNDEF))) - continue; - - if (ELF_ST_TYPE(sym->st_info) > STT_FUNC -#if USE_TLS - && ELF_ST_TYPE(sym->st_info) != STT_TLS + + /* 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]); + +#ifdef __LDSO_GNU_HASH_SUPPORT__ + /* Prefer GNU hash style, if any */ + if(tpnt->l_gnu_bitmask) { + if((sym = _dl_lookup_gnu_hash(tpnt, symtab, gnu_hash_number, name, type_class)) != NULL) + /* If sym has been found, do not search further */ + break; + } else { +#endif + /* 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); + + if((sym = _dl_lookup_sysv_hash(tpnt, symtab, elf_hash_number, name, type_class)) != NULL ) + break; + +#ifdef __LDSO_GNU_HASH_SUPPORT__ + } +#endif + } /* end of for (; rpnt; rpnt = rpnt->next) { */ + + if(sym) { + /* At this point we have found the requested symbol, do binding */ +#if USE_TLS + if(ELF_ST_TYPE(sym->st_info) == STT_TLS) { + _dl_assert((tls_tpnt != NULL)); + *tls_tpnt = tpnt; + + return (char*)sym->st_value; + } #endif - ) - continue; - if (_dl_strcmp(strtab + sym->st_name, name) != 0) - continue; - - switch (ELF_ST_BIND(sym->st_info)) { + switch (ELF_ST_BIND(sym->st_info)) { case STB_WEAK: #if 0 -/* Perhaps we should support old style weak symbol handling - * per what glibc does when you export LD_DYNAMIC_WEAK */ + /* Perhaps we should support old style weak symbol handling + * per what glibc does when you export LD_DYNAMIC_WEAK */ if (!weak_result) weak_result = (char *)tpnt->loadaddr + sym->st_value; break; + #endif case STB_GLOBAL: -#if USE_TLS - /* Update these pointers if we are doing TLS relocations. */ - *sym_tls = (ElfW(Sym) *) sym; - *tpnt_tls = tpnt; -#endif return (char*)tpnt->loadaddr + sym->st_value; default: /* Local symbols not handled here */ break; - } } } return weak_result; diff --git a/ldso/ldso/dl-tls.c b/ldso/ldso/dl-tls.c index 0dd270ed7..3e8ad1a18 100644 --- a/ldso/ldso/dl-tls.c +++ b/ldso/ldso/dl-tls.c @@ -53,7 +53,7 @@ _dl_calloc (size_t __nmemb, size_t __size) _dl_exit(1); } - if ((result = _dl_malloc(__size)) != NULL) { + if ((result = _dl_malloc(size)) != NULL) { _dl_memset(result, 0, size); } @@ -108,13 +108,6 @@ _dl_free (void *__ptr) /* Value used for dtv entries for which the allocation is delayed. */ #define TLS_DTV_UNALLOCATED ((void *) -1l) -/* Taken from glibc/elf/dl-reloc.c */ -#define CHECK_STATIC_TLS(sym_map) \ - do { \ - if (__builtin_expect ((sym_map)->l_tls_offset == NO_TLS_OFFSET, 0)) \ - _dl_allocate_static_tls (sym_map); \ - } while (0) - /* * We are trying to perform a static TLS relocation in MAP, but it was * dynamically loaded. This can only work if there is enough surplus in @@ -386,7 +379,8 @@ _dl_determine_tlsoffset (void) size_t offset = TLS_TCB_SIZE; size_t cnt; - for (cnt = 0; slotinfo[cnt].map != NULL; ++cnt) + /* The first slot is never used */ + for (cnt = 1; slotinfo[cnt].map != NULL; ++cnt) { _dl_assert (cnt < _dl_tls_dtv_slotinfo_list->len); @@ -1001,9 +995,9 @@ init_tls (void) { /* This is a module with TLS data. Store the map reference. The generation counter is zero. */ - slotinfo[i].map = l; - /* slotinfo[i].gen = 0; */ - ++i; + + /* Skeep slot[0]: it will be never used */ + slotinfo[++i].map = l; } _dl_assert (i == _dl_tls_max_dtv_idx); @@ -1027,7 +1021,7 @@ init_tls (void) /* And finally install it for the main thread. If ld.so itself uses TLS we know the thread pointer was initialized earlier. */ - const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD); + const char *lossage = (char *) TLS_INIT_TP (tcbp, USE___THREAD); if(__builtin_expect (lossage != NULL, 0)) { _dl_debug_early("cannot set up thread-local storage: %s\n", lossage); _dl_exit(30); diff --git a/ldso/ldso/ldso.c b/ldso/ldso/ldso.c index 3991eab04..738b970f4 100644 --- a/ldso/ldso/ldso.c +++ b/ldso/ldso/ldso.c @@ -166,6 +166,7 @@ static void __attribute__ ((destructor)) __attribute_used__ _dl_fini(void) if (tpnt->init_flag & FINI_FUNCS_CALLED) continue; tpnt->init_flag |= FINI_FUNCS_CALLED; + _dl_run_fini_array(tpnt); if (tpnt->dynamic_info[DT_FINI]) { void (*dl_elf_func) (void); @@ -872,7 +873,7 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, unsigned long load_addr, * ld.so.1, so we have to look up each symbol individually. */ - _dl_envp = (unsigned long *) (intptr_t) _dl_find_hash("__environ", _dl_symbol_tables, NULL, 0); + _dl_envp = (unsigned long *) (intptr_t) _dl_find_hash("__environ", _dl_symbol_tables, NULL, 0, NULL); if (_dl_envp) *_dl_envp = (unsigned long) envp; @@ -898,6 +899,14 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, unsigned long load_addr, /* Notify the debugger we have added some objects. */ _dl_debug_addr->r_state = RT_ADD; _dl_debug_state(); + + /* Run pre-initialization functions for the executable. */ + _dl_run_array_forward(_dl_loaded_modules->dynamic_info[DT_PREINIT_ARRAY], + _dl_loaded_modules->dynamic_info[DT_PREINIT_ARRAYSZ], + _dl_loaded_modules->loadaddr); + + /* Run initialization functions for loaded objects. For the + main executable, they will be run from __uClibc_main. */ for (i = nlist; i; --i) { tpnt = init_fini_list[i-1]; tpnt->init_fini = NULL; /* Clear, since alloca was used */ @@ -919,20 +928,23 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, unsigned long 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("malloc", - _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT); + _dl_malloc_function = (void* (*)(size_t)) (intptr_t) + _dl_find_hash("malloc", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT, NULL); #if 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("calloc", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT); + _dl_find_hash("calloc", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT, NULL); + _dl_realloc_function = (void* (*)(void *, size_t)) (intptr_t) - _dl_find_hash("recalloc", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT); + _dl_find_hash("realloc", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT, NULL); + _dl_free_function = (void (*)(void *)) (intptr_t) - _dl_find_hash("free", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT); + _dl_find_hash("free", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT, NULL); + _dl_memalign_function = (void* (*)(size_t, size_t)) (intptr_t) - _dl_find_hash("memalign", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT); - + _dl_find_hash("memalign", _dl_symbol_tables, NULL, ELF_RTYPE_CLASS_PLT, NULL); + if (!was_tls_init_tp_called && _dl_tls_max_dtv_idx > 0) ++_dl_tls_generation; @@ -947,7 +959,7 @@ void _dl_get_ready_to_run(struct elf_resolve *tpnt, unsigned long load_addr, TLS we know the thread pointer was initialized earlier. */ if (! tls_init_tp_called) { - const char *lossage = TLS_INIT_TP (tcbp, USE___THREAD); + const char *lossage = (char *) TLS_INIT_TP (tcbp, USE___THREAD); if (__builtin_expect (lossage != NULL, 0)) { _dl_debug_early("cannot set up thread-local storage: %s\n", lossage); diff --git a/ldso/ldso/sh/dl-debug.h b/ldso/ldso/sh/dl-debug.h index e862da1ee..e2e74f8e4 100644 --- a/ldso/ldso/sh/dl-debug.h +++ b/ldso/ldso/sh/dl-debug.h @@ -36,6 +36,8 @@ static const char *_dl_reltypes_tab[] = [25] "R_SH_SWITCH16","R_SH_SWITCH32","R_SH_USES", [28] "R_SH_COUNT", "R_SH_ALIGN", "R_SH_CODE", "R_SH_DATA", [32] "R_SH_LABEL", "R_SH_SWITCH8", "R_SH_GNU_VTINHERIT","R_SH_GNU_VTENTRY", +[144] "R_SH_TLS_GD_32","R_SH_TLS_LD_32", "R_SH_TLS_LDO_32", "R_SH_TLS_IE_32", +[148] "R_SH_TLS_LE_32","R_SH_TLS_DTPMOD32", "R_SH_TLS_DTPOFF32", "R_SH_TLS_TPOFF32", [160] "R_SH_GOT32", "R_SH_PLT32", "R_SH_COPY", "R_SH_GLOB_DAT", [164] "R_SH_JMP_SLOT","R_SH_RELATIVE","R_SH_GOTOFF", "R_SH_GOTPC", }; diff --git a/ldso/ldso/sh/dl-startup.h b/ldso/ldso/sh/dl-startup.h index 63a65940c..bd51cebe0 100644 --- a/ldso/ldso/sh/dl-startup.h +++ b/ldso/ldso/sh/dl-startup.h @@ -3,6 +3,7 @@ * needed for this architecture. */ asm( + " .text\n" " .globl _start\n" " .type _start,@function\n" "_start:\n" diff --git a/ldso/ldso/sh/dl-sysdep.h b/ldso/ldso/sh/dl-sysdep.h index da109782e..abe0a123f 100644 --- a/ldso/ldso/sh/dl-sysdep.h +++ b/ldso/ldso/sh/dl-sysdep.h @@ -6,6 +6,7 @@ /* Define this if the system uses RELOCA. */ #define ELF_USES_RELOCA #include <elf.h> +#include <tls.h> /* * Initialization sequence for a GOT. */ @@ -93,9 +94,17 @@ _dl_urem(unsigned int n, unsigned int base) define the value. ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one of the main executable's symbols, as for a COPY reloc. */ +#if defined USE_TLS +# define elf_machine_type_class(type) \ + ((((type) == R_SH_JMP_SLOT || (type) == R_SH_TLS_DTPMOD32 \ + || (type) == R_SH_TLS_DTPOFF32 || (type) == R_SH_TLS_TPOFF32) \ + * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_SH_COPY) * ELF_RTYPE_CLASS_COPY)) +#else #define elf_machine_type_class(type) \ ((((type) == R_SH_JMP_SLOT) * ELF_RTYPE_CLASS_PLT) \ | (((type) == R_SH_COPY) * ELF_RTYPE_CLASS_COPY)) +#endif /* Return the link-time address of _DYNAMIC. Conveniently, this is the first element of the GOT. This must be inlined in a function which diff --git a/ldso/ldso/sh/elfinterp.c b/ldso/ldso/sh/elfinterp.c index c34acdf95..db86b0c0c 100644 --- a/ldso/ldso/sh/elfinterp.c +++ b/ldso/ldso/sh/elfinterp.c @@ -77,7 +77,8 @@ 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); + new_addr = _dl_find_hash(symname, tpnt->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); _dl_exit(1); @@ -166,6 +167,10 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, #if defined (__SUPPORT_LD_DEBUG__) unsigned long old_val; #endif +#if USE_TLS +struct elf_resolve *tls_tpnt = NULL; +#endif + reloc_addr = (unsigned long *)(intptr_t) (tpnt->loadaddr + (unsigned long) rpnt->r_offset); reloc_type = ELF32_R_TYPE(rpnt->r_info); @@ -175,23 +180,33 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, if (symtab_index) { symbol_addr = (unsigned long) _dl_find_hash(symname, scope, tpnt, - elf_machine_type_class(reloc_type)); - + 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 * here, so all bases should be covered. */ - if (!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK) { + + if (!symbol_addr && (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_exit (1); + + /* Let the caller to handle the error: it may be non fatal if called from dlopen */ + return 1; } } #if defined (__SUPPORT_LD_DEBUG__) old_val = *reloc_addr; #endif + +#if USE_TLS + /* In case of a TLS reloc, tls_tpnt NULL means we have an 'anonymous' symbol. + This is the casa of a static tls variable, so the lookup module is just + that one is referencing the tls variable. */ + if(!tls_tpnt) + tls_tpnt = tpnt; +#endif switch (reloc_type) { case R_SH_NONE: break; @@ -218,12 +233,27 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, case R_SH_RELATIVE: *reloc_addr = (unsigned long) tpnt->loadaddr + rpnt->r_addend; break; +#if USE_TLS + case R_SH_TLS_DTPMOD32: + *reloc_addr = tls_tpnt->l_tls_modid; + break; + + case R_SH_TLS_DTPOFF32: + *reloc_addr = symbol_addr; + break; + + case R_SH_TLS_TPOFF32: + CHECK_STATIC_TLS ((struct link_map *) tls_tpnt); + *reloc_addr = tls_tpnt->l_tls_offset + symbol_addr + rpnt->r_addend; + break; +#endif default: - return -1; /*call _dl_exit(1) */ + + return -1; } #if defined (__SUPPORT_LD_DEBUG__) if (_dl_debug_reloc && _dl_debug_detail) - _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr); + _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", old_val, *reloc_addr, reloc_addr); #endif return 0; @@ -256,11 +286,11 @@ _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct dyn_elf *scope, *reloc_addr += (unsigned long) tpnt->loadaddr; break; default: - return -1; /*call _dl_exit(1) */ + return -1; } #if defined (__SUPPORT_LD_DEBUG__) if (_dl_debug_reloc && _dl_debug_detail) - _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr); + _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", old_val, *reloc_addr, reloc_addr); #endif return 0; diff --git a/ldso/libdl/Makefile.in b/ldso/libdl/Makefile.in index 245832447..b6384a22c 100644 --- a/ldso/libdl/Makefile.in +++ b/ldso/libdl/Makefile.in @@ -12,10 +12,6 @@ CFLAGS-libdl += -I$(top_srcdir)ldso/ldso/$(TARGET_ARCH) -I$(top_srcdir)ldso/incl CFLAGS-libdl += -DUCLIBC_RUNTIME_PREFIX=\"$(RUNTIME_PREFIX)\" -ifeq ($(SUPPORT_LD_DEBUG),y) -CFLAGS-libdl += -D__SUPPORT_LD_DEBUG__ -endif - CFLAGS-libdl.c := -DLDSO_ELFINTERP=\"$(TARGET_ARCH)/elfinterp.c\" LDFLAGS-libdl.so := $(LDFLAGS) -fini dl_cleanup diff --git a/ldso/libdl/libdl.c b/ldso/libdl/libdl.c index 462de4f3d..cd18729a9 100644 --- a/ldso/libdl/libdl.c +++ b/ldso/libdl/libdl.c @@ -32,59 +32,62 @@ #include <ldso.h> #include <stdio.h> +#include <tls.h> #if USE_TLS -# include <ldsodefs.h> +#include <ldsodefs.h> +extern void (*_dl_init_static_tls) (struct link_map *); +extern void _dl_add_to_slotinfo(struct link_map *l); #endif - #ifdef SHARED - -#if USE_TLS +# if USE_TLS # include <dl-tls.h> - -extern void _dl_add_to_slotinfo(struct link_map *l); extern struct link_map *_dl_update_slotinfo(unsigned long int req_modid); -extern struct elf_resolve *_dl_tls_reloc_tpnt; -extern ElfW(Sym) *_dl_tls_reloc_sym; -#endif +# endif /* When libdl is loaded as a shared library, we need to load in * and use a pile of symbols from ldso... */ -extern char *_dl_find_hash(const char *, struct dyn_elf *, struct elf_resolve *, int); -extern struct elf_resolve * _dl_load_shared_library(int, struct dyn_elf **, - struct elf_resolve *, char *, int); +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 void _dl_protect_relro(struct elf_resolve * tpnt); extern int _dl_errno; extern struct dyn_elf *_dl_symbol_tables; extern struct dyn_elf *_dl_handles; extern struct elf_resolve *_dl_loaded_modules; +extern void _dl_free (void *__ptr); extern struct r_debug *_dl_debug_addr; extern unsigned long _dl_error_number; extern void *(*_dl_malloc_function)(size_t); extern void _dl_run_init_array(struct elf_resolve *); extern void _dl_run_fini_array(struct elf_resolve *); -#ifdef __LDSO_CACHE_SUPPORT__ +# ifdef __LDSO_CACHE_SUPPORT__ int _dl_map_cache(void); int _dl_unmap_cache(void); -#endif -#ifdef __mips__ +# endif +# ifdef __mips__ extern void _dl_perform_mips_global_got_relocations(struct elf_resolve *tpnt, int lazy); -#endif -#ifdef __SUPPORT_LD_DEBUG__ +# endif +# ifdef __SUPPORT_LD_DEBUG__ extern char *_dl_debug; -#endif - - +# endif #else /* SHARED */ /* When libdl is linked as a static library, we need to replace all * the symbols that otherwise would have been loaded in from ldso... */ -#ifdef __SUPPORT_LD_DEBUG__ +# ifdef __SUPPORT_LD_DEBUG__ +/* Needed for 'strstr' prototype' */ +#include <string.h> char *_dl_debug = 0; -#endif +char *_dl_debug_symbols = 0; +char *_dl_debug_move = 0; +char *_dl_debug_reloc = 0; +char *_dl_debug_detail = 0; +char *_dl_debug_nofixups = 0; +char *_dl_debug_bindings = 0; +int _dl_debug_file = 2; +# endif const char *_dl_progname = ""; /* Program name */ char *_dl_library_path = 0; /* Where we look for libraries */ char *_dl_ldsopath = 0; /* Location of the shared lib loader */ @@ -93,16 +96,17 @@ size_t _dl_pagesize = PAGE_SIZE; /* Store the page size for use later /* This global variable is also to communicate with debuggers such as gdb. */ struct r_debug *_dl_debug_addr = NULL; #define _dl_malloc malloc +#define _dl_free free #include "../ldso/dl-debug.c" -#if USE_TLS -#include "../ldso/dl-tls.c" + +# if USE_TLS /* * Giving this initialized value preallocates some surplus bytes in the * static TLS area, see __libc_setup_tls (libc-tls.c). */ size_t _dl_tls_static_size = 2048; -#endif +# endif #include LDSO_ELFINTERP #include "../ldso/dl-hash.c" #define _dl_trace_loaded_objects 0 @@ -167,18 +171,18 @@ static const char *dl_error_names[] = { */ static void * internal_function -_dl_tls_symaddr(struct link_map *map, const ElfW(Sym) *ref) +_dl_tls_symaddr(struct link_map *map, const Elf32_Addr st_value) { # ifndef DONT_USE_TLS_INDEX tls_index tmp = { .ti_module = map->l_tls_modid, - .ti_offset = ref->st_value + .ti_offset = st_value }; return __TLS_GET_ADDR (&tmp); # else - return __TLS_GET_ADDR (map->l_tls_modid, ref->st_value); + return __TLS_GET_ADDR (map->l_tls_modid, st_value); # endif } #endif @@ -274,7 +278,7 @@ void *dlopen(const char *libname, int flag) struct init_fini_list *tmp, *runp, *runp2, *dep_list; unsigned int nlist, i; struct elf_resolve **init_fini_list; -#ifdef USE_TLS +#if USE_TLS bool any_tls = false; #endif @@ -290,6 +294,25 @@ void *dlopen(const char *libname, int flag) if (!libname) return _dl_symbol_tables; +#ifndef SHARED +# ifdef __SUPPORT_LD_DEBUG__ + _dl_debug = getenv("LD_DEBUG"); + if (_dl_debug) { + if (_dl_strstr(_dl_debug, "all")) { + _dl_debug_detail = _dl_debug_move = _dl_debug_symbols + = _dl_debug_reloc = _dl_debug_bindings = _dl_debug_nofixups = (void*)1; + } else { + _dl_debug_detail = strstr(_dl_debug, "detail"); + _dl_debug_move = strstr(_dl_debug, "move"); + _dl_debug_symbols = strstr(_dl_debug, "sym"); + _dl_debug_reloc = strstr(_dl_debug, "reloc"); + _dl_debug_nofixups = strstr(_dl_debug, "nofix"); + _dl_debug_bindings = strstr(_dl_debug, "bind"); + } + } +# endif +#endif + _dl_map_cache(); /* @@ -317,6 +340,11 @@ void *dlopen(const char *libname, int flag) if (getenv("LD_BIND_NOW")) now_flag = RTLD_NOW; +#ifndef SHARED + /* When statically linked, the _dl_library_path is not yet initialized */ + _dl_library_path = getenv("LD_LIBRARY_PATH"); +#endif + /* Try to load the specified library */ _dl_if_debug_print("Trying to dlopen '%s', RTLD_GLOBAL:%d RTLD_NOW:%d\n", (char*)libname, (flag & RTLD_GLOBAL ? 1:0), (now_flag & RTLD_NOW ? 1:0)); @@ -377,36 +405,31 @@ void *dlopen(const char *libname, int flag) tpnt1->rtld_flags |= (flag & RTLD_GLOBAL); - if (tpnt1->usage_count == 1) { - tpnt1->init_flag |= DL_OPENED; - /* This list is for dlsym() and relocation */ - dyn_ptr->next = (struct dyn_elf *) malloc(sizeof(struct dyn_elf)); - _dl_memset (dyn_ptr->next, 0, sizeof (struct dyn_elf)); - dyn_ptr = dyn_ptr->next; - dyn_ptr->dyn = tpnt1; - } - if (tpnt1->init_flag & DL_OPENED) { - /* Used to record RTLD_LOCAL scope */ - tmp = alloca(sizeof(struct init_fini_list)); - tmp->tpnt = tpnt1; - tmp->next = runp->tpnt->init_fini; - runp->tpnt->init_fini = tmp; - - for (tmp=dep_list; tmp; tmp = tmp->next) { - if (tpnt1 == tmp->tpnt) { /* if match => cirular dependency, drop it */ - _dl_if_debug_print("Circular dependency, skipping '%s',\n", - tmp->tpnt->libname); - tpnt1->usage_count--; - break; - } - } - if (!tmp) { /* Don't add if circular dependency detected */ - runp2->next = alloca(sizeof(*runp)); - runp2 = runp2->next; - runp2->tpnt = tpnt1; - runp2->next = NULL; + /* This list is for dlsym() and relocation */ + dyn_ptr->next = (struct dyn_elf *) malloc(sizeof(struct dyn_elf)); + _dl_memset (dyn_ptr->next, 0, sizeof (struct dyn_elf)); + dyn_ptr = dyn_ptr->next; + dyn_ptr->dyn = tpnt1; + /* Used to record RTLD_LOCAL scope */ + tmp = alloca(sizeof(struct init_fini_list)); + tmp->tpnt = tpnt1; + tmp->next = runp->tpnt->init_fini; + runp->tpnt->init_fini = tmp; + + for (tmp=dep_list; tmp; tmp = tmp->next) { + if (tpnt1 == tmp->tpnt) { /* if match => cirular dependency, drop it */ + _dl_if_debug_print("Circular dependency, skipping '%s',\n", + tmp->tpnt->libname); + tpnt1->usage_count--; + break; } } + if (!tmp) { /* Don't add if circular dependency detected */ + runp2->next = alloca(sizeof(*runp)); + runp2 = runp2->next; + runp2->tpnt = tpnt1; + runp2->next = NULL; + } } } } @@ -454,8 +477,8 @@ void *dlopen(const char *libname, int flag) fprintf(stderr, "lib: %s has deps:\n", init_fini_list[i]->libname); runp = init_fini_list[i]->init_fini; for (; runp; runp = runp->next) - printf(" %s ", runp->tpnt->libname); - printf("\n"); + fprintf(stderr, " %s ", runp->tpnt->libname); + fprintf(stderr, "\n"); } } #endif @@ -485,40 +508,42 @@ void *dlopen(const char *libname, int flag) } /* TODO: Should we set the protections of all pages back to R/O now ? */ + #if USE_TLS - tpnt = dyn_chain->dyn; - /* - * Only add TLS memory if this object is loaded now and - * therefore is not yet initialized. - */ - if (!(tpnt->init_flag & INIT_FUNCS_CALLED) - /* Only if the module defines thread local data. */ - && __builtin_expect(tpnt->l_tls_blocksize > 0, 0)) - { - /* - * Now that we know the object is loaded successfully the - * modules containing TLS data have been added to the slot - * info table. We might have to increase its size. - */ - if(tpnt->l_need_tls_init) - { - tpnt->l_need_tls_init = 0; + for (i=0; i < nlist; i++) { + struct elf_resolve *tmp_tpnt = init_fini_list[i]; + /* Only add TLS memory if this object is loaded now and + therefore is not yet initialized. */ + + if (!(tmp_tpnt->init_flag & INIT_FUNCS_CALLED) + /* Only if the module defines thread local data. */ + && __builtin_expect (tmp_tpnt->l_tls_blocksize > 0, 0)) { + + /* Now that we know the object is loaded successfully add + modules containing TLS data to the slot info table. We + might have to increase its size. */ + _dl_add_to_slotinfo ((struct link_map*)tmp_tpnt); + + /* It is the case in which we couldn't perform TLS static + initialization at relocation time, and we delayed it until + the relocation has been completed. */ + + if (tmp_tpnt->l_need_tls_init) { + tmp_tpnt->l_need_tls_init = 0; # ifdef SHARED - /* - * Update the slot information data for at least the - * generation of the DSO we are allocating data for. - */ - _dl_update_slotinfo (tpnt->l_tls_modid); + /* Update the slot information data for at least the + generation of the DSO we are allocating data for. */ + _dl_update_slotinfo (tmp_tpnt->l_tls_modid); # endif - _dl_init_static_tls((struct link_map *) tpnt); - - _dl_assert(tpnt->l_need_tls_init == 0); + _dl_init_static_tls((struct link_map*)tmp_tpnt); + _dl_assert (tmp_tpnt->l_need_tls_init == 0); } /* We have to bump the generation counter. */ any_tls = true; + } } /* Bump the generation number if necessary. */ @@ -581,6 +606,11 @@ void *dlsym(void *vhandle, const char *name) struct dyn_elf *rpnt; void *ret; +#if defined USE_TLS +struct elf_resolve *tls_tpnt = NULL; +#endif + + handle = (struct dyn_elf *) vhandle; /* First of all verify that we have a real handle @@ -616,26 +646,18 @@ 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((char*)name, handle, NULL, 0, &tls_tpnt); +#if defined USE_TLS && defined SHARED + if(tls_tpnt) { -#if defined(USE_TLS) && defined(SHARED) - ret = _dl_find_hash((char*)name, handle, NULL, 1); - - if (ret) - { - if(ELF_ST_TYPE(_dl_tls_reloc_sym->st_info) == STT_TLS) - { - /* - * The found symbol is a thread-local storage variable. - * Return the address for it to the current thread. - */ - ret = _dl_tls_symaddr((struct link_map *) _dl_tls_reloc_tpnt, - _dl_tls_reloc_sym); - } + /* The found symbol is a thread-local storage variable. + Return the address for to the current thread. */ + ret = _dl_tls_symaddr ((struct link_map *)tls_tpnt, (Elf32_Addr)ret); } - else -#else - ret = _dl_find_hash((char*)name, handle, NULL, 0); #endif /* @@ -819,7 +841,22 @@ static int do_dlclose(void *vhandle, int need_fini) # else # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined" # endif - } + } else { + +#define TLS_DTV_UNALLOCATED ((void *) -1l) + + dtv_t *dtv = THREAD_DTV (); + + _dl_assert(!(dtv[tls_lmap->l_tls_modid].pointer.is_static)); + if(dtv[tls_lmap->l_tls_modid].pointer.val != TLS_DTV_UNALLOCATED) { + /* Note that free is called for NULL is well. We + deallocate even if it is this dtv entry we are + supposed to load. The reason is that we call + memalign and not malloc. */ + _dl_free (dtv[tls_lmap->l_tls_modid].pointer.val); + dtv[tls_lmap->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED; + } + } } #endif @@ -867,7 +904,7 @@ static int do_dlclose(void *vhandle, int need_fini) free(tpnt->libname); free(tpnt); } - } + } /* end of for (j = 0; j < handle->init_fini.nlist; ++j) */ free(handle->init_fini.init_fini); free(handle); @@ -917,7 +954,7 @@ char *dlerror(void) } /* - * Dump information to stderrr about the current loaded modules + * Dump information to stderr about the current loaded modules */ #ifdef __USE_GNU static char *type[] = { "Lib", "Exe", "Int", "Mod" }; |
