1/*-
2 * Copyright (c) 2014 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Andrew Turner under
6 * sponsorship from the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <machine/asm.h>
31__FBSDID("$FreeBSD$");
32
33ENTRY(.rtld_start)
34	mov	x19, x0		/* Put ps_strings in a callee-saved register */
35	mov	x20, sp		/* And the stack pointer */
36
37	sub	sp, sp, #16	/* Make room for obj_main & exit proc */
38
39	mov	x1, sp		/* exit_proc */
40	add	x2, x1, #8	/* obj_main */
41	bl	_rtld		/* Call the loader */
42	mov	x8, x0		/* Backup the entry point */
43
44	ldr	x2, [sp]	/* Load cleanup */
45	ldr	x1, [sp, #8]	/* Load obj_main */
46	mov	x0, x19		/* Restore ps_strings */
47	mov	sp, x20		/* Restore the stack pointer */
48	br	x8		/* Jump to the entry point */
49END(.rtld_start)
50
51/*
52 * sp + 0 = &GOT[x + 3]
53 * sp + 8 = RA
54 * x16 = &GOT[2]
55 * x17 = &_rtld_bind_start
56 */
57ENTRY(_rtld_bind_start)
58	.cfi_startproc
59	mov	x17, sp
60
61	/* Save frame pointer and SP */
62	stp	x29, x30, [sp, #-16]!
63	mov	x29, sp
64	.cfi_def_cfa x29, 16
65	.cfi_offset x30, -8
66	.cfi_offset x29, -16
67
68	/* Save the arguments */
69	stp	x0, x1, [sp, #-16]!
70	stp	x2, x3, [sp, #-16]!
71	stp	x4, x5, [sp, #-16]!
72	stp	x6, x7, [sp, #-16]!
73	stp	x8, xzr, [sp, #-16]!
74
75	/* Save any floating-point arguments */
76	stp	q0, q1, [sp, #-32]!
77	stp	q2, q3, [sp, #-32]!
78	stp	q4, q5, [sp, #-32]!
79	stp	q6, q7, [sp, #-32]!
80
81	/* Calculate reloff */
82	ldr	x2, [x17, #0]	/* Get the address of the entry */
83	sub	x1, x2, x16	/* Find its offset */
84	sub	x1, x1, #8	/* Adjust for x16 not being at offset 0 */
85	/* Each rela item has 3 entriesso we need reloff = 3 * index */
86	lsl	x3, x1, #1	/* x3 = 2 * offset */
87	add	x1, x1, x3	/* x1 = x3 + offset = 3 * offset */
88
89	/* Load obj */
90	ldr	x0, [x16, #-8]
91
92	/* Call into rtld */
93	bl	_rtld_bind
94
95	/* Backup the address to branch to */
96	mov	x16, x0
97
98	/* restore the arguments */
99	ldp	q6, q7, [sp], #32
100	ldp	q4, q5, [sp], #32
101	ldp	q2, q3, [sp], #32
102	ldp	q0, q1, [sp], #32
103	ldp	x8, xzr, [sp], #16
104	ldp	x6, x7, [sp], #16
105	ldp	x4, x5, [sp], #16
106	ldp	x2, x3, [sp], #16
107	ldp	x0, x1, [sp], #16
108
109	/* Restore frame pointer */
110	ldp	x29, xzr, [sp], #16
111
112	 /* Restore link register saved by the plt code */
113	ldp	xzr, x30, [sp], #16
114
115	/* Call into the correct function */
116	br	x16
117	.cfi_endproc
118END(_rtld_bind_start)
119
120/*
121 * struct rel_tlsdesc {
122 *  uint64_t resolver_fnc;
123 *  uint64_t resolver_arg;
124 *
125 *
126 * uint64_t _rtld_tlsdesc_static(struct rel_tlsdesc *);
127 *
128 * Resolver function for TLS symbols resolved at load time
129 */
130ENTRY(_rtld_tlsdesc_static)
131	.cfi_startproc
132	ldr	x0, [x0, #8]
133	ret
134	.cfi_endproc
135END(_rtld_tlsdesc_static)
136
137/*
138 * uint64_t _rtld_tlsdesc_undef(void);
139 *
140 * Resolver function for weak and undefined TLS symbols
141 */
142ENTRY(_rtld_tlsdesc_undef)
143	.cfi_startproc
144	str	x1, [sp, #-16]!
145	.cfi_adjust_cfa_offset	16
146
147	mrs	x1, tpidr_el0
148	ldr	x0, [x0, #8]
149	sub	x0, x0, x1
150
151	ldr	x1, [sp], #16
152	.cfi_adjust_cfa_offset 	-16
153	.cfi_endproc
154	ret
155END(_rtld_tlsdesc_undef)
156
157/*
158 * uint64_t _rtld_tlsdesc_dynamic(struct rel_tlsdesc *);
159 *
160 * Resolver function for TLS symbols from dlopen()
161 */
162ENTRY(_rtld_tlsdesc_dynamic)
163	.cfi_startproc
164
165	/* Save registers used in fast path */
166	stp	x1,  x2, [sp, #(-2 * 16)]!
167	stp	x3,  x4, [sp, #(1 * 16)]
168	.cfi_adjust_cfa_offset	2 * 16
169	.cfi_rel_offset		x1, 0
170	.cfi_rel_offset		x2, 8
171	.cfi_rel_offset		x3, 16
172	.cfi_rel_offset		x4, 24
173
174	/* Test fastpath - inlined version of tls_get_addr_common(). */
175	ldr	x1, [x0, #8]		/* tlsdesc ptr */
176	mrs	x4, tpidr_el0
177	ldr	x0, [x4]		/* DTV pointer */
178	ldr	x2, [x0]		/* dtv[0] (generation count) */
179	ldr	x3, [x1]		/* tlsdec->dtv_gen */
180	cmp	x2, x3
181	b.ne	1f			/* dtv[0] != tlsdec->dtv_gen */
182
183	ldr	w2, [x1, #8]		/* tlsdec->tls_index */
184	add	w2, w2, #1
185	ldr     x3, [x0, w2, sxtw #3]	/* dtv[tlsdesc->tls_index + 1] */
186	cbz	x3, 1f
187
188	/* Return (dtv[tlsdesc->tls_index + 1] + tlsdesc->tls_offs - tp) */
189	ldr	x2, [x1, #16]		/* tlsdec->tls_offs */
190	add 	x2, x2, x3
191	sub	x0, x2, x4
192	/* Restore registers and return */
193	ldp	 x3,  x4, [sp, #(1 * 16)]
194	ldp	 x1,  x2, [sp], #(2 * 16)
195	.cfi_adjust_cfa_offset 	-2 * 16
196	ret
197
198	/*
199	 * Slow path
200	  * return(
201	 *    tls_get_addr_common(tp, tlsdesc->tls_index, tlsdesc->tls_offs));
202	 *
203	 */
2041:
205	/* Save all interger registers */
206	stp	x29, x30, [sp, #-(8 * 16)]!
207	.cfi_adjust_cfa_offset	8 * 16
208	.cfi_rel_offset		x29, 0
209	.cfi_rel_offset		x30, 8
210
211	mov	x29, sp
212	stp	x5,   x6, [sp, #(1 * 16)]
213	stp	x7,   x8, [sp, #(2 * 16)]
214	stp	x9,  x10, [sp, #(3 * 16)]
215	stp	x11, x12, [sp, #(4 * 16)]
216	stp	x13, x14, [sp, #(5 * 16)]
217	stp	x15, x16, [sp, #(6 * 16)]
218	stp	x17, x18, [sp, #(7 * 16)]
219	.cfi_rel_offset		 x5, 16
220	.cfi_rel_offset		 x6, 24
221	.cfi_rel_offset		 x7, 32
222	.cfi_rel_offset		 x8, 40
223	.cfi_rel_offset		 x9, 48
224	.cfi_rel_offset		x10, 56
225	.cfi_rel_offset		x11, 64
226	.cfi_rel_offset		x12, 72
227	.cfi_rel_offset		x13, 80
228	.cfi_rel_offset		x14, 88
229	.cfi_rel_offset		x15, 96
230	.cfi_rel_offset		x16, 104
231	.cfi_rel_offset		x17, 112
232	.cfi_rel_offset		x18, 120
233
234	/* Find the tls offset */
235	mov	x0, x4			/* tp */
236	mov	x3, x1			/* tlsdesc ptr */
237	ldr	w1, [x3, #8]		/* tlsdec->tls_index */
238	ldr	x2, [x3, #16]		/* tlsdec->tls_offs */
239	bl	tls_get_addr_common
240	mrs	x1, tpidr_el0
241	sub	x0, x0, x1
242
243	/* Restore slow patch registers */
244	ldp	x17, x18, [sp, #(7 * 16)]
245	ldp	x15, x16, [sp, #(6 * 16)]
246	ldp	x13, x14, [sp, #(5 * 16)]
247	ldp	x11, x12, [sp, #(4 * 16)]
248	ldp	x9, x10,  [sp, #(3 * 16)]
249	ldp	x7, x8,   [sp, #(2 * 16)]
250	ldp	x5, x6,   [sp, #(1 * 16)]
251	ldp	x29, x30, [sp], #(8 * 16)
252	.cfi_adjust_cfa_offset 	-8 * 16
253	.cfi_restore		x29
254	.cfi_restore		x30
255
256	/* Restore fast path registers and return */
257	ldp	 x3,  x4, [sp, #16]
258	ldp	 x1,  x2, [sp], #(2 * 16)
259	.cfi_adjust_cfa_offset	-2 * 16
260	.cfi_endproc
261	ret
262END(_rtld_tlsdesc_dynamic)
263