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