/* * 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-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) uint32_t rtems_rtl_elf_section_flags (const rtems_rtl_obj* obj, const Elf_Shdr* shdr) { return 0; } bool rtems_rtl_elf_rel_resolve_sym (Elf_Word type) { return true; } bool rtems_rtl_elf_relocate_rela (const 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) { 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 */ *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_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)); 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)); 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)); 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)))) { printf("Overflow REL14/REL24\n"); return false; } 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)); 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)); break; case R_TYPE(SDAREL16): /* * 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)); break; default: printf ("rtl: reloc unknown: sym = %lu, 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_rel (const 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) { printf ("rtl: rel type record not supported; please report\n"); 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); }