root/libexec/ld.elf_so/arch/vax/rtld_start.S
/*      $NetBSD: rtld_start.S,v 1.25 2024/08/03 21:59:59 riastradh 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)