1 /* Thread-local storage handling in the ELF dynamic linker. ARM version.
2 Copyright (C) 2006-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library. If not, see
17 <https://www.gnu.org/licenses/>. */
20 #include <arm-features.h>
22 #include <rtld-global-offsets.h>
26 @ emit debug information with cfi
27 @ use arm-specific pseudos for unwinding itself
29 .hidden _dl_tlsdesc_return
30 .global _dl_tlsdesc_return
31 .type _dl_tlsdesc_return,#function
40 .size _dl_tlsdesc_return, .-_dl_tlsdesc_return
42 .hidden _dl_tlsdesc_undefweak
43 .global _dl_tlsdesc_undefweak
44 .type _dl_tlsdesc_undefweak,#function
48 _dl_tlsdesc_undefweak:
54 .size _dl_tlsdesc_undefweak, .-_dl_tlsdesc_undefweak
57 .hidden _dl_tlsdesc_dynamic
58 .global _dl_tlsdesc_dynamic
59 .type _dl_tlsdesc_dynamic,#function
63 The assembly code that follows is a rendition of the following
64 C code, hand-optimized a little bit.
67 _dl_tlsdesc_dynamic(struct tlsdesc *tdp)
69 struct tlsdesc_dynamic_arg *td = tdp->argument.pointer;
70 dtv_t *dtv = (dtv_t *)THREAD_DTV();
71 if (__builtin_expect (td->gen_count <= dtv[0].counter
72 && dtv[td->tlsinfo.ti_module].pointer.val
73 != TLS_DTV_UNALLOCATED,
75 return dtv[td->tlsinfo.ti_module].pointer.val +
76 td->tlsinfo.ti_offset - __builtin_thread_pointer();
78 return __tls_get_addr (&td->tlsinfo) - __builtin_thread_pointer();
86 /* Our calling convention is to clobber r0, r1 and the processor
87 flags. All others that are modified must be saved. r5 is
88 used as the hwcap value to avoid reload after __tls_get_addr
89 call. If required we will save the vector register on the slow
91 eabi_save ({r2,r3,r4,r5,ip,lr})
92 push {r2,r3,r4,r5,ip,lr}
93 cfi_adjust_cfa_offset (24)
97 cfi_rel_offset (r5,12)
98 cfi_rel_offset (ip,16)
99 cfi_rel_offset (lr,20)
101 ldr r1, [r0] /* td */
103 mov r4, r0 /* r4 = tp */
105 ldr r2, [r1, #8] /* gen_count */
110 #ifndef ARM_NO_INDEX_REGISTER
111 ldr r2, [r0, r3, lsl #3]
113 add lr, r0, r3, lsl #3
124 /* Load the hwcap to check for vector support. */
126 ldr r1, .Lrtld_global_ro
129 ldr r5, [r2, #RTLD_GLOBAL_RO_DL_HWCAP_OFFSET]
132 tst r5, #HWCAP_ARM_VFP
136 /* Store the VFP registers. Don't use VFP instructions directly
137 because this code is used in non-VFP multilibs. */
138 #define VFP_STACK_REQ (32*8 + 8)
139 sub sp, sp, VFP_STACK_REQ
140 cfi_adjust_cfa_offset (VFP_STACK_REQ)
142 .inst 0xeca30b20 /* vstmia r3!, {d0-d15} */
143 tst r5, #HWCAP_ARM_VFPD32
145 .inst 0xece30b20 /* vstmia r3!, {d16-d31} */
146 /* Store the floating-point status register. */
147 4: .inst 0xeef12a10 /* vmrs r2, fpscr */
153 tst r5, #HWCAP_ARM_VFP
157 .inst 0xecb30b20 /* vldmia r3!, {d0-d15} */
158 tst r5, #HWCAP_ARM_VFPD32
160 .inst 0xecf30b20 /* vldmia r3!, {d16-d31} */
162 5: .inst 0xeee14a10 /* vmsr fpscr, r4 */
163 add sp, sp, VFP_STACK_REQ
164 cfi_adjust_cfa_offset (-VFP_STACK_REQ)
167 #if ((defined (__ARM_ARCH_4T__) && defined (__THUMB_INTERWORK__)) \
168 || defined (ARM_ALWAYS_BX))
169 pop {r2,r3,r4,r5,ip, lr}
170 cfi_adjust_cfa_offset (-20)
179 pop {r2,r3,r4,r5,ip, pc}
183 .size _dl_tlsdesc_dynamic, .-_dl_tlsdesc_dynamic
185 3: .long _GLOBAL_OFFSET_TABLE_ - 0b - PC_OFS
187 .long C_SYMBOL_NAME(_rtld_global_ro)(GOT)