blob: 463abdb6392f92413b4126aa8b00c473a806b167 [file] [log] [blame]
/*
* reloc.c
*
* DSP-BIOS Bridge driver support functions for TI OMAP processors.
*
* Copyright (C) 2005-2006 Texas Instruments, Inc.
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "header.h"
#if TMS32060
/* the magic symbol for the start of BSS */
static const char bsssymbol[] = { ".bss" };
#endif
#if TMS32060
#include "reloc_table_c6000.c"
#endif
#if TMS32060
/* From coff.h - ignore these relocation operations */
#define R_C60ALIGN 0x76 /* C60: Alignment info for compressor */
#define R_C60FPHEAD 0x77 /* C60: Explicit assembly directive */
#define R_C60NOCMP 0x100 /* C60: Don't compress this code scn */
#endif
/**************************************************************************
* Procedure dload_unpack
*
* Parameters:
* data pointer to storage unit containing lowest host address of
* image data
* fieldsz Size of bit field, 0 < fieldsz <= sizeof(rvalue)*BITS_PER_AU
* offset Offset from LSB, 0 <= offset < BITS_PER_AU
* sgn Signedness of the field (ROP_SGN, ROP_UNS, ROP_MAX, ROP_ANY)
*
* Effect:
* Extracts the specified field and returns it.
************************************************************************* */
rvalue dload_unpack(struct dload_state *dlthis, tgt_au_t *data, int fieldsz,
int offset, unsigned sgn)
{
register rvalue objval;
register int shift, direction;
register tgt_au_t *dp = data;
fieldsz -= 1; /* avoid nastiness with 32-bit shift of 32-bit value */
/* * collect up enough bits to contain the desired field */
if (TARGET_BIG_ENDIAN) {
dp += (fieldsz + offset) >> LOG_TGTAU_BITS;
direction = -1;
} else
direction = 1;
objval = *dp >> offset;
shift = TGTAU_BITS - offset;
while (shift <= fieldsz) {
dp += direction;
objval += (rvalue) *dp << shift;
shift += TGTAU_BITS;
}
/* * sign or zero extend the value appropriately */
if (sgn == ROP_UNS)
objval &= (2 << fieldsz) - 1;
else {
shift = sizeof(rvalue) * BITS_PER_AU - 1 - fieldsz;
objval = (objval << shift) >> shift;
}
return objval;
} /* dload_unpack */
/**************************************************************************
* Procedure dload_repack
*
* Parameters:
* val Value to insert
* data Pointer to storage unit containing lowest host address of
* image data
* fieldsz Size of bit field, 0 < fieldsz <= sizeof(rvalue)*BITS_PER_AU
* offset Offset from LSB, 0 <= offset < BITS_PER_AU
* sgn Signedness of the field (ROP_SGN, ROP_UNS, ROP_MAX, ROP_ANY)
*
* Effect:
* Stuffs the specified value in the specified field. Returns 0 for
* success
* or 1 if the value will not fit in the specified field according to the
* specified signedness rule.
************************************************************************* */
static const unsigned char ovf_limit[] = { 1, 2, 2 };
int dload_repack(struct dload_state *dlthis, rvalue val, tgt_au_t *data,
int fieldsz, int offset, unsigned sgn)
{
register urvalue objval, mask;
register int shift, direction;
register tgt_au_t *dp = data;
fieldsz -= 1; /* avoid nastiness with 32-bit shift of 32-bit value */
/* clip the bits */
mask = (2UL << fieldsz) - 1;
objval = (val & mask);
/* * store the bits through the specified mask */
if (TARGET_BIG_ENDIAN) {
dp += (fieldsz + offset) >> LOG_TGTAU_BITS;
direction = -1;
} else
direction = 1;
/* insert LSBs */
*dp = (*dp & ~(mask << offset)) + (objval << offset);
shift = TGTAU_BITS - offset;
/* align mask and objval with AU boundary */
objval >>= shift;
mask >>= shift;
while (mask) {
dp += direction;
*dp = (*dp & ~mask) + objval;
objval >>= TGTAU_BITS;
mask >>= TGTAU_BITS;
}
/*
* check for overflow
*/
if (sgn) {
unsigned tmp = (val >> fieldsz) + (sgn & 0x1);
if (tmp > ovf_limit[sgn - 1])
return 1;
}
return 0;
} /* dload_repack */
/* lookup table for the scaling amount in a C6x instruction */
#if TMS32060
#define SCALE_BITS 4 /* there are 4 bits in the scale field */
#define SCALE_MASK 0x7 /* we really only use the bottom 3 bits */
static const u8 c60_scale[SCALE_MASK + 1] = {
1, 0, 0, 0, 1, 1, 2, 2
};
#endif
/**************************************************************************
* Procedure dload_relocate
*
* Parameters:
* data Pointer to base of image data
* rp Pointer to relocation operation
*
* Effect:
* Performs the specified relocation operation
************************************************************************* */
void dload_relocate(struct dload_state *dlthis, tgt_au_t *data,
struct reloc_record_t *rp, bool *tramps_generated,
bool second_pass)
{
rvalue val, reloc_amt, orig_val = 0;
unsigned int fieldsz = 0;
unsigned int offset = 0;
unsigned int reloc_info = 0;
unsigned int reloc_action = 0;
register int rx = 0;
rvalue *stackp = NULL;
int top;
struct local_symbol *svp = NULL;
#ifdef RFV_SCALE
unsigned int scale = 0;
#endif
struct image_packet_t *img_pkt = NULL;
/* The image packet data struct is only used during first pass
* relocation in the event that a trampoline is needed. 2nd pass
* relocation doesn't guarantee that data is coming from an
* image_packet_t structure. See cload.c, dload_data for how img_data is
* set. If that changes this needs to be updated!!! */
if (second_pass == false)
img_pkt = (struct image_packet_t *)((u8 *) data -
sizeof(struct
image_packet_t));
rx = HASH_FUNC(rp->TYPE);
while (rop_map1[rx] != rp->TYPE) {
rx = HASH_L(rop_map2[rx]);
if (rx < 0) {
#if TMS32060
switch (rp->TYPE) {
case R_C60ALIGN:
case R_C60NOCMP:
case R_C60FPHEAD:
/* Ignore these reloc types and return */
break;
default:
/* Unknown reloc type, print error and return */
dload_error(dlthis, "Bad coff operator 0x%x",
rp->TYPE);
}
#else
dload_error(dlthis, "Bad coff operator 0x%x", rp->TYPE);
#endif
return;
}
}
rx = HASH_I(rop_map2[rx]);
if ((rx < (sizeof(rop_action) / sizeof(u16)))
&& (rx < (sizeof(rop_info) / sizeof(u16))) && (rx > 0)) {
reloc_action = rop_action[rx];
reloc_info = rop_info[rx];
} else {
dload_error(dlthis, "Buffer Overflow - Array Index Out "
"of Bounds");
}
/* Compute the relocation amount for the referenced symbol, if any */
reloc_amt = rp->UVAL;
if (RFV_SYM(reloc_info)) { /* relocation uses a symbol reference */
/* If this is first pass, use the module local symbol table,
* else use the trampoline symbol table. */
if (second_pass == false) {
if ((u32) rp->SYMNDX < dlthis->dfile_hdr.df_no_syms) {
/* real symbol reference */
svp = &dlthis->local_symtab[rp->SYMNDX];
reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ?
svp->delta : svp->value;
}
/* reloc references current section */
else if (rp->SYMNDX == -1) {
reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ?
dlthis->delta_runaddr :
dlthis->image_secn->run_addr;
}
}
}
/* relocation uses a symbol reference */
/* Handle stack adjustment */
val = 0;
top = RFV_STK(reloc_info);
if (top) {
top += dlthis->relstkidx - RSTK_UOP;
if (top >= STATIC_EXPR_STK_SIZE) {
dload_error(dlthis,
"Expression stack overflow in %s at offset "
FMT_UI32, dlthis->image_secn->name,
rp->vaddr + dlthis->image_offset);
return;
}
val = dlthis->relstk[dlthis->relstkidx];
dlthis->relstkidx = top;
stackp = &dlthis->relstk[top];
}
/* Derive field position and size, if we need them */
if (reloc_info & ROP_RW) { /* read or write action in our future */
fieldsz = RFV_WIDTH(reloc_action);
if (fieldsz) { /* field info from table */
offset = RFV_POSN(reloc_action);
if (TARGET_BIG_ENDIAN)
/* make sure vaddr is the lowest target
* address containing bits */
rp->vaddr += RFV_BIGOFF(reloc_info);
} else { /* field info from relocation op */
fieldsz = rp->FIELDSZ;
offset = rp->OFFSET;
if (TARGET_BIG_ENDIAN)
/* make sure vaddr is the lowest target
address containing bits */
rp->vaddr += (rp->WORDSZ - offset - fieldsz)
>> LOG_TARGET_AU_BITS;
}
data = (tgt_au_t *) ((char *)data + TADDR_TO_HOST(rp->vaddr));
/* compute lowest host location of referenced data */
#if BITS_PER_AU > TARGET_AU_BITS
/* conversion from target address to host address may lose
address bits; add loss to offset */
if (TARGET_BIG_ENDIAN) {
offset += -((rp->vaddr << LOG_TARGET_AU_BITS) +
offset + fieldsz) &
(BITS_PER_AU - TARGET_AU_BITS);
} else {
offset += (rp->vaddr << LOG_TARGET_AU_BITS) &
(BITS_PER_AU - 1);
}
#endif
#ifdef RFV_SCALE
scale = RFV_SCALE(reloc_info);
#endif
}
/* read the object value from the current image, if so ordered */
if (reloc_info & ROP_R) {
/* relocation reads current image value */
val = dload_unpack(dlthis, data, fieldsz, offset,
RFV_SIGN(reloc_info));
/* Save off the original value in case the relo overflows and
* we can trampoline it. */
orig_val = val;
#ifdef RFV_SCALE
val <<= scale;
#endif
}
/* perform the necessary arithmetic */
switch (RFV_ACTION(reloc_action)) { /* relocation actions */
case RACT_VAL:
break;
case RACT_ASGN:
val = reloc_amt;
break;
case RACT_ADD:
val += reloc_amt;
break;
case RACT_PCR:
/*-----------------------------------------------------------
* Handle special cases of jumping from absolute sections
* (special reloc type) or to absolute destination
* (symndx == -1). In either case, set the appropriate
* relocation amount to 0.
*----------------------------------------------------------- */
if (rp->SYMNDX == -1)
reloc_amt = 0;
val += reloc_amt - dlthis->delta_runaddr;
break;
case RACT_ADDISP:
val += rp->R_DISP + reloc_amt;
break;
case RACT_ASGPC:
val = dlthis->image_secn->run_addr + reloc_amt;
break;
case RACT_PLUS:
if (stackp != NULL)
val += *stackp;
break;
case RACT_SUB:
if (stackp != NULL)
val = *stackp - val;
break;
case RACT_NEG:
val = -val;
break;
case RACT_MPY:
if (stackp != NULL)
val *= *stackp;
break;
case RACT_DIV:
if (stackp != NULL)
val = *stackp / val;
break;
case RACT_MOD:
if (stackp != NULL)
val = *stackp % val;
break;
case RACT_SR:
if (val >= sizeof(rvalue) * BITS_PER_AU)
val = 0;
else if (stackp != NULL)
val = (urvalue) *stackp >> val;
break;
case RACT_ASR:
if (val >= sizeof(rvalue) * BITS_PER_AU)
val = sizeof(rvalue) * BITS_PER_AU - 1;
else if (stackp != NULL)
val = *stackp >> val;
break;
case RACT_SL:
if (val >= sizeof(rvalue) * BITS_PER_AU)
val = 0;
else if (stackp != NULL)
val = *stackp << val;
break;
case RACT_AND:
if (stackp != NULL)
val &= *stackp;
break;
case RACT_OR:
if (stackp != NULL)
val |= *stackp;
break;
case RACT_XOR:
if (stackp != NULL)
val ^= *stackp;
break;
case RACT_NOT:
val = ~val;
break;
#if TMS32060
case RACT_C6SECT:
/* actually needed address of secn containing symbol */
if (svp != NULL) {
if (rp->SYMNDX >= 0)
if (svp->secnn > 0)
reloc_amt = dlthis->ldr_sections
[svp->secnn - 1].run_addr;
}
/* !!! FALL THRU !!! */
case RACT_C6BASE:
if (dlthis->bss_run_base == 0) {
struct dynload_symbol *symp;
symp = dlthis->mysym->find_matching_symbol
(dlthis->mysym, bsssymbol);
/* lookup value of global BSS base */
if (symp)
dlthis->bss_run_base = symp->value;
else
dload_error(dlthis,
"Global BSS base referenced in %s "
"offset" FMT_UI32 " but not "
"defined",
dlthis->image_secn->name,
rp->vaddr + dlthis->image_offset);
}
reloc_amt -= dlthis->bss_run_base;
/* !!! FALL THRU !!! */
case RACT_C6DSPL:
/* scale factor determined by 3 LSBs of field */
scale = c60_scale[val & SCALE_MASK];
offset += SCALE_BITS;
fieldsz -= SCALE_BITS;
val >>= SCALE_BITS; /* ignore the scale field hereafter */
val <<= scale;
val += reloc_amt; /* do the usual relocation */
if (((1 << scale) - 1) & val)
dload_error(dlthis,
"Unaligned reference in %s offset "
FMT_UI32, dlthis->image_secn->name,
rp->vaddr + dlthis->image_offset);
break;
#endif
} /* relocation actions */
/* * Put back result as required */
if (reloc_info & ROP_W) { /* relocation writes image value */
#ifdef RFV_SCALE
val >>= scale;
#endif
if (dload_repack(dlthis, val, data, fieldsz, offset,
RFV_SIGN(reloc_info))) {
/* Check to see if this relo can be trampolined,
* but only in first phase relocation. 2nd phase
* relocation cannot trampoline. */
if ((second_pass == false) &&
(dload_tramp_avail(dlthis, rp) == true)) {
/* Before generating the trampoline, restore
* the value to its original so the 2nd pass
* relo will work. */
dload_repack(dlthis, orig_val, data, fieldsz,
offset, RFV_SIGN(reloc_info));
if (!dload_tramp_generate(dlthis,
(dlthis->image_secn -
dlthis->ldr_sections),
dlthis->image_offset,
img_pkt, rp)) {
dload_error(dlthis,
"Failed to "
"generate trampoline for "
"bit overflow");
dload_error(dlthis,
"Relocation val " FMT_UI32
" overflows %d bits in %s "
"offset " FMT_UI32, val,
fieldsz,
dlthis->image_secn->name,
dlthis->image_offset +
rp->vaddr);
} else
*tramps_generated = true;
} else {
dload_error(dlthis, "Relocation value "
FMT_UI32 " overflows %d bits in %s"
" offset " FMT_UI32, val, fieldsz,
dlthis->image_secn->name,
dlthis->image_offset + rp->vaddr);
}
}
} else if (top)
*stackp = val;
} /* reloc_value */