/* Thread-local storage handling in the ELF dynamic linker. AArch64 Morello version. Copyright (C) 2011-2022 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" #define NSAVEDQREGPAIRS 16 #define SAVE_Q_REGISTERS \ stp q0, q1, [csp, #-32*NSAVEDQREGPAIRS]!; \ cfi_adjust_cfa_offset (32*NSAVEDQREGPAIRS); \ stp q2, q3, [csp, #32*1]; \ stp q4, q5, [csp, #32*2]; \ stp q6, q7, [csp, #32*3]; \ stp q8, q9, [csp, #32*4]; \ stp q10, q11, [csp, #32*5]; \ stp q12, q13, [csp, #32*6]; \ stp q14, q15, [csp, #32*7]; \ stp q16, q17, [csp, #32*8]; \ stp q18, q19, [csp, #32*9]; \ stp q20, q21, [csp, #32*10]; \ stp q22, q23, [csp, #32*11]; \ stp q24, q25, [csp, #32*12]; \ stp q26, q27, [csp, #32*13]; \ stp q28, q29, [csp, #32*14]; \ stp q30, q31, [csp, #32*15]; #define RESTORE_Q_REGISTERS \ ldp q2, q3, [csp, #32*1]; \ ldp q4, q5, [csp, #32*2]; \ ldp q6, q7, [csp, #32*3]; \ ldp q8, q9, [csp, #32*4]; \ ldp q10, q11, [csp, #32*5]; \ ldp q12, q13, [csp, #32*6]; \ ldp q14, q15, [csp, #32*7]; \ ldp q16, q17, [csp, #32*8]; \ ldp q18, q19, [csp, #32*9]; \ ldp q20, q21, [csp, #32*10]; \ ldp q22, q23, [csp, #32*11]; \ ldp q24, q25, [csp, #32*12]; \ ldp q26, q27, [csp, #32*13]; \ ldp q28, q29, [csp, #32*14]; \ ldp q30, q31, [csp, #32*15]; \ ldp q0, q1, [csp], #32*NSAVEDQREGPAIRS; \ cfi_adjust_cfa_offset (-32*NSAVEDQREGPAIRS); .text /* Compute the address for symbols in the static TLS block. Prototype: _dl_tlsdesc_return (tlsdesc *tdp, void *unused, void *tp); */ .hidden _dl_tlsdesc_return .global _dl_tlsdesc_return .type _dl_tlsdesc_return,%function cfi_startproc .align 2 _dl_tlsdesc_return: ldp x0, x1, [c0, #PTR_SIZE] /* Load offset, size. */ add c0, c2, x0 scbndse c0, c0, x1 RET cfi_endproc .size _dl_tlsdesc_return, .-_dl_tlsdesc_return /* Handler for undefined weak TLS symbols: returns NULL. Prototype: _dl_tlsdesc_undefweak (tlsdesc *tdp, void *unused, void *tp); */ .hidden _dl_tlsdesc_undefweak .global _dl_tlsdesc_undefweak .type _dl_tlsdesc_undefweak,%function cfi_startproc .align 2 _dl_tlsdesc_undefweak: mov x0, 0 RET cfi_endproc .size _dl_tlsdesc_undefweak, .-_dl_tlsdesc_undefweak #ifdef SHARED /* Handler for dynamic TLS symbols. Prototype: _dl_tlsdesc_dynamic (tlsdesc *tdp, void *unused, void *tp); The second word of the descriptor points to a tlsdesc_dynamic_arg structure. Returns the address of the tls object. void * _dl_tlsdesc_dynamic (struct tlsdesc *tdp, void *unused, void *tp) { struct tlsdesc_dynamic_arg *td = tdp->arg; dtv_t *dtv = *(dtv_t **)((char *)tp + 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; return ___tls_get_addr (&td->tlsinfo); } */ .hidden _dl_tlsdesc_dynamic .global _dl_tlsdesc_dynamic .type _dl_tlsdesc_dynamic,%function cfi_startproc .align 2 _dl_tlsdesc_dynamic: /* Save just enough registers to support fast path, if we fall into slow path we will save additional registers. */ stp c3, c4, [csp, #-32]! cfi_adjust_cfa_offset (32) cfi_rel_offset (c3, 0) cfi_rel_offset (c4, 16) ldr c1, [c0,#TLSDESC_ARG] ldr c0, [c2,#TCBHEAD_DTV] ldr x3, [c1,#TLSDESC_GEN_COUNT] ldr x4, [c0,#DTV_COUNTER] cmp x3, x4 b.hi 2f /* Load r3 = td->tlsinfo.ti_module and r4 = td->tlsinfo.ti_offset. */ ldp x3, x4, [c1,#TLSDESC_MODID] lsl x3, x3, #(PTR_LOG_SIZE+1) ldr c0, [c0, x3] /* Load val member of DTV entry. */ cmp x0, #TLS_DTV_UNALLOCATED b.eq 2f cfi_remember_state /* Load r3 = td->tlsinfo.ti_size. */ ldr x3, [c1, #TLSDESC_SIZE] add c0, c0, x4 scbndse c0, c0, x3 1: ldp c3, c4, [csp], #32 cfi_adjust_cfa_offset (-32) 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. */ cfi_restore_state # define NSAVEXREGPAIRS 9 stp c29, c30, [csp,#-32*NSAVEXREGPAIRS]! cfi_adjust_cfa_offset (32*NSAVEXREGPAIRS) cfi_rel_offset (c29, 0) cfi_rel_offset (c30, 16) mov c29, csp stp c5, c6, [csp, #32*1] stp c7, c8, [csp, #32*2] stp c9, c10, [csp, #32*3] stp c11, c12, [csp, #32*4] stp c13, c14, [csp, #32*5] stp c15, c16, [csp, #32*6] stp c17, c18, [csp, #32*7] cfi_rel_offset (c5, 32*1) cfi_rel_offset (c6, 32*1+8) cfi_rel_offset (c7, 32*2) cfi_rel_offset (c8, 32*2+8) cfi_rel_offset (c9, 32*3) cfi_rel_offset (c10, 32*3+8) cfi_rel_offset (c11, 32*4) cfi_rel_offset (c12, 32*4+8) cfi_rel_offset (c13, 32*5) cfi_rel_offset (c14, 32*5+8) cfi_rel_offset (c15, 32*6) cfi_rel_offset (c16, 32*6+8) cfi_rel_offset (c17, 32*7) cfi_rel_offset (c18, 32*7+8) SAVE_Q_REGISTERS mov c0, c1 bl __tls_get_addr mrs c2, ctpidr_el0 /* Restore c2. */ RESTORE_Q_REGISTERS ldp c5, c6, [csp, #32*1] ldp c7, c8, [csp, #32*2] ldp c9, c10, [csp, #32*3] ldp c11, c12, [csp, #32*4] ldp c13, c14, [csp, #32*5] ldp c15, c16, [csp, #32*6] ldp c17, c18, [csp, #32*7] ldp c29, c30, [csp], #32*NSAVEXREGPAIRS cfi_adjust_cfa_offset (-32*NSAVEXREGPAIRS) cfi_restore (c29) cfi_restore (c30) b 1b cfi_endproc .size _dl_tlsdesc_dynamic, .-_dl_tlsdesc_dynamic # undef NSAVEXREGPAIRS #endif