summaryrefslogtreecommitdiff
path: root/cpukit/libdl/rtl-mdreloc-powerpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libdl/rtl-mdreloc-powerpc.c')
-rw-r--r--cpukit/libdl/rtl-mdreloc-powerpc.c359
1 files changed, 286 insertions, 73 deletions
diff --git a/cpukit/libdl/rtl-mdreloc-powerpc.c b/cpukit/libdl/rtl-mdreloc-powerpc.c
index b205ed5215..5c52823a0f 100644
--- a/cpukit/libdl/rtl-mdreloc-powerpc.c
+++ b/cpukit/libdl/rtl-mdreloc-powerpc.c
@@ -13,6 +13,7 @@
#include <sys/stat.h>
#include <rtems/rtl/rtl.h>
+#include "rtl-bit-alloc.h"
#include "rtl-elf.h"
#include "rtl-error.h"
#include <rtems/rtl/rtl-trace.h>
@@ -23,6 +24,57 @@
((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16)
#define l(x) ((u_int32_t)(x) & 0xffff)
+/*
+ * SDATA allocator.
+ */
+static rtems_rtl_bit_alloc* sdata;
+
+static Elf_Addr
+get_sda_base (void)
+{
+ uint32_t sda_base;
+ __asm__ volatile (" mr %0, 13\n" : "=r" (sda_base));
+ return sda_base;
+}
+
+/*
+ * Access the variables via asm statements to avoid any fix up issues
+ * generated by the C compiler which thinks they belong in the .sdata
+ * section.
+ */
+
+#define GET_ADDR(_l, _v) \
+ __asm__ volatile (" lis %0, " #_l "@h\n" \
+ " ori %0, %0, " #_l "@l\n" : "=r" (_v))
+
+static void*
+get_sdata_start (void)
+{
+ Elf_Addr addr;
+ GET_ADDR(__SDATA_START__, addr);
+ return (void*) addr;
+}
+
+static size_t
+get_sdata_sbss_size (void)
+{
+ Elf_Addr sdata_begin;
+ Elf_Addr sbss_end;
+ GET_ADDR(bsp_section_sdata_begin, sdata_begin);
+ GET_ADDR(bsp_section_sbss_end, sbss_end);
+ return sbss_end - sdata_begin;
+}
+
+static size_t
+get_sdata_libdl_size (void)
+{
+ Elf_Addr begin;
+ Elf_Addr end;
+ GET_ADDR(bsp_section_sdata_libdl_begin, begin);
+ GET_ADDR(bsp_section_sdata_libdl_end, end);
+ return end - begin;
+}
+
uint32_t
rtems_rtl_elf_section_flags (const rtems_rtl_obj* obj,
const Elf_Shdr* shdr)
@@ -30,6 +82,75 @@ rtems_rtl_elf_section_flags (const rtems_rtl_obj* obj,
return 0;
}
+uint32_t
+rtems_rtl_elf_arch_parse_section (const rtems_rtl_obj* obj,
+ int section,
+ const char* name,
+ const Elf_Shdr* shdr,
+ const uint32_t flags)
+{
+ struct {
+ const char* label;
+ size_t len;
+ } prefix[] = {
+ #define SEPARATED_PREFIX(_p) { _p, sizeof (_p) - 1 }
+ SEPARATED_PREFIX (".sdata"),
+ SEPARATED_PREFIX (".sbss"),
+ };
+ const size_t prefixes = sizeof (prefix) / sizeof (prefix[0]);
+ size_t p;
+ for (p = 0; p < prefixes; ++p)
+ {
+ if (strncmp (name, prefix[p].label, prefix[p].len) == 0)
+ return flags | RTEMS_RTL_OBJ_SECT_ARCH_ALLOC;
+ }
+ return flags;
+}
+
+bool
+rtems_rtl_elf_arch_section_alloc (const rtems_rtl_obj* obj,
+ rtems_rtl_obj_sect* sect)
+{
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
+ printf ("rtl: section: arch: alloc: name=%s size=%zu flags=%08" PRIx32 \
+ " order=%i link=%d info=%d\n",
+ sect->name, sect->size, sect->flags, sect->load_order,
+ sect->link, sect->info);
+
+ if (sdata == NULL)
+ {
+ sdata = rtems_rtl_bit_alloc_open (get_sdata_start (),
+ get_sdata_libdl_size (),
+ sizeof (uint32_t),
+ get_sdata_sbss_size ());
+ if (sdata == NULL)
+ {
+ rtems_rtl_set_error (ENOMEM, "no memory for sdata allocator");
+ return false;
+ }
+ }
+
+ sect->base = rtems_rtl_bit_alloc_balloc (sdata, sect->size);
+ if (sect->base == NULL)
+ {
+ rtems_rtl_set_error (ENOMEM, "no .sdata memory: %s", sect->name);
+ return false;
+ }
+
+ return true;
+}
+
+bool
+rtems_rtl_elf_arch_section_free (const rtems_rtl_obj* obj,
+ rtems_rtl_obj_sect* sect)
+{
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
+ printf ("rtl: section: arch: free: name=%s size=%zu\n", sect->name, sect->size);
+ if (sdata != NULL)
+ rtems_rtl_bit_alloc_bfree (sdata, sect->base, sect->size);
+ return true;
+}
+
bool
rtems_rtl_elf_rel_resolve_sym (Elf_Word type)
{
@@ -40,35 +161,51 @@ size_t
rtems_rtl_elf_relocate_tramp_max_size (void)
{
/*
- * Disable by returning 0.
+ * We have 4 instructions and each instruction is 32bits.
*/
- return 0;
+ return 4 * 4;
}
-bool
-rtems_rtl_elf_relocate_rela_tramp (rtems_rtl_obj* obj,
- const Elf_Rela* rela,
- const rtems_rtl_obj_sect* sect,
- const char* symname,
- const Elf_Byte syminfo,
- const Elf_Word symvalue)
+static void*
+set_veneer (void* tramopline, Elf_Addr target)
{
- (void) obj;
- (void) rela;
- (void) sect;
- (void) symname;
- (void) syminfo;
- (void) symvalue;
- return true;
+ /*
+ * http://shell-storm.org/online/Online-Assembler-and-Disassembler/
+ *
+ * lis 12,0x1234
+ * ori 12,12,0x5678
+ * mtctr 12
+ * bctr
+ */
+#if COMPILE_ASM
+ asm volatile (" lis 12,0x1234\n" \
+ " ori 12,12,0x5678\n" \
+ " mtctr 12\n" \
+ " bctr\n");
+#endif
+ uint32_t* tramp = (uint32_t*) tramopline;
+ *tramp++ = 0x3d800000 | (target >> 16);
+ *tramp++ = 0x618c0000 | (target & 0xffff);
+ *tramp++ = 0x7d8903a6;
+ *tramp++ = 0x4e800420;
+ return tramp;
}
-bool
-rtems_rtl_elf_relocate_rela (rtems_rtl_obj* obj,
- const Elf_Rela* rela,
- const rtems_rtl_obj_sect* sect,
- const char* symname,
- const Elf_Byte syminfo,
- const Elf_Word symvalue)
+static size_t
+get_veneer_size (int type)
+{
+ (void) type;
+ return rtems_rtl_elf_relocate_tramp_max_size ();
+}
+
+static bool
+rtems_rtl_elf_reloc_rela (rtems_rtl_obj* obj,
+ const Elf_Rela* rela,
+ const rtems_rtl_obj_sect* sect,
+ const char* symname,
+ const Elf_Byte syminfo,
+ const Elf_Word symvalue,
+ const bool parsing)
{
Elf_Addr* where;
Elf_Word tmp;
@@ -84,10 +221,12 @@ rtems_rtl_elf_relocate_rela (rtems_rtl_obj* obj,
/*
* value:1; Field: word32; Expression: S + A
*/
- *where = symvalue + rela->r_addend;
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
- printf ("rtl: ADDR32 %p @ %p in %s\n",
- (void *)*(where), where, rtems_rtl_obj_oname (obj));
+ if (!parsing) {
+ *where = symvalue + rela->r_addend;
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: ADDR32 %p @ %p in %s\n",
+ (void *)*(where), where, rtems_rtl_obj_oname (obj));
+ }
break;
case R_TYPE(14):
@@ -107,47 +246,67 @@ rtems_rtl_elf_relocate_rela (rtems_rtl_obj* obj,
}
tmp = (symvalue + rela->r_addend) >> 2;
if (tmp > ((1<<bits) - 1 )) {
- printf("Overflow ADDR14/ADDR24\n");
- return false;
+ Elf_Word tramp_addr;
+ size_t tramp_size = get_veneer_size(ELF_R_TYPE(rela->r_info));
+ if (parsing) {
+ obj->tramp_size += tramp_size;
+ return true;
+ }
+ tramp_addr = (Elf_Addr) obj->tramp_brk;
+ obj->tramp_brk = set_veneer(obj->tramp_brk,
+ symvalue + rela->r_addend);
+ tmp = *where;
+ tmp &= ~mask;
+ tmp |= (tramp_addr + rela->r_addend) & mask;
+ }
+ else {
+ tmp = *where;
+ tmp &= ~mask;
+ tmp |= (symvalue + rela->r_addend) & mask;
+ }
+
+ if (!parsing) {
+ *where = tmp;
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: ADDR14/ADDR24 %p @ %p in %s\n",
+ (void *)*where, where, rtems_rtl_obj_oname (obj));
}
- tmp = *where;
- tmp &= ~mask;
- tmp |= (symvalue + rela->r_addend) & mask;
- *where = tmp;
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
- printf ("rtl: ADDR14/ADDR24 %p @ %p in %s\n",
- (void *)*where, where, rtems_rtl_obj_oname (obj));
break;
case R_TYPE(16_HA):
/*
* value:6; Field:half16; Expression: #ha(S+A)
*/
-
- tmp = symvalue + rela->r_addend;
- *(uint16_t *)where = (((tmp >> 16) + ((tmp & 0x8000) ? 1: 0)) & 0xffff);
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
- printf ("rtl: 16_HA %p @ %p in %s\n",
- (void *)*(where), where, rtems_rtl_obj_oname (obj));
+ if (!parsing) {
+ tmp = symvalue + rela->r_addend;
+ *(uint16_t *)where = (((tmp >> 16) + ((tmp & 0x8000) ? 1: 0)) & 0xffff);
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: 16_HA %p @ %p in %s\n",
+ (void *)*(where), where, rtems_rtl_obj_oname (obj));
+ }
break;
case R_TYPE(16_HI):
/*
* value:5; Field:half16; Expression: #hi(S+A)
*/
- *(uint16_t *)where = ((symvalue + rela->r_addend) >> 16) & 0xffff;
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
- printf ("rtl: 16_HI %p @ %p in %s\n",
- (void *)*where, where, rtems_rtl_obj_oname (obj));
+ if (!parsing) {
+ *(uint16_t *)where = ((symvalue + rela->r_addend) >> 16) & 0xffff;
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: 16_HI %p @ %p in %s\n",
+ (void *)*where, where, rtems_rtl_obj_oname (obj));
+ }
break;
case R_TYPE(16_LO):
/*
* value:4; Field:half16; Expression: #lo(S+A)
*/
- *(uint16_t *)where = (symvalue + (rela->r_addend)) & 0xffff;
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
- printf ("rtl: 16_LO %p @ %p in %s\n",
- (void *)*where, where, rtems_rtl_obj_oname (obj));
+ if (!parsing) {
+ *(uint16_t *)where = (symvalue + (rela->r_addend)) & 0xffff;
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: 16_LO %p @ %p in %s\n",
+ (void *)*where, where, rtems_rtl_obj_oname (obj));
+ }
break;
case R_TYPE(REL14):
@@ -170,27 +329,44 @@ rtems_rtl_elf_relocate_rela (rtems_rtl_obj* obj,
tmp =((int) (symvalue + rela->r_addend - (Elf_Addr)where)) >> 2;
if (((Elf_Sword)tmp > ((1<<(bits-1)) - 1)) ||
((Elf_Sword)tmp < -(1<<(bits-1)))) {
- printf("Overflow REL14/REL24\n");
- return false;
+ Elf_Word tramp_addr;
+ size_t tramp_size = get_veneer_size(ELF_R_TYPE(rela->r_info));
+ if (parsing) {
+ obj->tramp_size += tramp_size;
+ return true;
+ }
+ tramp_addr = (Elf_Addr) obj->tramp_brk;
+ obj->tramp_brk = set_veneer(obj->tramp_brk,
+ symvalue + rela->r_addend);
+ tmp = *where;
+ tmp &= ~mask;
+ tmp |= (tramp_addr + rela->r_addend - (Elf_Addr)where) & mask;
+ }
+ else
+ {
+ tmp = *where;
+ tmp &= ~mask;
+ tmp |= (symvalue + rela->r_addend - (Elf_Addr)where) & mask;
}
- tmp = *where;
- tmp &= ~mask;
- tmp |= (symvalue + rela->r_addend - (Elf_Addr)where) & mask;
- *where = tmp;
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
- printf ("rtl: REL24/REL14 %p @ %p in %s\n",
- (void *)*where, where, rtems_rtl_obj_oname (obj));
+ if (!parsing) {
+ *where = tmp;
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: REL24/REL14 %p @ %p in %s\n",
+ (void *)*where, where, rtems_rtl_obj_oname (obj));
+ }
break;
case R_TYPE(REL32):
/*
* value:26; Field:word32*; Expression:S+A-P
*/
- *where = symvalue + rela->r_addend - (Elf_Addr)where;
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
- printf ("rtl: REL32 %p @ %p in %s\n",
- (void *)*where, where, rtems_rtl_obj_oname (obj));
+ if (!parsing) {
+ *where = symvalue + rela->r_addend - (Elf_Addr)where;
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: REL32 %p @ %p in %s\n",
+ (void *)*where, where, rtems_rtl_obj_oname (obj));
+ }
break;
case R_TYPE(SDAREL16):
@@ -198,19 +374,22 @@ rtems_rtl_elf_relocate_rela (rtems_rtl_obj* obj,
* A sign-extended 16 bit value relative to _SDA_BASE_, for use with
* small data items.
*/
- mask = 0xffff;
- tmp = *((Elf32_Half*) where);
- tmp &= ~mask;
- tmp |= (symvalue + rela->r_addend - (Elf_Addr)where) & mask;
- *((Elf32_Half*) where) = tmp;
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
- printf ("rtl: SDAREL16 %p @ %p in %s\n",
- (void *) (uintptr_t) *((Elf32_Half*) where),
- where, rtems_rtl_obj_oname (obj));
+ if (!parsing) {
+ Elf_Addr sda_base = get_sda_base ();
+ mask = 0xffff;
+ tmp = *((Elf32_Half*) where);
+ tmp &= ~mask;
+ tmp |= (symvalue + rela->r_addend - sda_base) & mask;
+ *((Elf32_Half*) where) = tmp;
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: SDAREL16 %p @ %p in %s\n",
+ (void *) (uintptr_t) *((Elf32_Half*) where),
+ where, rtems_rtl_obj_oname (obj));
+ }
break;
default:
- printf ("rtl: reloc unknown: sym = %lu, type = %" PRIu32 ", offset = %p, "
+ printf ("rtl: reloc unknown: sym = %" PRIu32 ", type = %" PRIu32 ", offset = %p, "
"contents = %p\n",
ELF_R_SYM(rela->r_info), (uint32_t) ELF_R_TYPE(rela->r_info),
(void *)rela->r_offset, (void *)*where);
@@ -224,6 +403,40 @@ rtems_rtl_elf_relocate_rela (rtems_rtl_obj* obj,
}
bool
+rtems_rtl_elf_relocate_rela_tramp (rtems_rtl_obj* obj,
+ const Elf_Rela* rela,
+ const rtems_rtl_obj_sect* sect,
+ const char* symname,
+ const Elf_Byte syminfo,
+ const Elf_Word symvalue)
+{
+ return rtems_rtl_elf_reloc_rela (obj,
+ rela,
+ sect,
+ symname,
+ syminfo,
+ symvalue,
+ true);
+}
+
+bool
+rtems_rtl_elf_relocate_rela (rtems_rtl_obj* obj,
+ const Elf_Rela* rela,
+ const rtems_rtl_obj_sect* sect,
+ const char* symname,
+ const Elf_Byte syminfo,
+ const Elf_Word symvalue)
+{
+ return rtems_rtl_elf_reloc_rela (obj,
+ rela,
+ sect,
+ symname,
+ syminfo,
+ symvalue,
+ false);
+}
+
+bool
rtems_rtl_elf_relocate_rel_tramp (rtems_rtl_obj* obj,
const Elf_Rel* rel,
const rtems_rtl_obj_sect* sect,