1*ce716eebSriastradh/* $NetBSD: rtld_start.S,v 1.25 2024/08/03 21:59:59 riastradh Exp $ */ 2741f18b6Smatt 3741f18b6Smatt/* 4741f18b6Smatt * Copyright 1996 Matt Thomas <matt@3am-software.com> 5d2fd5f83Smycroft * Portions copyright 2002, 2003 Charles M. Hannum <root@ihack.net> 6741f18b6Smatt * All rights reserved. 7741f18b6Smatt * 8741f18b6Smatt * Redistribution and use in source and binary forms, with or without 9741f18b6Smatt * modification, are permitted provided that the following conditions 10741f18b6Smatt * are met: 11741f18b6Smatt * 1. Redistributions of source code must retain the above copyright 12741f18b6Smatt * notice, this list of conditions and the following disclaimer. 13741f18b6Smatt * 2. Redistributions in binary form must reproduce the above copyright 14741f18b6Smatt * notice, this list of conditions and the following disclaimer in the 15741f18b6Smatt * documentation and/or other materials provided with the distribution. 16741f18b6Smatt * 3. The name of the author may not be used to endorse or promote products 17741f18b6Smatt * derived from this software without specific prior written permission. 18741f18b6Smatt * 19741f18b6Smatt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20741f18b6Smatt * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21741f18b6Smatt * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22741f18b6Smatt * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23741f18b6Smatt * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24741f18b6Smatt * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25741f18b6Smatt * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26741f18b6Smatt * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27741f18b6Smatt * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28741f18b6Smatt * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29741f18b6Smatt */ 30741f18b6Smatt 31741f18b6Smatt#include <machine/asm.h> 32741f18b6Smatt 33741f18b6Smatt/* R9 contains the address of PS_STRINGS and since its caller saved, 34741f18b6Smatt * we can just use it. R6 has a backup copy of the stack pointer which 35d2fd5f83Smycroft * we can use as well. 36741f18b6Smatt */ 3786d15d82SmattENTRY(_rtld_start, 0) 38741f18b6Smatt /* Allocate space on the stack for the cleanup and obj_main 39741f18b6Smatt * entries that _rtld() will provide for us. 40741f18b6Smatt */ 410ce5ca14Smatt clrl %fp 420ce5ca14Smatt subl2 $8,%sp 4363465d31Smatt 44d437f652Smycroft movab _DYNAMIC,%r0 45d2fd5f83Smycroft subl3 _GLOBAL_OFFSET_TABLE_,%r0,%r10 4606f9fa98Smycroft pushl %r10 /* relocbase */ 47d437f652Smycroft pushl %r0 /* &_DYNAMIC */ 48d437f652Smycroft calls $2,_rtld_relocate_nonplt_self 49d437f652Smycroft 5006f9fa98Smycroft pushl %r10 /* relocbase */ 5142fb5b53Smycroft pushal 4(%sp) /* sp */ 52d04429c6Smycroft calls $2,_rtld /* entry = _rtld(sp, relocbase) */ 53741f18b6Smatt 540ce5ca14Smatt movq (%sp)+,%r7 /* grab cleanup and obj_main into %r7/%r8 */ 550ce5ca14Smatt jmp 2(%r0) /* jump to entry point + 2 */ 56bf06b103SmattEND(_rtld_start) 57741f18b6Smatt 58741f18b6Smatt/* 59bf06b103Smatt * Lazy binding entry point, called via PLT via JMP into pltgot[1]. 60e26b436cSmatt * SP+4: address to relocation offset 61bf06b103Smatt * SP+0: obj entry points 62741f18b6Smatt */ 6386d15d82SmattALTENTRY(_rtld_bind_start) 64a1f21652Smatt pushl %r1 /* need to preserve r1 */ 65db747c73Smatt movq -8(%fp),%r0 /* get addresses of plt.got & reloc index */ 66e26b436cSmatt pushl (%r1) /* push relocation offset */ 670ce5ca14Smatt pushl %r0 /* push address of obj entry */ 68741f18b6Smatt calls $2,_rtld_bind 69db747c73Smatt 70e26b436cSmatt /* 71e26b436cSmatt * This code checks to see if we got called via a call{s,g} $n,*pcrel32 72e26b436cSmatt * This is by far the most common case (a call indirectly via the PLT). 73e26b436cSmatt */ 74e26b436cSmatt subl3 $7,16(%fp),%r1 /* return address */ 75e26b436cSmatt bicb3 $1,(%r1),%r2 /* fetch opcode of instruction */ 76e26b436cSmatt cmpb $0xfa,%r2 /* is it calls/callg */ 77e26b436cSmatt jneq 20f /* no it isn't */ 78e26b436cSmatt cmpb $0xff,2(%r1) /* and deferred 32-bit PC displacement? */ 79e26b436cSmatt jneq 20f /* no it isn't */ 80db747c73Smatt 81db747c73Smatt /* 82e26b436cSmatt * This makes sure the longword with the PLT's address has been updated 83e26b436cSmatt * to point to the routine's address. If it hasn't, then returning 84e26b436cSmatt * would put us in an infinite loop. Instead we punt and fake up a 85e26b436cSmatt * callframe. 86db747c73Smatt */ 87e26b436cSmatt movl 3(%r1),%r3 /* get displacement */ 88e26b436cSmatt addl2 16(%fp),%r3 /* add ending location */ 89e26b436cSmatt cmpl (%r3),%r0 /* does it contain the routine address? */ 90e26b436cSmatt#ifdef DEBUG 91e26b436cSmatt jneq 30f /* no it doesn't, die */ 92e26b436cSmatt#else 93e26b436cSmatt jneq 20f /* no it doesn't, go fake a new callframe */ 94e26b436cSmatt#endif 95db747c73Smatt 96e26b436cSmatt11: movl %r1,16(%fp) /* backup to the calls/callg */ 97e26b436cSmatt jbc $29,4(%fp),12f /* skip if this was a callg */ 98e26b436cSmatt clrl (%ap) /* clear argument count */ 99a1f21652Smatt12: movl (%sp)+,%r1 /* restore r1 */ 100a1f21652Smatt ret /* return and redo the call */ 101e26b436cSmatt 102e26b436cSmatt#if 1 103e26b436cSmatt20: 104db747c73Smatt /* 105e26b436cSmatt * Since the calling standard says only r6-r11 should be saved, 106e26b436cSmatt * that simplies things for us. That means we can use r0-r5 as 107e26b436cSmatt * temporaries without worrying about preserving them. This means 108e26b436cSmatt * can hold the current fixed callframe in r2-r5 as we build the 109e26b436cSmatt * callframe without having to worry about overwriting the existing 110e26b436cSmatt * callframe. 111db747c73Smatt */ 112e26b436cSmatt extzv $0,$12,(%r0),%r1/* get routine's save mask */ 113e26b436cSmatt bitw $0x3f,%r1 /* does the routine use r0-r5? */ 114e26b436cSmatt jneq 30f /* yes, that sucks */ 115e26b436cSmatt jbc $29,4(%fp),27f /* handle callg */ 116e26b436cSmatt movq 4(%fp),%r2 /* fetch callframe status & saved AP */ 117e26b436cSmatt movq 12(%fp),%r4 /* fetch callframe saved FP & PC */ 118e26b436cSmatt insv %r1,$16,$12,%r2 /* update save mask */ 119a1f21652Smatt movl (%sp)+,%fp /* use fp to keep saved r1 */ 120e26b436cSmatt movl %ap,%sp /* reset stack to top of callframe */ 121e26b436cSmatt22: pushr %r1 /* push registers */ 122e26b436cSmatt movq %r4,-(%sp) /* push callframe saved FP & PC */ 123e26b436cSmatt movq %r2,-(%sp) /* push callframe status & saved AP */ 124e26b436cSmatt pushl $0 /* push condition handler */ 125a1f21652Smatt movl %fp,%r1 /* restore r1 */ 126e26b436cSmatt movl %sp,%fp /* sp == fp now */ 127e26b436cSmatt#if 1 128e26b436cSmatt jmp 2(%r0) /* jump past entry mask */ 129e26b436cSmatt#else 130db747c73Smatt /* 131e26b436cSmatt * More correct but IV/DV are never set so ignore doing this for now. 132db747c73Smatt */ 133e26b436cSmatt movpsl -(%sp) /* push PSL */ 134e26b436cSmatt clrb (%sp) /* clear user flags */ 135e26b436cSmatt jbc $14,(%r0),24f /* IV need to be set? */ 136e26b436cSmatt bisb2 $0x20,(%sp) /* yes, set it. */ 137e26b436cSmatt24: jbc $15,(%r0),25f /* DV need to be set? */ 138e26b436cSmatt bisb2 $0x80,(%sp) /* yes, set it. */ 139e26b436cSmatt25: pushab 2(%r0) /* push address of first instruction */ 140e26b436cSmatt rei /* and go to it (updating PSW) */ 141e26b436cSmatt#endif 142db747c73Smatt 143db747c73Smatt /* 144e26b436cSmatt * Count how many registers are being used for callg. 145db747c73Smatt */ 146e26b436cSmatt27: movl $0x32212110,%r3 /* bit counts */ 147e26b436cSmatt extzv $6,$3,%r1,%r2 /* extract bits 6-8 */ 148e26b436cSmatt ashl $2,%r2,%r2 /* shift by 2 */ 149e26b436cSmatt extzv %r2,$4,%r3,%r4 /* extract count */ 150e26b436cSmatt extzv $9,$3,%r1,%r2 /* extract bits 9-11 */ 151e26b436cSmatt ashl $2,%r2,%r2 /* shift by 2 */ 152e26b436cSmatt extzv %r2,$4,%r3,%r5 /* extract count */ 153e26b436cSmatt movq 4(%fp),%r2 /* fetch callframe status & saved AP */ 154e26b436cSmatt insv %r1,$16,$12,%r2 /* update save mask */ 1555b335481Smatt addl3 %r4,%r5,%r1 /* add counts and discard them */ 156e26b436cSmatt movq 12(%fp),%r4 /* fetch callframe saved FP & PC */ 157e26b436cSmatt moval 20(%fp)[%r1],%sp/* pop callframe */ 158e26b436cSmatt extzv $16,$12,%r2,%r1 /* get save mask back */ 159e26b436cSmatt jbr 22b /* now build the new callframe */ 160e26b436cSmatt 161e26b436cSmatt30: 162e26b436cSmatt calls $0,_C_LABEL(_rtld_die) 163e26b436cSmatt#else 164e26b436cSmatt /* 165e26b436cSmatt * Check to see if called via call? $n,w^off(reg) 166e26b436cSmatt */ 167e26b436cSmatt20: addl2 $2,%r1 /* 16-bit displacement */ 168e26b436cSmatt bicb3 $1,(%r1),%r2 /* fetch opcode of instruction */ 169e26b436cSmatt cmpb $0xfa,%r2 /* is it calls/callg */ 170e26b436cSmatt jneq 30f /* no it isn't */ 171e26b436cSmatt bicb3 $0x1f,2(%r1),%r3/* extract addressing mode */ 172e26b436cSmatt cmpb $0xc0,%r3 /* 16-bit displacement? */ 173e26b436cSmatt jeql 11b /* yes, redo the call */ 174e26b436cSmatt halt 175e26b436cSmatt 176e26b436cSmatt /* 177e26b436cSmatt * Check to see if called via call? $n,b^off(reg) 178e26b436cSmatt */ 179e26b436cSmatt30: incl %r1 /* 8-bit displacement */ 180e26b436cSmatt bicb3 $1,(%r1),%r2 /* fetch opcode of instruction */ 181e26b436cSmatt cmpb $0xfa,%r2 /* is it calls/callg */ 182e26b436cSmatt jneq 40f /* no it isn't */ 183e26b436cSmatt bicb3 $0x1f,2(%r1),%r3/* extract addressing mode */ 184e26b436cSmatt cmpb $0xa0,%r3 /* 8-bit displacement? */ 185e26b436cSmatt jeql 11b /* yes, redo the call */ 186e26b436cSmatt halt 187e26b436cSmatt 188e26b436cSmatt /* 189e26b436cSmatt * Check to see if called via call? $n,(reg) 190e26b436cSmatt */ 191e26b436cSmatt40: incl %r1 /* no displacement */ 192e26b436cSmatt bicb3 $1,(%r1),%r2 /* fetch opcode of instruction */ 193e26b436cSmatt cmpb $0xfa,%r2 /* is it calls/callg */ 194e26b436cSmatt jeql 41f /* yes it is */ 195e26b436cSmatt halt /* no, die die die */ 196e26b436cSmatt41: bicb3 $0x0f,2(%r1),%r2/* extract addressing mode */ 197e26b436cSmatt bicb3 $0xf0,2(%r1),%r3/* extract register */ 198e26b436cSmatt extzv $0,$12,6(%fp),%r4/* extract saved mask */ 199e26b436cSmatt cmpb $0x60,%r2 /* register deferred? */ 200e26b436cSmatt jeql 42f /* yes, deal with it */ 201e26b436cSmatt cmpb $0x90,%r2 /* autoincrement deferred? */ 202e26b436cSmatt jeql 70f /* yes, deal with it */ 203e26b436cSmatt halt /* no, die die die */ 204e26b436cSmatt 205e26b436cSmatt42: cmpw %r4,$0xffc /* did we save r2-r11? */ 206e26b436cSmatt jneq 50f /* no, deal with it */ 207e26b436cSmatt jbc %r3,%r4,43f /* is the register in the saved mask? */ 208e26b436cSmatt 209e26b436cSmatt /* 210e26b436cSmatt * We saved r2-r11, so it's easy to replace the saved register with 211e26b436cSmatt * the right value by indexing into saved register (offset by 8). 212e26b436cSmatt */ 213e26b436cSmatt movl %r0,(20-8)(%fp)[%r3] /* replace address in saved registers */ 214e26b436cSmatt jbr 11b /* go back and redo call */ 215e26b436cSmatt /* 216e26b436cSmatt * Must have been called via r0 or r1 which are saved locally. 217e26b436cSmatt * So move the routine address in the appropriate slot on the stack. 218e26b436cSmatt */ 219e26b436cSmatt43: movl %r0,(%sp)[%r3] 220e26b436cSmatt jbr 11b /* go back and redo call */ 221e26b436cSmatt 222e26b436cSmatt50: jbs %r3,%r4,60f /* is the register in the saved mask? */ 223e26b436cSmatt jbs %r3,$0x3f,43b /* is it r0-r5? */ 224e26b436cSmatt /* 225e26b436cSmatt * The register used for the call was not saved so we need to move 226e26b436cSmatt * the new function address into it so the re-call will use the new 227e26b436cSmatt * address. 228e26b436cSmatt */ 229e26b436cSmatt pushl %r0 /* save function address on the stack */ 230e26b436cSmatt ashl %r5,$1,%r0 /* create a bitmask for the register */ 231e26b436cSmatt popr %r0 /* pop it off the stack. */ 232e26b436cSmatt jbr 11b /* and redo the call */ 233e26b436cSmatt 234e26b436cSmatt60: clrl %r2 /* starting offset into saved registers */ 235e26b436cSmatt clrl %r5 /* start with register 0 */ 236e26b436cSmatt 237e26b436cSmatt61: cmpl %r2,%r3 /* is the register to save? */ 238e26b436cSmatt jneq 62f /* no, advance to next */ 239e26b436cSmatt movl %r0,20(%fp)[%r5]/* yes, save return address in saved reg */ 240e26b436cSmatt jbr 11b /* and return the call */ 241e26b436cSmatt62: jbc %r5,%r4,63f /* is this register saved? */ 242e26b436cSmatt incl %r5 /* yes, account for it */ 243e26b436cSmatt63: incl %r2 /* increment register number */ 244e26b436cSmatt jbr 61b /* and loop */ 245e26b436cSmatt 246e26b436cSmatt70: cmpb %r3,$12 247e26b436cSmatt blss 71f 248e26b436cSmatt halt 249e26b436cSmatt 250e26b436cSmatt71: cmpw %r4,$0xffc /* did we save r2-r11? */ 251e26b436cSmatt jneq 72f /* no, deal with it */ 252e26b436cSmatt subl2 $4,(20-8)(%fp)[%r3] /* backup incremented register */ 253e26b436cSmatt jbr 11b /* and redo the call. 254e26b436cSmatt 255e26b436cSmatt72: jbs %r3,%r4,80f 256e26b436cSmatt jbs %r3,%3f,74f 257e26b436cSmatt ashl %r5,$1,%r0 /* create a bitmask for the register */ 258e26b436cSmatt pushr %r0 /* pop it off the stack. */ 259e26b436cSmatt subl2 $4,(%sp) /* backup incremented register */ 260e26b436cSmatt popr %r0 /* pop it off the stack. */ 261e26b436cSmatt jbr 11b /* and redo the call. 262e26b436cSmatt 263e26b436cSmatt73: subl2 %4,(%sp)[%r3] /* backup incremented register */ 264e26b436cSmatt jbr 11b /* and redo the call. 265e26b436cSmatt 266e26b436cSmatt80: clrl %r2 /* starting offset into saved registers */ 267e26b436cSmatt clrl %r5 /* start with register 0 */ 268e26b436cSmatt 269e26b436cSmatt81: cmpl %r2,%r3 /* is the register to save? */ 270e26b436cSmatt jneq 82f /* no, advance to next */ 271e26b436cSmatt subl $4,20(%fp)[%r5] /* yes, backup incremented register */ 272e26b436cSmatt jbr 11b /* and return the call */ 273e26b436cSmatt82: jbc %r5,%r4,83f /* is this register saved? */ 274e26b436cSmatt incl %r5 /* yes, account for it */ 275e26b436cSmatt83: incl %r2 /* increment register number */ 276e26b436cSmatt jbr 81b /* and loop */ 277e26b436cSmatt#endif 278bf06b103SmattEND(_rtld_bind_start) 279