boot_elf.s revision b02637af6dc592eb1f43cb4c74f06268648dbd2d
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <link.h>
#include "machdep.h"
#include "_audit.h"
#if defined(lint)
#include "_rtld.h"
#else
#include <sys/asm_linkage.h>
.file "boot_elf.s"
.seg ".text"
#endif
/*
* We got here because the initial call to a function resolved to a procedure
* linkage table entry. That entry did a branch to the first PLT entry, which
* in turn did a call to elf_rtbndr (refer elf_plt_init()).
*
* the code sequence that got us here was:
*
* PLT entry for foo():
* sethi (.-PLT0), %g1 ! not changed by rtld
* ba,a .PLT0 ! patched atomically 2nd
* nop ! patched first
*
* Therefore on entry, %i7 has the address of the call, which will be added
* to the offset to the plt entry in %g1 to calculate the plt entry address
* we must also subtract 4 because the address of PLT0 points to the
* save instruction before the call.
*
* the plt entry is rewritten:
*
* PLT entry for foo():
* sethi (.-PLT0), %g1
* sethi %hi(entry_pt), %g1
* jmpl %g1 + %lo(entry_pt), %g0
*/
#if defined(lint)
static void
{
}
#else
.align 4
#endif
#if defined(lint)
void
{
/* LINTED */
/* iflush(base + len) */;
}
#else
bge,a 1b
#endif
/*
* Initialize the first plt entry so that function calls go to elf_rtbndr
*
* The first plt entry (PLT0) is:
*
* save %sp, -64, %sp
* call elf_rtbndr
* nop
* address of lm
*/
#if defined(lint)
void
{
((unsigned long)plt)) >> 2);
}
#else
.align 4
1:
call 2f
2:
#endif
#if defined(lint)
{
return (0);
}
#else
.align 4
/*
* The dyn_plt that called us has already created a stack-frame for
* us and placed the following entries in it:
*
* [%fp - 0x4] * dyndata
* [%fp - 0x8] * prev stack size
*
* dyndata currently contains:
*
* dyndata:
* 0x0 uintptr_t *reflmp
* 0x4 uintptr_t *deflmp
* 0x8 ulong_t symndx
* 0xc ulong_t sb_flags
* 0x10 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
/*
* save all registers into La_sparcv8_regs
*/
st %i6, [%o4 + 0x18] ! the %o* registers that the final
st %i7, [%o4 + 0x1c] ! procedure shall see.
ld [%fp + -0x4], %l1 ! %l1 == * dyndata
ld [%l1 + REFLMP_OFF], %o0 ! %o0 = reflmp
ld [%l1 + DEFLMP_OFF], %o1 ! %o1 = deflmp
add %l1, SYMDEF_OFF, %o2 ! %o2 = symp
ld [%l1 + SYMNDX_OFF], %o3 ! %o3 = symndx
call audit_pltenter
add %l1, SBFLAGS_OFF, %o5 ! %o3 = * sb_flags
mov %o0, %l0 ! %l0 == calling address
add %sp, 0x20, %sp ! cleanup La_sparcv8_regs off
! of the stack.
.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 self from the stack at the same time.
*/
ld [%l7+audit_flags], %l3
ld [%l3], %l3 ! %l3 = audit_flags
andcc %l3, AF_PLTEXIT, %g0
beq .bypass_pltexit
ld [%fp + -0x4], %l1 ! %l1 = * dyndata
ld [%l1 + SBFLAGS_OFF], %l2 ! %l2 = sb_flags
andcc %l2, LA_SYMB_NOPLTEXIT, %g0
bne .bypass_pltexit
nop
ba .start_pltexit
nop
.bypass_pltexit:
jmpl %l0, %g0
restore
.start_pltexit:
/*
* In order to call la_pltexit() we must duplicate the
*
* First we check the size of the callers stack and grow
* our stack to hold any of the arguments. That need
* duplicating (these are arguments 6->N), because the
* first 6 (0->5) are passed via register windows on sparc.
*/
/*
* The first calculation is to determine how large the
* argument passing area might be. Since there is no
* cover both.
*/
ld [%fp + -0x8], %l1 ! %l1 = callers stack size
sub %l1, 0x58, %l1 ! %l1 = argument space on caller's
! stack
/*
* Next we compare the prev. stack size against the audit_argcnt.
* We copy at most 'audit_argcnt' arguments.
*
* NOTE: on sparc we always copy at least six args since these
* are in reg-windows and not on the stack.
*
* NOTE: Also note that we multiply (shift really) the arg count
* by 4 which is the 'word size' to calculate the amount
* of stack space needed.
*/
/*
* When duplicating the stack we skip the first '0x5c' bytes.
* This is the space on the stack reserved for preserving
* the register windows and such and do not need to be duplicated
* on this new stack frame. We start duplicating at the
* portion of the stack reserved for argument's above 6.
*/
1:
ble 2f
ba 1b
2:
#endif
/*
* After the first call to a plt, elf_bndr() will have determined the true
* address of the function being bound. The plt is now rewritten so that
* any subsequent calls go directly to the bound function. If the library
* to which the function belongs is being profiled refer to _plt_cg_write.
*
* the new plt entry is:
*
* sethi (.-PLT0), %g1 ! constant
* sethi %hi(function address), %g1 ! patched second
* jmpl %g1 + %lo(function address, %g0 ! patched first
*/
#if defined(lint)
void
{
}
#else
st %o3, [%o0 + 8] ! Store instruction in plt[2]
iflush %o0 + 8
stbar
srl %o1, 10, %o1 ! Get high part of function address
sethi %hi(M_SETHIG1), %o3 ! Get sethi instruction
or %o3, %o1, %o3 ! Add sethi and function address
st %o3, [%o0 + 4] ! Store instruction in plt[1]
retl
iflush %o0 + 4
SET_SIZE(plt_full_range)
#endif /* defined(lint) */