NetBSD/libexec/ld.aout_so/arch/arm32/md.c

487 lines
11 KiB
C

/* $NetBSD: md.c,v 1.12 2005/12/24 20:59:30 perry Exp $ */
/*
* Copyright (C) 1997 Mark Brinicombe
* Copyright (C) 1997 Causality Limited
* Copyright (C) 1996 Wolfgang Solfrank
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Wolfgang Solfrank.
* This product includes software developed by Causality Limited.
* 4. Neither the name of the University nor the names of its contributors
* may 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.
*/
/* Second cut for arm32 (used to be a simple copy of i386 code) */
#include <sys/param.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <err.h>
#include <fcntl.h>
#include <a.out.h>
#include <stab.h>
#include <string.h>
#ifdef RTLD
#include <machine/sysarch.h>
#include <sys/syscall.h>
#endif /* RTLD */
#include "ld.h"
#ifndef RTLD
/* Pull in the ld(1) bits as well */
#include "ld_i.h"
#endif
#ifdef RTLD
inline void iflush __P((void *addr, int len));
/*
* Flush the instruction cache of the specified address
* Some processors have separate instruction caches and
* as such may need a flush following a jump slot fixup.
*/
inline void
iflush(addr, len)
void *addr;
int len;
{
struct arm32_sync_icache_args p;
/*
* This is not an efficient way to flush a chunk of memory
* if we need to flush lots of small chunks i.e. a jmpslot
* per function call.
*/
p.addr = (u_int)addr;
p.len = len;
__asm volatile("mov r0, %0; mov r1, %1; swi %2"
: : "I" (ARM32_SYNC_ICACHE), "r" (&p), "J" (SYS_sysarch));
}
#endif /* RTLD */
/*
* Get relocation addend corresponding to relocation record RP
* from address ADDR
*/
long
md_get_addend(rp, addr)
struct relocation_info *rp;
unsigned char *addr;
{
long rel;
switch (rp->r_length) {
case 0:
rel = get_byte(addr);
break;
case 1:
rel = get_short(addr);
break;
case 2:
rel = get_long(addr);
break;
case 3: /* looks like a special hack for b & bl */
rel = (((long)get_long(addr) & 0xffffff) << 8) >> 6;
/*
* XXX
* Address the addend to be relative to the start of the file
* The implecation of doing this is that this adjustment is
* done every time the reloc goes through ld.
* This means that the adjustment can be applied multiple
* times if ld -r is used to do a partial link.
*
* Solution:
* 1. Put a hack in md_relocate so that PC relative
* relocations are not modified if
* relocatable_output == 1
* 2. Modify the assembler to apply this adjustment.
*/
rel -= rp->r_address;
break;
default:
errx(1, "Unsupported relocation size: %x",
rp->r_length);
}
return rp->r_neg ? -rel : rel; /* Hack to make r_neg work */
}
/*
* Put RELOCATION at ADDR according to relocation record RP.
*/
void
md_relocate(rp, relocation, addr, relocatable_output)
struct relocation_info *rp;
long relocation;
unsigned char *addr;
int relocatable_output;
{
/*
* XXX
* See comments above in md_get_addend
*/
#ifndef RTLD
if (RELOC_PCREL_P(rp) && relocatable_output)
relocation += (rp->r_address + pc_relocation);
if (rp->r_pcrel && rp->r_length == 2 && relocatable_output)
relocation -= rp->r_address;
#endif
if (rp->r_neg) /* Not sure, whether this works in all cases XXX */
relocation = -relocation;
switch (rp->r_length) {
case 0:
put_byte(addr, relocation);
break;
case 1:
put_short(addr, relocation);
break;
case 2:
put_long(addr, relocation);
break;
case 3:
put_long(addr,
(get_long(addr)&0xff000000)
| ((relocation>>2)&0xffffff));
break;
default:
errx(1, "Unsupported relocation size: %x",
rp->r_length);
}
}
/*
* Set up a "direct" transfer (ie. not through the run-time binder) from
* jmpslot at OFFSET to ADDR. Used by `ld' when the SYMBOLIC flag is on,
* and by `ld.so' after resolving the symbol.
*/
#ifdef RTLD
extern void binder_entry __P((void));
#endif
void
md_fix_jmpslot(sp, offset, addr, first)
jmpslot_t *sp;
long offset;
u_long addr;
int first;
{
/*
* For jmpslot 0 (the binder)
* generate
* sub ip, pc, ip
* ldr pc, [pc, #-4]
* .word addr
* <unused>
*
* For jump slots generated by the linker (i.e. -Bsymbolic)
* build a direct jump to the absolute address
* i.e.
* ldr pc, [pc]
* <unused>
* .word new_addr
* <unused>
*
* For jump slots generated by ld.so at run time,
* just modify the address offset since the slot
* will have been created with md_make_jmpslot().
* i.e.
* ldr ip, [pc]
* add pc, pc, ip
* .word new_rel_addr
* <unused>
*/
if (first) {
/* Build binder jump slot */
sp->opcode1 = GETSLOTADDR;
sp->opcode2 = LDRPCADDR;
sp->address = addr;
#ifdef RTLD
iflush(sp, sizeof(jmpslot_t));
#endif /* RTLD */
} else {
#ifdef RTLD
/*
* Change the relative offset to the binder
* into a relative offset to the function
*/
sp->address = (addr - (long)sp - 12);
#else
/*
* Build a direct transfer jump slot
* as we not doing a run time fixup.
*/
sp->opcode1 = JUMP;
sp->address = addr;
#endif /* RTLD */
}
}
void
md_set_breakpoint(where, savep)
long where;
long *savep;
{
*savep = *(long *)where;
*(long *)where = TRAP;
#ifdef RTLD
iflush((long *)where, sizeof(long));
#endif /* RTLD */
}
#ifndef RTLD
/*
* Machine dependent part of claim_rrs_reloc().
* Set RRS relocation type.
*/
int
md_make_reloc(rp, r, type)
struct relocation_info *rp, *r;
int type;
{
/* Copy most attributes */
r->r_pcrel = rp->r_pcrel;
r->r_length = rp->r_length;
r->r_neg = rp->r_neg;
r->r_baserel = rp->r_baserel;
r->r_jmptable = rp->r_jmptable;
r->r_relative = rp->r_relative;
return 0;
}
/*
* Set up a transfer from jmpslot at OFFSET (relative to the PLT table)
* to the binder slot (which is at offset 0 of the PLT).
*/
void
md_make_jmpslot(sp, offset, index)
jmpslot_t *sp;
long offset;
long index;
{
/*
* Build the jump slot as follows
*
* ldr ip, [pc]
* add pc, pc, ip
* .word rel_addr
* .word reloc_index
*/
sp->opcode1 = GETRELADDR;
sp->opcode2 = ADDPC;
sp->address = - (offset + 12);
sp->reloc_index = index;
}
/*
* Update the relocation record for a RRS jmpslot.
*/
void
md_make_jmpreloc(rp, r, type)
struct relocation_info *rp, *r;
int type;
{
r->r_address += 8;
r->r_pcrel = 0;
r->r_length = 2;
r->r_neg = 0;
r->r_baserel = 0;
r->r_jmptable = 1;
r->r_relative = 0;
}
/*
* Set relocation type for a RRS GOT relocation.
*/
void
md_make_gotreloc(rp, r, type)
struct relocation_info *rp, *r;
int type;
{
r->r_pcrel = 0;
r->r_length = 2;
r->r_neg = 0;
r->r_baserel = 1;
r->r_jmptable = 0;
r->r_relative = 0;
}
/*
* Set relocation type for a RRS copy operation.
*/
void
md_make_cpyreloc(rp, r)
struct relocation_info *rp, *r;
{
r->r_pcrel = 0;
r->r_length = 2;
r->r_neg = 0;
r->r_baserel = 0;
r->r_jmptable = 0;
r->r_relative = 0;
}
/*
* Initialize (output) exec header such that useful values are
* obtained from subsequent N_*() macro evaluations.
*/
void
md_init_header(hp, magic, flags)
struct exec *hp;
int magic, flags;
{
N_SETMAGIC((*hp), magic, MID_ARM6, flags);
/* TEXT_START depends on the value of outheader.a_entry. */
if (!(link_mode & SHAREABLE))
hp->a_entry = PAGSIZ;
}
#endif /* RTLD */
#ifdef NEED_SWAP
/*
* Byte swap routines for cross-linking.
*/
void
md_swapin_exec_hdr(h)
struct exec *h;
{
/* NetBSD: Always leave magic alone */
int skip = 1;
swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip);
}
void
md_swapout_exec_hdr(h)
struct exec *h;
{
/* NetBSD: Always leave magic alone */
int skip = 1;
swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip);
}
void
md_swapout_jmpslot(j, n)
jmpslot_t *j;
int n;
{
for (; n; n--, j++) {
j->opcode1 = md_swap_long(j->opcode1);
j->opcode2 = md_swap_long(j->opcode2);
j->address = md_swap_long(j->address);
j->reloc_index = md_swap_long(j->reloc_index);
}
}
#endif /* NEED_SWAP */
/*
* md_swapin_reloc()
*
* As well as provide bit swapping for cross compiling with different
* endianness we need to munge some of the reloc bits.
* This munging is due to the assemble packing all the PIC related
* relocs so that only 1 extra bit in the reloc structure is needed
* The result is that jmpslot branches are packed as a baserel branch
* Spot this case and internally use a jmptable bit.
*/
void
md_swapin_reloc(r, n)
struct relocation_info *r;
int n;
{
#ifdef NEED_SWAP
int bits;
#endif
for (; n; n--, r++) {
#ifdef NEED_SWAP
r->r_address = md_swap_long(r->r_address);
bits = ((int *)r)[1];
r->r_symbolnum = md_swap_long(bits) & 0x00ffffff;
r->r_pcrel = (bits & 1);
r->r_length = (bits >> 1) & 3;
r->r_extern = (bits >> 3) & 1;
r->r_neg = (bits >> 4) & 1;
r->r_baserel = (bits >> 5) & 1;
r->r_jmptable = (bits >> 6) & 1;
r->r_relative = (bits >> 7) & 1;
#endif
/* Look for PIC relocation */
if (r->r_baserel) {
/* Look for baserel branch */
if (r->r_length == 3 && r->r_pcrel == 0) {
r->r_jmptable = 1;
}
/* Look for GOTPC reloc */
if (r->r_length == 2 && r->r_pcrel == 1)
r->r_baserel = 0;
}
}
}
void
md_swapout_reloc(r, n)
struct relocation_info *r;
int n;
{
#ifdef NEED_SWAP
int bits;
#endif
for (; n; n--, r++) {
/* Look for jmptable relocation */
if (r->r_jmptable && r->r_pcrel == 0 && r->r_length == 3) {
r->r_jmptable = 0;
r->r_baserel = 1;
}
#ifdef NEED_SWAP
r->r_address = md_swap_long(r->r_address);
bits = md_swap_long(r->r_symbolnum) & 0xffffff00;
bits |= (r->r_pcrel & 1);
bits |= (r->r_length & 3) << 1;
bits |= (r->r_extern & 1) << 3;
bits |= (r->r_neg & 1) << 4;
bits |= (r->r_baserel & 1) << 5;
bits |= (r->r_jmptable & 1) << 6;
bits |= (r->r_relative & 1) << 7;
((int *)r)[1] = bits;
#endif
}
}