| /* |
| * 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 */ |