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