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 "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
* ba,a .PLT0 ! patched atomically 2nd
* nop ! patched 1st
* nop
* nop
* nop
* nop
* nop
*
* 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 in one of several ways. For the full 64-bit
* span, the following sequence is generated:
*
* nop
* sethi %hh(entry_pt), %g1
* sethi %lm(entry_pt), %g5
* or %g1, %hm(entry_pt), %g1
* sllx %g1, 32, %g1
* or %g1, %g5, %g5
* jmpl %g5 + %lo(entry_pt), %g0
* nop
*
* Shorter code sequences are possible, depending on reachability
* constraints. Note that 'call' is not as useful as it might seem in
* this context, because it is only capable of plus or minus 2Gbyte
* PC-relative jumps, and the rdpc instruction is very slow.
*
* At the time of writing, the present and future SPARC CPUs that will use
* this code are only capable of addressing the bottom 43-bits and top 43-bits
* of the address space. And since shared libraries are placed at the top
* of the address space, the "top 44-bits" sequence will effectively always be
* used. See elf_plt_write() below. The "top 32-bits" are used when they
* can reach.
*/
#if defined(lint)
/*
* We're called here from .PLTn in a new frame, with %o0 containing
* the result of a sethi (. - .PLT0), and %o1 containing the pc of
* the jmpl instruction we're got here with inside .PLT1
*/
void
{
}
#else
#endif
#if defined(lint)
void
{
}
#else
#endif
/*
* Initialize a plt entry so that function calls go to 'bindfunc'
* (We parameterize the binding function here because we call this
* routine twice - once for PLT0 and once for PLT1 with different
* binding functions.)
*
* The plt entries (PLT0 and PLT1) look like:
*
* save %sp, -176, %sp
* sethi %hh(bindfunc), %l0
* sethi %lm(bindfunc), %l1
* or %l0, %hm(bindfunc), %l0
* sllx %l0, 32, %l0
* or %l0, %l1, %l0
* jmpl %l0 + %lo(bindfunc), %o1
* mov %g1, %o0
*/
#if defined(lint)
#define HH22(x) 0 /* for lint's benefit */
#define LM22(x) 0
#define HM10(x) 0
#define LO10(x) 0
/* ARGSUSED */
void
{
_plt[0] = M_SAVE_SP176SP;
}
#else
#endif
#if defined(lint)
/*
* The V9 ABI assigns the link map identifier, the
* Rt_map pointer, to the start of .PLT2.
*/
void
{
/* LINTED */
}
#else
#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.
*
* For complete 64-bit spanning, the new plt entry is:
*
* nop
* sethi %hh(function address), %g1
* sethi %lm(function address), %g5
* or %g1, %hm(function address), %g1
* sllx %g1, 32, %g1
* or %g1, %g5, %g5
* jmpl %g5, %lo(function address), %g0
* nop
*
* However, shorter instruction sequences are possible and useful.
* This version gets us anywhere in the top 44 bits of the
* address space - since this is where shared objects live most
* of the time, this case is worth optimizing.
*
* nop
* sethi %h44(~function_address), %g5
* xnor %g5, %m44(~function address), %g1
* sllx %g1, 12, %g1
* jmpl %g1 + %l44(function address), %g0
* nop
* nop
* nop
*
* This version gets anywhere in the top 32 bits:
*
* nop
* sethi %hi(~function_address), %g5
* xnor %g5, %lo(~function_address), %g1
* jmpl %g1, %g0
* nop
* nop
* nop
* nop
*
* This version get's us to a destination within
* +- 8megs of the PLT's address:
*
* nop
* ba,a <dest>
* nop
* nop
* nop
* nop
* nop
* nop
*
* This version get's us to a destination within
* +- 2megs of the PLT's address:
*
* nop
* ba,a,pt %icc, <dest>
* nop
* nop
* nop
* nop
* nop
* nop
*
*
* The PLT is written in reverse order to ensure re-entrant behaviour.
* Note that the first two instructions must be overwritten with a
* single stx.
*
* Note that even in the 44-bit case, we deliberately use both %g5 and
* %g1 to prevent anyone accidentally relying on either of them being
* non-volatile across a function call.
*/
#if defined(lint)
/* ARGSUSED */
#endif
#if defined(lint)
void
/* ARGSUSED1 */
{
/* LINTED */
}
#else
!
!
#endif /* defined lint */
#if defined(lint)
void
/* ARGSUSED1 */
{
/* LINTED */
/* LINTED */
}
#else
!
!
st %o3, [%o0 + 0x10] ! store instruction in plt[4]
iflush %o0 + 0x10 ! .. and flush
setuw M_SLLX_G112G1, %o3 ! Get "sllx %g1, 12, %g1" insn
st %o3, [%o0 + 0xc] ! store instruction in plt[3]
not %o1, %o4
setuw M_XNOR_G5G1, %o3 ! Get "xnor %g5, 0, %g1" insn
srlx %o4, 12, %o2 ! get %m44(0 - function address)
and %o2, 0x3ff, %o2 ! pick out bits 21-12
or %o3, %o2, %o3 ! or value into instruction
st %o3, [%o0 + 8] ! store instruction in plt[2]
iflush %o0 + 8 ! .. and flush
setuw M_SETHI_G5, %o3 ! Get "sethi 0x0, %g5" insn
srlx %o4, 22, %o2 ! get %h44(0 - function address)
or %o3, %o2, %o3 ! or value into instruction
setuw M_NOP, %o4 ! Get "nop" instruction
sllx %o4, 32, %o4 ! shift to top of instruction pair
or %o3, %o4, %o3 ! or value into instruction pair
stx %o3, [%o0] ! store instructions into plt[0] plt[1]
retl
iflush %o0 ! .. and flush
SET_SIZE(plt_upper_44)
#endif /* defined(lint) */
#if defined(lint)
void
/* ARGSUSED1 */
plt_full_range(uintptr_t pc, uintptr_t symval)
{
uint_t * plttab = (uint_t *)pc;
plttab[6] = M_JMPL_G5G0 | LO10(symval);
plttab[5] = M_OR_G1G5G5;
plttab[4] = M_SLLX_G132G1;
plttab[3] = M_OR_G1G1 | HM10(symval);
plttab[2] = M_SETHI_G5 | LM22(symval);
*(ulong_t *)pc =
((ulong_t)M_NOP << 32) | (M_SETHI_G1 | HH22(symval));
}
#else
ENTRY(plt_full_range)
!
! Address lies anywhere in 64-bit address space, so use
! full PLT sequence
!
sethi %hi(M_JMPL_G5G0), %o3 ! Get "jmpl %g5, %g0" insn
and %o1, 0x3ff, %o2 ! lower 10 bits of function address
#endif /* defined(lint) */
/*
* performs the 'iflush' instruction on a range of memory.
*/
#if defined(lint)
void
{
/* LINTED */
/* iflush(base + len) */;
}
#else
#endif
#if defined(lint)
{
return (0);
}
#else
/*
* The dyn_plt that called us has already created a stack-frame for
* us and placed the following entries in it:
*
* [%fp + STACK_BIAS + -0x8] * dyndata
* [%fp + STACK_BIAS + -0x10] * prev stack size
*
* dyndata currently contains:
*
* dyndata:
* 0x0 Addr *reflmp
* 0x8 Addr *deflmp
* 0x10 Word symndx
* 0x14 Word sb_flags
* 0x18 Sym symdef.st_name
* 0x1c symdef.st_info
* 0x1d symdef.st_other
* 0x1e symdef.st_shndx
* 0x20 symdef.st_value
* 0x28 symdef.st_size
*/
#define REFLMP_OFF 0x0
#define DEFLMP_OFF 0x8
#define SYMNDX_OFF 0x10
#define SBFLAGS_OFF 0x14
#define SYMDEF_OFF 0x18
#define SYMDEF_VALUE_OFF 0x20
/*
* save all registers into La_sparcv9_regs
*/
stx %i6, [%o4 + 0x30] ! the %o* registers that the final
stx %i7, [%o4 + 0x38] ! procedure shall see.
mov %g4, %l5 ! save g4 (safe across function calls)
ldx [%fp + STACK_BIAS + -CLONGSIZE], %l1 ! %l1 == * dyndata
ldx [%l1 + REFLMP_OFF], %o0 ! %o0 = reflmp
ldx [%l1 + DEFLMP_OFF], %o1 ! %o1 = deflmp
add %l1, SYMDEF_OFF, %o2 ! %o2 = symp
lduw [%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, LAREGSSZ, %sp ! cleanup La_sparcv9_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 ourself
* from the stack at the same time.
*/
ldx [%l7+audit_flags], %l3
lduw [%l3], %l3 ! %l3 = audit_flags
andcc %l3, AF_PLTEXIT, %g0 ! AF_PLTEXIT = 2
be,pt %icc, .bypass_pltexit
ldx [%fp + STACK_BIAS + -CLONGSIZE], %l1 ! %l1 = * dyndata
lduw [%l1 + SBFLAGS_OFF], %l2 ! %l2 = sb_flags
andcc %l2, LA_SYMB_NOPLTEXIT, %g0 ! LA_SYMB_NOPLTEXIT = 2
bne,a,pt %icc, .bypass_pltexit
nop
ba,a,pt %icc, .start_pltexit
nop
.bypass_pltexit:
mov %l5, %g4 ! restore g4
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.
*/
ldx [%fp + STACK_BIAS + -(2 * CLONGSIZE)], %l1 ! %l1 = callers
! stack size
sub %l1, MINFRAME, %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. The default arg count is 64.
*
* 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 8 which is the 'word size' to calculate the amount
* of stack space needed.
*/
/*
* When duplicating the stack we skip the first SA(MINFRAME)
* 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:
2:
#endif