diff options
Diffstat (limited to 'cpukit/libdl/rtl-mdreloc-powerpc.c')
-rw-r--r-- | cpukit/libdl/rtl-mdreloc-powerpc.c | 359 |
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, |