diff options
Diffstat (limited to 'src/core/dsp/ocl_load/C60_DLOAD_REL/c60_reloc.c')
-rw-r--r-- | src/core/dsp/ocl_load/C60_DLOAD_REL/c60_reloc.c | 1101 |
1 files changed, 1101 insertions, 0 deletions
diff --git a/src/core/dsp/ocl_load/C60_DLOAD_REL/c60_reloc.c b/src/core/dsp/ocl_load/C60_DLOAD_REL/c60_reloc.c new file mode 100644 index 0000000..3c79e35 --- /dev/null +++ b/src/core/dsp/ocl_load/C60_DLOAD_REL/c60_reloc.c @@ -0,0 +1,1101 @@ +/* +* c60_reloc.c +* +* Process C6x-specific dynamic relocations for core dynamic loader. +* +* Copyright (C) 2009-2014 Texas Instruments Incorporated - http://www.ti.com/ +* +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 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. +* +* Neither the name of Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS +* "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 COPYRIGHT +* OWNER OR CONTRIBUTORS 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. +* +*/ + +#include <limits.h> +#include "relocate.h" +#include "symtab.h" +#include "c60_elf32.h" +#include "dload_api.h" +#include "util.h" +#include "dload_endian.h" +#include "c60_reloc.h" + +#define MASK(n,s) (((1 << n) - 1) << s) + +/*---------------------------------------------------------------------------*/ +/* C6x Relocations Supported */ +/* */ +/* See the C6000 ELF ABI Specification for more details. */ +/* */ +/* R_C6000_ABS32 | .field X,32 */ +/* R_C6000_ABS16 | .field X,16 */ +/* R_C6000_ABS8 | .field X,8 */ +/* R_C6000_PCR_S21 | B foo */ +/* CALLP foo, B3 */ +/* R_C6000_PCR_S12 | BNOP foo */ +/* R_C6000_PCR_S10 | BPOS foo, A10 */ +/* BDEC foo, A1 */ +/* R_C6000_PCR_S7 | ADDKPC foo, B3, 4 */ +/* R_C6000_ABS_S16 | MVK sym, A0 */ +/* R_C6000_ABS_L16 | MVKL sym, A0 */ +/* MVKLH sym, A0 */ +/* R_C6000_ABS_H16 | MVKH sym, A0 */ +/* R_C6000_SBR_U15_B | LDB *+B14(sym), A1 */ +/* ADDAB B14, sym, A1 */ +/* R_C6000_SBR_U15_H | LDH *+B14(sym), A1 */ +/* ADDAH B14, sym, A1 */ +/* R_C6000_SBR_U15_W | LDW *+B14(sym), A1 */ +/* ADDAW B14, sym, A1 */ +/* R_C6000_SBR_S16 | MVK sym-$bss, A0 */ +/* R_C6000_SBR_L16_B | MVKL (sym-$bss), A0 */ +/* R_C6000_SBR_L16_H | MVKL (sym-$bss)/2,A0 */ +/* R_C6000_SBR_L16_W | MVKL (sym-$bss)/4,A0 */ +/* R_C6000_SBR_H16_B | MVKH (sym-$bss), A0 */ +/* R_C6000_SBR_H16_H | MVKH (sym-$bss)/2,A0 */ +/* R_C6000_SBR_H16_W | MVKH (sym-$bss)/4,A0 */ +/* R_C6000_SBR_GOT_U15_W | LDW *+B14[GOT(sym)],A0 */ +/* R_C6000_SBR_GOT_L16_W | MVKL $DPR_GOT(sym), A0 */ +/* R_C6000_SBR_GOT_H16_W | MVKH $DPR_GOT(sym), A0 */ +/* R_C6000_DSBT_INDEX | LDW *+B14[$DSBT_index()], DP */ +/* */ +/*---------------------------------------------------------------------------*/ + +/*****************************************************************************/ +/* WRITE_RELOC_R() - Perform a relocation into a buffered segment. */ +/*****************************************************************************/ +static void write_reloc_r(uint8_t* buffered_segment, + uint32_t segment_offset, + int r_type, uint32_t r) +{ + uint32_t* rel_field_ptr = (uint32_t*)(buffered_segment + segment_offset); + +#if LOADER_DEBUG + /*------------------------------------------------------------------------*/ + /* Print some details about the relocation we are about to process. */ + /*------------------------------------------------------------------------*/ + if(debugging_on) + { + DLIF_trace("RWRT: segment_offset: %d\n", segment_offset); + DLIF_trace("RWRT: buffered_segment: 0x%x\n", + (uint32_t)buffered_segment); + DLIF_trace("RWRT: rel_field_ptr: 0x%x\n", (uint32_t)rel_field_ptr); + DLIF_trace("RWRT: result: 0x%x\n", r); + } +#endif + + + /*------------------------------------------------------------------------*/ + /* Given the relocation type, carry out relocation into a 4 byte packet */ + /* within the buffered segment. */ + /*------------------------------------------------------------------------*/ + switch(r_type) + { + case R_C6000_ABS32: + *rel_field_ptr = r; + break; + case R_C6000_PREL31: + *rel_field_ptr = (*rel_field_ptr & ~MASK(30,0)) | r; + break; + case R_C6000_ABS16: + *((uint16_t*)(buffered_segment + segment_offset)) = r; + break; + case R_C6000_ABS8: + *((uint8_t*)(buffered_segment + segment_offset)) = r; + break; + case R_C6000_PCR_S21: + *rel_field_ptr = (*rel_field_ptr & ~MASK(21,7)) | (r << 7); + break; + case R_C6000_PCR_S12: + *rel_field_ptr = (*rel_field_ptr & ~MASK(12,16)) | (r << 16); + break; + case R_C6000_PCR_S10: + *rel_field_ptr = (*rel_field_ptr & ~MASK(10,13)) | (r << 13); + break; + case R_C6000_PCR_S7: + *rel_field_ptr = (*rel_field_ptr & ~MASK(7,16)) | (r << 16); + break; + + case R_C6000_ABS_S16: + *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7); + break; + case R_C6000_ABS_L16: + *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7); + break; + case R_C6000_ABS_H16: + *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7); + break; + + case R_C6000_SBR_U15_B: + *rel_field_ptr = (*rel_field_ptr & ~MASK(15,8)) | (r << 8); + break; + case R_C6000_SBR_U15_H: + *rel_field_ptr = (*rel_field_ptr & ~MASK(15,8)) | (r << 8); + break; + case R_C6000_SBR_U15_W: + case R_C6000_DSBT_INDEX: + *rel_field_ptr = (*rel_field_ptr & ~MASK(15,8)) | (r << 8); + break; + + case R_C6000_SBR_S16: + case R_C6000_SBR_L16_B: + case R_C6000_SBR_L16_H: + case R_C6000_SBR_L16_W: + case R_C6000_SBR_H16_B: + case R_C6000_SBR_H16_H: + case R_C6000_SBR_H16_W: + *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7); + break; + + /*---------------------------------------------------------------------*/ + /* Linux "import-as-own" copy relocations are not yet supported. */ + /*---------------------------------------------------------------------*/ + case R_C6000_COPY: + + default: + DLIF_error(DLET_RELOC, + "write_reloc_r called with invalid relocation type!\n"); + } + +#if LOADER_DEBUG + if (debugging_on) + DLIF_trace("reloc_field 0x%x\n", *rel_field_ptr); +#endif +} + +/*****************************************************************************/ +/* PACK_RESULT() - Pack the result of a relocation calculation for storage */ +/* in the relocation field. */ +/*****************************************************************************/ +static int32_t pack_result(int32_t unpacked_result, int r_type) +{ + switch(r_type) + { + case R_C6000_ABS32: + case R_C6000_ABS16: + case R_C6000_ABS8: + case R_C6000_ABS_S16: + case R_C6000_ABS_L16: + case R_C6000_SBR_U15_B: + case R_C6000_SBR_S16: + case R_C6000_SBR_L16_B: + return unpacked_result; + + case R_C6000_SBR_U15_H: + case R_C6000_SBR_L16_H: + case R_C6000_PREL31: + return unpacked_result >> 1; + + case R_C6000_PCR_S21: + case R_C6000_PCR_S12: + case R_C6000_PCR_S10: + case R_C6000_PCR_S7: + case R_C6000_SBR_U15_W: + case R_C6000_SBR_L16_W: + case R_C6000_DSBT_INDEX: + return unpacked_result >> 2; + + case R_C6000_ABS_H16: + case R_C6000_SBR_H16_B: + return unpacked_result >> 16; + + case R_C6000_SBR_H16_H: + return unpacked_result >> 17; + + case R_C6000_SBR_H16_W: + return unpacked_result >> 18; + + /*---------------------------------------------------------------------*/ + /* Linux "import-as-own" copy relocations are not yet supported. */ + /*---------------------------------------------------------------------*/ + case R_C6000_COPY: + + default: + DLIF_error(DLET_RELOC, + "pack_result called with invalid relocation type!\n"); + return 0; + } +} + +/*****************************************************************************/ +/* MASK_RESULT() - Mask the result of a relocation calculation so that it */ +/* fits the size of the relocation type's field. */ +/*****************************************************************************/ +static int32_t mask_result(int32_t unmasked_result, int r_type) +{ + switch(r_type) + { + case R_C6000_ABS8: + return unmasked_result & 0xFF; + + case R_C6000_ABS32: + return unmasked_result; + + case R_C6000_ABS16: + case R_C6000_ABS_S16: + case R_C6000_ABS_L16: + case R_C6000_ABS_H16: + case R_C6000_SBR_S16: + case R_C6000_SBR_L16_B: + case R_C6000_SBR_L16_H: + case R_C6000_SBR_L16_W: + case R_C6000_SBR_H16_B: + case R_C6000_SBR_H16_H: + case R_C6000_SBR_H16_W: + return unmasked_result & 0xFFFF; + + case R_C6000_PCR_S21: + return unmasked_result & 0x1FFFFF; + + case R_C6000_PCR_S12: + return unmasked_result & 0xFFF; + + case R_C6000_PCR_S10: + return unmasked_result & 0x3FF; + + case R_C6000_PCR_S7: + return unmasked_result & 0x7F; + + case R_C6000_SBR_U15_B: + case R_C6000_SBR_U15_H: + case R_C6000_SBR_U15_W: + case R_C6000_DSBT_INDEX: + return unmasked_result & 0x7FFF; + + case R_C6000_PREL31: + return unmasked_result & 0x7FFFFFFF; + + /*---------------------------------------------------------------------*/ + /* Linux "import-as-own" copy relocations are not yet supported. */ + /*---------------------------------------------------------------------*/ + case R_C6000_COPY: + + default: + DLIF_error(DLET_RELOC, + "mask_result called with invalid relocation type!\n"); + return 0; + } +} + +/*****************************************************************************/ +/* REL_OVERFLOW() */ +/* */ +/* Check relocation value against the range associated with a given */ +/* relocation type field size and signedness. */ +/* */ +/*****************************************************************************/ +static BOOL rel_overflow(C60_RELOC_TYPE r_type, int32_t reloc_value) +{ + /*------------------------------------------------------------------------*/ + /* Select appropriate range check based on relocation type. */ + /*------------------------------------------------------------------------*/ + switch(r_type) + { + case R_C6000_ABS16: return ((reloc_value > 65535) || + (reloc_value < -32768)); + case R_C6000_ABS8: return ((reloc_value > 255) || + (reloc_value < -128)); + case R_C6000_PCR_S21: return ((reloc_value >= 0x400000) || + (reloc_value < -0x400000)); + case R_C6000_PCR_S12: return ((reloc_value >= 0x2000) || + (reloc_value < -0x2000)); + case R_C6000_PCR_S10: return ((reloc_value >= 0x800) || + (reloc_value < -0x800)); + case R_C6000_PCR_S7: return ((reloc_value >= 0x100) || + (reloc_value < -0x100)); + case R_C6000_SBR_S16: + case R_C6000_ABS_S16: return ((reloc_value >= 0x8000) || + (reloc_value < -0x8000)); + case R_C6000_SBR_U15_B: return (((uint32_t)reloc_value) >= 0x8000); + case R_C6000_SBR_U15_H: return (((uint32_t)reloc_value) >= 0xFFFF); + case R_C6000_DSBT_INDEX: + case R_C6000_SBR_U15_W: return (((uint32_t)reloc_value) >= 0x1FFFD); + + + /*---------------------------------------------------------------------*/ + /* Some relocation types suppress overflow checking at link-time. */ + /*---------------------------------------------------------------------*/ + case R_C6000_ABS_L16: + case R_C6000_ABS_H16: + case R_C6000_SBR_L16_B: + case R_C6000_SBR_L16_H: + case R_C6000_SBR_L16_W: + case R_C6000_SBR_H16_B: + case R_C6000_SBR_H16_H: + case R_C6000_SBR_H16_W: + return 0; + + /*---------------------------------------------------------------------*/ + /* 32-bit relocation field values are not checked for overflow. */ + /*---------------------------------------------------------------------*/ + case R_C6000_ABS32: + case R_C6000_PREL31: + return 0; + + /*---------------------------------------------------------------------*/ + /* If relocation type did not appear in the above switch, then we */ + /* didn't expect to see it. */ + /*---------------------------------------------------------------------*/ + default: + DLIF_error(DLET_RELOC, + "rel_overflow called with invalid relocation type!\n"); + } + + return 1; +} + +#if LOADER_DEBUG || LOADER_PROFILE +extern int DLREL_relocations; +extern time_t DLREL_total_reloc_time; +#endif + +/*****************************************************************************/ +/* RELOC_DO() - Process a single relocation entry. */ +/*****************************************************************************/ +static void reloc_do(C60_RELOC_TYPE r_type, + uint32_t segment_vaddr, + uint8_t *segment_buffer, + uint32_t addend, + uint32_t symval, + uint32_t spc, + int wrong_endian, + uint32_t base_pointer, + int32_t dsbt_index) +{ + int32_t reloc_value = 0; + +#if LOADER_DEBUG || LOADER_PROFILE + /*------------------------------------------------------------------------*/ + /* In debug mode, keep a count of the number of relocations processed. */ + /* In profile mode, start the clock on a given relocation. */ + /*------------------------------------------------------------------------*/ + int start_time = 0; + if (debugging_on || profiling_on) + { + DLREL_relocations++; + if (profiling_on) start_time = clock(); + } +#endif + + /*------------------------------------------------------------------------*/ + /* Calculate the relocation value according to the rules associated with */ + /* the given relocation type. */ + /*------------------------------------------------------------------------*/ + switch(r_type) + { + /*---------------------------------------------------------------------*/ + /* Straight-Up Address relocations (address references). */ + /*---------------------------------------------------------------------*/ + case R_C6000_ABS32: + case R_C6000_ABS16: + case R_C6000_ABS8: + case R_C6000_ABS_S16: + case R_C6000_ABS_L16: + case R_C6000_ABS_H16: + reloc_value = symval + addend; + break; + + /*---------------------------------------------------------------------*/ + /* PC-Relative relocations (calls and branches). */ + /*---------------------------------------------------------------------*/ + case R_C6000_PCR_S21: + case R_C6000_PCR_S12: + case R_C6000_PCR_S10: + case R_C6000_PCR_S7: + { + /*------------------------------------------------------------------*/ + /* Add SPC to segment address to get the PC. Mask for exec-packet */ + /* boundary. */ + /*------------------------------------------------------------------*/ + int32_t opnd_p = (spc + segment_vaddr) & 0xffffffe0; + reloc_value = symval + addend - opnd_p; + break; + } + + /*---------------------------------------------------------------------*/ + /* "Place"-relative relocations (TDEH). */ + /*---------------------------------------------------------------------*/ + /* These relocations occur in data and refer to a label that occurs */ + /* at some signed 32-bit offset from the place where the relocation */ + /* occurs. */ + /*---------------------------------------------------------------------*/ + case R_C6000_PREL31: + { + /*------------------------------------------------------------------*/ + /* Compute location of relocation entry and subtract it from the */ + /* address of the location being referenced (it is computed very */ + /* much like a PC-relative relocation, but it occurs in data and */ + /* is called a "place"-relative relocation). */ + /*------------------------------------------------------------------*/ + /* If this is an Elf32_Rel type relocation, then addend is assumed */ + /* to have been scaled when it was unpacked (field << 1). */ + /*------------------------------------------------------------------*/ + /* For Elf32_Rela type relocations the addend is assumed to be a */ + /* signed 32-bit integer value. */ + /*------------------------------------------------------------------*/ + /* Offset is not fetch-packet relative; doesn't need to be masked. */ + /*------------------------------------------------------------------*/ + int32_t opnd_p = (spc + segment_vaddr); + reloc_value = symval + addend - opnd_p; + break; + } + + /*---------------------------------------------------------------------*/ + /* Static-Base Relative relocations (near-DP). */ + /*---------------------------------------------------------------------*/ + case R_C6000_SBR_U15_B: + case R_C6000_SBR_U15_H: + case R_C6000_SBR_U15_W: + case R_C6000_SBR_S16: + case R_C6000_SBR_L16_B: + case R_C6000_SBR_L16_H: + case R_C6000_SBR_L16_W: + case R_C6000_SBR_H16_B: + case R_C6000_SBR_H16_H: + case R_C6000_SBR_H16_W: + reloc_value = symval + addend - base_pointer; + break; + + /*---------------------------------------------------------------------*/ + /* R_C6000_DSBT_INDEX - uses value assigned by the dynamic loader to */ + /* be the DSBT index for this module as a scaled offset when */ + /* referencing the DSBT. The DSBT base address is in symval and the */ + /* static base is in base_pointer. DP-relative offset to slot in */ + /* DSBT is the offset of the DSBT relative to the DP plus the */ + /* scaled DSBT index into the DSBT. */ + /*---------------------------------------------------------------------*/ + case R_C6000_DSBT_INDEX: + reloc_value = ((symval + addend) - base_pointer) + (dsbt_index << 2); + break; + + /*---------------------------------------------------------------------*/ + /* Linux "import-as-own" copy relocation: after DSO initialization, */ + /* copy the named object from the DSO into the executable's BSS */ + /*---------------------------------------------------------------------*/ + /* Linux "import-as-own" copy relocations are not yet supported. */ + /*---------------------------------------------------------------------*/ + case R_C6000_COPY: + + /*---------------------------------------------------------------------*/ + /* Unrecognized relocation type. */ + /*---------------------------------------------------------------------*/ + default: + DLIF_error(DLET_RELOC, + "reloc_do called with invalid relocation type!\n"); + break; + } + + /*------------------------------------------------------------------------*/ + /* Overflow checking. Is relocation value out of range for the size and */ + /* type of the current relocation? */ + /*------------------------------------------------------------------------*/ + if (rel_overflow(r_type, reloc_value)) + DLIF_error(DLET_RELOC, "relocation overflow!\n"); + + /*------------------------------------------------------------------------*/ + /* Move relocation value to appropriate offset for relocation field's */ + /* location. */ + /*------------------------------------------------------------------------*/ + reloc_value = pack_result(reloc_value, r_type); + + /*------------------------------------------------------------------------*/ + /* Mask packed result to the size of the relocation field. */ + /*------------------------------------------------------------------------*/ + reloc_value = mask_result(reloc_value, r_type); + + /*------------------------------------------------------------------------*/ + /* If necessary, Swap endianness of data at relocation address. */ + /*------------------------------------------------------------------------*/ + if (wrong_endian) + DLIMP_change_endian32((int32_t*)(segment_buffer + spc)); + + /*------------------------------------------------------------------------*/ + /* Write the relocated 4-byte packet back to the segment buffer. */ + /*------------------------------------------------------------------------*/ + write_reloc_r(segment_buffer, spc, r_type, reloc_value); + + /*------------------------------------------------------------------------*/ + /* Change endianness of segment address back to original. */ + /*------------------------------------------------------------------------*/ + if (wrong_endian) + DLIMP_change_endian32((int32_t*)(segment_buffer + spc)); + +#if LOADER_DEBUG || LOADER_PROFILE + /*------------------------------------------------------------------------*/ + /* In profile mode, add elapsed time for this relocation to total time */ + /* spent doing relocations. */ + /*------------------------------------------------------------------------*/ + if (profiling_on) + DLREL_total_reloc_time += (clock() - start_time); + if (debugging_on) + DLIF_trace("reloc_value = 0x%x\n", reloc_value); +#endif +} + +/*****************************************************************************/ +/* REL_UNPACK_ADDEND() */ +/* */ +/* Unpack addend value from the relocation field. */ +/* */ +/*****************************************************************************/ +static void rel_unpack_addend(C60_RELOC_TYPE r_type, + uint8_t *address, + uint32_t *addend) +{ + /*------------------------------------------------------------------------*/ + /* C6000 does not support Elf32_Rel type relocations in the dynamic */ + /* loader core. We will emit an internal error and abort until this */ + /* support is added. I abort here because this is necessarily a target- */ + /* specific part of the relocation infrastructure. */ + /*------------------------------------------------------------------------*/ + *addend = 0; + + DLIF_error(DLET_RELOC, + "Internal Error: unpacking addend values from the relocation " + "field is not supported in the C6000 dynamic loader at this " + "time; aborting\n"); + DLIF_exit(1); +} + +/*****************************************************************************/ +/* REL_SWAP_ENDIAN() */ +/* */ +/* Return TRUE if we should change the endianness of a relocation field. */ +/* */ +/*****************************************************************************/ +static BOOL rel_swap_endian(DLIMP_Dynamic_Module *dyn_module, + C60_RELOC_TYPE r_type) +{ + if (dyn_module->wrong_endian) return TRUE; + + return FALSE; +} + +/*****************************************************************************/ +/* REL_CHANGE_ENDIAN() */ +/* */ +/* Change the endianness of the relocation field at the specified address */ +/* in the segment's data. */ +/* */ +/*****************************************************************************/ +static void rel_change_endian(C60_RELOC_TYPE r_type, uint8_t *address) +{ + /*------------------------------------------------------------------------*/ + /* On C6000, all instructions are 32-bits wide. */ + /*------------------------------------------------------------------------*/ + DLIMP_change_endian32((int32_t *)address); +} + +/*****************************************************************************/ +/* READ_REL_TABLE() */ +/* */ +/* Read in an Elf32_Rel type relocation table. This function allocates */ +/* host memory for the table. */ +/* */ +/*****************************************************************************/ +static void read_rel_table(struct Elf32_Rel **rel_table, + int32_t table_offset, + uint32_t relnum, uint32_t relent, + LOADER_FILE_DESC *fd, BOOL wrong_endian) +{ + if (relnum == 0) { *rel_table = NULL; return; } + + *rel_table = (struct Elf32_Rel *)DLIF_malloc(relnum * relent); + DLIF_fseek(fd, table_offset, LOADER_SEEK_SET); + DLIF_fread(*rel_table, relnum, relent, fd); + + if (wrong_endian) + { + int i; + for (i = 0; i < relnum; i++) + DLIMP_change_rel_endian(*rel_table + i); + } +} + +/*****************************************************************************/ +/* PROCESS_REL_TABLE() */ +/* */ +/* Process table of Elf32_Rel type relocations. */ +/* */ +/*****************************************************************************/ +static void process_rel_table(DLOAD_HANDLE handle, + DLIMP_Loaded_Segment* seg, + struct Elf32_Rel *rel_table, + uint32_t relnum, + int32_t *start_relidx, + uint32_t ti_static_base, + DLIMP_Dynamic_Module* dyn_module) +{ + Elf32_Addr seg_start_addr = seg->input_vaddr; + Elf32_Addr seg_end_addr = seg_start_addr + seg->phdr.p_memsz; + BOOL found = FALSE; + int32_t relidx = *start_relidx; + + /*------------------------------------------------------------------------*/ + /* If the given start reloc index is out of range, then start from the */ + /* beginning of the given table. */ + /*------------------------------------------------------------------------*/ + if (relidx >= relnum) relidx = 0; + + /*------------------------------------------------------------------------*/ + /* Spin through Elf32_Rel type relocation table. */ + /*------------------------------------------------------------------------*/ + for ( ; relidx < relnum; relidx++) + { + /*---------------------------------------------------------------------*/ + /* If the relocation offset falls within the segment, process it. */ + /*---------------------------------------------------------------------*/ + if (rel_table[relidx].r_offset >= seg_start_addr && + rel_table[relidx].r_offset < seg_end_addr) + { + Elf32_Addr r_symval = 0; + C60_RELOC_TYPE r_type = + (C60_RELOC_TYPE)ELF32_R_TYPE(rel_table[relidx].r_info); + int32_t r_symid = ELF32_R_SYM(rel_table[relidx].r_info); + + uint8_t *reloc_address = NULL; + uint32_t pc = 0; + uint32_t addend = 0; + + BOOL change_endian = FALSE; + + found = TRUE; + + /*------------------------------------------------------------------*/ + /* If symbol definition is not found, don't do the relocation. */ + /* An error is generated by the lookup function. */ + /*------------------------------------------------------------------*/ + if (!DLSYM_canonical_lookup(handle, r_symid, dyn_module, &r_symval)) + continue; + + /*------------------------------------------------------------------*/ + /* Addend value is stored in the relocation field. */ + /* We'll need to unpack it from the data for the segment that is */ + /* currently being relocated. */ + /*------------------------------------------------------------------*/ + pc = rel_table[relidx].r_offset - seg->input_vaddr; + reloc_address = (uint8_t *)seg->host_address + pc; + + change_endian = rel_swap_endian(dyn_module, r_type); + if (change_endian) + rel_change_endian(r_type, reloc_address); + + rel_unpack_addend( + (C60_RELOC_TYPE)ELF32_R_TYPE(rel_table[relidx].r_info), + reloc_address, &addend); + + /*------------------------------------------------------------------*/ + /* Perform actual relocation. This is a really wide function */ + /* interface and could do with some encapsulation. */ + /*------------------------------------------------------------------*/ + reloc_do(r_type, + seg->phdr.p_vaddr, + seg->host_address, + addend, + r_symval, + pc, + dyn_module->wrong_endian, + ti_static_base, + dyn_module->dsbt_index); + + } + + else if (found) + break; + } +} + +/*****************************************************************************/ +/* READ_RELA_TABLE() */ +/* */ +/* Read in an Elf32_Rela type relocation table. This function allocates */ +/* host memory for the table. */ +/* */ +/*****************************************************************************/ +static void read_rela_table(struct Elf32_Rela **rela_table, + int32_t table_offset, + uint32_t relanum, uint32_t relaent, + LOADER_FILE_DESC *fd, BOOL wrong_endian) +{ + if (relanum == 0) { *rela_table = NULL; return; } + *rela_table = (struct Elf32_Rela *)DLIF_malloc(relanum * relaent); + DLIF_fseek(fd, table_offset, LOADER_SEEK_SET); + DLIF_fread(*rela_table, relanum, relaent, fd); + + if (wrong_endian) + { + int i; + for (i = 0; i < relanum; i++) + DLIMP_change_rela_endian(*rela_table + i); + } +} + +/*****************************************************************************/ +/* PROCESS_RELA_TABLE() */ +/* */ +/* Process a table of Elf32_Rela type relocations. */ +/* */ +/*****************************************************************************/ +static void process_rela_table(DLOAD_HANDLE handle, + DLIMP_Loaded_Segment *seg, + struct Elf32_Rela *rela_table, + uint32_t relanum, + int32_t *start_relidx, + uint32_t ti_static_base, + DLIMP_Dynamic_Module *dyn_module) +{ + Elf32_Addr seg_start_addr = seg->input_vaddr; + Elf32_Addr seg_end_addr = seg_start_addr + seg->phdr.p_memsz; + BOOL found = FALSE; + int32_t relidx = *start_relidx; + + /*-----------------------------------------------------------------------*/ + /* If the given start reloc index is out of range, then start from */ + /* the beginning of the given table. */ + /*-----------------------------------------------------------------------*/ + if (relidx > relanum) relidx = 0; + + /*-----------------------------------------------------------------------*/ + /* Spin through RELA relocation table. */ + /*-----------------------------------------------------------------------*/ + for ( ; relidx < relanum; relidx++) + { + /*-------------------------------------------------------------------*/ + /* If the relocation offset falls within the segment, process it. */ + /*-------------------------------------------------------------------*/ + if (rela_table[relidx].r_offset >= seg_start_addr && + rela_table[relidx].r_offset < seg_end_addr) + { + Elf32_Addr r_symval; + C60_RELOC_TYPE r_type = + (C60_RELOC_TYPE)ELF32_R_TYPE(rela_table[relidx].r_info); + int32_t r_symid = ELF32_R_SYM(rela_table[relidx].r_info); + + found = TRUE; + + /*---------------------------------------------------------------*/ + /* If symbol definition is not found, don't do the relocation. */ + /* An error is generated by the lookup function. */ + /*---------------------------------------------------------------*/ + if (!DLSYM_canonical_lookup(handle, r_symid, dyn_module, &r_symval)) + continue; + + /*---------------------------------------------------------------*/ + /* Perform actual relocation. This is a really wide function */ + /* interface and could do with some encapsulation. */ + /*---------------------------------------------------------------*/ + reloc_do(r_type, + seg->phdr.p_vaddr, + seg->host_address, + rela_table[relidx].r_addend, + r_symval, + rela_table[relidx].r_offset - seg->input_vaddr, + dyn_module->wrong_endian, + ti_static_base, + dyn_module->dsbt_index); + } + + else if (found) + break; + } +} + +/*****************************************************************************/ +/* PROCESS_GOT_RELOCS() */ +/* */ +/* Process all GOT relocations. It is possible to have both Elf32_Rel */ +/* and Elf32_Rela type relocations in the same file, so we handle tham */ +/* both. */ +/* */ +/*****************************************************************************/ +static void process_got_relocs(DLOAD_HANDLE handle, + struct Elf32_Rel* rel_table, uint32_t relnum, + struct Elf32_Rela* rela_table, uint32_t relanum, + DLIMP_Dynamic_Module* dyn_module) +{ + DLIMP_Loaded_Segment *seg = + (DLIMP_Loaded_Segment*)(dyn_module->loaded_module->loaded_segments.buf); + uint32_t num_segs = dyn_module->loaded_module->loaded_segments.size; + int32_t rel_relidx = 0; + int32_t rela_relidx = 0; + uint32_t seg_idx = 0; + uint32_t ti_static_base = 0; + + /*------------------------------------------------------------------------*/ + /* Get the value of the static base (__TI_STATIC_BASE) which will be */ + /* passed into the relocation table processing functions. */ + /*------------------------------------------------------------------------*/ + if (!DLSYM_lookup_local_symtab("__TI_STATIC_BASE", dyn_module->symtab, + dyn_module->symnum, &ti_static_base)) + DLIF_error(DLET_RELOC, "Could not resolve value of __TI_STATIC_BASE\n"); + + /*------------------------------------------------------------------------*/ + /* Process relocations segment by segment. */ + /*------------------------------------------------------------------------*/ + for (seg_idx = 0; seg_idx < num_segs; seg_idx++) + { + /*---------------------------------------------------------------------*/ + /* Relocations should not occur in uninitialized segments. */ + /*---------------------------------------------------------------------*/ + if (!seg[seg_idx].phdr.p_filesz) continue; + + if (rela_table) + process_rela_table(handle, (seg + seg_idx), + rela_table, relanum, &rela_relidx, + ti_static_base, dyn_module); + + if (rel_table) + process_rel_table(handle, (seg + seg_idx), + rel_table, relnum, &rel_relidx, + ti_static_base, dyn_module); + } +} + +/*****************************************************************************/ +/* PROCESS_PLTGOT_RELOCS() */ +/* */ +/* Process all PLTGOT relocation entries. The PLTGOT relocation table */ +/* can be either Elf32_Rel or Elf32_Rela type. All PLTGOT relocations */ +/* ar guaranteed to belong to the same segment. */ +/* */ +/*****************************************************************************/ +static void process_pltgot_relocs(DLOAD_HANDLE handle, + void* plt_reloc_table, + int reltype, + uint32_t pltnum, + DLIMP_Dynamic_Module* dyn_module) +{ + Elf32_Addr r_offset = (reltype == DT_REL) ? + ((struct Elf32_Rel *)plt_reloc_table)->r_offset : + ((struct Elf32_Rela *)plt_reloc_table)->r_offset; + + DLIMP_Loaded_Segment* seg = + (DLIMP_Loaded_Segment*)(dyn_module->loaded_module->loaded_segments.buf); + + uint32_t num_segs = dyn_module->loaded_module->loaded_segments.size; + int32_t plt_relidx = 0; + uint32_t seg_idx = 0; + uint32_t ti_static_base = 0; + + /*------------------------------------------------------------------------*/ + /* Get the value of the static base (__TI_STATIC_BASE) which will be */ + /* passed into the relocation table processing functions. */ + /*------------------------------------------------------------------------*/ + if (!DLSYM_lookup_local_symtab("__TI_STATIC_BASE", dyn_module->symtab, + dyn_module->symnum, &ti_static_base)) + DLIF_error(DLET_RELOC, "Could not resolve value of __TI_STATIC_BASE\n"); + + /*------------------------------------------------------------------------*/ + /* For each segment s, check if the relocation falls within s. If so, */ + /* then all other relocations are guaranteed to fall with s. Process */ + /* all relocations and then return. */ + /*------------------------------------------------------------------------*/ + for (seg_idx = 0; seg_idx < num_segs; seg_idx++) + { + Elf32_Addr seg_start_addr = seg[seg_idx].input_vaddr; + Elf32_Addr seg_end_addr = seg_start_addr + seg[seg_idx].phdr.p_memsz; + + /*---------------------------------------------------------------------*/ + /* Relocations should not occur in uninitialized segments. */ + /*---------------------------------------------------------------------*/ + if(!seg[seg_idx].phdr.p_filesz) continue; + + if (r_offset >= seg_start_addr && + r_offset < seg_end_addr) + { + if (reltype == DT_REL) + process_rel_table(handle, (seg + seg_idx), + (struct Elf32_Rel *)plt_reloc_table, + pltnum, &plt_relidx, + ti_static_base, dyn_module); + else + process_rela_table(handle, (seg + seg_idx), + (struct Elf32_Rela *)plt_reloc_table, + pltnum, &plt_relidx, + ti_static_base, dyn_module); + + break; + } + } +} + +/*****************************************************************************/ +/* RELOCATE() - Perform RELA and REL type relocations for given ELF object */ +/* file that we are in the process of loading and relocating. */ +/*****************************************************************************/ +void DLREL_c60_relocate(DLOAD_HANDLE handle, + LOADER_FILE_DESC *fd, DLIMP_Dynamic_Module *dyn_module) +{ + struct Elf32_Dyn *dyn_nugget = dyn_module->dyntab; + struct Elf32_Rela *rela_table = NULL; + struct Elf32_Rel *rel_table = NULL; + struct Elf32_Rela *rela_plt_table = NULL; + struct Elf32_Rel *rel_plt_table = NULL; + + /*------------------------------------------------------------------------*/ + /* Read the size of the relocation table (DT_RELASZ) and the size per */ + /* relocation (DT_RELAENT) from the dynamic segment. */ + /*------------------------------------------------------------------------*/ + uint32_t relasz = DLIMP_get_first_dyntag(DT_RELASZ, dyn_nugget); + uint32_t relaent = DLIMP_get_first_dyntag(DT_RELAENT, dyn_nugget); + uint32_t relanum = 0; + + /*------------------------------------------------------------------------*/ + /* Read the size of the relocation table (DT_RELSZ) and the size per */ + /* relocation (DT_RELENT) from the dynamic segment. */ + /*------------------------------------------------------------------------*/ + uint32_t relsz = DLIMP_get_first_dyntag(DT_RELSZ, dyn_nugget); + uint32_t relent = DLIMP_get_first_dyntag(DT_RELENT, dyn_nugget); + uint32_t relnum = 0; + + /*------------------------------------------------------------------------*/ + /* Read the size of the relocation table (DT_PLTRELSZ) and the type of */ + /* of the PLTGOT relocation table (DT_PLTREL): one of DT_REL or DT_RELA */ + /*------------------------------------------------------------------------*/ + uint32_t pltrelsz = DLIMP_get_first_dyntag(DT_PLTRELSZ, dyn_nugget); + int pltreltyp = DLIMP_get_first_dyntag(DT_PLTREL, dyn_nugget); + uint32_t pltnum = 0; + + /*------------------------------------------------------------------------*/ + /* Find/record DSBT index associated with this module. */ + /*------------------------------------------------------------------------*/ + if (is_dsbt_module(dyn_module) && + (dyn_module->dsbt_index == DSBT_INDEX_INVALID)) + dyn_module->dsbt_index = + DLIF_get_dsbt_index(dyn_module->loaded_module->file_handle); + + /*------------------------------------------------------------------------*/ + /* Read the PLTGOT relocation table from the file */ + /* The PLTGOT table is a subsection at the end of either the DT_REL or */ + /* DT_RELA table. The size of the table it belongs to DT_REL(A)SZ */ + /* includes the size of the PLTGOT table. So it must be adjusted so that */ + /* the GOT relocation tables only contain actual GOT relocations. */ + /*------------------------------------------------------------------------*/ + if (pltrelsz != INT_MAX && pltrelsz != 0) + { + if (pltreltyp == DT_REL) + { + pltnum = pltrelsz/relent; + relsz -= pltrelsz; + read_rel_table((&rel_plt_table), + DLIMP_get_first_dyntag(DT_JMPREL, dyn_nugget), + pltnum, relent, fd, dyn_module->wrong_endian); + } + + else if (pltreltyp == DT_RELA) + { + pltnum = pltrelsz/relaent; + relasz -= pltrelsz; + read_rela_table((&rela_plt_table), + DLIMP_get_first_dyntag(DT_JMPREL, dyn_nugget), + pltnum, relaent, fd, dyn_module->wrong_endian); + } + + else + { + DLIF_error(DLET_RELOC, + "DT_PLTREL is invalid: must be either %d or %d\n", + DT_REL, DT_RELA); + } + } + + /*------------------------------------------------------------------------*/ + /* Read the DT_RELA GOT relocation table from the file */ + /*------------------------------------------------------------------------*/ + if (relasz != INT_MAX && relasz != 0) + { + relanum = relasz/relaent; + read_rela_table(&rela_table, DLIMP_get_first_dyntag(DT_RELA, dyn_nugget), + relanum, relaent, fd, dyn_module->wrong_endian); + } + + /*------------------------------------------------------------------------*/ + /* Read the DT_REL GOT relocation table from the file */ + /*------------------------------------------------------------------------*/ + if (relsz != INT_MAX && relsz != 0) + { + relnum = relsz/relent; + read_rel_table(&rel_table, DLIMP_get_first_dyntag(DT_REL, dyn_nugget), + relnum, relent, fd, dyn_module->wrong_endian); + } + + /*------------------------------------------------------------------------*/ + /* Process the PLTGOT relocations */ + /*------------------------------------------------------------------------*/ + if (rela_plt_table) + process_pltgot_relocs(handle, rela_plt_table, pltreltyp, pltnum, + dyn_module); + + if (rel_plt_table) + process_pltgot_relocs(handle, rel_plt_table, pltreltyp, pltnum, + dyn_module); + + /*------------------------------------------------------------------------*/ + /* Process the GOT relocations */ + /*------------------------------------------------------------------------*/ + if (rel_table || rela_table) + process_got_relocs(handle, rel_table, relnum, rela_table, relanum, + dyn_module); + + /*-------------------------------------------------------------------------*/ + /* Free memory used for ELF relocation table copies. */ + /*-------------------------------------------------------------------------*/ + if (rela_table) DLIF_free(rela_table); + if (rel_table) DLIF_free(rel_table); + if (rela_plt_table) DLIF_free(rela_plt_table); + if (rel_plt_table) DLIF_free(rel_plt_table); +} + +/*****************************************************************************/ +/* UNIT TESTING INTERFACE */ +/*****************************************************************************/ +#ifdef UNIT_TEST +void unit_c60_reloc_do(C60_RELOC_TYPE r_type, + uint8_t *address_space, + uint32_t addend, uint32_t symval, uint32_t pc, + uint32_t static_base, int wrong_endian, + int32_t dsbt_index) +{ + reloc_do(r_type, (uint32_t)address_space, address_space, + addend, symval, pc, FALSE, static_base, dsbt_index); +} + +#if 0 /* RELA TYPE RELOCATIONS HAVE ADDEND IN RELOCATION ENTRY */ +void unit_c60_rel_unpack_addend(C60_RELOC_TYPE r_type, + uint8_t* address, + uint32_t* addend) +{ + rel_unpack_addend(r_type, address, addend); +} +#endif + +BOOL unit_c60_rel_overflow(C60_RELOC_TYPE r_type, int32_t reloc_value) +{ + return rel_overflow(r_type, reloc_value); +} +#endif + |