aboutsummaryrefslogtreecommitdiff
path: root/src/core/dsp/ocl_load/C60_DLOAD_REL/c60_reloc.c
diff options
context:
space:
mode:
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.c1101
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
+