1 /* $NetBSD: rtld_start.S,v 1.7 2024/07/23 18:11:53 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2014 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Matt Thomas of 3am Software Foundry. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /*- 33 * Copyright (c) 2014 The FreeBSD Foundation 34 * All rights reserved. 35 * 36 * This software was developed by Andrew Turner under 37 * sponsorship from the FreeBSD Foundation. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 */ 60 61 #include <machine/asm.h> 62 63 RCSID("$NetBSD: rtld_start.S,v 1.7 2024/07/23 18:11:53 riastradh Exp $") 64 65 /* 66 * void _rtld_start(void (*cleanup)(void), const Obj_Entry *obj, 67 * struct ps_strings *ps_strings); 68 * 69 * X0 = NULL 70 * X1 = NULL 71 * X2 = ps_strings 72 * X30 (LR) = 0 73 * X29 (FP) = 0 74 */ 75 ENTRY_NP(_rtld_start) 76 mov x24, x2 /* save ps_strings */ 77 78 adrp x1, :got:_DYNAMIC /* load _DYNAMIC offset from GOT */ 79 ldr x1, [x1, #:got_lo12:_DYNAMIC] 80 81 adrp x0, _DYNAMIC /* get &_DYNAMIC */ 82 add x0, x0, #:lo12:_DYNAMIC 83 84 sub x25, x0, x1 /* relocbase = &_DYNAMIC - GOT:_DYNAMIC */ 85 mov x1, x25 /* pass as 2nd argument */ 86 bl _C_LABEL(_rtld_relocate_nonplt_self) 87 88 sub sp, sp, #16 /* reserve space for returns */ 89 mov x0, sp /* pointer to reserved space */ 90 mov x1, x25 /* pass relocbase */ 91 bl _C_LABEL(_rtld) 92 mov x17, x0 /* save entry point */ 93 94 ldp x0, x1, [sp], #16 /* pop cleanup & obj_main */ 95 mov x2, x24 /* restore ps_strings */ 96 97 br x17 /* call saved entry point */ 98 END(_rtld_start) 99 100 /* 101 * Upon entry from plt0 entry: 102 * 103 * SP+0 = &PLTGOT[n + 3] 104 * SP+8 = return addr 105 * X16 = &PLTGOT[2] 106 */ 107 ENTRY_NP(_rtld_bind_start) 108 ldr x9, [sp] /* x9 = &PLTGOT[n+3] */ 109 110 /* save x0-x8 for arguments */ 111 stp x0, x1, [sp, #-16]! 112 stp x2, x3, [sp, #-16]! 113 stp x4, x5, [sp, #-16]! 114 stp x6, x7, [sp, #-16]! 115 stp x8, xzr, [sp, #-16]! 116 117 /* save q0-q7 for arguments */ 118 stp q0, q1, [sp, #-32]! 119 stp q2, q3, [sp, #-32]! 120 stp q4, q5, [sp, #-32]! 121 stp q6, q7, [sp, #-32]! 122 123 ldr x0, [x16, #-8] /* x0 = PLTGOT[1] */ 124 sub x1, x9, x16 /* x1 = &PLTGOT[n+3] - &PLTGOT[1] = offset+8 */ 125 sub x1, x1, #8 /* x1 = offset */ 126 lsr x1, x1, #3 /* x1 /= sizeof(void *) */ 127 128 bl _C_LABEL(_rtld_bind) 129 mov x17, x0 /* save result */ 130 131 /* restore q0-q7 for arguments */ 132 ldp q6, q7, [sp], #32 133 ldp q4, q5, [sp], #32 134 ldp q2, q3, [sp], #32 135 ldp q0, q1, [sp], #32 136 137 /* restore x0-x8 for arguments */ 138 ldp x8, xzr, [sp], #16 139 ldp x6, x7, [sp], #16 140 ldp x4, x5, [sp], #16 141 ldp x2, x3, [sp], #16 142 ldp x0, x1, [sp], #16 143 144 ldp xzr, lr, [sp], #16 /* restore original lr pushed by plt0 */ 145 br x17 /* call bound function */ 146 END(_rtld_bind_start) 147 148 /* 149 * Entry points used by _rtld_tlsdesc_fill. They will be passed in x0 150 * a pointer to: 151 * 152 * struct rel_tlsdesc { 153 * uint64_t resolver_fnc; 154 * uint64_t resolver_arg; 155 * }; 156 * 157 * They are called with nonstandard calling convention and must 158 * preserve all registers except x0. 159 */ 160 161 /* 162 * uint64_t@x0 163 * _rtld_tlsdesc_static(struct rel_tlsdesc *rel_tlsdesc@x0); 164 * 165 * Resolver function for TLS symbols resolved at load time. 166 * 167 * rel_tlsdesc->resolver_arg is the offset of the static 168 * thread-local storage region, relative to the start of the TCB. 169 * 170 * Nonstandard calling convention: Must preserve all registers 171 * except x0. 172 */ 173 ENTRY(_rtld_tlsdesc_static) 174 .cfi_startproc 175 ldr x0, [x0, #8] /* x0 := tcboffset */ 176 ret /* return x0 = tcboffset */ 177 .cfi_endproc 178 END(_rtld_tlsdesc_static) 179 180 /* 181 * uint64_t@x0 182 * _rtld_tlsdesc_undef(struct rel_tlsdesc *rel_tlsdesc@x0); 183 * 184 * Resolver function for weak and undefined TLS symbols. 185 * 186 * rel_tlsdesc->resolver_arg is the Elf_Rela rela->r_addend. 187 * 188 * Nonstandard calling convention: Must preserve all registers 189 * except x0. 190 */ 191 ENTRY(_rtld_tlsdesc_undef) 192 .cfi_startproc 193 str x1, [sp, #-16]! /* save x1 on stack */ 194 .cfi_adjust_cfa_offset 16 195 196 mrs x1, tpidr_el0 /* x1 := current thread tcb */ 197 ldr x0, [x0, #8] /* x0 := rela->r_addend */ 198 sub x0, x0, x1 /* x0 := rela->r_addend - tcb */ 199 200 ldr x1, [sp], #16 /* restore x1 from stack */ 201 .cfi_adjust_cfa_offset -16 202 .cfi_endproc 203 ret /* return x0 = rela->r_addend - tcb */ 204 END(_rtld_tlsdesc_undef) 205 206 /* 207 * uint64_t@x0 208 * _rtld_tlsdesc_dynamic(struct rel_tlsdesc *tlsdesc@x0); 209 * 210 * Resolver function for TLS symbols from dlopen(). 211 * 212 * rel_tlsdesc->resolver_arg is a pointer to a struct tls_data 213 * object allocated during relocation. 214 * 215 * Nonstandard calling convention: Must preserve all registers 216 * except x0. 217 */ 218 ENTRY(_rtld_tlsdesc_dynamic) 219 .cfi_startproc 220 221 /* Save registers used in fast path */ 222 stp x1, x2, [sp, #(-2 * 16)]! 223 stp x3, x4, [sp, #(1 * 16)] 224 .cfi_adjust_cfa_offset 2 * 16 225 .cfi_rel_offset x1, 0 226 .cfi_rel_offset x2, 8 227 .cfi_rel_offset x3, 16 228 .cfi_rel_offset x4, 24 229 230 /* Try for the fast path -- inlined version of __tls_get_addr. */ 231 232 ldr x1, [x0, #8] /* x1 := tlsdesc (struct tls_data *) */ 233 mrs x4, tpidr_el0 /* x4 := tcb */ 234 ldr x0, [x4] /* x0 := dtv = tcb->tcb_dtv */ 235 236 ldr x3, [x0, #-8] /* x3 := max = DTV_MAX_INDEX(dtv) */ 237 ldr x2, [x1, #0] /* x2 := idx = tlsdesc->td_tlsindex */ 238 cmp x2, x3 239 b.gt 1f /* Slow path if idx > max */ 240 241 ldr x3, [x0, x2, lsl #3] /* x3 := dtv[idx] */ 242 cbz x3, 1f /* Slow path if dtv[idx] is null */ 243 244 /* 245 * Fast path 246 * 247 * return (dtv[tlsdesc->td_tlsindex] + tlsdesc->td_tlsoffs - tcb) 248 */ 249 ldr x2, [x1, #8] /* x2 := offs = tlsdesc->td_tlsoffs */ 250 add x2, x2, x3 /* x2 := addr = dtv[idx] + offs */ 251 sub x0, x2, x4 /* x0 := addr - tcb 252 253 /* Restore fast path registers and return */ 254 ldp x3, x4, [sp, #(1 * 16)] 255 ldp x1, x2, [sp], #(2 * 16) 256 .cfi_adjust_cfa_offset -2 * 16 257 ret /* return x0 = addr - tcb */ 258 259 /* 260 * Slow path 261 * 262 * return _rtld_tls_get_addr(tp, tlsdesc->td_tlsindex, 263 * tlsdesc->td_tlsoffs); 264 * 265 */ 266 1: 267 /* Save all integer registers */ 268 stp x29, x30, [sp, #-(8 * 16)]! 269 .cfi_adjust_cfa_offset 8 * 16 270 .cfi_rel_offset x29, 0 271 .cfi_rel_offset x30, 8 272 273 stp x5, x6, [sp, #(1 * 16)] 274 stp x7, x8, [sp, #(2 * 16)] 275 stp x9, x10, [sp, #(3 * 16)] 276 stp x11, x12, [sp, #(4 * 16)] 277 stp x13, x14, [sp, #(5 * 16)] 278 stp x15, x16, [sp, #(6 * 16)] 279 stp x17, x18, [sp, #(7 * 16)] 280 .cfi_rel_offset x5, 16 281 .cfi_rel_offset x6, 24 282 .cfi_rel_offset x7, 32 283 .cfi_rel_offset x8, 40 284 .cfi_rel_offset x9, 48 285 .cfi_rel_offset x10, 56 286 .cfi_rel_offset x11, 64 287 .cfi_rel_offset x12, 72 288 .cfi_rel_offset x13, 80 289 .cfi_rel_offset x14, 88 290 .cfi_rel_offset x15, 96 291 .cfi_rel_offset x16, 104 292 .cfi_rel_offset x17, 112 293 .cfi_rel_offset x18, 120 294 295 /* Find the tls offset */ 296 mov x0, x4 /* x0 := tcb */ 297 mov x3, x1 /* x3 := tlsdesc */ 298 ldr x1, [x3, #0] /* x1 := idx = tlsdesc->td_tlsindex */ 299 ldr x2, [x3, #8] /* x2 := offs = tlsdesc->td_tlsoffs */ 300 bl _rtld_tls_get_addr /* x0 := addr = _rtld_tls_get_addr(tcb, 301 * idx, offs) */ 302 mrs x1, tpidr_el0 /* x1 := tcb */ 303 sub x0, x0, x1 /* x0 := addr - tcb */ 304 305 /* Restore slow path registers */ 306 ldp x17, x18, [sp, #(7 * 16)] 307 ldp x15, x16, [sp, #(6 * 16)] 308 ldp x13, x14, [sp, #(5 * 16)] 309 ldp x11, x12, [sp, #(4 * 16)] 310 ldp x9, x10, [sp, #(3 * 16)] 311 ldp x7, x8, [sp, #(2 * 16)] 312 ldp x5, x6, [sp, #(1 * 16)] 313 ldp x29, x30, [sp], #(8 * 16) 314 .cfi_adjust_cfa_offset -8 * 16 315 .cfi_restore x29 316 .cfi_restore x30 317 318 /* Restore fast path registers and return */ 319 ldp x3, x4, [sp, #16] 320 ldp x1, x2, [sp], #(2 * 16) 321 .cfi_adjust_cfa_offset -2 * 16 322 .cfi_endproc 323 ret /* return x0 = addr - tcb */ 324 END(_rtld_tlsdesc_dynamic) 325