summaryrefslogtreecommitdiffstats
path: root/cpukit/libdl/rtl-mdreloc-arm.c
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2019-01-15 17:47:41 +1100
committerChris Johns <chrisj@rtems.org>2019-02-09 10:06:34 +1100
commitd8c70ba65b13cd023b50b8aed5d91e455017cdd5 (patch)
treef78c3441ef74a7315ef4f7850bd27631bf0d2502 /cpukit/libdl/rtl-mdreloc-arm.c
parentlibdl: Fix the support for constructors and desctructors. (diff)
downloadrtems-d8c70ba65b13cd023b50b8aed5d91e455017cdd5.tar.bz2
libdl: Add support for trampolines
- Trampolines or fixups for veneers provide long jump support for instruciton sets that implement short relative address branches. The linker provides trampolines when creating a static image. This patch adds trampoline support to libdl and the ARM architecture. - The dl09 test requires enough memory so modules are outside the relative branch instruction ranges for the architecture. Updates #3685
Diffstat (limited to 'cpukit/libdl/rtl-mdreloc-arm.c')
-rw-r--r--cpukit/libdl/rtl-mdreloc-arm.c301
1 files changed, 216 insertions, 85 deletions
diff --git a/cpukit/libdl/rtl-mdreloc-arm.c b/cpukit/libdl/rtl-mdreloc-arm.c
index ef00701d32..a00f0f8825 100644
--- a/cpukit/libdl/rtl-mdreloc-arm.c
+++ b/cpukit/libdl/rtl-mdreloc-arm.c
@@ -33,18 +33,17 @@
static inline Elf_Addr
load_ptr(void *where)
{
- Elf_Addr res;
+ Elf_Addr res;
- memcpy(&res, where, sizeof(res));
+ memcpy(&res, where, sizeof(res));
- return (res);
+ return (res);
}
static inline void
store_ptr(void *where, Elf_Addr val)
{
-
- memcpy(where, &val, sizeof(val));
+ memcpy(where, &val, sizeof(val));
}
/*
@@ -67,6 +66,33 @@ sign_extend31(Elf_Addr val)
return 0x7fffffff & val;
}
+static void*
+set_veneer(void* tramopline, Elf_Addr target)
+{
+ /*
+ * http://shell-storm.org/online/Online-Assembler-and-Disassembler/
+ *
+ * ldr.w pc, [pc]
+ */
+ uint32_t* tramp = (uint32_t*) tramopline;
+ *tramp++ = 0xf000f8df;
+ *tramp++ = (uint32_t) target;
+ return tramp;
+}
+
+static size_t
+get_veneer_size(int type)
+{
+ (void) type;
+ return 8;
+}
+
+size_t
+rtems_rtl_elf_relocate_tramp_max_size (void)
+{
+ return 8;
+}
+
uint32_t
rtems_rtl_elf_section_flags (const rtems_rtl_obj* obj,
const Elf_Shdr* shdr)
@@ -84,24 +110,49 @@ rtems_rtl_elf_rel_resolve_sym (Elf_Word type)
}
bool
-rtems_rtl_elf_relocate_rela (const rtems_rtl_obj* obj,
+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)
+{
+ (void) obj;
+ (void) rela;
+ (void) sect;
+ (void) symname;
+ (void) syminfo;
+ (void) symvalue;
+ rtems_rtl_set_error (EINVAL, "rela type record not supported");
+ return false;
+}
+
+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)
{
+ (void) obj;
+ (void) rela;
+ (void) sect;
+ (void) symname;
+ (void) syminfo;
+ (void) symvalue;
rtems_rtl_set_error (EINVAL, "rela type record not supported");
return false;
}
-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)
+static bool
+rtems_rtl_elf_relor_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,
+ const bool parsing)
{
Elf_Addr *where;
Elf_Addr tmp;
@@ -142,21 +193,40 @@ rtems_rtl_elf_relocate_rel (const rtems_rtl_obj* obj,
tmp = (Elf_Sword)tmp >> 2;
if (((Elf_Sword)tmp > 0x7fffff) || ((Elf_Sword)tmp < -0x800000)) {
- printf("CALL/JUMP24 Overflow\n");
- return false;
- }
+ Elf_Word tramp_addr;
+ size_t tramp_size = get_veneer_size(ELF_R_TYPE(rel->r_info));
- *where = (*where & 0xff000000) | (tmp & 0xffffff);
+ if (parsing) {
+ obj->tramp_size += tramp_size;
+ return true;
+ }
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
- printf ("rtl: JUMP24/PC24/CALL %p @ %p in %s\n",
- (void *)*where, where, rtems_rtl_obj_oname (obj));
+ if (!rtems_rtl_obj_has_ramp_space (obj, tramp_size)) {
+ rtems_rtl_set_error (EINVAL,
+ "%s: CALL/JUMP24: overflow: no tramp memory",
+ sect->name);
+ return false;
+ }
+ tramp_addr = ((Elf_Addr) obj->tramp_brk) | (symvalue & 1);
+ obj->tramp_brk = set_veneer(obj->tramp_brk, symvalue);
+
+ tmp = tramp_addr + (addend << 2) - (Elf_Addr)where;
+ tmp = (Elf_Sword)tmp >> 2;
+ }
+
+ if (!parsing) {
+ *where = (*where & 0xff000000) | (tmp & 0xffffff);
+
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: JUMP24/PC24/CALL %p @ %p in %s\n",
+ (void *)*where, where, rtems_rtl_obj_oname (obj));
+ }
break;
case R_TYPE(V4BX):
/* Miscellaneous, ignore */
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) {
+ if (!parsing && rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) {
printf ("rtl: V4BX %p @ %p in %s\n",
(void *)*where, where, rtems_rtl_obj_oname (obj));
}
@@ -182,72 +252,78 @@ rtems_rtl_elf_relocate_rel (const rtems_rtl_obj* obj,
}
}
- *where = (insn & 0xfff0f000) | ((tmp & 0xf000) << 4) | (tmp & 0xfff);
+ if (!parsing) {
+ *where = (insn & 0xfff0f000) | ((tmp & 0xf000) << 4) | (tmp & 0xfff);
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
- printf ("rtl: MOVT_ABS/MOVW_ABS_NC %p @ %p in %s\n",
- (void *)*where, where, rtems_rtl_obj_oname (obj));
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: MOVT_ABS/MOVW_ABS_NC %p @ %p in %s\n",
+ (void *)*where, where, rtems_rtl_obj_oname (obj));
+ }
break;
-
case R_TYPE(REL32): /* word32 (S + A) | T - P */
case R_TYPE(ABS32): /* word32 (S + A) | T */
case R_TYPE(GLOB_DAT): /* word32 (S + A) | T */
case R_TYPE(PREL31): /* word32 (S + A) | T - P */
case R_TYPE(TARGET1): /* Equivalent to ABS32 */
case R_TYPE(TARGET2): /* Equivalent to REL32 */
- if (__predict_true(RELOC_ALIGNED_P(where))) {
- tmp = *where + symvalue;
- if (isThumb(symvalue))
- tmp |= 1;
- if (ELF_R_TYPE(rel->r_info) == R_TYPE(REL32) ||
- ELF_R_TYPE(rel->r_info) == R_TYPE(TARGET2))
- tmp -= (Elf_Addr)where;
- else if (ELF_R_TYPE(rel->r_info) == R_TYPE(PREL31))
- tmp = sign_extend31(tmp - (Elf_Addr)where);
- *where = tmp;
- } else {
- tmp = load_ptr(where) + symvalue;
- if (isThumb(symvalue))
- tmp |= 1;
- if (ELF_R_TYPE(rel->r_info) == R_TYPE(REL32) ||
- ELF_R_TYPE(rel->r_info) == R_TYPE(TARGET2))
- tmp -= (Elf_Addr)where;
- else if (ELF_R_TYPE(rel->r_info) == R_TYPE(PREL31))
- tmp = sign_extend31(tmp - (Elf_Addr)where);
- store_ptr(where, tmp);
- }
+ if (!parsing) {
+ if (__predict_true(RELOC_ALIGNED_P(where))) {
+ tmp = *where + symvalue;
+ if (isThumb(symvalue))
+ tmp |= 1;
+ if (ELF_R_TYPE(rel->r_info) == R_TYPE(REL32) ||
+ ELF_R_TYPE(rel->r_info) == R_TYPE(TARGET2))
+ tmp -= (Elf_Addr)where;
+ else if (ELF_R_TYPE(rel->r_info) == R_TYPE(PREL31))
+ tmp = sign_extend31(tmp - (Elf_Addr)where);
+ if (!parsing) {
+ *where = tmp;
+ }
+ } else {
+ tmp = load_ptr(where) + symvalue;
+ if (isThumb(symvalue))
+ tmp |= 1;
+ if (ELF_R_TYPE(rel->r_info) == R_TYPE(REL32) ||
+ ELF_R_TYPE(rel->r_info) == R_TYPE(TARGET2))
+ tmp -= (Elf_Addr)where;
+ else if (ELF_R_TYPE(rel->r_info) == R_TYPE(PREL31))
+ tmp = sign_extend31(tmp - (Elf_Addr)where);
+ store_ptr(where, tmp);
+ }
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
- printf ("rtl: REL32/ABS32/GLOB_DAT/PREL31/TARGET2 %p @ %p in %s\n",
- (void *)tmp, where, rtems_rtl_obj_oname (obj));
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
+ printf ("rtl: REL32/ABS32/GLOB_DAT/PREL31/TARGET2 %p @ %p in %s\n",
+ (void *)tmp, where, rtems_rtl_obj_oname (obj));
+ }
break;
case R_TYPE(THM_MOVT_ABS):
case R_TYPE(THM_MOVW_ABS_NC):
- upper_insn = *(uint16_t *)where;
- lower_insn = *((uint16_t *)where + 1);
-
- addend = ((upper_insn & 0x000f) << 12) | ((upper_insn & 0x0400) << 1) |
- ((lower_insn & 0x7000) >> 4) | (lower_insn & 0x00ff);
- addend = (addend ^ 0x8000) - 0x8000;
-
- tmp = addend + symvalue;
- if (ELF32_R_TYPE(rel->r_info) == R_ARM_THM_MOVT_ABS)
- tmp >>= 16;
-
- *(uint16_t *)where = (uint16_t)((upper_insn & 0xfbf0) |
- ((tmp & 0xf000) >> 12) |
- ((tmp & 0x0800) >> 1));
- *((uint16_t *)where + 1) = (uint16_t)((lower_insn & 0x8f00) |
- ((tmp & 0x0700) << 4) |
- (tmp & 0x00ff));
-
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) {
- printf ("rtl: THM_MOVT_ABS/THM_MOVW_ABS_NC %p @ %p in %s\n",
- (void *)*where, where, rtems_rtl_obj_oname (obj));
+ if (!parsing) {
+ upper_insn = *(uint16_t *)where;
+ lower_insn = *((uint16_t *)where + 1);
+
+ addend = ((upper_insn & 0x000f) << 12) | ((upper_insn & 0x0400) << 1) |
+ ((lower_insn & 0x7000) >> 4) | (lower_insn & 0x00ff);
+ addend = (addend ^ 0x8000) - 0x8000;
+
+ tmp = addend + symvalue;
+ if (ELF32_R_TYPE(rel->r_info) == R_ARM_THM_MOVT_ABS)
+ tmp >>= 16;
+
+ *(uint16_t *)where = (uint16_t)((upper_insn & 0xfbf0) |
+ ((tmp & 0xf000) >> 12) |
+ ((tmp & 0x0800) >> 1));
+ *((uint16_t *)where + 1) = (uint16_t)((lower_insn & 0x8f00) |
+ ((tmp & 0x0700) << 4) |
+ (tmp & 0x00ff));
+
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) {
+ printf ("rtl: THM_MOVT_ABS/THM_MOVW_ABS_NC %p @ %p in %s\n",
+ (void *)*where, where, rtems_rtl_obj_oname (obj));
+ }
}
-
break;
case R_TYPE(THM_JUMP24):
@@ -278,24 +354,44 @@ rtems_rtl_elf_relocate_rel (const rtems_rtl_obj* obj,
tmp = tmp - (Elf_Addr)where;
if (((Elf_Sword)tmp > 0x7fffff) || ((Elf_Sword)tmp < -0x800000)) {
- printf("THM_CALL/JUMP24 overflow\n");
- return false;
- }
+ Elf_Word tramp_addr;
+ size_t tramp_size = get_veneer_size(ELF_R_TYPE(rel->r_info));
+
+ if (parsing) {
+ obj->tramp_size += tramp_size;
+ return true;
+ }
+
+ if (!rtems_rtl_obj_has_ramp_space (obj, tramp_size)) {
+ rtems_rtl_set_error (EINVAL,
+ "%s: THM_CALL/JUMP24: overflow: no tramp memory",
+ sect->name);
+ return false;
+ }
- sign = (tmp >> 24) & 1;
- *(uint16_t *)where = (uint16_t)((upper_insn & 0xf800) | (sign << 10) |
- ((tmp >> 12) & 0x3ff));
+ tramp_addr = ((Elf_Addr) obj->tramp_brk) | (symvalue & 1);
+ obj->tramp_brk = set_veneer(obj->tramp_brk, symvalue);
- *((uint16_t *)where + 1) = (uint16_t)((lower_insn & 0xd000)|
- ((sign ^ (~(tmp >> 23) & 1)) << 13) |
- ((sign ^ (~(tmp >> 22) & 1)) << 11) |
- ((tmp >> 1) & 0x7ff));
- if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)){
- printf ("rtl: THM_CALL/JUMP24 %p @ %p in %s\n",
- (void *)*where, where, rtems_rtl_obj_oname (obj));
+ tmp = tramp_addr + addend;
+ tmp = tmp - (Elf_Addr)where;
}
+ if (!parsing) {
+ sign = (tmp >> 24) & 1;
+ *(uint16_t *)where = (uint16_t)((upper_insn & 0xf800) | (sign << 10) |
+ ((tmp >> 12) & 0x3ff));
+
+ *((uint16_t *)where + 1) = (uint16_t)((lower_insn & 0xd000)|
+ ((sign ^ (~(tmp >> 23) & 1)) << 13) |
+ ((sign ^ (~(tmp >> 22) & 1)) << 11) |
+ ((tmp >> 1) & 0x7ff));
+
+ if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)){
+ printf ("rtl: THM_CALL/JUMP24 %p @ %p in %s\n",
+ (void *)*where, where, rtems_rtl_obj_oname (obj));
+ }
+ }
break;
case R_TYPE(THM_JUMP19):
@@ -327,6 +423,7 @@ rtems_rtl_elf_relocate_rel (const rtems_rtl_obj* obj,
rtems_rtl_set_error (EINVAL, "%s: Overflow %" PRIu32 " "
"THM_JUMP19 relocations",
sect->name, (uint32_t) ELF_R_TYPE(rel->r_info));
+ return true;
return false;
}
@@ -359,6 +456,40 @@ rtems_rtl_elf_relocate_rel (const rtems_rtl_obj* obj,
}
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)
+{
+ return rtems_rtl_elf_relor_rel (obj,
+ rel,
+ sect,
+ symname,
+ syminfo,
+ symvalue,
+ true);
+}
+
+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)
+{
+ return rtems_rtl_elf_relor_rel (obj,
+ rel,
+ sect,
+ symname,
+ syminfo,
+ symvalue,
+ false);
+}
+
+bool
rtems_rtl_elf_unwind_parse (const rtems_rtl_obj* obj,
const char* name,
uint32_t flags)