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 #include +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.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 #include +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 #include -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.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