root/usr/src/cmd/sgs/rtld/i386/boot_elf.S
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 *      Copyright (c) 1988 AT&T
 *        All Rights Reserved
 *
 *
 *      Copyright 2000-2002 Sun Microsystems, Inc.  All rights reserved.
 *      Use is subject to license terms.
 */

#if     defined(lint)

#include        <sys/types.h>
#include        "_rtld.h"
#include        "_audit.h"
#include        "_elf.h"

/* ARGSUSED0 */
int
elf_plt_trace()
{
        return (0);
}
#else

#include        <link.h>
#include        "_audit.h"

        .file   "boot_elf.s"
        .text

/*
 * On entry the 'glue code' has already  done the following:
 *
 *      pushl   %ebp
 *      movl    %esp, %ebp
 *      pushl   dyndata_ptr
 *      jmp     elf_plt_trace
 *
 * so - -4(%ebp) contains the dyndata ptr
 *
 *      0x0     uintptr_t       reflmp
 *      0x4     uintptr_t       deflmp
 *      0x8     ulong_t         symndx
 *      0xc     ulont_t         sb_flags
 *      0x10    Elf32_Sym       symdef.st_name
 *      0x14                    symdef.st_value
 *      0x18                    symdef.st_size
 *      0x1c                    symdef.st_info
 *      0x1d                    symdef.st_other
 *      0x1e                    symdef.st_shndx
 */
#define REFLMP_OFF              0x0
#define DEFLMP_OFF              0x4
#define SYMNDX_OFF              0x8
#define SBFLAGS_OFF             0xc
#define SYMDEF_OFF              0x10
#define SYMDEF_VALUE_OFF        0x14

        .globl  elf_plt_trace
        .type   elf_plt_trace,@function
        .align 16
elf_plt_trace:
        subl    $84,%esp                        / create some local storage
        pushl   %eax
        pushl   %ebx
        pushl   %edi
        pushl   %esi
        call    .L1                             / initialize %ebx to GOT
.L1:
        popl    %ebx
        addl    $_GLOBAL_OFFSET_TABLE_+[.-.L1], %ebx
        /*
         * Local stack space storage is allocated as follows:
         *
         *      -4(%ebp)        store dyndata ptr
         *      -8(%ebp)        store call destination
         *      -84(%ebp)       space for gregset
         *      -88(%ebp)       prev stack size
         *      -92(%ebp)       entering %eax
         *      -96(%ebp)       entering %ebx
         *      -100(%ebp)      entering %edi
         *      -104(%ebp)      entering %esi
         */
        movl    -4(%ebp), %eax                  / %eax = dyndata
        testb   $LA_SYMB_NOPLTENTER, 0xc(%eax)  / <link.h>
        je      .start_pltenter
        movl    SYMDEF_VALUE_OFF(%eax), %edi
        movl    %edi, -8(%ebp)                  / save destination address
        jmp     .end_pltenter

.start_pltenter:
        /*
         * save all registers into gregset_t
         */
        lea     4(%ebp), %edi
        movl    %edi, -84(%ebp)         / %esp
        movl    0(%ebp), %edi
        movl    %edi, -80(%ebp)         / %ebp
        /*
         * trapno, err, eip, cs, efl, uesp, ss
         */
        movl    -4(%ebp), %edi
        lea     SBFLAGS_OFF(%edi), %eax
        pushl   %eax                            / arg5 (&sb_flags)
        lea     -84(%ebp), %eax
        pushl   %eax                            / arg4 (regset)
        pushl   SYMNDX_OFF(%edi)                / arg3 (symndx)
        lea     SYMDEF_OFF(%edi), %eax
        pushl   %eax                            / arg2 (&sym)
        pushl   DEFLMP_OFF(%edi)                / arg1 (dlmp)
        pushl   REFLMP_OFF(%edi)                / arg0 (rlmp)
        call    audit_pltenter@PLT
        addl    $24, %esp                       / cleanup stack
        movl    %eax, -8(%ebp)                  / save calling address
.end_pltenter:

        /*
         * If *no* la_pltexit() routines exist
         * we do not need to keep the stack frame
         * before we call the actual routine.  Instead we
         * jump to it and remove our stack from the stack
         * at the same time.
         */
        movl    audit_flags@GOT(%ebx), %eax
        movl    (%eax), %eax
        andl    $AF_PLTEXIT, %eax               / value of audit.h:AF_PLTEXIT
        cmpl    $0, %eax
        je      .bypass_pltexit
        /*
         * Has the *nopltexit* flag been set for this entry point
         */
        testb   $LA_SYMB_NOPLTEXIT, 12(%edi)
        je      .start_pltexit

.bypass_pltexit:
        /*
         * No PLTEXIT processing required.
         */
        movl    0(%ebp), %eax
        movl    %eax, -4(%ebp)
        movl    -8(%ebp), %eax                  / eax == calling destination
        movl    %eax, 0(%ebp)                   / store destination at top

        popl    %esi                            /
        popl    %edi                            /    clean up stack
        popl    %ebx                            /
        popl    %eax                            /
        subl    $4, %ebp                        / adjust %ebp for 'ret'
        /*
         * At this point, after a little doctoring, we should
         * have the following on the stack:
         *
         *      8(%esp):  ret addr
         *      4(%esp):  dest_addr
         *      0(%esp):  Previous %ebp
         *
         * So - we pop the previous %ebp, and then
         * ret to our final destination.
         */
        movl    %ebp, %esp                      /
        popl    %ebp                            /
        ret                                     / jmp to final destination
                                                / and clean up stack :)

