/* $NetBSD: rtld_start.S,v 1.24 2014/08/17 16:57:37 matt Exp $ */
/*
* Copyright 1996 Matt Thomas <matt@3am-software.com>
* Portions copyright 2002, 2003 Charles M. Hannum <root@ihack.net>
* 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 <machine/asm.h>
/* R9 contains the address of PS_STRINGS and since its caller saved,
* we can just use it. R6 has a backup copy of the stack pointer which
* we can use as well.
*/
ENTRY(_rtld_start, 0)
/* Allocate space on the stack for the cleanup and obj_main
* entries that _rtld() will provide for us.
*/
clrl %fp
subl2 $8,%sp
movab _DYNAMIC,%r0
subl3 _GLOBAL_OFFSET_TABLE_,%r0,%r10
pushl %r10 /* relocbase */
pushl %r0 /* &_DYNAMIC */
calls $2,_rtld_relocate_nonplt_self
pushl %r10 /* relocbase */
pushal 4(%sp) /* sp */
calls $2,_rtld /* entry = _rtld(sp, relocbase) */
movq (%sp)+,%r7 /* grab cleanup and obj_main into %r7/%r8 */
jmp 2(%r0) /* jump to entry point + 2 */
END(_rtld_start)
/*
* Lazy binding entry point, called via PLT via JMP into pltgot[1].
* SP+4: address to relocation offset
* SP+0: obj entry points
*/
ALTENTRY(_rtld_bind_start)
pushl %r1 /* need to preserve r1 */
movq -8(%fp),%r0 /* get addresses of plt.got & reloc index */
pushl (%r1) /* push relocation offset */
pushl %r0 /* push address of obj entry */
calls $2,_rtld_bind
/*
* This code checks to see if we got called via a call{s,g} $n,*pcrel32
* This is by far the most common case (a call indirectly via the PLT).
*/
subl3 $7,16(%fp),%r1 /* return address */
bicb3 $1,(%r1),%r2 /* fetch opcode of instruction */
cmpb $0xfa,%r2 /* is it calls/callg */
jneq 20f /* no it isn't */
cmpb $0xff,2(%r1) /* and deferred 32-bit PC displacement? */
jneq 20f /* no it isn't */
/*
* This makes sure the longword with the PLT's address has been updated
* to point to the routine's address. If it hasn't, then returning
* would put us in an infinite loop. Instead we punt and fake up a
* callframe.
*/
movl 3(%r1),%r3 /* get displacement */
addl2 16(%fp),%r3 /* add ending location */
cmpl (%r3),%r0 /* does it contain the routine address? */
#ifdef DEBUG
jneq 30f /* no it doesn't, die */
#else
jneq 20f /* no it doesn't, go fake a new callframe */
#endif
11: movl %r1,16(%fp) /* backup to the calls/callg */
jbc $29,4(%fp),12f /* skip if this was a callg */
clrl (%ap) /* clear argument count */
12: movl (%sp)+,%r1 /* restore r1 */
ret /* return and redo the call */
#if 1
20:
/*
* Since the calling standard says only r6-r11 should be saved,
* that simplies things for us. That means we can use r0-r5 as
* temporaries without worrying about preserving them. This means
* can hold the current fixed callframe in r2-r5 as we build the
* callframe without having to worry about overwriting the existing
* callframe.
*/
extzv $0,$12,(%r0),%r1/* get routine's save mask */
bitw $0x3f,%r1 /* does the routine use r0-r5? */
jneq 30f /* yes, that sucks */
jbc $29,4(%fp),27f /* handle callg */
movq 4(%fp),%r2 /* fetch callframe status & saved AP */
movq 12(%fp),%r4 /* fetch callframe saved FP & PC */
insv %r1,$16,$12,%r2 /* update save mask */
movl (%sp)+,%fp /* use fp to keep saved r1 */
movl %ap,%sp /* reset stack to top of callframe */
22: pushr %r1 /* push registers */
movq %r4,-(%sp) /* push callframe saved FP & PC */
movq %r2,-(%sp) /* push callframe status & saved AP */
pushl $0 /* push condition handler */
movl %fp,%r1 /* restore r1 */
movl %sp,%fp /* sp == fp now */
#if 1
jmp 2(%r0) /* jump past entry mask */
#else
/*
* More correct but IV/DV are never set so ignore doing this for now.
*/
movpsl -(%sp) /* push PSL */
clrb (%sp) /* clear user flags */
jbc $14,(%r0),24f /* IV need to be set? */
bisb2 $0x20,(%sp) /* yes, set it. */
24: jbc $15,(%r0),25f /* DV need to be set? */
bisb2 $0x80,(%sp) /* yes, set it. */
25: pushab 2(%r0) /* push address of first instruction */
rei /* and go to it (updating PSW) */
#endif
/*
* Count how many registers are being used for callg.
*/
27: movl $0x32212110,%r3 /* bit counts */
extzv $6,$3,%r1,%r2 /* extract bits 6-8 */
ashl $2,%r2,%r2 /* shift by 2 */
extzv %r2,$4,%r3,%r4 /* extract count */
extzv $9,$3,%r1,%r2 /* extract bits 9-11 */
ashl $2,%r2,%r2 /* shift by 2 */
extzv %r2,$4,%r3,%r5 /* extract count */
movq 4(%fp),%r2 /* fetch callframe status & saved AP */
insv %r1,$16,$12,%r2 /* update save mask */
addl3 %r4,%r5,%r1 /* add counts and discard them */
movq 12(%fp),%r4 /* fetch callframe saved FP & PC */
moval 20(%fp)[%r1],%sp/* pop callframe */
extzv $16,$12,%r2,%r1 /* get save mask back */
jbr 22b /* now build the new callframe */
30:
calls $0,_C_LABEL(_rtld_die)
#else
/*
* Check to see if called via call? $n,w^off(reg)
*/
20: addl2 $2,%r1 /* 16-bit displacement */
bicb3 $1,(%r1),%r2 /* fetch opcode of instruction */
cmpb $0xfa,%r2 /* is it calls/callg */
jneq 30f /* no it isn't */
bicb3 $0x1f,2(%r1),%r3/* extract addressing mode */
cmpb $0xc0,%r3 /* 16-bit displacement? */
jeql 11b /* yes, redo the call */
halt
/*
* Check to see if called via call? $n,b^off(reg)
*/
30: incl %r1 /* 8-bit displacement */
bicb3 $1,(%r1),%r2 /* fetch opcode of instruction */
cmpb $0xfa,%r2 /* is it calls/callg */
jneq 40f /* no it isn't */
bicb3 $0x1f,2(%r1),%r3/* extract addressing mode */
cmpb $0xa0,%r3 /* 8-bit displacement? */
jeql 11b /* yes, redo the call */
halt
/*
* Check to see if called via call? $n,(reg)
*/
40: incl %r1 /* no displacement */
bicb3 $1,(%r1),%r2 /* fetch opcode of instruction */
cmpb $0xfa,%r2 /* is it calls/callg */
jeql 41f /* yes it is */
halt /* no, die die die */
41: bicb3 $0x0f,2(%r1),%r2/* extract addressing mode */
bicb3 $0xf0,2(%r1),%r3/* extract register */
extzv $0,$12,6(%fp),%r4/* extract saved mask */
cmpb $0x60,%r2 /* register deferred? */
jeql 42f /* yes, deal with it */
cmpb $0x90,%r2 /* autoincrement deferred? */
jeql 70f /* yes, deal with it */
halt /* no, die die die */
42: cmpw %r4,$0xffc /* did we save r2-r11? */
jneq 50f /* no, deal with it */
jbc %r3,%r4,43f /* is the register in the saved mask? */
/*
* We saved r2-r11, so it's easy to replace the saved register with
* the right value by indexing into saved register (offset by 8).
*/
movl %r0,(20-8)(%fp)[%r3] /* replace address in saved registers */
jbr 11b /* go back and redo call */
/*
* Must have been called via r0 or r1 which are saved locally.
* So move the routine address in the appropriate slot on the stack.
*/
43: movl %r0,(%sp)[%r3]
jbr 11b /* go back and redo call */
50: jbs %r3,%r4,60f /* is the register in the saved mask? */
jbs %r3,$0x3f,43b /* is it r0-r5? */
/*
* The register used for the call was not saved so we need to move
* the new function address into it so the re-call will use the new
* address.
*/
pushl %r0 /* save function address on the stack */
ashl %r5,$1,%r0 /* create a bitmask for the register */
popr %r0 /* pop it off the stack. */
jbr 11b /* and redo the call */
60: clrl %r2 /* starting offset into saved registers */
clrl %r5 /* start with register 0 */
61: cmpl %r2,%r3 /* is the register to save? */
jneq 62f /* no, advance to next */
movl %r0,20(%fp)[%r5]/* yes, save return address in saved reg */
jbr 11b /* and return the call */
62: jbc %r5,%r4,63f /* is this register saved? */
incl %r5 /* yes, account for it */
63: incl %r2 /* increment register number */
jbr 61b /* and loop */
70: cmpb %r3,$12
blss 71f
halt
71: cmpw %r4,$0xffc /* did we save r2-r11? */
jneq 72f /* no, deal with it */
subl2 $4,(20-8)(%fp)[%r3] /* backup incremented register */
jbr 11b /* and redo the call.
72: jbs %r3,%r4,80f
jbs %r3,%3f,74f
ashl %r5,$1,%r0 /* create a bitmask for the register */
pushr %r0 /* pop it off the stack. */
subl2 $4,(%sp) /* backup incremented register */
popr %r0 /* pop it off the stack. */
jbr 11b /* and redo the call.
73: subl2 %4,(%sp)[%r3] /* backup incremented register */
jbr 11b /* and redo the call.
80: clrl %r2 /* starting offset into saved registers */
clrl %r5 /* start with register 0 */
81: cmpl %r2,%r3 /* is the register to save? */
jneq 82f /* no, advance to next */
subl $4,20(%fp)[%r5] /* yes, backup incremented register */
jbr 11b /* and return the call */
82: jbc %r5,%r4,83f /* is this register saved? */
incl %r5 /* yes, account for it */
83: incl %r2 /* increment register number */
jbr 81b /* and loop */
#endif
END(_rtld_bind_start)