/* $NetBSD: rtld_start.S,v 1.17 2025/04/18 17:56:49 riastradh Exp $ */ /* * Copyright 1996 Matt Thomas * Portions copyright 2002 Charles M. Hannum * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include /* * Note: we can call ourselves LEAF even though we use callee-saved * registers because we're the root of the call graph. */ LEAF_NOPROFILE(_rtld_start, 0) .set noreorder br pv, 1f 1: LDGP(pv) /* * Relocate ourself. */ br s2, 2f /* get our PC */ 2: ldiq s3, 2b /* get where the linker thought we were */ subq s2, s3, a1 /* relocbase */ lda t5, _DYNAMIC addq a1, t5, a0 /* &_DYNAMIC */ /* Squirrel away ps_strings. */ mov a3, s0 bsr ra, _rtld_relocate_nonplt_self LDGP(ra) /* * Allocate space on the stack for the cleanup and obj_main * entries that _rtld() will provide for us. */ lda sp, -16(sp) subq s2, s3, a1 /* relocbase */ mov sp, a0 /* sp */ CALL(_rtld) /* v0 = _rtld(sp, relocbase); */ ldq a1, 0(sp) /* cleanup */ ldq a2, 8(sp) /* obj_main */ lda sp, 16(sp) /* pop stack */ mov sp, a0 /* stack pointer */ mov s0, a3 /* ps_strings */ mov v0, pv /* set up PV for entry point */ jsr ra, (v0), 0 /* (*_start)(sp, cleanup, obj, ps_strings); */ ldgp gp, 0(ra) CALL(exit) halt END(_rtld_start) #define RTLD_BIND_START_PROLOGUE \ /* at_reg already used by PLT code. */ \ .set noat ; \ \ /* \ * Allocate stack frame and preserve all registers that the \ * caller would have normally saved themselves. \ */ \ lda sp, -168(sp) ; \ stq ra, 0(sp) ; \ stq v0, 8(sp) ; \ stq t0, 16(sp) /* XXX t0-t7 necessary? */ ; \ stq t1, 24(sp) ; \ stq t2, 32(sp) ; \ stq t3, 40(sp) ; \ stq t4, 48(sp) ; \ stq t5, 56(sp) ; \ stq t6, 64(sp) ; \ stq t7, 72(sp) ; \ stq a0, 80(sp) ; \ stq a1, 88(sp) ; \ stq a2, 96(sp) ; \ stq a3, 104(sp) ; \ stq a4, 112(sp) ; \ stq a5, 120(sp) ; \ stq t8, 128(sp) /* XXX t8-t11 necessary? */ ; \ stq t9, 136(sp) ; \ stq t10, 144(sp) ; \ stq t11, 152(sp) ; \ stq gp, 160(sp) ; \ \ /* \ * Load our global pointer. Note, can't use pv, since it is \ * already used by the PLT code. \ */ \ br t0, 1f ; \ 1: LDGP(t0) #define RTLD_BIND_START_EPILOGUE(imb) \ /* Move the destination address into position. */ \ mov v0, pv ; \ \ /* Restore program registers. */ \ ldq ra, 0(sp) ; \ ldq v0, 8(sp) ; \ ldq t0, 16(sp) ; \ ldq t1, 24(sp) ; \ ldq t2, 32(sp) ; \ ldq t3, 40(sp) ; \ ldq t4, 48(sp) ; \ ldq t5, 56(sp) ; \ ldq t6, 64(sp) ; \ ldq t7, 72(sp) ; \ ldq a0, 80(sp) ; \ ldq a1, 88(sp) ; \ ldq a2, 96(sp) ; \ ldq a3, 104(sp) ; \ ldq a4, 112(sp) ; \ ldq a5, 120(sp) ; \ ldq t8, 128(sp) ; \ ldq t9, 136(sp) ; \ ldq t10, 144(sp) ; \ ldq t11, 152(sp) ; \ ldq gp, 160(sp) ; \ /* XXX LDGP? */ \ \ /* \ * We've patched the PLT; sync the I-stream. \ */ \ imb ; \ \ /* Pop the stack frame and turn control to the destination. */ \ lda sp, 168(sp) ; \ jmp zero, (pv) /* * _rtld_bind_start_secureplt(_rtld_bind_start_secureplt@pv, obj@at, * (sizeof(Elf_Rela)*index)@t11) * * Lazy binding entry point, called via PLT with read-only * secureplt, when DT_ALPHA_PLTRO is set. The PLT itself looks * something like this: * * _PROCEDURE_LINKAGE_TABLE_: * subq pv, at, t11 // t11 := pv - ent0 = 4*index * s4subq t11, t11, t11 // t11 := 12*index * addq t11, t11, t11 // t11 := 24*index * // = sizeof(Elf_Rela)*index * ldah at, ...(at) // at := PLTGOT * lda at, ...(at) * ldq pv, 0(at) // pv := PLTGOT[0] * // = _rtld_bind_start_secureplt * ldq at, 8(at) // at := PLTGOT[1] * // = obj * jmp (pv) * 0: br at, _PROCEDURE_LINKAGE_TABLE_ // at := ent0 * ent0: br 0b // pv - ent0 = 0 = 4*index * ent1: br 0b // pv - ent0 = 4 = 4*index * ent2: br 0b // pv - ent0 = 8 = 4*index * ... */ NESTED_NOPROFILE(_rtld_bind_start_secureplt, 0, 168, ra, 0, 0) RTLD_BIND_START_PROLOGUE /* Set up the arguments for _rtld_bind. */ mov at_reg, a0 mov t11, a1 CALL(_rtld_bind) RTLD_BIND_START_EPILOGUE(/* no text writes, so no imb */) END(_rtld_bind_start_secureplt) /* * _rtld_bind_start(_rtld_bind_start@pv, &PLTGOT[2]@pv, * (ent0 + 4*(3*index + 1))@at) * * Lazy binding entry point, called via PLT with read/write * non-secureplt, when DT_ALPHA_PLTRO is not set. The PLT itself * looks something like this at program startup, with PLTGOT (an * array of 64-bit Elf_Addr) pointing at _PROCEDURE_LINKAGE_TABLE_ * and PLTGOT[2] and PLTGOT[3] initialized by _rtld_setup_pltgot: * * _PROCEDURE_LINKAGE_TABLE_: * br pv, .Lref // pv := .Lref * .Lref: ldq pv, 12(pv) // pv := PLTGOT[2] * = _rtld_bind_start * unop // no-op for alignment * jmp pv, (pv) // pv := &PLTGOT[2] * .qword (_rtld_bind_start) // PLTGOT[2] * .qword (object pointer) // PLTGOT[3] * ent0: br at, _PROCEDURE_LINKAGE_TABLE_ * unop // space for adjusted stub * unop // space for adjusted stub * ent1: br at, _PROCEDURE_LINKAGE_TABLE_ * unop * unop * ent2: br at, _PROCEDURE_LINKAGE_TABLE_ * unop * unop * ... * * Note: Distance from &PLTGOT[2] (pv) to ent[0] + 4 (at) is 20 * bytes, and each ent[index] + 4 (at) after that is separated by * 3 instructions, i.e., 12 bytes. */ NESTED_NOPROFILE(_rtld_bind_start, 0, 168, ra, 0, 0) RTLD_BIND_START_PROLOGUE /* Set up the arguments for _rtld_bind. */ subq at_reg, pv, a1 /* calculate offset of reloc entry */ ldq a0, 8(pv) /* object structure */ subq a1, 20, a1 /* = (at - pv - 20) / 12 * 24 */ addq a1, a1, a1 CALL(_rtld_bind) RTLD_BIND_START_EPILOGUE(imb) END(_rtld_bind_start) /* * _rtld_bind_start_old(&PLTGOT[2]@pv, (sizeof(Elf_Rela)*index)@at) * * Lazy binding entry point, called via PLT. This version is for * the old PLT entry format, for which the PLT looks something * like this at program startup, with PLTGOT (an array of 64-bit * Elf_Addr) pointing at _PROCEDURE_LINKAGE_TABLE_, and PLTGOT[2] * and PLTGOT[3] initialized by _rtld_setup_pltgot: * * _PROCEDURE_LINKAGE_TABLE_: * br pv, 1f // pv := .Lref * .Lref: ldq pv, 12(pv) // pv := PLTGOT[2] * = _rtld_bind_start * unop // no-op for alignment * jmp pv, (pv) // pv := &PLTGOT[2] * .qword (_rtld_bind_start) // PLTGOT[2] * .qword (object pointer) // PLTGOT[3] * ent0: ldah at, 0 // at := 24*0 * lda at, 0(at) // = sizeof(Elf_Rela)*index * br _PROCEDURE_LINKAGE_TABLE_ * ent1: ldah at, 0 // at := 24*1 * lda at, 24(at) // = sizeof(Elf_Rela)*index * br _PROCEDURE_LINKAGE_TABLE_ * ent3: ldah at, 0 // at := 24*2 * lda at, 48(at) // = sizeof(Elf_Rela)*index * br _PROCEDURE_LINKAGE_TABLE_ * ... */ NESTED_NOPROFILE(_rtld_bind_start_old, 0, 168, ra, 0, 0) RTLD_BIND_START_PROLOGUE /* Set up the arguments for _rtld_bind. */ ldq a0, 8(pv) /* object structure */ mov at_reg, a1 /* offset of reloc entry */ CALL(_rtld_bind) RTLD_BIND_START_EPILOGUE(imb) END(_rtld_bind_start_old)