.start_pltexit:

        /*
         * In order to call the destination procedure and then return
         * to audit_pltexit() for post analysis we must first grow
         * our stack frame and then duplicate the original callers
         * stack state.  This duplicates all of the arguements
         * that were to be passed to the destination procedure.
         */
        movl    %ebp, %edi                      /
        addl    $8, %edi                        /    %edi = src
        movl    (%ebp), %edx                    /
        subl    %edi, %edx                      /    %edx == prev frame sz
        /*
         * If audit_argcnt > 0 then we limit the number of
         * arguements that will be duplicated to audit_argcnt.
         *
         * If (prev_stack_size > (audit_argcnt * 4))
         *      prev_stack_size = audit_argcnt * 4;
         */
        movl    audit_argcnt@GOT(%ebx),%eax
        movl    (%eax), %eax                    /    %eax = audit_argcnt
        cmpl    $0, %eax
        jle     .grow_stack
        lea     (,%eax,4), %eax                 /    %eax = %eax * 4
        cmpl    %eax,%edx
        jle     .grow_stack
        movl    %eax, %edx
        /*
         * Grow the stack and duplicate the arguements of the
         * original caller.
         */
.grow_stack:
        subl    %edx, %esp                      /    grow the stack
        movl    %edx, -88(%ebp)                 /    -88(%ebp) == prev frame sz
        movl    %esp, %ecx                      /    %ecx = dest
        addl    %ecx, %edx                      /    %edx == tail of dest
.while_base:
        cmpl    %edx, %ecx                      /   while (base+size >= src++) {
        jge     .end_while                              /
        movl    (%edi), %esi
        movl    %esi,(%ecx)                     /        *dest = *src
        addl    $4, %edi                        /        src++
        addl    $4, %ecx                        /        dest++
        jmp     .while_base                     /    }

        /*
         * The above stack is now an exact duplicate of
         * the stack of the original calling procedure.
         */
.end_while:
        movl    -92(%ebp), %eax                 / restore %eax
        movl    -96(%ebp), %ebx                 / restore %ebx
        movl    -104(%ebp), %esi                / restore %esi

        movl    -8(%ebp), %edi
        call    *%edi                           / call dest_proc()

        addl    -88(%ebp), %esp                 / cleanup dupped stack

        movl    -4(%ebp), %edi
        pushl   SYMNDX_OFF(%edi)                / arg4 (symndx)
        lea     SYMDEF_OFF(%edi), %ecx
        pushl   %ecx                            / arg3 (symp)
        pushl   DEFLMP_OFF(%edi)                / arg2 (dlmp)
        pushl   REFLMP_OFF(%edi)                / arg1 (rlmp)
        pushl   %eax                            / arg0 (retval)
        call    audit_pltexit@PLT
        addl    $20, %esp                       / cleanup stack

        /*
         * Clean up after ourselves and return to the
         * original calling procedure.
         */
        popl    %esi                            /
        popl    %edi                            / clean up stack
        popl    %ebx                            /
        movl    %ebp, %esp                      /
        popl    %ebp                            /
        ret                                     / return to caller
        .size   elf_plt_trace, .-elf_plt_trace
#endif

/*
 * We got here because a call to a function resolved to a procedure
 * linkage table entry.  That entry did a JMPL to the first PLT entry, which
 * in turn did a call to elf_rtbndr.
 *
 * the code sequence that got us here was:
 *
 * PLT entry for foo:
 *      jmp     *name1@GOT(%ebx)
 *      pushl   $rel.plt.foo
 *      jmp     PLT0
 *
 * 1st PLT entry (PLT0):
 *      pushl   4(%ebx)
 *      jmp     *8(%ebx)
 *      nop; nop; nop;nop;
 *
 */
#if defined(lint)

extern unsigned long    elf_bndr(Rt_map *, unsigned long, caddr_t);

void
elf_rtbndr(Rt_map * lmp, unsigned long reloc, caddr_t pc)
{
        (void) elf_bndr(lmp, reloc, pc);
}

#else
        .globl  elf_bndr
        .globl  elf_rtbndr
        .weak   _elf_rtbndr
        _elf_rtbndr = elf_rtbndr        / Make dbx happy
        .type   elf_rtbndr,@function
        .align  4

elf_rtbndr:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %eax
        pushl   %ecx
        pushl   %edx
        pushl   12(%ebp)                / push pc
        pushl   8(%ebp)                 / push reloc
        pushl   4(%ebp)                 / push *lmp
        call    elf_bndr@PLT            / call the C binder code
        addl    $12, %esp               / pop args
        movl    %eax, 8(%ebp)           / store final destination
        popl    %edx
        popl    %ecx
        popl    %eax
        movl    %ebp, %esp
        popl    %ebp
        addl    $4,%esp                 / pop args
        ret                             / invoke resolved function
        .size   elf_rtbndr, .-elf_rtbndr
#endif