/* * Taken from NetBSD and stripped of the relocations not needed on RTEMS. */ /* $NetBSD: ppc_reloc.c,v 1.44 2010/01/13 20:17:22 christos Exp $ */ #include #include #include #include #include #include #include #include "rtl-bit-alloc.h" #include "rtl-elf.h" #include "rtl-error.h" #include #include "rtl-unwind.h" #include "rtl-unwind-dw2.h" #define ha(x) ((((u_int32_t)(x) & 0x8000) ? \ ((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) { #if _ARCH_PPC64 return NULL; #else Elf_Addr addr; GET_ADDR(__SDATA_START__, addr); return (void*) addr; #endif } #if !_ARCH_PPC64 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; } #endif uint32_t rtems_rtl_elf_section_flags (const rtems_rtl_obj* obj, const Elf_Shdr* shdr) { 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) { #if !_ARCH_PPC64 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; } #endif return flags; } bool rtems_rtl_elf_arch_section_alloc (const rtems_rtl_obj* obj, rtems_rtl_obj_sect* sect) { #if _ARCH_PPC64 rtems_rtl_set_error (ENOMEM, ".sdata no supported by ABI"); return false; #else 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; #endif } bool rtems_rtl_elf_arch_section_free (const rtems_rtl_obj* obj, rtems_rtl_obj_sect* sect) { #if !_ARCH_PPC64 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); #endif return true; } bool rtems_rtl_elf_rel_resolve_sym (Elf_Word type) { return true; } size_t rtems_rtl_elf_relocate_tramp_max_size (void) { /* * We have 4 instructions and each instruction is 32bits. */ return 4 * 4; } static void* set_veneer (void* tramopline, Elf_Addr target) { /* * 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; } 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; uint32_t mask = 0; uint32_t bits = 0; where = (Elf_Addr *)(sect->base + rela->r_offset); switch (ELF_R_TYPE(rela->r_info)) { case R_TYPE(NONE): break; case R_TYPE(32): /* * value:1; Field: word32; Expression: S + A */ 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): /* * value:7; Field: low14*; Expression: (S + A) >> 2 */ case R_TYPE(24): /* * value:2; Field: low24*; Expression: (S + A) >> 2 */ if (ELF_R_TYPE(rela->r_info) == R_TYPE(14)) { bits = 14; mask = 0xfffc; } else { bits = 24; mask = 0x3fffffc; } tmp = (symvalue + rela->r_addend) >> 2; if (tmp > ((1<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)); } break; case R_TYPE(16_HA): /* * value:6; Field:half16; Expression: #ha(S+A) */ 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) */ 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) */ 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): /* * value:11; Field:low14*; Expression:(S+A-P)>>2 */ case R_TYPE(REL24): /* * value:10; Field:low24*; Expression:(S+A-P)>>2 */ if (ELF_R_TYPE(rela->r_info) == R_TYPE(REL24)) { mask = 0x3fffffc; bits = 24; } else if (ELF_R_TYPE(rela->r_info) == R_TYPE(REL14)) { mask = 0xfffc; bits = 14; } tmp =((int) (symvalue + rela->r_addend - (Elf_Addr)where)) >> 2; if (((Elf_Sword)tmp > ((1<<(bits-1)) - 1)) || ((Elf_Sword)tmp < -(1<<(bits-1)))) { 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; } 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 */ 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): /* * A sign-extended 16 bit value relative to _SDA_BASE_, for use with * small data items. */ 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 = %" 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); rtems_rtl_set_error (EINVAL, "%s: Unsupported relocation type %" PRId32 "in non-PLT relocations", sect->name, (uint32_t) ELF_R_TYPE(rela->r_info)); return false; } return true; } 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, const char* symname, const Elf_Byte syminfo, const Elf_Word symvalue) { (void) obj; (void) rel; (void) sect; (void) symname; (void) syminfo; (void) symvalue; rtems_rtl_set_error (EINVAL, "rel type record not supported"); return false; } bool rtems_rtl_elf_relocate_rel (rtems_rtl_obj* obj, const Elf_Rel* rel, const rtems_rtl_obj_sect* sect, const char* symname, const Elf_Byte syminfo, const Elf_Word symvalue) { (void) obj; (void) rel; (void) sect; (void) symname; (void) syminfo; (void) symvalue; rtems_rtl_set_error (EINVAL, "rel type record not supported"); return false; } bool rtems_rtl_elf_unwind_parse (const rtems_rtl_obj* obj, const char* name, uint32_t flags) { return rtems_rtl_elf_unwind_dw2_parse (obj, name, flags); } bool rtems_rtl_elf_unwind_register (rtems_rtl_obj* obj) { return rtems_rtl_elf_unwind_dw2_register (obj); } bool rtems_rtl_elf_unwind_deregister (rtems_rtl_obj* obj) { return rtems_rtl_elf_unwind_dw2_deregister (obj); }