Current ARM relocation doesn't handle the cases when the relocation cant be satisfied directly (like thumb call over 1M of distance or jump24 to thumb mode. Attached patch adds missing tampoline and missing relocation handling to EFI code (it didn't allow to use ARM (no-Thumb) binary with EFI). I couldn't test it on either arm-efi or ARM64
diff --git a/conf/Makefile.common b/conf/Makefile.common
index 8a71f13..152847c 100644
--- a/conf/Makefile.common
+++ b/conf/Makefile.common
@@ -11,7 +11,7 @@ if COND_sparc64_ieee1275
LDFLAGS_PLATFORM = -Wl,-melf64_sparc -mno-relax
endif
if COND_arm
- CFLAGS_PLATFORM += -mthumb-interwork -mlong-calls
+ CFLAGS_PLATFORM += -mthumb-interwork
CCASFLAGS_PLATFORM = -mthumb-interwork
LDFLAGS_PLATFORM = -Wl,--wrap=__clear_cache
endif
diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c
index 4563a52..603bfa4 100644
--- a/grub-core/kern/arm/dl.c
+++ b/grub-core/kern/arm/dl.c
@@ -25,11 +25,90 @@
#include <grub/i18n.h>
#include <grub/arm/reloc.h>
+struct trampoline_arm
+{
+#define ARM_LOAD_IP 0xe59fc000
+#define ARM_BX 0xe12fff1c
+#define ARM_MOV_PC 0xe1a0f00c
+ grub_uint32_t load_ip; /* ldr ip, [pc] */
+ grub_uint32_t bx; /* bx ip or mov pc, ip*/
+ grub_uint32_t addr;
+};
+
+static grub_uint16_t thumb_template[8] =
+ {
+ 0x468c, /* mov ip, r1 */
+ 0x4903, /* ldr r1, [pc, #12] ; (10 <.text+0x10>) */
+ /* Exchange R1 and IP in limited Thumb instruction set.
+ IP gets negated but we compensate it by C code. */
+ /* R1 IP */
+ /* -A R1 */
+ 0x4461, /* add r1, ip */ /* R1-A R1 */
+ 0x4249, /* negs r1, r1 */ /* A-R1 R1 */
+ 0x448c, /* add ip, r1 */ /* A-R1 A */
+ 0x4249, /* negs r1, r1 */ /* R1-A A */
+ 0x4461, /* add r1, ip */ /* R1 A */
+ 0x4760 /* bx ip */
+ };
+
+struct trampoline_thumb
+{
+ grub_uint16_t template[8];
+ grub_uint32_t neg_addr;
+};
+
+
+grub_err_t
+grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
+ grub_size_t *got)
+{
+ const Elf_Ehdr *e = ehdr;
+ const Elf_Shdr *s;
+ unsigned i;
+
+ *tramp = 0;
+ *got = 0;
+
+ for (i = 0, s = (const Elf_Shdr *) ((grub_addr_t) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (const Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize))
+ if (s->sh_type == SHT_REL)
+ {
+ const Elf_Rel *rel, *max;
+
+ for (rel = (const Elf_Rel *) ((grub_addr_t) e + s->sh_offset),
+ max = rel + s->sh_size / s->sh_entsize;
+ rel < max;
+ rel++)
+ switch (ELF_R_TYPE (rel->r_info))
+ {
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ {
+ *tramp += sizeof (struct trampoline_arm);
+ break;
+ }
+ case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24:
+ case R_ARM_THM_JUMP19:
+ {
+ *tramp += sizeof (struct trampoline_thumb);
+ break;
+ }
+ }
+ }
+
+ grub_dprintf ("dl", "trampoline size %x\n", *tramp);
+
+ return GRUB_ERR_NONE;
+}
+
/*************************************************
* Runtime dynamic linker with helper functions. *
*************************************************/
static grub_err_t
-do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
+do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod,
+ grub_uint32_t **tptr)
{
grub_dl_segment_t seg;
Elf_Rel *rel;
@@ -76,16 +155,58 @@ do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
case R_ARM_CALL:
case R_ARM_JUMP24:
{
- retval = grub_arm_reloc_jump24 (target, sym_addr);
- if (retval != GRUB_ERR_NONE)
- return retval;
+ grub_int32_t offset;
+
+ sym_addr += grub_arm_jump24_get_offset (target);
+ offset = sym_addr - (grub_uint32_t) target;
+
+ if ((sym_addr & 1) || !grub_arm_jump24_check_offset (offset))
+ {
+ struct trampoline_arm *tp = (struct trampoline_arm *) *tptr;
+ *tptr += sizeof (struct trampoline_arm) / sizeof (**tptr);
+ tp->load_ip = ARM_LOAD_IP;
+ tp->bx = (sym_addr & 1) ? ARM_BX : ARM_MOV_PC;
+ tp->addr = sym_addr + 8;
+ offset = (grub_uint8_t *) tp - (grub_uint8_t *) target - 8;
+ }
+ if (!grub_arm_jump24_check_offset (offset))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "trampoline out of range");
+ grub_arm_jump24_set_offset (target, offset);
}
break;
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
{
/* Thumb instructions can be 16-bit aligned */
- retval = grub_arm_reloc_thm_call ((grub_uint16_t *) target, sym_addr);
+ grub_int32_t offset;
+
+ sym_addr += grub_arm_thm_call_get_offset ((grub_uint16_t *) target);
+
+ grub_dprintf ("dl", " sym_addr = 0x%08x\n", sym_addr);
+
+ offset = sym_addr - (grub_uint32_t) target;
+
+ grub_dprintf("dl", " BL*: target=%p, sym_addr=0x%08x, offset=%d\n",
+ target, sym_addr, offset);
+
+ if (!(sym_addr & 1) || (offset < -0x200000 || offset >= 0x200000))
+ {
+ struct trampoline_thumb *tp = (struct trampoline_thumb *) *tptr;
+ *tptr += sizeof (struct trampoline_thumb) / sizeof (**tptr);
+ grub_memcpy (tp->template, thumb_template, sizeof (tp->template));
+ tp->neg_addr = -sym_addr - 4;
+ offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1;
+ }
+
+ if (offset < -0x200000 || offset >= 0x200000)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "trampoline out of range");
+
+ grub_dprintf ("dl", " relative destination = %p\n",
+ (char *) target + offset);
+
+ retval = grub_arm_thm_call_set_offset ((grub_uint16_t *) target, offset);
if (retval != GRUB_ERR_NONE)
return retval;
}
@@ -98,9 +219,31 @@ do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
case R_ARM_THM_JUMP19:
{
/* Thumb instructions can be 16-bit aligned */
- retval = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr);
- if (retval != GRUB_ERR_NONE)
- return retval;
+ grub_int32_t offset;
+
+ if (!(sym_addr & 1))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("Relocation targeting wrong execution state"));
+
+ sym_addr += grub_arm_thm_jump19_get_offset ((grub_uint16_t *) target);
+
+ offset = sym_addr - (grub_uint32_t) target;
+
+ if (!grub_arm_thm_jump19_check_offset (offset)
+ || !(sym_addr & 1))
+ {
+ struct trampoline_thumb *tp = (struct trampoline_thumb *) *tptr;
+ *tptr += sizeof (struct trampoline_thumb) / sizeof (**tptr);
+ grub_memcpy (tp->template, thumb_template, sizeof (tp->template));
+ tp->neg_addr = -sym_addr - 4;
+ offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1;
+ }
+
+ if (!grub_arm_thm_jump19_check_offset (offset))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "trampoline out of range");
+
+ grub_arm_thm_jump19_set_offset ((grub_uint16_t *) target, offset);
}
break;
default:
@@ -161,6 +304,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
Elf_Ehdr *e = ehdr;
Elf_Shdr *s;
unsigned i;
+ grub_uint32_t *tptr = mod->tramp;
if (!has_symtab (e))
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
@@ -169,35 +313,14 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
#define NEXT_SHDR(x, y) ((Elf_Shdr *) ((grub_addr_t)(y) + (x)->e_shentsize))
for (i = 0, s = FIRST_SHDR (e); i < e->e_shnum; i++, s = NEXT_SHDR (e, s))
- {
- grub_err_t ret;
-
- switch (s->sh_type)
- {
- case SHT_REL:
- {
- /* Relocations, no addends */
- ret = do_relocations (s, e, mod);
- if (ret != GRUB_ERR_NONE)
- return ret;
- }
- break;
- case SHT_NULL:
- case SHT_PROGBITS:
- case SHT_SYMTAB:
- case SHT_STRTAB:
- case SHT_NOBITS:
- case SHT_ARM_ATTRIBUTES:
- break;
- case SHT_RELA:
- default:
- {
- grub_dprintf ("dl", "unhandled section_type: %d (0x%08x)\n",
- s->sh_type, s->sh_type);
- return GRUB_ERR_NOT_IMPLEMENTED_YET;
- };
- }
- }
+ if (s->sh_type == SHT_REL)
+ {
+ grub_err_t ret;
+ /* Relocations, no addends */
+ ret = do_relocations (s, e, mod, &tptr);
+ if (ret != GRUB_ERR_NONE)
+ return ret;
+ }
#undef FIRST_SHDR
#undef NEXT_SHDR
diff --git a/grub-core/kern/arm/dl_helper.c b/grub-core/kern/arm/dl_helper.c
index 68a9e3b..5721939 100644
--- a/grub-core/kern/arm/dl_helper.c
+++ b/grub-core/kern/arm/dl_helper.c
@@ -38,8 +38,6 @@ grub_arm_reloc_abs32 (Elf32_Word *target, Elf32_Addr sym_addr)
tmp = grub_le_to_cpu32 (*target);
tmp += sym_addr;
*target = grub_cpu_to_le32 (tmp);
- grub_dprintf ("dl", " %s: reloc_abs32 0x%08x => 0x%08x", __FUNCTION__,
- (unsigned int) sym_addr, (unsigned int) tmp);
return GRUB_ERR_NONE;
}
@@ -51,37 +49,16 @@ grub_arm_reloc_abs32 (Elf32_Word *target, Elf32_Addr sym_addr)
* little-endian, requiring some additional fiddling. *
********************************************************************/
-/*
- * R_ARM_THM_CALL/THM_JUMP24
- *
- * Relocate Thumb (T32) instruction set relative branches:
- * B.W, BL and BLX
- */
-grub_err_t
-grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
+grub_int32_t
+grub_arm_thm_call_get_offset (grub_uint16_t *target)
{
- grub_int32_t offset, offset_low, offset_high;
- grub_uint32_t sign, j1, j2, is_blx;
- grub_uint32_t insword, insmask;
+ grub_uint32_t sign, j1, j2;
+ grub_uint32_t insword;
+ grub_int32_t offset;
/* Extract instruction word in alignment-safe manner */
insword = (grub_le_to_cpu16 (*target) << 16)
| (grub_le_to_cpu16(*(target + 1)));
- insmask = 0xf800d000;
-
- /* B.W/BL or BLX? Affects range and expected target state */
- if (((insword >> 12) & 0xd) == 0xc)
- is_blx = 1;
- else
- is_blx = 0;
-
- /* If BLX, target symbol must be ARM (target address LSB == 0) */
- if (is_blx && (sym_addr & 1))
- return grub_error (GRUB_ERR_BAD_MODULE,
- N_("Relocation targeting wrong execution state"));
-
- offset_low = -16777216;
- offset_high = is_blx ? 16777212 : 16777214;
/* Extract bitfields from instruction words */
sign = (insword >> 26) & 1;
@@ -95,22 +72,32 @@ grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
if (offset & (1 << 24))
offset -= (1 << 25);
- grub_dprintf ("dl", " sym_addr = 0x%08x", sym_addr);
+ return offset;
+}
- offset += sym_addr;
-#ifndef GRUB_UTIL
- offset -= (grub_uint32_t) target;
-#endif
+grub_err_t
+grub_arm_thm_call_set_offset (grub_uint16_t *target, grub_int32_t offset)
+{
+ grub_uint32_t sign, j1, j2;
+ const grub_uint32_t insmask = 0xf800d000;
+ grub_uint32_t insword;
+ int is_blx;
- grub_dprintf("dl", " %s: target=%p, sym_addr=0x%08x, offset=%d\n",
- is_blx ? "BLX" : "BL", target, sym_addr, offset);
+ /* Extract instruction word in alignment-safe manner */
+ insword = (grub_le_to_cpu16 (*target) << 16)
+ | (grub_le_to_cpu16(*(target + 1)));
+
+ if (((insword >> 12) & 0xd) == 0xc)
+ is_blx = 1;
+ else
+ is_blx = 0;
- if ((offset < offset_low) || (offset > offset_high))
- return grub_error (GRUB_ERR_BAD_MODULE,
- N_("THM_CALL Relocation out of range."));
+ if (!is_blx && !(offset & 1))
+ return grub_error (GRUB_ERR_BAD_MODULE, "bl/b.w targettting ARM");
- grub_dprintf ("dl", " relative destination = %p",
- (char *) target + offset);
+ /* Transform blx into bl if necessarry. */
+ if (is_blx && (offset & 1))
+ insword |= (1 << 12);
/* Reassemble instruction word */
sign = (offset >> 24) & 1;
@@ -130,21 +117,15 @@ grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
return GRUB_ERR_NONE;
}
-/*
- * R_ARM_THM_JUMP19
- *
- * Relocate conditional Thumb (T32) B<c>.W
- */
-grub_err_t
-grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
+grub_int32_t
+grub_arm_thm_jump19_get_offset (grub_uint16_t *target)
{
grub_int32_t offset;
- grub_uint32_t insword, insmask;
+ grub_uint32_t insword;
/* Extract instruction word in alignment-safe manner */
- insword = grub_le_to_cpu16 ((*target)) << 16
- | grub_le_to_cpu16 (*(target + 1));
- insmask = 0xfbc0d000;
+ insword = (grub_le_to_cpu16 (*target) << 16)
+ | (grub_le_to_cpu16(*(target + 1)));
/* Extract and sign extend offset */
offset = ((insword >> 26) & 1) << 19
@@ -156,18 +137,22 @@ grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
if (offset & (1 << 20))
offset -= (1 << 21);
- /* Adjust and re-truncate offset */
- offset += sym_addr;
-#ifndef GRUB_UTIL
- offset -= (grub_uint32_t) target;
-#endif
- if ((offset > 1048574) || (offset < -1048576))
- return grub_error (GRUB_ERR_BAD_MODULE,
- N_("THM_JUMP19 Relocation out of range."));
+ return offset;
+}
+
+void
+grub_arm_thm_jump19_set_offset (grub_uint16_t *target, grub_int32_t offset)
+{
+ grub_uint32_t insword;
+ const grub_uint32_t insmask = 0xfbc0d000;
offset >>= 1;
offset &= 0xfffff;
+ /* Extract instruction word in alignment-safe manner */
+ insword = grub_le_to_cpu16 ((*target)) << 16
+ | grub_le_to_cpu16 (*(target + 1));
+
/* Reassemble instruction word and write back */
insword &= insmask;
insword |= ((offset >> 19) & 1) << 26
@@ -177,9 +162,15 @@ grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
| (offset & 0x7ff);
*target = grub_cpu_to_le16 (insword >> 16);
*(target + 1) = grub_cpu_to_le16 (insword & 0xffff);
- return GRUB_ERR_NONE;
}
+int
+grub_arm_thm_jump19_check_offset (grub_int32_t offset)
+{
+ if ((offset > 1048574) || (offset < -1048576))
+ return 0;
+ return 1;
+}
/***********************************************************
@@ -188,35 +179,38 @@ grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
* ARM instructions are 32-bit in size and 32-bit aligned. *
***********************************************************/
-/*
- * R_ARM_JUMP24
- *
- * Relocate ARM (A32) B
- */
-grub_err_t
-grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
+grub_int32_t
+grub_arm_jump24_get_offset (grub_uint32_t *target)
{
- grub_uint32_t insword;
grub_int32_t offset;
-
- if (sym_addr & 1)
- return grub_error (GRUB_ERR_BAD_MODULE,
- N_("Relocation targeting wrong execution state"));
+ grub_uint32_t insword;
insword = grub_le_to_cpu32 (*target);
offset = (insword & 0x00ffffff) << 2;
if (offset & 0x02000000)
offset -= 0x04000000;
- offset += sym_addr;
-#ifndef GRUB_UTIL
- offset -= (grub_uint32_t) target;
-#endif
+ return offset;
+}
+
+int
+grub_arm_jump24_check_offset (grub_int32_t offset)
+{
+ if (offset >= 0x02000000 || offset < -0x02000000)
+ return 0;
+ return 1;
+}
+
+void
+grub_arm_jump24_set_offset (grub_uint32_t *target,
+ grub_int32_t offset)
+{
+ grub_uint32_t insword;
+
+ insword = grub_le_to_cpu32 (*target);
insword &= 0xff000000;
insword |= (offset >> 2) & 0x00ffffff;
*target = grub_cpu_to_le32 (insword);
-
- return GRUB_ERR_NONE;
}
diff --git a/grub-core/kern/arm64/dl.c b/grub-core/kern/arm64/dl.c
index afd0de2..a48a2ce 100644
--- a/grub-core/kern/arm64/dl.c
+++ b/grub-core/kern/arm64/dl.c
@@ -25,6 +25,15 @@
#include <grub/i18n.h>
#include <grub/cpu/reloc.h>
+struct trampoline
+{
+#define LDR 0x58000050
+#define BR 0xd61f0200
+ grub_uint32_t ldr; /* ldr x16, 8 */
+ grub_uint32_t br; /* br x16 */
+ grub_uint64_t addr;
+};
+
/*
* Check if EHDR is a valid ELF header.
*/
@@ -42,19 +51,54 @@ grub_arch_dl_check_header (void *ehdr)
return GRUB_ERR_NONE;
}
+grub_err_t
+grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
+ grub_size_t *got)
+{
+ const Elf_Ehdr *e = ehdr;
+ const Elf_Shdr *s;
+ unsigned i;
+
+ *tramp = 0;
+ *got = 0;
+
+ for (i = 0, s = (const Elf_Shdr *) ((grub_addr_t) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (const Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize))
+ if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA)
+ {
+ const Elf_Rel *rel, *max;
+
+ for (rel = (const Elf_Rel *) ((grub_addr_t) e + s->sh_offset),
+ max = rel + s->sh_size / s->sh_entsize;
+ rel < max;
+ rel = (const Elf_Rel *) ((grub_uint8_t *) rel + s->sh_entsize))
+ switch (ELF_R_TYPE (rel->r_info))
+ {
+ case R_AARCH64_CALL26:
+ case R_AARCH64_JUMP26:
+ {
+ *tramp += sizeof (struct trampoline);
+ break;
+ }
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
/*
* Unified function for both REL and RELA
*/
static grub_err_t
-do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
+do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod,
+ struct trampoline **tptr)
{
grub_err_t retval;
grub_dl_segment_t segment;
Elf_Rel *rel;
- Elf_Rela *rela;
Elf_Sym *symbol;
int i, entnum;
- unsigned long long entsize;
/* Find the target segment for this relocation section. */
for (segment = mod->segment ; segment != 0 ; segment = segment->next)
@@ -64,13 +108,8 @@ do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
return grub_error (GRUB_ERR_EOF, N_("relocation segment not found"));
rel = (Elf_Rel *) ((grub_addr_t) e + relhdr->sh_offset);
- rela = (Elf_Rela *) rel;
- if (relhdr->sh_type == SHT_RELA)
- entsize = sizeof (Elf_Rela);
- else
- entsize = sizeof (Elf_Rel);
- entnum = relhdr->sh_size / entsize;
+ entnum = relhdr->sh_size / relhdr->sh_entsize;
retval = GRUB_ERR_NONE;
grub_dprintf("dl", "Processing %d relocation entries.\n", entnum);
@@ -90,7 +129,7 @@ do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
sym_addr = symbol[symidx].st_value;
if (relhdr->sh_type == SHT_RELA)
- sym_addr += rela->r_addend;
+ sym_addr += ((Elf_Rela *) rel)->r_addend;
place = (void *) ((grub_addr_t) segment->addr + rel->r_offset);
@@ -108,7 +147,25 @@ do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
break;
case R_AARCH64_CALL26:
case R_AARCH64_JUMP26:
- retval = grub_arm64_reloc_xxxx26 (place, sym_addr);
+ {
+ grub_int64_t offset = sym_addr - (grub_uint64_t) place;
+
+ if (!grub_arm_64_check_xxxx26_offset (offset))
+ {
+ struct trampoline *tp = *tptr;
+ *tptr = tp + 1;
+ tp->ldr = LDR;
+ tp->br = BR;
+ tp->addr = sym_addr;
+ offset = (grub_uint8_t *) tp - (grub_uint8_t *) place;
+ }
+
+ if (!grub_arm_64_check_xxxx26_offset (offset))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("Trampoline out of range"));
+
+ grub_arm64_set_xxxx26_offset (place, offset);
+ }
break;
default:
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
@@ -119,8 +176,7 @@ do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
if (retval != GRUB_ERR_NONE)
break;
- rel = (Elf_Rel *) ((grub_addr_t) rel + entsize);
- rela++;
+ rel = (Elf_Rel *) ((grub_addr_t) rel + relhdr->sh_entsize);
}
return retval;
@@ -155,6 +211,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
Elf_Ehdr *e = ehdr;
Elf_Shdr *s;
unsigned i;
+ struct trampoline *tptr = mod->tramp;
if (!has_symtab (e))
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
@@ -163,34 +220,14 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
#define NEXT_SHDR(x, y) ((Elf_Shdr *) ((grub_addr_t)(y) + (x)->e_shentsize))
for (i = 0, s = FIRST_SHDR (e); i < e->e_shnum; i++, s = NEXT_SHDR (e, s))
- {
- grub_err_t ret;
-
- switch (s->sh_type)
- {
- case SHT_REL:
- case SHT_RELA:
- {
- ret = do_relX (s, e, mod);
- if (ret != GRUB_ERR_NONE)
- return ret;
- }
- break;
- case SHT_ARM_ATTRIBUTES:
- case SHT_NOBITS:
- case SHT_NULL:
- case SHT_PROGBITS:
- case SHT_SYMTAB:
- case SHT_STRTAB:
- break;
- default:
- {
- grub_dprintf ("dl", "unhandled section_type: %d (0x%08x)\n",
- s->sh_type, s->sh_type);
- return GRUB_ERR_NOT_IMPLEMENTED_YET;
- };
- }
- }
+ if (s->sh_type == SHT_REL
+ || s->sh_type == SHT_RELA)
+ {
+ grub_err_t ret;
+ ret = do_relX (s, e, mod, &tptr);
+ if (ret != GRUB_ERR_NONE)
+ return ret;
+ }
#undef FIRST_SHDR
#undef NEXT_SHDR
diff --git a/grub-core/kern/arm64/dl_helper.c b/grub-core/kern/arm64/dl_helper.c
index 6f99087..d213ab9 100644
--- a/grub-core/kern/arm64/dl_helper.c
+++ b/grub-core/kern/arm64/dl_helper.c
@@ -25,46 +25,31 @@
#include <grub/i18n.h>
#include <grub/arm64/reloc.h>
-static grub_ssize_t
-sign_compress_offset (grub_ssize_t offset, int bitpos)
-{
- return offset & ((1LL << (bitpos + 1)) - 1);
-}
-
/*
* grub_arm64_reloc_xxxx26():
*
* JUMP26/CALL26 relocations for B and BL instructions.
*/
-grub_err_t
-grub_arm64_reloc_xxxx26 (grub_uint32_t *place, Elf64_Addr adjust)
+int
+grub_arm_64_check_xxxx26_offset (grub_int64_t offset)
{
- grub_uint32_t insword, insmask;
- grub_ssize_t offset;
const grub_ssize_t offset_low = -(1 << 27), offset_high = (1 << 27) - 1;
- insword = grub_le_to_cpu32 (*place);
- insmask = 0xfc000000;
-
- offset = adjust;
-#ifndef GRUB_UTIL
- offset -= (grub_addr_t) place;
-#endif
-
if ((offset < offset_low) || (offset > offset_high))
- {
- return grub_error (GRUB_ERR_BAD_MODULE,
- N_("CALL26 Relocation out of range"));
- }
+ return 0;
+ return 1;
+}
+
+void
+grub_arm64_set_xxxx26_offset (grub_uint32_t *place, grub_int64_t offset)
+{
+ const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfc000000);
grub_dprintf ("dl", " reloc_xxxx64 %p %c= 0x%llx\n",
place, offset > 0 ? '+' : '-',
offset < 0 ? (long long) -(unsigned long long) offset : offset);
- offset = sign_compress_offset (offset, 27) >> 2;
-
- *place = grub_cpu_to_le32 ((insword & insmask) | offset);
-
- return GRUB_ERR_NONE;
+ *place &= insmask;
+ *place |= grub_cpu_to_le32 (offset >> 2) & ~insmask;
}
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
index 1d68414..3b6e4cf 100644
--- a/grub-core/kern/dl.c
+++ b/grub-core/kern/dl.c
@@ -229,7 +229,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
unsigned i;
Elf_Shdr *s;
grub_size_t tsize = 0, talign = 1;
-#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
grub_size_t tramp;
grub_size_t got;
grub_err_t err;
@@ -245,7 +245,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
talign = s->sh_addralign;
}
-#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got);
if (err)
return err;
@@ -314,7 +314,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
mod->segment = seg;
}
}
-#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN);
mod->tramp = ptr;
ptr += tramp;
diff --git a/include/grub/arm/reloc.h b/include/grub/arm/reloc.h
index 50d070f..b938037 100644
--- a/include/grub/arm/reloc.h
+++ b/include/grub/arm/reloc.h
@@ -20,8 +20,27 @@
#define GRUB_ARM_RELOC_H 1
grub_err_t grub_arm_reloc_abs32 (grub_uint32_t *addr, Elf32_Addr sym_addr);
-grub_err_t grub_arm_reloc_jump24 (grub_uint32_t *addr, Elf32_Addr sym_addr);
-grub_err_t grub_arm_reloc_thm_call (grub_uint16_t *addr, Elf32_Addr sym_addr);
-grub_err_t grub_arm_reloc_thm_jump19 (grub_uint16_t *addr, Elf32_Addr sym_addr);
+
+int
+grub_arm_thm_call_check_offset (grub_int32_t offset, int is_thumb);
+grub_int32_t
+grub_arm_thm_call_get_offset (grub_uint16_t *target);
+grub_err_t
+grub_arm_thm_call_set_offset (grub_uint16_t *target, grub_int32_t offset);
+
+grub_int32_t
+grub_arm_thm_jump19_get_offset (grub_uint16_t *target);
+void
+grub_arm_thm_jump19_set_offset (grub_uint16_t *target, grub_int32_t offset);
+int
+grub_arm_thm_jump19_check_offset (grub_int32_t offset);
+
+grub_int32_t
+grub_arm_jump24_get_offset (grub_uint32_t *target);
+int
+grub_arm_jump24_check_offset (grub_int32_t offset);
+void
+grub_arm_jump24_set_offset (grub_uint32_t *target,
+ grub_int32_t offset);
#endif
diff --git a/include/grub/arm64/reloc.h b/include/grub/arm64/reloc.h
index 606d71c..4aed3d7 100644
--- a/include/grub/arm64/reloc.h
+++ b/include/grub/arm64/reloc.h
@@ -19,6 +19,8 @@
#ifndef GRUB_ARM64_RELOC_H
#define GRUB_ARM64_RELOC_H 1
-grub_err_t grub_arm64_reloc_xxxx26 (grub_uint32_t *target, Elf64_Addr sym_addr);
+int grub_arm_64_check_xxxx26_offset (grub_int64_t offset);
+void
+grub_arm64_set_xxxx26_offset (grub_uint32_t *place, grub_int64_t offset);
#endif
diff --git a/include/grub/dl.h b/include/grub/dl.h
index d1d20d9..263e839 100644
--- a/include/grub/dl.h
+++ b/include/grub/dl.h
@@ -179,7 +179,7 @@ struct grub_dl
Elf_Sym *symtab;
void (*init) (struct grub_dl *mod);
void (*fini) (void);
-#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
void *got;
void *tramp;
#endif
@@ -256,11 +256,16 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
grub_size_t *got);
#endif
-#if defined (__powerpc__) || defined (__mips__)
+#if defined (__powerpc__) || defined (__mips__) || defined (__arm__)
#define GRUB_ARCH_DL_TRAMP_ALIGN 4
#define GRUB_ARCH_DL_GOT_ALIGN 4
#endif
+#if defined (__aarch64__)
+#define GRUB_ARCH_DL_TRAMP_ALIGN 8
+#define GRUB_ARCH_DL_GOT_ALIGN 8
+#endif
+
#endif
#endif /* ! GRUB_DL_H */
diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index 5240fe2..e2e598e 100644
--- a/util/grub-mkimagexx.c
+++ b/util/grub-mkimagexx.c
@@ -731,13 +731,13 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
case R_AARCH64_JUMP26:
case R_AARCH64_CALL26:
{
- grub_err_t err;
sym_addr -= offset;
sym_addr -= SUFFIX (entry_point);
- err = grub_arm64_reloc_xxxx26((grub_uint32_t *)target,
+ if (!grub_arm_64_check_xxxx26_offset (sym_addr))
+ grub_util_error ("%s", _("CALL26 Relocation out of range"));
+
+ grub_arm64_set_xxxx26_offset((grub_uint32_t *)target,
sym_addr);
- if (err)
- grub_util_error ("%s", grub_errmsg);
}
break;
default:
@@ -764,6 +764,11 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
*target = grub_host_to_target32 (grub_target_to_host32 (*target) + sym_addr);
}
break;
+ /* Happens when compiled with -march=armv4.
+ Since currently we need at least armv5, keep bx as-is.
+ */
+ case R_ARM_V4BX:
+ break;
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
{
@@ -790,6 +795,20 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
grub_util_error ("%s", grub_errmsg);
}
break;
+
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ {
+ grub_err_t err;
+ grub_util_info (" JUMP24:\ttarget=0x%08lx\toffset=(0x%08x)", (unsigned long) target, sym_addr);
+ sym_addr -= offset;
+ err = grub_arm_reloc_jump24 (target,
+ sym_addr);
+ if (err)
+ grub_util_error ("%s", grub_errmsg);
+ }
+ break;
+
default:
grub_util_error (_("relocation 0x%x is not implemented yet!"), ELF_R_TYPE (info));
break;
@@ -1054,11 +1073,13 @@ SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out,
case EM_ARM:
switch (ELF_R_TYPE (info))
{
+ case R_ARM_V4BX:
/* Relative relocations do not require fixup entries. */
case R_ARM_JUMP24:
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
+ case R_ARM_CALL:
{
Elf_Addr addr;
diff --git a/util/mkimage.c b/util/mkimage.c
index 9b48271..41b51df 100644
--- a/util/mkimage.c
+++ b/util/mkimage.c
@@ -831,6 +831,94 @@ struct fixup_block_list
struct grub_pe32_fixup_block b;
};
+/*
+ * R_ARM_THM_CALL/THM_JUMP24
+ *
+ * Relocate Thumb (T32) instruction set relative branches:
+ * B.W, BL and BLX
+ */
+static grub_err_t
+grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
+{
+ grub_int32_t offset;
+
+ offset = grub_arm_thm_call_get_offset (target);
+
+ grub_dprintf ("dl", " sym_addr = 0x%08x", sym_addr);
+
+ offset += sym_addr;
+
+ grub_dprintf("dl", " BL*: target=%p, sym_addr=0x%08x, offset=%d\n",
+ target, sym_addr, offset);
+
+ /* Keep traditional (pre-Thumb2) limits on blx. In any case if the kernel
+ is bigger than 2M (currently under 150K) then we probably have a problem
+ somewhere else. */
+ if (offset < -0x200000 || offset >= 0x200000)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("THM_CALL Relocation out of range."));
+
+ grub_dprintf ("dl", " relative destination = %p",
+ (char *) target + offset);
+
+ return grub_arm_thm_call_set_offset (target, offset);
+}
+
+/*
+ * R_ARM_THM_JUMP19
+ *
+ * Relocate conditional Thumb (T32) B<c>.W
+ */
+static grub_err_t
+grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
+{
+ grub_int32_t offset;
+
+ if (!(sym_addr & 1))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("Relocation targeting wrong execution state"));
+
+ offset = grub_arm_thm_jump19_get_offset (target);
+
+ /* Adjust and re-truncate offset */
+ offset += sym_addr;
+
+ if (!grub_arm_thm_jump19_check_offset (offset))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("THM_JUMP19 Relocation out of range."));
+
+ grub_arm_thm_jump19_set_offset (target, offset);
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * R_ARM_JUMP24
+ *
+ * Relocate ARM (A32) B
+ */
+static grub_err_t
+grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
+{
+ grub_int32_t offset;
+
+ if (sym_addr & 1)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("Relocation targeting wrong execution state"));
+
+ offset = grub_arm_jump24_get_offset (target);
+ offset += sym_addr;
+
+ if (!grub_arm_jump24_check_offset (offset))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("JUMP24 Relocation out of range."));
+
+
+ grub_arm_jump24_set_offset (target, offset);
+
+ return GRUB_ERR_NONE;
+}
+
#pragma GCC diagnostic ignored "-Wcast-align"
#define MKIMAGE_ELF32 1
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Grub-devel mailing list [email protected] https://lists.gnu.org/mailman/listinfo/grub-devel
