1/* Thread-local storage handling in the ELF dynamic linker.  ARM version.
2   Copyright (C) 2006-2021 Free Software Foundation, Inc.
3   This file is part of the GNU C Library.
4
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.
9
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.
14
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/>.  */
18
19#include <sysdep.h>
20#include <arm-features.h>
21#include <tls.h>
22#include "tlsdesc.h"
23
24	.text
25	@ emit debug information with cfi
26	@ use arm-specific pseudos for unwinding itself
27	CFI_SECTIONS
28	.hidden _dl_tlsdesc_return
29	.global	_dl_tlsdesc_return
30	.type	_dl_tlsdesc_return,#function
31	cfi_startproc
32	eabi_fnstart
33	.align 2
34_dl_tlsdesc_return:
35	ldr	r0, [r0]
36	BX	(lr)
37	eabi_fnend
38	cfi_endproc
39	.size	_dl_tlsdesc_return, .-_dl_tlsdesc_return
40
41	.hidden _dl_tlsdesc_undefweak
42	.global	_dl_tlsdesc_undefweak
43	.type	_dl_tlsdesc_undefweak,#function
44	cfi_startproc
45	eabi_fnstart
46	.align 2
47_dl_tlsdesc_undefweak:
48	GET_TLS (r1)
49	rsb 	r0, r0, #0
50	BX	(lr)
51	cfi_endproc
52	eabi_fnend
53	.size	_dl_tlsdesc_undefweak, .-_dl_tlsdesc_undefweak
54
55#ifdef SHARED
56	.hidden _dl_tlsdesc_dynamic
57	.global	_dl_tlsdesc_dynamic
58	.type	_dl_tlsdesc_dynamic,#function
59
60
61/*
62	The assembly code that follows is a rendition of the following
63	C code, hand-optimized a little bit.
64
65ptrdiff_t
66_dl_tlsdesc_dynamic(struct tlsdesc *tdp)
67{
68       struct tlsdesc_dynamic_arg *td = tdp->argument.pointer;
69       dtv_t *dtv = (dtv_t *)THREAD_DTV();
70       if (__builtin_expect (td->gen_count <= dtv[0].counter
71                             && dtv[td->tlsinfo.ti_module].pointer.val
72                                != TLS_DTV_UNALLOCATED,
73                             1))
74               return dtv[td->tlsinfo.ti_module].pointer.val +
75                       td->tlsinfo.ti_offset - __builtin_thread_pointer();
76
77       return __tls_get_addr (&td->tlsinfo) - __builtin_thread_pointer();
78}
79
80*/
81	cfi_startproc
82	eabi_fnstart
83	.align 2
84_dl_tlsdesc_dynamic:
85	/* Our calling convention is to clobber r0, r1 and the processor
86	   flags.  All others that are modified must be saved */
87	eabi_save ({r2,r3,r4,lr})
88	push	{r2,r3,r4,lr}
89	cfi_adjust_cfa_offset (16)
90	cfi_rel_offset (r2,0)
91	cfi_rel_offset (r3,4)
92	cfi_rel_offset (r4,8)
93	cfi_rel_offset (lr,12)
94	ldr	r1, [r0] /* td */
95	GET_TLS (lr)
96	mov	r4, r0 /* r4 = tp */
97	ldr	r0, [r0]
98	ldr	r2, [r1, #8] /* gen_count */
99	ldr	r3, [r0]
100	cmp	r2, r3
101	bhi	1f
102	ldr	r3, [r1]
103#ifndef ARM_NO_INDEX_REGISTER
104	ldr	r2, [r0, r3, lsl #3]
105#else
106	add	lr, r0, r3, lsl #3
107	ldr	r2, [lr]
108#endif
109	cmn	r2, #1
110	ittt	ne
111	ldrne	r3, [r1, #4]
112	addne	r3, r2, r3
113	rsbne	r0, r4, r3
114	bne	2f
1151:	mov	r0, r1
116	bl	__tls_get_addr
117	rsb	r0, r4, r0
1182:
119#if ((defined (__ARM_ARCH_4T__) && defined (__THUMB_INTERWORK__)) \
120     || defined (ARM_ALWAYS_BX))
121	pop	{r2,r3,r4, lr}
122	cfi_adjust_cfa_offset (-16)
123	cfi_restore (lr)
124	cfi_restore (r4)
125	cfi_restore (r3)
126	cfi_restore (r2)
127	bx	lr
128#else
129	pop	{r2,r3,r4, pc}
130#endif
131	eabi_fnend
132	cfi_endproc
133	.size	_dl_tlsdesc_dynamic, .-_dl_tlsdesc_dynamic
134#endif /* SHARED */
135