]>
Commit | Line | Data |
---|---|---|
554066b8 MS |
1 | /* Thread-local storage handling in the ELF dynamic linker. |
2 | AArch64 version. | |
d4697bc9 | 3 | Copyright (C) 2011-2014 Free Software Foundation, Inc. |
554066b8 MS |
4 | |
5 | This file is part of the GNU C Library. | |
6 | ||
7 | The GNU C Library is free software; you can redistribute it and/or | |
8 | modify it under the terms of the GNU Lesser General Public | |
9 | License as published by the Free Software Foundation; either | |
10 | version 2.1 of the License, or (at your option) any later version. | |
11 | ||
12 | The GNU C Library is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Lesser General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Lesser General Public | |
18 | License along with the GNU C Library; if not, see | |
19 | <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | #include <sysdep.h> | |
22 | #include <tls.h> | |
23 | #include "tlsdesc.h" | |
24 | ||
25 | #define NSAVEDQREGPAIRS 16 | |
26 | #define SAVE_Q_REGISTERS \ | |
27 | stp q0, q1, [sp, #-32*NSAVEDQREGPAIRS]!; \ | |
28 | cfi_adjust_cfa_offset (32*NSAVEDQREGPAIRS); \ | |
29 | stp q2, q3, [sp, #32*1]; \ | |
30 | stp q4, q5, [sp, #32*2]; \ | |
31 | stp q6, q7, [sp, #32*3]; \ | |
32 | stp q8, q9, [sp, #32*4]; \ | |
33 | stp q10, q11, [sp, #32*5]; \ | |
34 | stp q12, q13, [sp, #32*6]; \ | |
35 | stp q14, q15, [sp, #32*7]; \ | |
36 | stp q16, q17, [sp, #32*8]; \ | |
37 | stp q18, q19, [sp, #32*9]; \ | |
38 | stp q20, q21, [sp, #32*10]; \ | |
39 | stp q22, q23, [sp, #32*11]; \ | |
40 | stp q24, q25, [sp, #32*12]; \ | |
41 | stp q26, q27, [sp, #32*13]; \ | |
42 | stp q28, q29, [sp, #32*14]; \ | |
43 | stp q30, q31, [sp, #32*15]; | |
44 | ||
45 | #define RESTORE_Q_REGISTERS \ | |
46 | ldp q2, q3, [sp, #32*1]; \ | |
47 | ldp q4, q5, [sp, #32*2]; \ | |
48 | ldp q6, q7, [sp, #32*3]; \ | |
49 | ldp q8, q9, [sp, #32*4]; \ | |
50 | ldp q10, q11, [sp, #32*5]; \ | |
51 | ldp q12, q13, [sp, #32*6]; \ | |
52 | ldp q14, q15, [sp, #32*7]; \ | |
53 | ldp q16, q17, [sp, #32*8]; \ | |
54 | ldp q18, q19, [sp, #32*9]; \ | |
55 | ldp q20, q21, [sp, #32*10]; \ | |
56 | ldp q22, q23, [sp, #32*11]; \ | |
57 | ldp q24, q25, [sp, #32*12]; \ | |
58 | ldp q26, q27, [sp, #32*13]; \ | |
59 | ldp q28, q29, [sp, #32*14]; \ | |
60 | ldp q30, q31, [sp, #32*15]; \ | |
61 | ldp q0, q1, [sp], #32*NSAVEDQREGPAIRS; \ | |
62 | cfi_adjust_cfa_offset (-32*NSAVEDQREGPAIRS); | |
63 | ||
64 | .text | |
65 | ||
66 | /* Compute the thread pointer offset for symbols in the static | |
67 | TLS block. The offset is the same for all threads. | |
68 | Prototype: | |
69 | _dl_tlsdesc_return (tlsdesc *) ; | |
70 | */ | |
71 | .hidden _dl_tlsdesc_return | |
72 | .global _dl_tlsdesc_return | |
73 | .type _dl_tlsdesc_return,%function | |
74 | cfi_startproc | |
75 | .align 2 | |
76 | _dl_tlsdesc_return: | |
77 | ldr x0, [x0, #8] | |
78 | RET | |
79 | cfi_endproc | |
80 | .size _dl_tlsdesc_return, .-_dl_tlsdesc_return | |
81 | ||
82 | /* Handler for undefined weak TLS symbols. | |
83 | Prototype: | |
84 | _dl_tlsdesc_undefweak (tlsdesc *); | |
85 | ||
86 | The second word of the descriptor contains the addend. | |
87 | Return the addend minus the thread pointer. This ensures | |
88 | that when the caller adds on the thread pointer it gets back | |
89 | the addend. */ | |
90 | ||
91 | .hidden _dl_tlsdesc_undefweak | |
92 | .global _dl_tlsdesc_undefweak | |
93 | .type _dl_tlsdesc_undefweak,%function | |
94 | cfi_startproc | |
95 | .align 2 | |
96 | _dl_tlsdesc_undefweak: | |
97 | str x1, [sp, #-16]! | |
98 | cfi_adjust_cfa_offset(16) | |
99 | ldr x0, [x0, #8] | |
100 | mrs x1, tpidr_el0 | |
101 | sub x0, x0, x1 | |
102 | ldr x1, [sp], #16 | |
103 | cfi_adjust_cfa_offset(16) | |
104 | RET | |
105 | cfi_endproc | |
106 | .size _dl_tlsdesc_undefweak, .-_dl_tlsdesc_undefweak | |
107 | ||
108 | #ifdef SHARED | |
109 | /* Handler for dynamic TLS symbols. | |
110 | Prototype: | |
111 | _dl_tlsdesc_dynamic (tlsdesc *) ; | |
112 | ||
113 | The second word of the descriptor points to a | |
114 | tlsdesc_dynamic_arg structure. | |
115 | ||
116 | Returns the offset between the thread pointer and the | |
117 | object referenced by the argument. | |
118 | ||
119 | ptrdiff_t | |
120 | __attribute__ ((__regparm__ (1))) | |
121 | _dl_tlsdesc_dynamic (struct tlsdesc *tdp) | |
122 | { | |
123 | struct tlsdesc_dynamic_arg *td = tdp->arg; | |
124 | dtv_t *dtv = *(dtv_t **)((char *)__thread_pointer + DTV_OFFSET); | |
125 | if (__builtin_expect (td->gen_count <= dtv[0].counter | |
126 | && (dtv[td->tlsinfo.ti_module].pointer.val | |
127 | != TLS_DTV_UNALLOCATED), | |
128 | 1)) | |
129 | return dtv[td->tlsinfo.ti_module].pointer.val | |
130 | + td->tlsinfo.ti_offset | |
131 | - __thread_pointer; | |
132 | ||
133 | return ___tls_get_addr (&td->tlsinfo) - __thread_pointer; | |
134 | } | |
135 | */ | |
136 | ||
137 | .hidden _dl_tlsdesc_dynamic | |
138 | .global _dl_tlsdesc_dynamic | |
139 | .type _dl_tlsdesc_dynamic,%function | |
140 | cfi_startproc | |
141 | .align 2 | |
142 | _dl_tlsdesc_dynamic: | |
143 | # define NSAVEXREGPAIRS 2 | |
144 | stp x29, x30, [sp,#-(32+16*NSAVEXREGPAIRS)]! | |
145 | cfi_adjust_cfa_offset (32+16*NSAVEXREGPAIRS) | |
146 | mov x29, sp | |
147 | ||
148 | /* Save just enough registers to support fast path, if we fall | |
149 | into slow path we will save additional registers. */ | |
150 | ||
151 | stp x1, x2, [sp, #32+16*0] | |
152 | stp x3, x4, [sp, #32+16*1] | |
153 | ||
154 | mrs x4, tpidr_el0 | |
155 | ldr x1, [x0,#8] | |
156 | ldr x0, [x4] | |
157 | ldr x3, [x1,#16] | |
158 | ldr x2, [x0] | |
159 | cmp x3, x2 | |
160 | b.hi 2f | |
161 | ldr x2, [x1] | |
162 | add x0, x0, x2, lsl #4 | |
163 | ldr x0, [x0] | |
164 | cmn x0, #0x1 | |
165 | b.eq 2f | |
166 | ldr x1, [x1,#8] | |
167 | add x0, x0, x1 | |
168 | sub x0, x0, x4 | |
169 | 1: | |
170 | ldp x1, x2, [sp, #32+16*0] | |
171 | ldp x3, x4, [sp, #32+16*1] | |
172 | ||
173 | ldp x29, x30, [sp], #(32+16*NSAVEXREGPAIRS) | |
174 | cfi_adjust_cfa_offset (32+16*NSAVEXREGPAIRS) | |
175 | # undef NSAVEXREGPAIRS | |
176 | RET | |
177 | 2: | |
178 | /* This is the slow path. We need to call __tls_get_addr() which | |
179 | means we need to save and restore all the register that the | |
180 | callee will trash. */ | |
181 | ||
182 | /* Save the remaining registers that we must treat as caller save. */ | |
183 | # define NSAVEXREGPAIRS 7 | |
184 | stp x5, x6, [sp, #-16*NSAVEXREGPAIRS]! | |
185 | cfi_adjust_cfa_offset (16*NSAVEXREGPAIRS) | |
186 | stp x7, x8, [sp, #16*1] | |
187 | stp x9, x10, [sp, #16*2] | |
188 | stp x11, x12, [sp, #16*3] | |
189 | stp x13, x14, [sp, #16*4] | |
190 | stp x15, x16, [sp, #16*5] | |
191 | stp x17, x18, [sp, #16*6] | |
192 | ||
193 | SAVE_Q_REGISTERS | |
194 | ||
195 | mov x0, x1 | |
196 | bl __tls_get_addr | |
197 | ||
198 | mrs x1, tpidr_el0 | |
199 | sub x0, x0, x1 | |
200 | ||
201 | RESTORE_Q_REGISTERS | |
202 | ||
203 | ldp x7, x8, [sp, #16*1] | |
204 | ldp x9, x10, [sp, #16*2] | |
205 | ldp x11, x12, [sp, #16*3] | |
206 | ldp x13, x14, [sp, #16*4] | |
207 | ldp x15, x16, [sp, #16*5] | |
208 | ldp x17, x18, [sp, #16*6] | |
209 | ldp x5, x6, [sp], #16*NSAVEXREGPAIRS | |
210 | cfi_adjust_cfa_offset (-16*NSAVEXREGPAIRS) | |
211 | b 1b | |
212 | cfi_endproc | |
213 | .size _dl_tlsdesc_dynamic, .-_dl_tlsdesc_dynamic | |
214 | # undef NSAVEXREGPAIRS | |
215 | #endif | |
216 | ||
217 | /* This function is a wrapper for a lazy resolver for TLS_DESC | |
218 | RELA relocations. | |
219 | When the actual resolver returns, it will have adjusted the | |
220 | TLS descriptor such that we can tail-call it for it to return | |
221 | the TP offset of the symbol. */ | |
222 | ||
223 | .hidden _dl_tlsdesc_resolve_rela | |
224 | .global _dl_tlsdesc_resolve_rela | |
225 | .type _dl_tlsdesc_resolve_rela,%function | |
226 | cfi_startproc | |
227 | .align 2 | |
228 | _dl_tlsdesc_resolve_rela: | |
229 | #define NSAVEXREGPAIRS 9 | |
230 | stp x29, x30, [sp, #-(32+16*NSAVEXREGPAIRS)]! | |
231 | cfi_adjust_cfa_offset (32+16*NSAVEXREGPAIRS) | |
232 | mov x29, sp | |
233 | stp x1, x4, [sp, #32+16*0] | |
234 | stp x5, x6, [sp, #32+16*1] | |
235 | stp x7, x8, [sp, #32+16*2] | |
236 | stp x9, x10, [sp, #32+16*3] | |
237 | stp x11, x12, [sp, #32+16*4] | |
238 | stp x13, x14, [sp, #32+16*5] | |
239 | stp x15, x16, [sp, #32+16*6] | |
240 | stp x17, x18, [sp, #32+16*7] | |
241 | str x0, [sp, #32+16*8] | |
242 | ||
243 | SAVE_Q_REGISTERS | |
244 | ||
245 | ldr x1, [x3, #8] | |
246 | bl _dl_tlsdesc_resolve_rela_fixup | |
247 | ||
248 | RESTORE_Q_REGISTERS | |
249 | ||
250 | ldr x0, [sp, #32+16*8] | |
251 | ldr x1, [x0] | |
252 | blr x1 | |
253 | ||
254 | ldp x1, x4, [sp, #32+16*0] | |
255 | ldp x5, x6, [sp, #32+16*1] | |
256 | ldp x7, x8, [sp, #32+16*2] | |
257 | ldp x9, x10, [sp, #32+16*3] | |
258 | ldp x11, x12, [sp, #32+16*4] | |
259 | ldp x13, x14, [sp, #32+16*5] | |
260 | ldp x15, x16, [sp, #32+16*6] | |
261 | ldp x17, x18, [sp, #32+16*7] | |
262 | ldp x29, x30, [sp], #(32+16*NSAVEXREGPAIRS) | |
263 | cfi_adjust_cfa_offset (-32+16*NSAVEXREGPAIRS) | |
264 | ldp x2, x3, [sp], #16 | |
265 | cfi_adjust_cfa_offset (-16) | |
266 | RET | |
267 | #undef NSAVEXREGPAIRS | |
268 | cfi_endproc | |
269 | .size _dl_tlsdesc_resolve_rela, .-_dl_tlsdesc_resolve_rela | |
270 | ||
271 | /* This function is a placeholder for lazy resolving of TLS | |
272 | relocations. Once some thread starts resolving a TLS | |
273 | relocation, it sets up the TLS descriptor to use this | |
274 | resolver, such that other threads that would attempt to | |
275 | resolve it concurrently may skip the call to the original lazy | |
276 | resolver and go straight to a condition wait. | |
277 | ||
278 | When the actual resolver returns, it will have adjusted the | |
279 | TLS descriptor such that we can tail-call it for it to return | |
280 | the TP offset of the symbol. */ | |
281 | ||
282 | .hidden _dl_tlsdesc_resolve_hold | |
283 | .global _dl_tlsdesc_resolve_hold | |
284 | .type _dl_tlsdesc_resolve_hold,%function | |
285 | cfi_startproc | |
286 | .align 2 | |
287 | _dl_tlsdesc_resolve_hold: | |
288 | #define NSAVEXREGPAIRS 10 | |
289 | 1: | |
290 | stp x29, x30, [sp, #-(32+16*NSAVEXREGPAIRS)]! | |
291 | cfi_adjust_cfa_offset (32+16*NSAVEXREGPAIRS) | |
292 | mov x29, sp | |
293 | stp x1, x2, [sp, #32+16*0] | |
294 | stp x3, x4, [sp, #32+16*1] | |
295 | stp x5, x6, [sp, #32+16*2] | |
296 | stp x7, x8, [sp, #32+16*3] | |
297 | stp x9, x10, [sp, #32+16*4] | |
298 | stp x11, x12, [sp, #32+16*5] | |
299 | stp x13, x14, [sp, #32+16*6] | |
300 | stp x15, x16, [sp, #32+16*7] | |
301 | stp x17, x18, [sp, #32+16*8] | |
302 | str x0, [sp, #32+16*9] | |
303 | ||
304 | SAVE_Q_REGISTERS | |
305 | ||
306 | adr x1, 1b | |
307 | bl _dl_tlsdesc_resolve_hold_fixup | |
308 | ||
309 | RESTORE_Q_REGISTERS | |
310 | ||
311 | ldr x0, [sp, #32+16*9] | |
312 | ldr x1, [x0] | |
313 | blr x1 | |
314 | ||
315 | ldp x1, x2, [sp, #32+16*0] | |
316 | ldp x3, x4, [sp, #32+16*1] | |
317 | ldp x5, x6, [sp, #32+16*2] | |
318 | ldp x7, x8, [sp, #32+16*3] | |
319 | ldp x9, x10, [sp, #32+16*4] | |
320 | ldp x11, x12, [sp, #32+16*5] | |
321 | ldp x13, x14, [sp, #32+16*6] | |
322 | ldp x15, x16, [sp, #32+16*7] | |
323 | ldp x17, x18, [sp, #32+16*8] | |
324 | ldp x29, x30, [sp], #(32+16*NSAVEXREGPAIRS) | |
325 | cfi_adjust_cfa_offset (-32+16*NSAVEXREGPAIRS) | |
326 | RET | |
327 | cfi_endproc | |
328 | .size _dl_tlsdesc_resolve_hold, .-_dl_tlsdesc_resolve_hold | |
329 | #undef NSAVEXREGPAIRS |