From 4c3023bc803012656cf45749960282351efc8020 Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Sat, 20 Sep 2014 22:36:23 +0200 Subject: xtensa: add support for NPTL Changes from: https://github.com/foss-xtensa/uClibc/commits/xtensa_nptl Author: Chris Zankel Author: Baruch Siach --- ldso/ldso/dl-tls.c | 29 +++-- ldso/ldso/fdpic/dl-inlines.h | 270 ++---------------------------------------- ldso/ldso/xtensa/dl-debug.h | 77 +++++------- ldso/ldso/xtensa/dl-startup.h | 2 +- ldso/ldso/xtensa/dl-sysdep.h | 9 +- ldso/ldso/xtensa/dl-tlsdesc.S | 96 +++++++++++++++ ldso/ldso/xtensa/elfinterp.c | 51 +++++++- 7 files changed, 208 insertions(+), 326 deletions(-) create mode 100644 ldso/ldso/xtensa/dl-tlsdesc.S (limited to 'ldso/ldso') diff --git a/ldso/ldso/dl-tls.c b/ldso/ldso/dl-tls.c index 6679693f4..5d6d3b9d3 100644 --- a/ldso/ldso/dl-tls.c +++ b/ldso/ldso/dl-tls.c @@ -100,20 +100,16 @@ _dl_realloc (void * __ptr, size_t __size) * the static TLS area already allocated for each running thread. If this * object's TLS segment is too big to fit, we fail. If it fits, * we set MAP->l_tls_offset and return. - * This function intentionally does not return any value but signals error - * directly, as static TLS should be rare and code handling it should - * not be inlined as much as possible. */ -void -internal_function __attribute_noinline__ -_dl_allocate_static_tls (struct link_map *map) +int +internal_function +_dl_try_allocate_static_tls (struct link_map* map) { /* If the alignment requirements are too high fail. */ if (map->l_tls_align > _dl_tls_static_align) { fail: - _dl_dprintf(2, "cannot allocate memory in static TLS block"); - _dl_exit(30); + return -1; } # ifdef TLS_TCB_AT_TP @@ -169,6 +165,23 @@ fail: } else map->l_need_tls_init = 1; + + return 0; +} + +/* + * This function intentionally does not return any value but signals error + * directly, as static TLS should be rare and code handling it should + * not be inlined as much as possible. + */ +void +internal_function __attribute_noinline__ +_dl_allocate_static_tls (struct link_map *map) +{ + if (_dl_try_allocate_static_tls (map)) { + _dl_dprintf(2, "cannot allocate memory in static TLS block"); + _dl_exit(30); + } } #ifdef SHARED diff --git a/ldso/ldso/fdpic/dl-inlines.h b/ldso/ldso/fdpic/dl-inlines.h index 14a491689..ebbd0334c 100644 --- a/ldso/ldso/fdpic/dl-inlines.h +++ b/ldso/ldso/fdpic/dl-inlines.h @@ -5,6 +5,8 @@ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. */ +#include + /* Initialize a DL_LOADADDR_TYPE given a got pointer and a complete load map. */ static __always_inline void __dl_init_loadaddr_map(struct elf32_fdpic_loadaddr *loadaddr, Elf32_Addr dl_boot_got_pointer, @@ -143,269 +145,18 @@ __dl_addr_in_loadaddr(void *p, struct elf32_fdpic_loadaddr loadaddr) return 0; } -/* - * The hashcode handling code below is heavily inspired in libiberty's - * hashtab code, but with most adaptation points and support for - * deleting elements removed. - * - * Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. - * Contributed by Vladimir Makarov (vmakarov@cygnus.com). - */ -static __always_inline unsigned long -higher_prime_number(unsigned long n) -{ - /* These are primes that are near, but slightly smaller than, a power of two. */ - static const unsigned long primes[] = { - 7, - 13, - 31, - 61, - 127, - 251, - 509, - 1021, - 2039, - 4093, - 8191, - 16381, - 32749, - 65521, - 131071, - 262139, - 524287, - 1048573, - 2097143, - 4194301, - 8388593, - 16777213, - 33554393, - 67108859, - 134217689, - 268435399, - 536870909, - 1073741789, - /* 4294967291 */ - ((unsigned long) 2147483647) + ((unsigned long) 2147483644), - }; - const unsigned long *low = &primes[0]; - const unsigned long *high = &primes[ARRAY_SIZE(primes)]; - - while (low != high) { - const unsigned long *mid = low + (high - low) / 2; - if (n > *mid) - low = mid + 1; - else - high = mid; - } - -#if 0 - /* If we've run out of primes, abort. */ - if (n > *low) { - fprintf(stderr, "Cannot find prime bigger than %lu\n", n); - abort(); - } -#endif - - return *low; -} - -struct funcdesc_ht -{ - /* Table itself */ - struct funcdesc_value **entries; - - /* Current size (in entries) of the hash table */ - size_t size; - - /* Current number of elements */ - size_t n_elements; -}; - -static __always_inline int -hash_pointer(const void *p) +static int +hash_pointer(void *p) { return (int) ((long)p >> 3); } -static __always_inline struct funcdesc_ht * -htab_create(void) -{ - struct funcdesc_ht *ht = _dl_malloc(sizeof(*ht)); - size_t ent_size; - - if (!ht) - return NULL; - ht->size = 3; - ent_size = sizeof(struct funcdesc_ht_value *) * ht->size; - ht->entries = _dl_malloc(ent_size); - if (!ht->entries) - return NULL; - - ht->n_elements = 0; - _dl_memset(ht->entries, 0, ent_size); - - return ht; -} - -/* - * This is only called from _dl_loadaddr_unmap, so it's safe to call - * _dl_free(). See the discussion below. - */ -static __always_inline void -htab_delete(struct funcdesc_ht *htab) +static int +eq_pointer(void *p, void *q) { - size_t i; - - for (i = htab->size - 1; i >= 0; i--) - if (htab->entries[i]) - _dl_free(htab->entries[i]); - - _dl_free(htab->entries); - _dl_free(htab); -} - -/* - * Similar to htab_find_slot, but without several unwanted side effects: - * - Does not call htab->eq_f when it finds an existing entry. - * - Does not change the count of elements/searches/collisions in the - * hash table. - * This function also assumes there are no deleted entries in the table. - * HASH is the hash value for the element to be inserted. - */ -static __always_inline struct funcdesc_value ** -find_empty_slot_for_expand(struct funcdesc_ht *htab, int hash) -{ - size_t size = htab->size; - unsigned int index = hash % size; - struct funcdesc_value **slot = htab->entries + index; - int hash2; - - if (!*slot) - return slot; - - hash2 = 1 + hash % (size - 2); - for (;;) { - index += hash2; - if (index >= size) - index -= size; - - slot = htab->entries + index; - if (!*slot) - return slot; - } -} - -/* - * The following function changes size of memory allocated for the - * entries and repeatedly inserts the table elements. The occupancy - * of the table after the call will be about 50%. Naturally the hash - * table must already exist. Remember also that the place of the - * table entries is changed. If memory allocation failures are allowed, - * this function will return zero, indicating that the table could not be - * expanded. If all goes well, it will return a non-zero value. - */ -static __always_inline int -htab_expand(struct funcdesc_ht *htab) -{ - struct funcdesc_value **oentries; - struct funcdesc_value **olimit; - struct funcdesc_value **p; - struct funcdesc_value **nentries; - size_t nsize; - - oentries = htab->entries; - olimit = oentries + htab->size; - - /* - * Resize only when table after removal of unused elements is either - * too full or too empty. - */ - if (htab->n_elements * 2 > htab->size) - nsize = higher_prime_number(htab->n_elements * 2); - else - nsize = htab->size; - - nentries = _dl_malloc(sizeof(*nentries) * nsize); - _dl_memset(nentries, 0, sizeof(*nentries) * nsize); - if (nentries == NULL) - return 0; - htab->entries = nentries; - htab->size = nsize; - - p = oentries; - do { - if (*p) - *find_empty_slot_for_expand(htab, hash_pointer((*p)->entry_point)) = *p; - p++; - } while (p < olimit); - -#if 0 - /* - * We can't tell whether this was allocated by the _dl_malloc() - * built into ld.so or malloc() in the main executable or libc, - * and calling free() for something that wasn't malloc()ed could - * do Very Bad Things (TM). Take the conservative approach - * here, potentially wasting as much memory as actually used by - * the hash table, even if multiple growths occur. That's not - * so bad as to require some overengineered solution that would - * enable us to keep track of how it was allocated. - */ - _dl_free(oentries); -#endif - return 1; -} - -/* - * This function searches for a hash table slot containing an entry - * equal to the given element. To delete an entry, call this with - * INSERT = 0, then call htab_clear_slot on the slot returned (possibly - * after doing some checks). To insert an entry, call this with - * INSERT = 1, then write the value you want into the returned slot. - * When inserting an entry, NULL may be returned if memory allocation - * fails. - */ -static __always_inline struct funcdesc_value ** -htab_find_slot(struct funcdesc_ht *htab, void *ptr, int insert) -{ - unsigned int index; - int hash, hash2; - size_t size; - struct funcdesc_value **entry; - - if (htab->size * 3 <= htab->n_elements * 4 && - htab_expand(htab) == 0) - return NULL; - - hash = hash_pointer(ptr); - - size = htab->size; - index = hash % size; - - entry = &htab->entries[index]; - if (!*entry) - goto empty_entry; - else if ((*entry)->entry_point == ptr) - return entry; - - hash2 = 1 + hash % (size - 2); - for (;;) { - index += hash2; - if (index >= size) - index -= size; - - entry = &htab->entries[index]; - if (!*entry) - goto empty_entry; - else if ((*entry)->entry_point == ptr) - return entry; - } - - empty_entry: - if (!insert) - return NULL; + struct funcdesc_value *entry = p; - htab->n_elements++; - return entry; + return entry->entry_point == q; } void * @@ -424,7 +175,7 @@ _dl_funcdesc_for (void *entry_point, void *got_value) tpnt->funcdesc_ht = ht; } - entry = htab_find_slot(ht, entry_point, 1); + entry = htab_find_slot(ht, entry_point, 1, hash_pointer, eq_pointer); if (*entry) { _dl_assert((*entry)->entry_point == entry_point); return _dl_stabilize_funcdesc(*entry); @@ -459,7 +210,8 @@ _dl_lookup_address(void const *address) if (fd->got_value != rpnt->loadaddr.got_value) continue; - address = htab_find_slot(rpnt->funcdesc_ht, (void *)fd->entry_point, 0); + address = htab_find_slot(rpnt->funcdesc_ht, (void *)fd->entry_point, 0, + hash_pointer, eq_pointer); if (address && *(struct funcdesc_value *const*)address == fd) { address = (*(struct funcdesc_value *const*)address)->entry_point; diff --git a/ldso/ldso/xtensa/dl-debug.h b/ldso/ldso/xtensa/dl-debug.h index 4128d9452..18beae5ca 100644 --- a/ldso/ldso/xtensa/dl-debug.h +++ b/ldso/ldso/xtensa/dl-debug.h @@ -8,54 +8,31 @@ static const char * const _dl_reltypes_tab[] = { - "R_XTENSA_NONE", - "R_XTENSA_32", - "R_XTENSA_RTLD", - "R_XTENSA_GLOB_DAT", - "R_XTENSA_JMP_SLOT", - "R_XTENSA_RELATIVE", - "R_XTENSA_PLT", - "R_XTENSA_UNUSED7", - "R_XTENSA_OP0", - "R_XTENSA_OP1", - "R_XTENSA_OP2", - "R_XTENSA_ASM_EXPAND", - "R_XTENSA_ASM_SIMPLIFY", - "R_XTENSA_UNUSED13", - "R_XTENSA_UNUSED14", - "R_XTENSA_GNU_VTINHERIT", - "R_XTENSA_GNU_VTENTRY", - "R_XTENSA_DIFF8", - "R_XTENSA_DIFF16", - "R_XTENSA_DIFF32", - "R_XTENSA_SLOT0_OP", - "R_XTENSA_SLOT1_OP", - "R_XTENSA_SLOT2_OP", - "R_XTENSA_SLOT3_OP", - "R_XTENSA_SLOT4_OP", - "R_XTENSA_SLOT5_OP", - "R_XTENSA_SLOT6_OP", - "R_XTENSA_SLOT7_OP", - "R_XTENSA_SLOT8_OP", - "R_XTENSA_SLOT9_OP", - "R_XTENSA_SLOT10_OP", - "R_XTENSA_SLOT11_OP", - "R_XTENSA_SLOT12_OP", - "R_XTENSA_SLOT13_OP", - "R_XTENSA_SLOT14_OP", - "R_XTENSA_SLOT0_ALT", - "R_XTENSA_SLOT1_ALT", - "R_XTENSA_SLOT2_ALT", - "R_XTENSA_SLOT3_ALT", - "R_XTENSA_SLOT4_ALT", - "R_XTENSA_SLOT5_ALT", - "R_XTENSA_SLOT6_ALT", - "R_XTENSA_SLOT7_ALT", - "R_XTENSA_SLOT8_ALT", - "R_XTENSA_SLOT9_ALT", - "R_XTENSA_SLOT10_ALT", - "R_XTENSA_SLOT11_ALT", - "R_XTENSA_SLOT12_ALT", - "R_XTENSA_SLOT13_ALT", - "R_XTENSA_SLOT14_ALT" + [0] "R_XTENSA_NONE", "R_XTENSA_32", + [2] "R_XTENSA_RTLD", "R_XTENSA_GLOB_DAT", + [4] "R_XTENSA_JMP_SLOT", "R_XTENSA_RELATIVE", + [6] "R_XTENSA_PLT", "R_XTENSA_UNUSED7", + [8] "R_XTENSA_OP0", "R_XTENSA_OP1", + [10] "R_XTENSA_OP2", "R_XTENSA_ASM_EXPAND", + [12] "R_XTENSA_ASM_SIMPLIFY", "R_XTENSA_UNUSED13", + [14] "R_XTENSA_UNUSED14", "R_XTENSA_GNU_VTINHERIT", + [16] "R_XTENSA_GNU_VTENTRY", "R_XTENSA_DIFF8", + [18] "R_XTENSA_DIFF16", "R_XTENSA_DIFF32", + [20] "R_XTENSA_SLOT0_OP", "R_XTENSA_SLOT1_OP", + [22] "R_XTENSA_SLOT2_OP", "R_XTENSA_SLOT3_OP", + [24] "R_XTENSA_SLOT4_OP", "R_XTENSA_SLOT5_OP", + [26] "R_XTENSA_SLOT6_OP", "R_XTENSA_SLOT7_OP", + [28] "R_XTENSA_SLOT8_OP", "R_XTENSA_SLOT9_OP", + [30] "R_XTENSA_SLOT10_OP", "R_XTENSA_SLOT11_OP", + [32] "R_XTENSA_SLOT12_OP", "R_XTENSA_SLOT13_OP", + [34] "R_XTENSA_SLOT14_OP", "R_XTENSA_SLOT0_ALT", + [36] "R_XTENSA_SLOT1_ALT", "R_XTENSA_SLOT2_ALT", + [38] "R_XTENSA_SLOT3_ALT", "R_XTENSA_SLOT4_ALT", + [40] "R_XTENSA_SLOT5_ALT", "R_XTENSA_SLOT6_ALT", + [42] "R_XTENSA_SLOT7_ALT", "R_XTENSA_SLOT8_ALT", + [44] "R_XTENSA_SLOT9_ALT", "R_XTENSA_SLOT10_ALT", + [46] "R_XTENSA_SLOT11_ALT", "R_XTENSA_SLOT12_ALT", + [48] "R_XTENSA_SLOT13_ALT", "R_XTENSA_SLOT14_ALT", + [50] "R_XTENSA_TLSDESC_FN", "R_XTENSA_TLSDESC_ARG", + [52] "R_XTENSA_TLS_TPOFF" }; diff --git a/ldso/ldso/xtensa/dl-startup.h b/ldso/ldso/xtensa/dl-startup.h index b135a4cb8..70a6255d9 100644 --- a/ldso/ldso/xtensa/dl-startup.h +++ b/ldso/ldso/xtensa/dl-startup.h @@ -11,7 +11,7 @@ __asm__ ( " .text\n" " .align 4\n" - " .literal_position\n" + " .literal_position\n" " .global _start\n" " .type _start, @function\n" " .hidden _start\n" diff --git a/ldso/ldso/xtensa/dl-sysdep.h b/ldso/ldso/xtensa/dl-sysdep.h index a0ed4e5f2..148de5b95 100644 --- a/ldso/ldso/xtensa/dl-sysdep.h +++ b/ldso/ldso/xtensa/dl-sysdep.h @@ -78,10 +78,13 @@ typedef struct xtensa_got_location_struct { struct elf_resolve; extern unsigned long _dl_linux_resolver (struct elf_resolve *, int); -/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so - undefined references should not be allowed to define the value. */ +/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or + TLS variable, so undefined references should not be allowed to define + the value. */ #define elf_machine_type_class(type) \ - (((type) == R_XTENSA_JMP_SLOT) * ELF_RTYPE_CLASS_PLT) + (((type) == R_XTENSA_JMP_SLOT || (type) == R_XTENSA_TLS_TPOFF \ + || (type) == R_XTENSA_TLSDESC_FN || (type) == R_XTENSA_TLSDESC_ARG) \ + * ELF_RTYPE_CLASS_PLT) /* Return the link-time address of _DYNAMIC. */ static __always_inline Elf32_Addr diff --git a/ldso/ldso/xtensa/dl-tlsdesc.S b/ldso/ldso/xtensa/dl-tlsdesc.S new file mode 100644 index 000000000..a6ebc949e --- /dev/null +++ b/ldso/ldso/xtensa/dl-tlsdesc.S @@ -0,0 +1,96 @@ +/* Thread-local storage handling in the ELF dynamic linker. Xtensa version. + Copyright (C) 2012-2013 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library. If not, see + . */ + +#include +#include +#include "tlsdesc.h" + + + .text + .align 4 + .hidden _dl_tlsdesc_return + .global _dl_tlsdesc_return + .type _dl_tlsdesc_return, @function +_dl_tlsdesc_return: + entry a1, 16 + rur.threadptr a3 + add a2, a2, a3 + retw + .size _dl_tlsdesc_return, .-_dl_tlsdesc_return + +#ifdef SHARED + + + /* This function is used for symbols that need dynamic TLS. + + The argument passed to this function points to the TLS descriptor. + + The assembly code that follows is a rendition of the following + C code, hand-optimized a little bit. + + ptrdiff_t + _dl_tlsdesc_dynamic(struct tlsdesc_dynamic_arg *td) + { + dtv_t *dtv = (dtv_t *)THREAD_DTV(); + if (td->gen_count <= dtv[0].counter + && dtv[td->tlsinfo.ti_module].pointer.val + != TLS_DTV_UNALLOCATED) + return dtv[td->tlsinfo.ti_module].pointer.val + + td->tlsinfo.ti_offset - __builtin_thread_pointer(); + return __tls_get_addr (&td->tlsinfo) - __builtin_thread_pointer(); + } + */ + + .align 4 + .hidden _dl_tlsdesc_dynamic + .global _dl_tlsdesc_dynamic + .type _dl_tlsdesc_dynamic, @function +_dl_tlsdesc_dynamic: + entry a1, 32 + + /* dtv_t *dtv = (dtv_t *)THREAD_DTV(); */ + rur.threadptr a3 + l32i a4, a3, 0 + + /* if (td->gen_count <= dtv[0].counter */ + l32i a6, a2, TLSDESC_GEN_COUNT + l32i a7, a4, 0 + blt a7, a6, .Lslow + + /* && dtv[td->tlsinfo.ti_module].pointer.val != TLS_DTV_UNALLOCATED) */ + l32i a6, a2, TLSDESC_MODID + addx8 a6, a3, a6 + l32i a6, a6, 0 + beqi a6, -1, .Lslow + + /* return dtv[td->tlsinfo.ti_module].pointer.val + + td->tlsinfo.ti_offset - __builtin_thread_pointer(); */ + l32i a6, a2, TLSDESC_MODOFF + sub a2, a6, a3 + retw + + /* return __tls_get_addr (&td->tlsinfo) - __builtin_thread_pointer(); */ +.Lslow: + mov a10, a2 + movi a8, __tls_get_addr + callx8 a8 + sub a2, a10, a3 + retw + .size _dl_tlsdesc_dynamic, .-_dl_tlsdesc_dynamic + +#endif /* SHARED */ diff --git a/ldso/ldso/xtensa/elfinterp.c b/ldso/ldso/xtensa/elfinterp.c index b4cf9752d..1397e95c9 100644 --- a/ldso/ldso/xtensa/elfinterp.c +++ b/ldso/ldso/xtensa/elfinterp.c @@ -31,6 +31,8 @@ */ #include "ldso.h" +#include "dl-tls.h" +#include "tlsdeschtab.h" unsigned long _dl_linux_resolver (struct elf_resolve *tpnt, int reloc_entry) @@ -146,6 +148,9 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope, int reloc_type; int symtab_index; char *symname; +#if defined USE_TLS && USE_TLS + struct elf_resolve *tls_tpnt = NULL; +#endif struct symbol_ref sym_ref; ElfW(Addr) *reloc_addr; ElfW(Addr) symbol_addr; @@ -172,15 +177,22 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope, * here, so all bases should be covered. */ if (unlikely (!symbol_addr && + ELF_ST_TYPE (sym_ref.sym->st_info) != STT_TLS && ELF_ST_BIND (sym_ref.sym->st_info) != STB_WEAK)) { - _dl_dprintf (2, "%s: can't resolve symbol '%s'\n", - _dl_progname, symname); - _dl_exit (1); + return 1; } if (_dl_trace_prelink) { _dl_debug_lookup (symname, tpnt, &symtab[symtab_index], &sym_ref, elf_machine_type_class(reloc_type)); } +#if defined USE_TLS && USE_TLS + tls_tpnt = sym_ref.tpnt; +#endif + } else { + symbol_addr =symtab[symtab_index].st_value; +#if defined USE_TLS && USE_TLS + tls_tpnt = tpnt; +#endif } #if defined (__SUPPORT_LD_DEBUG__) @@ -198,8 +210,8 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope, case R_XTENSA_RTLD: if (rpnt->r_addend == 1) { - /* Grab the function pointer stashed at the beginning of the - GOT by the GOT_INIT function. */ + /* Grab the function pointer stashed at the beginning + of the GOT by the GOT_INIT function. */ *reloc_addr = *(ElfW(Addr) *) tpnt->dynamic_info[DT_PLTGOT]; } else if (rpnt->r_addend == 2) { /* Store the link map for the object. */ @@ -213,6 +225,35 @@ _dl_do_reloc (struct elf_resolve *tpnt, struct r_scope_elem *scope, *reloc_addr += tpnt->loadaddr + rpnt->r_addend; break; +#if defined USE_TLS && USE_TLS + case R_XTENSA_TLS_TPOFF: + CHECK_STATIC_TLS((struct link_map *) tls_tpnt); + *reloc_addr = symbol_addr + tls_tpnt->l_tls_offset + rpnt->r_addend; + break; + case R_XTENSA_TLSDESC_FN: +#ifndef SHARED + CHECK_STATIC_TLS((struct link_map *) tls_tpnt); +#else + if (!TRY_STATIC_TLS ((struct link_map *) tls_tpnt)) + *reloc_addr = (ElfW(Addr)) _dl_tlsdesc_dynamic; + else +#endif + *reloc_addr = (ElfW(Addr)) _dl_tlsdesc_return; + break; + case R_XTENSA_TLSDESC_ARG: +#ifndef SHARED + CHECK_STATIC_TLS((struct link_map *) tls_tpnt); +#else + if (!TRY_STATIC_TLS ((struct link_map *) tls_tpnt)) + *reloc_addr = (ElfW(Addr)) + _dl_make_tlsdesc_dynamic((struct link_map *) tls_tpnt, + symbol_addr + *reloc_addr); + else +#endif + *reloc_addr += symbol_addr + tls_tpnt->l_tls_offset; + break; +#endif + default: return -1; /* Calls _dl_exit(1). */ } -- cgit v1.2.3