/* * Copyright (C) 2017 Waldemar Brodkorb * Ported from GNU C Library * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. */ /* Thread-local storage handling in the ELF dynamic linker. AArch64 version. Copyright (C) 2011-2017 Free Software Foundation, Inc. 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 #if defined __UCLIBC_HAS_TLS__ #include #include "tlsdesc.h" #define PTR_REG(n) x##n #define PTR_LOG_SIZE 3 #define PTR_SIZE (1<arg; dtv_t *dtv = *(dtv_t **)((char *)__thread_pointer + TCBHEAD_DTV); if (__builtin_expect (td->gen_count <= dtv[0].counter && (dtv[td->tlsinfo.ti_module].pointer.val != TLS_DTV_UNALLOCATED), 1)) return dtv[td->tlsinfo.ti_module].pointer.val + td->tlsinfo.ti_offset - __thread_pointer; return ___tls_get_addr (&td->tlsinfo) - __thread_pointer; } */ .hidden _dl_tlsdesc_dynamic .global _dl_tlsdesc_dynamic .type _dl_tlsdesc_dynamic,%function cfi_startproc .align 2 _dl_tlsdesc_dynamic: # define NSAVEXREGPAIRS 2 stp x29, x30, [sp,#-(32+16*NSAVEXREGPAIRS)]! cfi_adjust_cfa_offset (32+16*NSAVEXREGPAIRS) mov x29, sp /* Save just enough registers to support fast path, if we fall into slow path we will save additional registers. */ stp x1, x2, [sp, #32+16*0] stp x3, x4, [sp, #32+16*1] mrs x4, tpidr_el0 /* The ldar here happens after the load from [x0] at the call site (that is generated by the compiler as part of the TLS access ABI), so it reads the same value (this function is the final value of td->entry) and thus it synchronizes with the release store to td->entry in _dl_tlsdesc_resolve_rela_fixup ensuring that the load from [x0,#PTR_SIZE] here happens after the initialization of td->arg. */ ldar PTR_REG (zr), [x0] ldr PTR_REG (1), [x0,#TLSDESC_ARG] ldr PTR_REG (0), [x4,#TCBHEAD_DTV] ldr PTR_REG (3), [x1,#TLSDESC_GEN_COUNT] ldr PTR_REG (2), [x0,#DTV_COUNTER] cmp PTR_REG (3), PTR_REG (2) b.hi 2f ldr PTR_REG (2), [x1,#TLSDESC_MODID] add PTR_REG (0), PTR_REG (0), PTR_REG (2), lsl #(PTR_LOG_SIZE + 1) ldr PTR_REG (0), [x0] /* Load val member of DTV entry. */ cmp x0, #TLS_DTV_UNALLOCATED b.eq 2f ldr PTR_REG (1), [x1,#TLSDESC_MODOFF] add PTR_REG (0), PTR_REG (0), PTR_REG (1) sub PTR_REG (0), PTR_REG (0), PTR_REG (4) 1: ldp x1, x2, [sp, #32+16*0] ldp x3, x4, [sp, #32+16*1] ldp x29, x30, [sp], #(32+16*NSAVEXREGPAIRS) cfi_adjust_cfa_offset (-32-16*NSAVEXREGPAIRS) # undef NSAVEXREGPAIRS ret 2: /* This is the slow path. We need to call __tls_get_addr() which means we need to save and restore all the register that the callee will trash. */ /* Save the remaining registers that we must treat as caller save. */ # define NSAVEXREGPAIRS 7 stp x5, x6, [sp, #-16*NSAVEXREGPAIRS]! cfi_adjust_cfa_offset (16*NSAVEXREGPAIRS) stp x7, x8, [sp, #16*1] stp x9, x10, [sp, #16*2] stp x11, x12, [sp, #16*3] stp x13, x14, [sp, #16*4] stp x15, x16, [sp, #16*5] stp x17, x18, [sp, #16*6] SAVE_Q_REGISTERS mov x0, x1 bl __tls_get_addr mrs x1, tpidr_el0 sub PTR_REG (0), PTR_REG (0), PTR_REG (1) RESTORE_Q_REGISTERS ldp x7, x8, [sp, #16*1] ldp x9, x10, [sp, #16*2] ldp x11, x12, [sp, #16*3] ldp x13, x14, [sp, #16*4] ldp x15, x16, [sp, #16*5] ldp x17, x18, [sp, #16*6] ldp x5, x6, [sp], #16*NSAVEXREGPAIRS cfi_adjust_cfa_offset (-16*NSAVEXREGPAIRS) b 1b cfi_endproc .size _dl_tlsdesc_dynamic, .-_dl_tlsdesc_dynamic # undef NSAVEXREGPAIRS #endif // SHARED #endif // __UCLIBC_HAS_TLS__