rtld_start.S revision 1.17 1 /* $NetBSD: rtld_start.S,v 1.17 2025/04/18 17:56:49 riastradh Exp $ */
2
3 /*
4 * Copyright 1996 Matt Thomas <matt (at) 3am-software.com>
5 * Portions copyright 2002 Charles M. Hannum <root (at) ihack.net>
6 * All rights reserved.
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 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include <machine/asm.h>
32
33 /*
34 * Note: we can call ourselves LEAF even though we use callee-saved
35 * registers because we're the root of the call graph.
36 */
37 LEAF_NOPROFILE(_rtld_start, 0)
38 .set noreorder
39 br pv, 1f
40 1: LDGP(pv)
41
42 /*
43 * Relocate ourself.
44 */
45 br s2, 2f /* get our PC */
46 2: ldiq s3, 2b /* get where the linker thought we were */
47
48 subq s2, s3, a1 /* relocbase */
49 lda t5, _DYNAMIC
50 addq a1, t5, a0 /* &_DYNAMIC */
51
52 /* Squirrel away ps_strings. */
53 mov a3, s0
54
55 bsr ra, _rtld_relocate_nonplt_self
56 LDGP(ra)
57
58 /*
59 * Allocate space on the stack for the cleanup and obj_main
60 * entries that _rtld() will provide for us.
61 */
62 lda sp, -16(sp)
63
64 subq s2, s3, a1 /* relocbase */
65 mov sp, a0 /* sp */
66 CALL(_rtld) /* v0 = _rtld(sp, relocbase); */
67
68 ldq a1, 0(sp) /* cleanup */
69 ldq a2, 8(sp) /* obj_main */
70 lda sp, 16(sp) /* pop stack */
71
72 mov sp, a0 /* stack pointer */
73 mov s0, a3 /* ps_strings */
74
75 mov v0, pv /* set up PV for entry point */
76
77 jsr ra, (v0), 0 /* (*_start)(sp, cleanup, obj, ps_strings); */
78 ldgp gp, 0(ra)
79
80 CALL(exit)
81 halt
82 END(_rtld_start)
83
84 #define RTLD_BIND_START_PROLOGUE \
85 /* at_reg already used by PLT code. */ \
86 .set noat ; \
87 \
88 /* \
89 * Allocate stack frame and preserve all registers that the \
90 * caller would have normally saved themselves. \
91 */ \
92 lda sp, -168(sp) ; \
93 stq ra, 0(sp) ; \
94 stq v0, 8(sp) ; \
95 stq t0, 16(sp) /* XXX t0-t7 necessary? */ ; \
96 stq t1, 24(sp) ; \
97 stq t2, 32(sp) ; \
98 stq t3, 40(sp) ; \
99 stq t4, 48(sp) ; \
100 stq t5, 56(sp) ; \
101 stq t6, 64(sp) ; \
102 stq t7, 72(sp) ; \
103 stq a0, 80(sp) ; \
104 stq a1, 88(sp) ; \
105 stq a2, 96(sp) ; \
106 stq a3, 104(sp) ; \
107 stq a4, 112(sp) ; \
108 stq a5, 120(sp) ; \
109 stq t8, 128(sp) /* XXX t8-t11 necessary? */ ; \
110 stq t9, 136(sp) ; \
111 stq t10, 144(sp) ; \
112 stq t11, 152(sp) ; \
113 stq gp, 160(sp) ; \
114 \
115 /* \
116 * Load our global pointer. Note, can't use pv, since it is \
117 * already used by the PLT code. \
118 */ \
119 br t0, 1f ; \
120 1: LDGP(t0)
121
122 #define RTLD_BIND_START_EPILOGUE(imb) \
123 /* Move the destination address into position. */ \
124 mov v0, pv ; \
125 \
126 /* Restore program registers. */ \
127 ldq ra, 0(sp) ; \
128 ldq v0, 8(sp) ; \
129 ldq t0, 16(sp) ; \
130 ldq t1, 24(sp) ; \
131 ldq t2, 32(sp) ; \
132 ldq t3, 40(sp) ; \
133 ldq t4, 48(sp) ; \
134 ldq t5, 56(sp) ; \
135 ldq t6, 64(sp) ; \
136 ldq t7, 72(sp) ; \
137 ldq a0, 80(sp) ; \
138 ldq a1, 88(sp) ; \
139 ldq a2, 96(sp) ; \
140 ldq a3, 104(sp) ; \
141 ldq a4, 112(sp) ; \
142 ldq a5, 120(sp) ; \
143 ldq t8, 128(sp) ; \
144 ldq t9, 136(sp) ; \
145 ldq t10, 144(sp) ; \
146 ldq t11, 152(sp) ; \
147 ldq gp, 160(sp) ; \
148 /* XXX LDGP? */ \
149 \
150 /* \
151 * We've patched the PLT; sync the I-stream. \
152 */ \
153 imb ; \
154 \
155 /* Pop the stack frame and turn control to the destination. */ \
156 lda sp, 168(sp) ; \
157 jmp zero, (pv)
158
159 /*
160 * _rtld_bind_start_secureplt(_rtld_bind_start_secureplt@pv, obj@at,
161 * (sizeof(Elf_Rela)*index)@t11)
162 *
163 * Lazy binding entry point, called via PLT with read-only
164 * secureplt, when DT_ALPHA_PLTRO is set. The PLT itself looks
165 * something like this:
166 *
167 * _PROCEDURE_LINKAGE_TABLE_:
168 * subq pv, at, t11 // t11 := pv - ent0 = 4*index
169 * s4subq t11, t11, t11 // t11 := 12*index
170 * addq t11, t11, t11 // t11 := 24*index
171 * // = sizeof(Elf_Rela)*index
172 * ldah at, ...(at) // at := PLTGOT
173 * lda at, ...(at)
174 * ldq pv, 0(at) // pv := PLTGOT[0]
175 * // = _rtld_bind_start_secureplt
176 * ldq at, 8(at) // at := PLTGOT[1]
177 * // = obj
178 * jmp (pv)
179 * 0: br at, _PROCEDURE_LINKAGE_TABLE_ // at := ent0
180 * ent0: br 0b // pv - ent0 = 0 = 4*index
181 * ent1: br 0b // pv - ent0 = 4 = 4*index
182 * ent2: br 0b // pv - ent0 = 8 = 4*index
183 * ...
184 */
185 NESTED_NOPROFILE(_rtld_bind_start_secureplt, 0, 168, ra, 0, 0)
186
187 RTLD_BIND_START_PROLOGUE
188
189 /* Set up the arguments for _rtld_bind. */
190 mov at_reg, a0
191 mov t11, a1
192
193 CALL(_rtld_bind)
194
195 RTLD_BIND_START_EPILOGUE(/* no text writes, so no imb */)
196
197 END(_rtld_bind_start_secureplt)
198
199 /*
200 * _rtld_bind_start(_rtld_bind_start@pv, &PLTGOT[2]@pv,
201 * (ent0 + 4*(3*index + 1))@at)
202 *
203 * Lazy binding entry point, called via PLT with read/write
204 * non-secureplt, when DT_ALPHA_PLTRO is not set. The PLT itself
205 * looks something like this at program startup, with PLTGOT (an
206 * array of 64-bit Elf_Addr) pointing at _PROCEDURE_LINKAGE_TABLE_
207 * and PLTGOT[2] and PLTGOT[3] initialized by _rtld_setup_pltgot:
208 *
209 * _PROCEDURE_LINKAGE_TABLE_:
210 * br pv, .Lref // pv := .Lref
211 * .Lref: ldq pv, 12(pv) // pv := PLTGOT[2]
212 * = _rtld_bind_start
213 * unop // no-op for alignment
214 * jmp pv, (pv) // pv := &PLTGOT[2]
215 * .qword (_rtld_bind_start) // PLTGOT[2]
216 * .qword (object pointer) // PLTGOT[3]
217 * ent0: br at, _PROCEDURE_LINKAGE_TABLE_
218 * unop // space for adjusted stub
219 * unop // space for adjusted stub
220 * ent1: br at, _PROCEDURE_LINKAGE_TABLE_
221 * unop
222 * unop
223 * ent2: br at, _PROCEDURE_LINKAGE_TABLE_
224 * unop
225 * unop
226 * ...
227 *
228 * Note: Distance from &PLTGOT[2] (pv) to ent[0] + 4 (at) is 20
229 * bytes, and each ent[index] + 4 (at) after that is separated by
230 * 3 instructions, i.e., 12 bytes.
231 */
232 NESTED_NOPROFILE(_rtld_bind_start, 0, 168, ra, 0, 0)
233
234 RTLD_BIND_START_PROLOGUE
235
236 /* Set up the arguments for _rtld_bind. */
237 subq at_reg, pv, a1 /* calculate offset of reloc entry */
238 ldq a0, 8(pv) /* object structure */
239 subq a1, 20, a1 /* = (at - pv - 20) / 12 * 24 */
240 addq a1, a1, a1
241
242 CALL(_rtld_bind)
243
244 RTLD_BIND_START_EPILOGUE(imb)
245
246 END(_rtld_bind_start)
247
248 /*
249 * _rtld_bind_start_old(&PLTGOT[2]@pv, (sizeof(Elf_Rela)*index)@at)
250 *
251 * Lazy binding entry point, called via PLT. This version is for
252 * the old PLT entry format, for which the PLT looks something
253 * like this at program startup, with PLTGOT (an array of 64-bit
254 * Elf_Addr) pointing at _PROCEDURE_LINKAGE_TABLE_, and PLTGOT[2]
255 * and PLTGOT[3] initialized by _rtld_setup_pltgot:
256 *
257 * _PROCEDURE_LINKAGE_TABLE_:
258 * br pv, 1f // pv := .Lref
259 * .Lref: ldq pv, 12(pv) // pv := PLTGOT[2]
260 * = _rtld_bind_start
261 * unop // no-op for alignment
262 * jmp pv, (pv) // pv := &PLTGOT[2]
263 * .qword (_rtld_bind_start) // PLTGOT[2]
264 * .qword (object pointer) // PLTGOT[3]
265 * ent0: ldah at, 0 // at := 24*0
266 * lda at, 0(at) // = sizeof(Elf_Rela)*index
267 * br _PROCEDURE_LINKAGE_TABLE_
268 * ent1: ldah at, 0 // at := 24*1
269 * lda at, 24(at) // = sizeof(Elf_Rela)*index
270 * br _PROCEDURE_LINKAGE_TABLE_
271 * ent3: ldah at, 0 // at := 24*2
272 * lda at, 48(at) // = sizeof(Elf_Rela)*index
273 * br _PROCEDURE_LINKAGE_TABLE_
274 * ...
275 */
276 NESTED_NOPROFILE(_rtld_bind_start_old, 0, 168, ra, 0, 0)
277
278 RTLD_BIND_START_PROLOGUE
279
280 /* Set up the arguments for _rtld_bind. */
281 ldq a0, 8(pv) /* object structure */
282 mov at_reg, a1 /* offset of reloc entry */
283
284 CALL(_rtld_bind)
285
286 RTLD_BIND_START_EPILOGUE(imb)
287
288 END(_rtld_bind_start_old)
289