diff --git a/ChangeLog b/ChangeLog index 6fac2eacd..ee38108f7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2013-12-06 Vladimir Serbinenko + + Revamp relocation handling. + + Move more code to common dl.c. Add missing veneers for arm and arm64. + Decreases kernel size by 70 bytes on i386-pc (40-50 compressed) + 2013-12-05 Vladimir Serbinenko * util/grub-mkimagexx.c: Fix reloc section generation for ia64. diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c index 4563a52f9..26da0ac22 100644 --- a/grub-core/kern/arm/dl.c +++ b/grub-core/kern/arm/dl.c @@ -25,45 +25,113 @@ #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; +}; + +#pragma GCC diagnostic ignored "-Wcast-align" + +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) +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) { - grub_dl_segment_t seg; - Elf_Rel *rel; - Elf_Sym *sym; - int i, entnum; + Elf_Rel *rel, *max; - entnum = relhdr->sh_size / sizeof (Elf_Rel); - - /* Find the target segment for this relocation section. */ - for (seg = mod->segment ; seg ; seg = seg->next) - if (seg->section == relhdr->sh_info) - break; - if (!seg) - return grub_error (GRUB_ERR_EOF, N_("relocation segment not found")); - - rel = (Elf_Rel *) ((grub_addr_t) e + relhdr->sh_offset); - - /* Step through all relocations */ - for (i = 0, sym = mod->symtab; i < entnum; i++) + for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) { Elf_Addr *target, sym_addr; - int relsym, reltype; grub_err_t retval; + Elf_Sym *sym; - if (seg->size < rel[i].r_offset) + if (seg->size < rel->r_offset) return grub_error (GRUB_ERR_BAD_MODULE, "reloc offset is out of the segment"); - relsym = ELF_R_SYM (rel[i].r_info); - reltype = ELF_R_TYPE (rel[i].r_info); - target = (void *) ((grub_addr_t) seg->addr + rel[i].r_offset); + target = (void *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); - sym_addr = sym[relsym].st_value; + sym_addr = sym->st_value; - switch (reltype) + switch (ELF_R_TYPE (rel->r_info)) { case R_ARM_ABS32: { @@ -76,16 +144,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 = mod->trampptr; + mod->trampptr = tp + 1; + 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 = mod->trampptr; + mod->trampptr = tp + 1; + 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,15 +208,37 @@ 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 = mod->gotptr; + mod->gotptr = tp + 1; + 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: return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, N_("relocation 0x%x is not implemented yet"), - reltype); + ELF_R_TYPE (rel->r_info)); } } @@ -130,77 +262,3 @@ grub_arch_dl_check_header (void *ehdr) return GRUB_ERR_NONE; } - -/* - * Verify that provided ELF header contains reference to a symbol table - */ -static int -has_symtab (Elf_Ehdr * e) -{ - int i; - Elf_Shdr *s; - - for (i = 0, s = (Elf_Shdr *) ((grub_uint32_t) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((grub_uint32_t) s + e->e_shentsize)) - if (s->sh_type == SHT_SYMTAB) - return 1; - - return 0; -} - -/* - * grub_arch_dl_relocate_symbols(): - * Only externally visible function in this file. - * Locates the relocations section of the ELF object, and calls - * do_relocations() to deal with it. - */ -grub_err_t -grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) -{ - Elf_Ehdr *e = ehdr; - Elf_Shdr *s; - unsigned i; - - if (!has_symtab (e)) - return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); - -#define FIRST_SHDR(x) ((Elf_Shdr *) ((grub_addr_t)(x) + (x)->e_shoff)) -#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; - }; - } - } - -#undef FIRST_SHDR -#undef NEXT_SHDR - - return GRUB_ERR_NONE; -} diff --git a/grub-core/kern/arm/dl_helper.c b/grub-core/kern/arm/dl_helper.c index 68a9e3bbf..5721939c1 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 ((offset < offset_low) || (offset > offset_high)) - return grub_error (GRUB_ERR_BAD_MODULE, - N_("THM_CALL Relocation out of range.")); + if (((insword >> 12) & 0xd) == 0xc) + is_blx = 1; + else + is_blx = 0; - grub_dprintf ("dl", " relative destination = %p", - (char *) target + offset); + if (!is_blx && !(offset & 1)) + return grub_error (GRUB_ERR_BAD_MODULE, "bl/b.w targettting ARM"); + + /* 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 afd0de2f3..07756110d 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,59 +51,76 @@ grub_arch_dl_check_header (void *ehdr) return GRUB_ERR_NONE; } +#pragma GCC diagnostic ignored "-Wcast-align" + +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_addr_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) +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) { - 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; + Elf_Rel *rel, *max; - /* Find the target segment for this relocation section. */ - for (segment = mod->segment ; segment != 0 ; segment = segment->next) - if (segment->section == relhdr->sh_info) - break; - if (!segment) - 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; - retval = GRUB_ERR_NONE; - - grub_dprintf("dl", "Processing %d relocation entries.\n", entnum); - - /* Step through all relocations */ - for (i = 0, symbol = mod->symtab; i < entnum; i++) + for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) { + Elf_Sym *sym; void *place; - grub_uint64_t sym_addr, symidx, reltype; + grub_uint64_t sym_addr; - if (rel->r_offset >= segment->size) + if (rel->r_offset >= seg->size) return grub_error (GRUB_ERR_BAD_MODULE, "reloc offset is out of the segment"); - symidx = ELF_R_SYM (rel->r_info); - reltype = ELF_R_TYPE (rel->r_info); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); - sym_addr = symbol[symidx].st_value; - if (relhdr->sh_type == SHT_RELA) - sym_addr += rela->r_addend; + sym_addr = sym->st_value; + if (s->sh_type == SHT_RELA) + sym_addr += ((Elf_Rela *) rel)->r_addend; - place = (void *) ((grub_addr_t) segment->addr + rel->r_offset); + place = (void *) ((grub_addr_t) seg->addr + rel->r_offset); - switch (reltype) + switch (ELF_R_TYPE (rel->r_info)) { case R_AARCH64_ABS64: { @@ -108,92 +134,32 @@ 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 = mod->trampptr; + mod->trampptr = 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, N_("relocation 0x%x is not implemented yet"), - reltype); - } - - if (retval != GRUB_ERR_NONE) - break; - - rel = (Elf_Rel *) ((grub_addr_t) rel + entsize); - rela++; - } - - return retval; -} - -/* - * Verify that provided ELF header contains reference to a symbol table - */ -static int -has_symtab (Elf_Ehdr * e) -{ - int i; - Elf_Shdr *s; - - for (i = 0, s = (Elf_Shdr *) ((grub_addr_t) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize)) - if (s->sh_type == SHT_SYMTAB) - return 1; - - return 0; -} - -/* - * grub_arch_dl_relocate_symbols(): - * Locates the relocations section of the ELF object, and calls - * do_relX() to deal with it. - */ -grub_err_t -grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) -{ - Elf_Ehdr *e = ehdr; - Elf_Shdr *s; - unsigned i; - - if (!has_symtab (e)) - return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); - -#define FIRST_SHDR(x) ((Elf_Shdr *) ((grub_addr_t)(x) + (x)->e_shoff)) -#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; - }; + ELF_R_TYPE (rel->r_info)); } } -#undef FIRST_SHDR -#undef NEXT_SHDR - return GRUB_ERR_NONE; } diff --git a/grub-core/kern/arm64/dl_helper.c b/grub-core/kern/arm64/dl_helper.c index 6f99087e3..d213ab93e 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 1d6841479..f01fcfd4f 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,12 +314,14 @@ 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; + mod->trampptr = ptr; ptr += tramp; ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_GOT_ALIGN); mod->got = ptr; + mod->gotptr = ptr; ptr += got; #endif @@ -352,6 +354,7 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e) #else mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset); #endif + mod->symsize = s->sh_entsize; sym = mod->symtab; size = s->sh_size; entsize = s->sh_entsize; @@ -561,6 +564,37 @@ grub_dl_flush_cache (grub_dl_t mod) grub_arch_sync_caches (mod->base, mod->sz); } +static grub_err_t +grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr) +{ + Elf_Ehdr *e = ehdr; + Elf_Shdr *s; + unsigned i; + + for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) + if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA) + { + grub_dl_segment_t seg; + grub_err_t err; + + /* Find the target segment. */ + for (seg = mod->segment; seg; seg = seg->next) + if (seg->section == s->sh_info) + break; + + if (seg) + { + err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg); + if (err) + return err; + } + } + + return GRUB_ERR_NONE; +} + /* Load a module from core memory. */ grub_dl_t grub_dl_load_core_noinit (void *addr, grub_size_t size) @@ -607,7 +641,7 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size) || grub_dl_resolve_dependencies (mod, e) || grub_dl_load_segments (mod, e) || grub_dl_resolve_symbols (mod, e) - || grub_arch_dl_relocate_symbols (mod, e)) + || grub_dl_relocate_symbols (mod, e)) { mod->fini = 0; grub_dl_unload (mod); diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c index 8034c637b..e056ded97 100644 --- a/grub-core/kern/emu/full.c +++ b/grub-core/kern/emu/full.c @@ -39,10 +39,13 @@ grub_arch_dl_check_header (void *ehdr) } grub_err_t -grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) { (void) mod; (void) ehdr; + (void) s; + (void) seg; return GRUB_ERR_BAD_MODULE; } diff --git a/grub-core/kern/i386/dl.c b/grub-core/kern/i386/dl.c index c5539cb05..1346da5cc 100644 --- a/grub-core/kern/i386/dl.c +++ b/grub-core/kern/i386/dl.c @@ -40,75 +40,42 @@ grub_arch_dl_check_header (void *ehdr) /* Relocate symbols. */ grub_err_t -grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) { - Elf_Ehdr *e = ehdr; - Elf_Shdr *s; - Elf_Word entsize; - unsigned i; + Elf_Rel *rel, *max; - /* Find a symbol table. */ - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_SYMTAB) - break; + for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) + { + Elf_Word *addr; + Elf_Sym *sym; - if (i == e->e_shnum) - return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); - entsize = s->sh_entsize; + addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_REL) - { - grub_dl_segment_t seg; + switch (ELF_R_TYPE (rel->r_info)) + { + case R_386_32: + *addr += sym->st_value; + break; - /* Find the target segment. */ - for (seg = mod->segment; seg; seg = seg->next) - if (seg->section == s->sh_info) - break; - - if (seg) - { - Elf_Rel *rel, *max; - - for (rel = (Elf_Rel *) ((char *) e + s->sh_offset), - max = rel + s->sh_size / s->sh_entsize; - rel < max; - rel++) - { - Elf_Word *addr; - Elf_Sym *sym; - - if (seg->size < rel->r_offset) - return grub_error (GRUB_ERR_BAD_MODULE, - "reloc offset is out of the segment"); - - addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset); - sym = (Elf_Sym *) ((char *) mod->symtab - + entsize * ELF_R_SYM (rel->r_info)); - - switch (ELF_R_TYPE (rel->r_info)) - { - case R_386_32: - *addr += sym->st_value; - break; - - case R_386_PC32: - *addr += (sym->st_value - (Elf_Word) seg->addr - - rel->r_offset); - break; - default: - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - N_("relocation 0x%x is not implemented yet"), - ELF_R_TYPE (rel->r_info)); - } - } - } - } + case R_386_PC32: + *addr += (sym->st_value - (grub_addr_t) addr); + break; + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + } return GRUB_ERR_NONE; } diff --git a/grub-core/kern/ia64/dl.c b/grub-core/kern/ia64/dl.c index 91a2645ce..e623cdc81 100644 --- a/grub-core/kern/ia64/dl.c +++ b/grub-core/kern/ia64/dl.c @@ -46,118 +46,82 @@ grub_arch_dl_check_header (void *ehdr) /* Relocate symbols. */ grub_err_t -grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) { - Elf_Ehdr *e = ehdr; - Elf_Shdr *s; - Elf_Word entsize; - unsigned i; - grub_uint64_t *gp, *gpptr; - struct grub_ia64_trampoline *tr; + Elf_Rela *rel, *max; - gp = (grub_uint64_t *) mod->base; - gpptr = (grub_uint64_t *) mod->got; - tr = mod->tramp; + for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rela *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rela *) ((char *) rel + s->sh_entsize)) + { + grub_addr_t addr; + Elf_Sym *sym; + grub_uint64_t value; - /* Find a symbol table. */ - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_SYMTAB) - break; + if (seg->size < (rel->r_offset & ~3)) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); - if (i == e->e_shnum) - return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); + addr = (grub_addr_t) seg->addr + rel->r_offset; + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); - entsize = s->sh_entsize; + /* On the PPC the value does not have an explicit + addend, add it. */ + value = sym->st_value + rel->r_addend; - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_RELA) - { - grub_dl_segment_t seg; - - /* Find the target segment. */ - for (seg = mod->segment; seg; seg = seg->next) - if (seg->section == s->sh_info) - break; - - if (seg) + switch (ELF_R_TYPE (rel->r_info)) + { + case R_IA64_PCREL21B: { - Elf_Rela *rel, *max; + grub_uint64_t noff; + struct grub_ia64_trampoline *tr = mod->trampptr; + grub_ia64_make_trampoline (tr, value); + noff = ((char *) tr - (char *) (addr & ~3)) >> 4; + mod->trampptr = tr + 1; - for (rel = (Elf_Rela *) ((char *) e + s->sh_offset), - max = rel + s->sh_size / s->sh_entsize; - rel < max; - rel++) - { - grub_addr_t addr; - Elf_Sym *sym; - grub_uint64_t value; - - if (seg->size < (rel->r_offset & ~3)) - return grub_error (GRUB_ERR_BAD_MODULE, - "reloc offset is out of the segment"); - - addr = (grub_addr_t) seg->addr + rel->r_offset; - sym = (Elf_Sym *) ((char *) mod->symtab - + entsize * ELF_R_SYM (rel->r_info)); - - /* On the PPC the value does not have an explicit - addend, add it. */ - value = sym->st_value + rel->r_addend; - - switch (ELF_R_TYPE (rel->r_info)) - { - case R_IA64_PCREL21B: - { - grub_uint64_t noff; - grub_ia64_make_trampoline (tr, value); - noff = ((char *) tr - (char *) (addr & ~3)) >> 4; - tr++; - - if (noff & ~MASK19) - return grub_error (GRUB_ERR_BAD_OS, - "trampoline offset too big (%lx)", noff); - grub_ia64_add_value_to_slot_20b (addr, noff); - } - break; - case R_IA64_SEGREL64LSB: - *(grub_uint64_t *) addr += value - (grub_addr_t) seg->addr; - break; - case R_IA64_FPTR64LSB: - case R_IA64_DIR64LSB: - *(grub_uint64_t *) addr += value; - break; - case R_IA64_PCREL64LSB: - *(grub_uint64_t *) addr += value - addr; - break; - case R_IA64_GPREL22: - grub_ia64_add_value_to_slot_21 (addr, value - (grub_addr_t) gp); - break; - - case R_IA64_LTOFF22X: - case R_IA64_LTOFF22: - if (ELF_ST_TYPE (sym->st_info) == STT_FUNC) - value = *(grub_uint64_t *) sym->st_value + rel->r_addend; - case R_IA64_LTOFF_FPTR22: - *gpptr = value; - grub_ia64_add_value_to_slot_21 (addr, (grub_addr_t) gpptr - (grub_addr_t) gp); - gpptr++; - break; - - /* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV. */ - case R_IA64_LDXMOV: - break; - default: - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - N_("relocation 0x%x is not implemented yet"), - ELF_R_TYPE (rel->r_info)); - } - } + if (noff & ~MASK19) + return grub_error (GRUB_ERR_BAD_OS, + "trampoline offset too big (%lx)", noff); + grub_ia64_add_value_to_slot_20b (addr, noff); } - } + break; + case R_IA64_SEGREL64LSB: + *(grub_uint64_t *) addr += value - (grub_addr_t) seg->addr; + break; + case R_IA64_FPTR64LSB: + case R_IA64_DIR64LSB: + *(grub_uint64_t *) addr += value; + break; + case R_IA64_PCREL64LSB: + *(grub_uint64_t *) addr += value - addr; + break; + case R_IA64_GPREL22: + grub_ia64_add_value_to_slot_21 (addr, value - (grub_addr_t) mod->base); + break; + case R_IA64_LTOFF22X: + case R_IA64_LTOFF22: + if (ELF_ST_TYPE (sym->st_info) == STT_FUNC) + value = *(grub_uint64_t *) sym->st_value + rel->r_addend; + case R_IA64_LTOFF_FPTR22: + { + grub_uint64_t *gpptr = mod->gotptr; + *gpptr = value; + grub_ia64_add_value_to_slot_21 (addr, (grub_addr_t) gpptr - (grub_addr_t) mod->base); + mod->gotptr = gpptr + 1; + break; + } + /* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV. */ + case R_IA64_LDXMOV: + break; + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + } return GRUB_ERR_NONE; } diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c index b97510a1e..8c057e01d 100644 --- a/grub-core/kern/mips/dl.c +++ b/grub-core/kern/mips/dl.c @@ -58,23 +58,13 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, { const Elf_Ehdr *e = ehdr; const Elf_Shdr *s; - unsigned i; /* FIXME: suboptimal. */ grub_size_t gp_size = 0; + unsigned i; *tramp = 0; *got = 0; - /* Find a symbol table. */ - for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) - if (s->sh_type == SHT_SYMTAB) - break; - - if (i == e->e_shnum) - return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); - for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); i < e->e_shnum; i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) @@ -106,192 +96,166 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, /* Relocate symbols. */ grub_err_t -grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) { - Elf_Ehdr *e = ehdr; - Elf_Shdr *s; - Elf_Word entsize; - unsigned i; - /* FIXME: suboptimal. */ - grub_uint32_t *gp, *gpptr; grub_uint32_t gp0; + Elf_Ehdr *e = ehdr; - /* Find a symbol table. */ - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_SYMTAB) - break; + if (!mod->reginfo) + { + unsigned i; + Elf_Shdr *ri; - if (i == e->e_shnum) - return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); + /* Find reginfo. */ + for (i = 0, ri = (Elf_Shdr *) ((char *) ehdr + e->e_shoff); + i < e->e_shnum; + i++, ri = (Elf_Shdr *) ((char *) ri + e->e_shentsize)) + if (ri->sh_type == SHT_MIPS_REGINFO) + break; + if (i == e->e_shnum) + return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found"); + mod->reginfo = (grub_uint32_t *)((char *) ehdr + ri->sh_offset); + } - entsize = s->sh_entsize; + gp0 = mod->reginfo[5]; + Elf_Rel *rel, *max; - /* Find reginfo. */ - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_MIPS_REGINFO) - break; + for (rel = (Elf_Rel *) ((char *) e + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) + { + grub_uint8_t *addr; + Elf_Sym *sym; + grub_uint32_t sym_value; - if (i == e->e_shnum) - return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found"); + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); - gp0 = ((grub_uint32_t *)((char *) e + s->sh_offset))[5]; - - gpptr = gp = mod->got; - - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_REL) - { - grub_dl_segment_t seg; - - /* Find the target segment. */ - for (seg = mod->segment; seg; seg = seg->next) - if (seg->section == s->sh_info) - break; - - if (seg) + addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + sym_value = sym->st_value; + if (sym_value == (grub_addr_t) &__gnu_local_gp_dummy) + sym_value = (grub_addr_t) mod->got; + else if (sym_value == (grub_addr_t) &_gp_disp_dummy) + { + sym_value = (grub_addr_t) mod->got - (grub_addr_t) addr; + if (ELF_R_TYPE (rel->r_info) == R_MIPS_LO16) + /* ABI mandates +4 even if partner lui doesn't + immediately precede addiu. */ + sym_value += 4; + } + switch (ELF_R_TYPE (rel->r_info)) + { + case R_MIPS_HI16: { - Elf_Rel *rel, *max; - - for (rel = (Elf_Rel *) ((char *) e + s->sh_offset), - max = rel + s->sh_size / s->sh_entsize; - rel < max; - rel++) - { - grub_uint8_t *addr; - Elf_Sym *sym; - grub_uint32_t sym_value; - - if (seg->size < rel->r_offset) - return grub_error (GRUB_ERR_BAD_MODULE, - "reloc offset is out of the segment"); - - addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset); - sym = (Elf_Sym *) ((char *) mod->symtab - + entsize * ELF_R_SYM (rel->r_info)); - sym_value = sym->st_value; - if (sym_value == (grub_addr_t) &__gnu_local_gp_dummy) - sym_value = (grub_addr_t) gp; - else if (sym_value == (grub_addr_t) &_gp_disp_dummy) - { - sym_value = (grub_addr_t) gp - (grub_addr_t) addr; - if (ELF_R_TYPE (rel->r_info) == R_MIPS_LO16) - /* ABI mandates +4 even if partner lui doesn't - immediately precede addiu. */ - sym_value += 4; - } - switch (ELF_R_TYPE (rel->r_info)) - { - case R_MIPS_HI16: - { - grub_uint32_t value; - Elf_Rel *rel2; + grub_uint32_t value; + Elf_Rel *rel2; #ifdef GRUB_CPU_WORDS_BIGENDIAN - addr += 2; + addr += 2; #endif - /* Handle partner lo16 relocation. Lower part is - treated as signed. Hence add 0x8000 to compensate. - */ - value = (*(grub_uint16_t *) addr << 16) - + sym_value + 0x8000; - for (rel2 = rel + 1; rel2 < max; rel2++) - if (ELF_R_SYM (rel2->r_info) - == ELF_R_SYM (rel->r_info) - && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) - { - value += *(grub_int16_t *) - ((char *) seg->addr + rel2->r_offset + /* Handle partner lo16 relocation. Lower part is + treated as signed. Hence add 0x8000 to compensate. + */ + value = (*(grub_uint16_t *) addr << 16) + + sym_value + 0x8000; + for (rel2 = rel + 1; rel2 < max; rel2++) + if (ELF_R_SYM (rel2->r_info) + == ELF_R_SYM (rel->r_info) + && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) + { + value += *(grub_int16_t *) + ((char *) seg->addr + rel2->r_offset #ifdef GRUB_CPU_WORDS_BIGENDIAN - + 2 + + 2 #endif - ); - break; - } - *(grub_uint16_t *) addr = (value >> 16) & 0xffff; - } - break; - case R_MIPS_LO16: -#ifdef GRUB_CPU_WORDS_BIGENDIAN - addr += 2; -#endif - *(grub_uint16_t *) addr += sym_value & 0xffff; - break; - case R_MIPS_32: - *(grub_uint32_t *) addr += sym_value; - break; - case R_MIPS_GPREL32: - *(grub_uint32_t *) addr = sym_value - + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)gp; - break; - - case R_MIPS_26: - { - grub_uint32_t value; - grub_uint32_t raw; - raw = (*(grub_uint32_t *) addr) & 0x3ffffff; - value = raw << 2; - value += sym_value; - raw = (value >> 2) & 0x3ffffff; - - *(grub_uint32_t *) addr = - raw | ((*(grub_uint32_t *) addr) & 0xfc000000); - } - break; - case R_MIPS_GOT16: - if (ELF_ST_BIND (sym->st_info) == STB_LOCAL) - { - Elf_Rel *rel2; - /* Handle partner lo16 relocation. Lower part is - treated as signed. Hence add 0x8000 to compensate. - */ - sym_value += (*(grub_uint16_t *) addr << 16) - + 0x8000; - for (rel2 = rel + 1; rel2 < max; rel2++) - if (ELF_R_SYM (rel2->r_info) - == ELF_R_SYM (rel->r_info) - && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) - { - sym_value += *(grub_int16_t *) - ((char *) seg->addr + rel2->r_offset -#ifdef GRUB_CPU_WORDS_BIGENDIAN - + 2 -#endif - ); - break; - } - sym_value &= 0xffff0000; - *(grub_uint16_t *) addr = 0; - } - case R_MIPS_CALL16: - /* FIXME: reuse*/ -#ifdef GRUB_CPU_WORDS_BIGENDIAN - addr += 2; -#endif - *gpptr = sym_value + *(grub_uint16_t *) addr; - *(grub_uint16_t *) addr - = sizeof (grub_uint32_t) * (gpptr - gp); - gpptr++; - break; - case R_MIPS_JALR: - break; - default: - { - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - N_("relocation 0x%x is not implemented yet"), - ELF_R_TYPE (rel->r_info)); - } - break; - } - } + ); + break; + } + *(grub_uint16_t *) addr = (value >> 16) & 0xffff; } - } + break; + case R_MIPS_LO16: +#ifdef GRUB_CPU_WORDS_BIGENDIAN + addr += 2; +#endif + *(grub_uint16_t *) addr += sym_value & 0xffff; + break; + case R_MIPS_32: + *(grub_uint32_t *) addr += sym_value; + break; + case R_MIPS_GPREL32: + *(grub_uint32_t *) addr = sym_value + + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)mod->got; + break; + + case R_MIPS_26: + { + grub_uint32_t value; + grub_uint32_t raw; + raw = (*(grub_uint32_t *) addr) & 0x3ffffff; + value = raw << 2; + value += sym_value; + raw = (value >> 2) & 0x3ffffff; + + *(grub_uint32_t *) addr = + raw | ((*(grub_uint32_t *) addr) & 0xfc000000); + } + break; + case R_MIPS_GOT16: + if (ELF_ST_BIND (sym->st_info) == STB_LOCAL) + { + Elf_Rel *rel2; + /* Handle partner lo16 relocation. Lower part is + treated as signed. Hence add 0x8000 to compensate. + */ + sym_value += (*(grub_uint16_t *) addr << 16) + + 0x8000; + for (rel2 = rel + 1; rel2 < max; rel2++) + if (ELF_R_SYM (rel2->r_info) + == ELF_R_SYM (rel->r_info) + && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) + { + sym_value += *(grub_int16_t *) + ((char *) seg->addr + rel2->r_offset +#ifdef GRUB_CPU_WORDS_BIGENDIAN + + 2 +#endif + ); + break; + } + sym_value &= 0xffff0000; + *(grub_uint16_t *) addr = 0; + } + case R_MIPS_CALL16: + { + grub_uint32_t *gpptr = mod->gotptr; + /* FIXME: reuse*/ +#ifdef GRUB_CPU_WORDS_BIGENDIAN + addr += 2; +#endif + *gpptr = sym_value + *(grub_uint16_t *) addr; + *(grub_uint16_t *) addr + = sizeof (grub_uint32_t) * (gpptr - (grub_uint32_t *) mod->got); + mod->gotptr = gpptr + 1; + break; + } + case R_MIPS_JALR: + break; + default: + { + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + break; + } + } return GRUB_ERR_NONE; } diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c index e87423438..ec204f35b 100644 --- a/grub-core/kern/powerpc/dl.c +++ b/grub-core/kern/powerpc/dl.c @@ -101,109 +101,77 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, /* Relocate symbols. */ grub_err_t -grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) { - Elf_Ehdr *e = ehdr; - Elf_Shdr *s; - Elf_Word entsize; - unsigned i; - struct trampoline *tptr = mod->tramp; + Elf_Rela *rel, *max; - /* Find a symbol table. */ - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_SYMTAB) - break; + for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rela *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rela *) ((char *) rel + s->sh_entsize)) + { + Elf_Word *addr; + Elf_Sym *sym; + grub_uint32_t value; - if (i == e->e_shnum) - return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); - entsize = s->sh_entsize; + addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_RELA) - { - grub_dl_segment_t seg; + /* On the PPC the value does not have an explicit + addend, add it. */ + value = sym->st_value + rel->r_addend; + switch (ELF_R_TYPE (rel->r_info)) + { + case GRUB_ELF_R_PPC_ADDR16_LO: + *(Elf_Half *) addr = value; + break; - /* Find the target segment. */ - for (seg = mod->segment; seg; seg = seg->next) - if (seg->section == s->sh_info) - break; - - if (seg) + case GRUB_ELF_R_PPC_REL24: { - Elf_Rela *rel, *max; + Elf_Sword delta = value - (Elf_Word) addr; - for (rel = (Elf_Rela *) ((char *) e + s->sh_offset), - max = rel + s->sh_size / s->sh_entsize; - rel < max; - rel++) + if (delta << 6 >> 6 != delta) { - Elf_Word *addr; - Elf_Sym *sym; - grub_uint32_t value; - - if (seg->size < rel->r_offset) - return grub_error (GRUB_ERR_BAD_MODULE, - "reloc offset is out of the segment"); - - addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset); - sym = (Elf_Sym *) ((char *) mod->symtab - + entsize * ELF_R_SYM (rel->r_info)); - - /* On the PPC the value does not have an explicit - addend, add it. */ - value = sym->st_value + rel->r_addend; - switch (ELF_R_TYPE (rel->r_info)) - { - case GRUB_ELF_R_PPC_ADDR16_LO: - *(Elf_Half *) addr = value; - break; - - case GRUB_ELF_R_PPC_REL24: - { - Elf_Sword delta = value - (Elf_Word) addr; - - if (delta << 6 >> 6 != delta) - { - grub_memcpy (tptr, &trampoline_template, - sizeof (*tptr)); - delta = (grub_uint8_t *) tptr - (grub_uint8_t *) addr; - tptr->lis |= (((value) >> 16) & 0xffff); - tptr->ori |= ((value) & 0xffff); - tptr++; - } - - if (delta << 6 >> 6 != delta) - return grub_error (GRUB_ERR_BAD_MODULE, - "relocation overflow"); - *addr = (*addr & 0xfc000003) | (delta & 0x3fffffc); - break; - } - - case GRUB_ELF_R_PPC_ADDR16_HA: - *(Elf_Half *) addr = (value + 0x8000) >> 16; - break; - - case GRUB_ELF_R_PPC_ADDR32: - *addr = value; - break; - - case GRUB_ELF_R_PPC_REL32: - *addr = value - (Elf_Word) addr; - break; - - default: - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - N_("relocation 0x%x is not implemented yet"), - ELF_R_TYPE (rel->r_info)); - } + struct trampoline *tptr = mod->trampptr; + grub_memcpy (tptr, &trampoline_template, + sizeof (*tptr)); + delta = (grub_uint8_t *) tptr - (grub_uint8_t *) addr; + tptr->lis |= (((value) >> 16) & 0xffff); + tptr->ori |= ((value) & 0xffff); + mod->trampptr = tptr + 1; } + + if (delta << 6 >> 6 != delta) + return grub_error (GRUB_ERR_BAD_MODULE, + "relocation overflow"); + *addr = (*addr & 0xfc000003) | (delta & 0x3fffffc); + break; } - } + + case GRUB_ELF_R_PPC_ADDR16_HA: + *(Elf_Half *) addr = (value + 0x8000) >> 16; + break; + + case GRUB_ELF_R_PPC_ADDR32: + *addr = value; + break; + + case GRUB_ELF_R_PPC_REL32: + *addr = value - (Elf_Word) addr; + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + } return GRUB_ERR_NONE; } diff --git a/grub-core/kern/sparc64/dl.c b/grub-core/kern/sparc64/dl.c index d188c4f23..fa086ff5c 100644 --- a/grub-core/kern/sparc64/dl.c +++ b/grub-core/kern/sparc64/dl.c @@ -42,106 +42,74 @@ grub_arch_dl_check_header (void *ehdr) /* Relocate symbols. */ grub_err_t -grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) { - Elf_Ehdr *e = ehdr; - Elf_Shdr *s; - Elf_Word entsize; - unsigned i; + Elf_Rela *rel, *max; - /* Find a symbol table. */ - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_SYMTAB) - break; + for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rela *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rela *) ((char *) rel + s->sh_entsize)) + { + Elf_Word *addr; + Elf_Sym *sym; + Elf_Addr value; - if (i == e->e_shnum) - return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); - entsize = s->sh_entsize; + addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); - for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_RELA) - { - grub_dl_segment_t seg; - - /* Find the target segment. */ - for (seg = mod->segment; seg; seg = seg->next) - if (seg->section == s->sh_info) - break; - - if (seg) - { - Elf_Rela *rel, *max; - - for (rel = (Elf_Rela *) ((char *) e + s->sh_offset), - max = rel + s->sh_size / s->sh_entsize; - rel < max; - rel++) - { - Elf_Word *addr; - Elf_Sym *sym; - Elf_Addr value; - - if (seg->size < rel->r_offset) - return grub_error (GRUB_ERR_BAD_MODULE, - "reloc offset is out of the segment"); - - addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset); - sym = (Elf_Sym *) ((char *) mod->symtab - + entsize * ELF_R_SYM (rel->r_info)); - - value = sym->st_value + rel->r_addend; - switch (ELF_R_TYPE (rel->r_info) & 0xff) - { - case R_SPARC_32: /* 3 V-word32 */ - if (value & 0xFFFFFFFF00000000) - return grub_error (GRUB_ERR_BAD_MODULE, - "address out of 32 bits range"); - *addr = value; - break; - case R_SPARC_WDISP30: /* 7 V-disp30 */ - if (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) && - (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) - != 0xFFFFFFFF00000000)) - return grub_error (GRUB_ERR_BAD_MODULE, - "displacement out of 30 bits range"); - *addr = (*addr & 0xC0000000) | - (((grub_int32_t) ((value - (Elf_Addr) addr) >> 2)) & - 0x3FFFFFFF); - break; - case R_SPARC_HH22: /* 9 V-imm22 */ - *addr = (*addr & 0xFFC00000) | ((value >> 42) & 0x3FFFFF); - break; - case R_SPARC_HM10: /* 12 T-simm13 */ - *addr = (*addr & 0xFFFFFC00) | ((value >> 32) & 0x3FF); - break; - case R_SPARC_HI22: /* 9 V-imm22 */ - *addr = (*addr & 0xFFC00000) | ((value >> 10) & 0x3FFFFF); - break; - case R_SPARC_LO10: /* 12 T-simm13 */ - *addr = (*addr & 0xFFFFFC00) | (value & 0x3FF); - break; - case R_SPARC_64: /* 32 V-xwords64 */ - *(Elf_Xword *) addr = value; - break; - case R_SPARC_OLO10: - *addr = (*addr & ~0x1fff) - | (((value & 0x3ff) + - (ELF_R_TYPE (rel->r_info) >> 8)) - & 0x1fff); - break; - default: - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - N_("relocation 0x%x is not implemented yet"), - ELF_R_TYPE (rel->r_info)); - } - } - } - } + value = sym->st_value + rel->r_addend; + switch (ELF_R_TYPE (rel->r_info) & 0xff) + { + case R_SPARC_32: /* 3 V-word32 */ + if (value & 0xFFFFFFFF00000000) + return grub_error (GRUB_ERR_BAD_MODULE, + "address out of 32 bits range"); + *addr = value; + break; + case R_SPARC_WDISP30: /* 7 V-disp30 */ + if (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) && + (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) + != 0xFFFFFFFF00000000)) + return grub_error (GRUB_ERR_BAD_MODULE, + "displacement out of 30 bits range"); + *addr = (*addr & 0xC0000000) | + (((grub_int32_t) ((value - (Elf_Addr) addr) >> 2)) & + 0x3FFFFFFF); + break; + case R_SPARC_HH22: /* 9 V-imm22 */ + *addr = (*addr & 0xFFC00000) | ((value >> 42) & 0x3FFFFF); + break; + case R_SPARC_HM10: /* 12 T-simm13 */ + *addr = (*addr & 0xFFFFFC00) | ((value >> 32) & 0x3FF); + break; + case R_SPARC_HI22: /* 9 V-imm22 */ + *addr = (*addr & 0xFFC00000) | ((value >> 10) & 0x3FFFFF); + break; + case R_SPARC_LO10: /* 12 T-simm13 */ + *addr = (*addr & 0xFFFFFC00) | (value & 0x3FF); + break; + case R_SPARC_64: /* 32 V-xwords64 */ + *(Elf_Xword *) addr = value; + break; + case R_SPARC_OLO10: + *addr = (*addr & ~0x1fff) + | (((value & 0x3ff) + + (ELF_R_TYPE (rel->r_info) >> 8)) + & 0x1fff); + break; + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + } return GRUB_ERR_NONE; } diff --git a/grub-core/kern/x86_64/dl.c b/grub-core/kern/x86_64/dl.c index 17c121544..e00e51d40 100644 --- a/grub-core/kern/x86_64/dl.c +++ b/grub-core/kern/x86_64/dl.c @@ -40,101 +40,69 @@ grub_arch_dl_check_header (void *ehdr) /* Relocate symbols. */ grub_err_t -grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) { - Elf64_Ehdr *e = ehdr; - Elf64_Shdr *s; - Elf64_Word entsize; - unsigned i; + Elf64_Rela *rel, *max; - /* Find a symbol table. */ - for (i = 0, s = (Elf64_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf64_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_SYMTAB) - break; + for (rel = (Elf64_Rela *) ((char *) ehdr + s->sh_offset), + max = (Elf64_Rela *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf64_Rela *) ((char *) rel + s->sh_entsize)) + { + Elf64_Word *addr32; + Elf64_Xword *addr64; + Elf64_Sym *sym; - if (i == e->e_shnum) - return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); - entsize = s->sh_entsize; + addr32 = (Elf64_Word *) ((char *) seg->addr + rel->r_offset); + addr64 = (Elf64_Xword *) addr32; + sym = (Elf64_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); - for (i = 0, s = (Elf64_Shdr *) ((char *) e + e->e_shoff); - i < e->e_shnum; - i++, s = (Elf64_Shdr *) ((char *) s + e->e_shentsize)) - if (s->sh_type == SHT_RELA) - { - grub_dl_segment_t seg; + switch (ELF_R_TYPE (rel->r_info)) + { + case R_X86_64_64: + *addr64 += rel->r_addend + sym->st_value; + break; - /* Find the target segment. */ - for (seg = mod->segment; seg; seg = seg->next) - if (seg->section == s->sh_info) - break; - - if (seg) + case R_X86_64_PC32: { - Elf64_Rela *rel, *max; - - for (rel = (Elf64_Rela *) ((char *) e + s->sh_offset), - max = rel + s->sh_size / s->sh_entsize; - rel < max; - rel++) - { - Elf64_Word *addr32; - Elf64_Xword *addr64; - Elf64_Sym *sym; - - if (seg->size < rel->r_offset) - return grub_error (GRUB_ERR_BAD_MODULE, - "reloc offset is out of the segment"); - - addr32 = (Elf64_Word *) ((char *) seg->addr + rel->r_offset); - addr64 = (Elf64_Xword *) addr32; - sym = (Elf64_Sym *) ((char *) mod->symtab - + entsize * ELF_R_SYM (rel->r_info)); - - switch (ELF_R_TYPE (rel->r_info)) - { - case R_X86_64_64: - *addr64 += rel->r_addend + sym->st_value; - break; - - case R_X86_64_PC32: - { - grub_int64_t value; - value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value - - (Elf64_Xword) seg->addr - rel->r_offset; - if (value != (grub_int32_t) value) - return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range"); - *addr32 = value; - } - break; - - case R_X86_64_32: - { - grub_uint64_t value = *addr32 + rel->r_addend + sym->st_value; - if (value != (grub_uint32_t) value) - return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range"); - *addr32 = value; - } - break; - case R_X86_64_32S: - { - grub_int64_t value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value; - if (value != (grub_int32_t) value) - return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range"); - *addr32 = value; - } - break; - - default: - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - N_("relocation 0x%x is not implemented yet"), - ELF_R_TYPE (rel->r_info)); - } - } + grub_int64_t value; + value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value - + (Elf64_Xword) seg->addr - rel->r_offset; + if (value != (grub_int32_t) value) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range"); + *addr32 = value; } - } + break; + + case R_X86_64_32: + { + grub_uint64_t value = *addr32 + rel->r_addend + sym->st_value; + if (value != (grub_uint32_t) value) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range"); + *addr32 = value; + } + break; + case R_X86_64_32S: + { + grub_int64_t value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value; + if (value != (grub_int32_t) value) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range"); + *addr32 = value; + } + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + } return GRUB_ERR_NONE; } diff --git a/include/grub/arm/reloc.h b/include/grub/arm/reloc.h index 50d070f01..b938037a0 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 606d71c77..4aed3d715 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 d1d20d9d2..6c758c01a 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -177,11 +177,17 @@ struct grub_dl grub_dl_dep_t dep; grub_dl_segment_t segment; Elf_Sym *symtab; + grub_size_t symsize; 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 *gotptr; void *tramp; + void *trampptr; +#endif +#ifdef __mips__ + grub_uint32_t *reginfo; #endif void *base; grub_size_t sz; @@ -232,7 +238,11 @@ grub_err_t grub_dl_register_symbol (const char *name, void *addr, int isfunc, grub_dl_t mod); grub_err_t grub_arch_dl_check_header (void *ehdr); -grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr); +#ifndef GRUB_UTIL +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg); +#endif #if defined (_mips) #define GRUB_LINKER_HAVE_INIT 1 @@ -256,11 +266,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 b562fddf2..b7c01ab1a 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -493,6 +493,85 @@ SUFFIX (count_funcs) (Elf_Ehdr *e, Elf_Shdr *symtab_section, } #endif +#ifdef MKIMAGE_ELF32 +/* Deal with relocation information. This function relocates addresses + within the virtual address space starting from 0. So only relative + addresses can be fully resolved. Absolute addresses must be relocated + again by a PE32 relocator when loaded. */ +static grub_size_t +arm_get_trampoline_size (Elf_Ehdr *e, + Elf_Shdr *sections, + Elf_Half section_entsize, + Elf_Half num_sections, + const struct grub_install_image_target_desc *image_target) +{ + Elf_Half i; + Elf_Shdr *s; + grub_size_t ret = 0; + + for (i = 0, s = sections; + i < num_sections; + i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) + if ((s->sh_type == grub_host_to_target32 (SHT_REL)) || + (s->sh_type == grub_host_to_target32 (SHT_RELA))) + { + Elf_Rela *r; + Elf_Word rtab_size, r_size, num_rs; + Elf_Off rtab_offset; + Elf_Shdr *symtab_section; + Elf_Word j; + + symtab_section = (Elf_Shdr *) ((char *) sections + + (grub_target_to_host32 (s->sh_link) + * section_entsize)); + + rtab_size = grub_target_to_host (s->sh_size); + r_size = grub_target_to_host (s->sh_entsize); + rtab_offset = grub_target_to_host (s->sh_offset); + num_rs = rtab_size / r_size; + + for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset); + j < num_rs; + j++, r = (Elf_Rela *) ((char *) r + r_size)) + { + Elf_Addr info; + Elf_Addr sym_addr; + + info = grub_target_to_host (r->r_info); + sym_addr = SUFFIX (get_symbol_address) (e, symtab_section, + ELF_R_SYM (info), image_target); + + sym_addr += (s->sh_type == grub_target_to_host32 (SHT_RELA)) ? + grub_target_to_host (r->r_addend) : 0; + + switch (ELF_R_TYPE (info)) + { + case R_ARM_ABS32: + case R_ARM_V4BX: + break; + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + case R_ARM_THM_JUMP19: + if (!(sym_addr & 1)) + ret += 8; + break; + + case R_ARM_CALL: + case R_ARM_JUMP24: + if (sym_addr & 1) + ret += 16; + break; + + default: + grub_util_error (_("relocation 0x%x is not implemented yet!"), ELF_R_TYPE (info)); + break; + } + } + } + return ret; +} +#endif + /* Deal with relocation information. This function relocates addresses within the virtual address space starting from 0. So only relative addresses can be fully resolved. Absolute addresses must be relocated @@ -512,6 +591,8 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections, struct grub_ia64_trampoline *tr = (void *) (pe_target + tramp_off); grub_uint64_t *gpptr = (void *) (pe_target + got_off); #define MASK19 ((1 << 19) - 1) +#else + grub_uint32_t *tr = (void *) (pe_target + tramp_off); #endif for (i = 0, s = sections; @@ -731,13 +812,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,32 +845,74 @@ 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: - { - grub_err_t err; - grub_util_info (" THM_JUMP24:\ttarget=0x%08lx\toffset=(0x%08x)", (unsigned long) target, sym_addr); - sym_addr -= offset; - /* Thumb instructions can be 16-bit aligned */ - err = grub_arm_reloc_thm_call ((grub_uint16_t *) target, - sym_addr); - if (err) - grub_util_error ("%s", grub_errmsg); - } - break; case R_ARM_THM_JUMP19: { grub_err_t err; - grub_util_info (" THM_JUMP19:\toffset=%d\t(0x%08x)", - sym_addr, sym_addr); - sym_addr -= offset; + grub_util_info (" THM_JUMP24:\ttarget=0x%08lx\toffset=(0x%08x)", (unsigned long) target, sym_addr); + if (!(sym_addr & 1)) + { + grub_uint32_t tr_addr; + grub_int32_t new_offset; + tr_addr = (char *) tr - (char *) pe_target + - target_section_addr; + new_offset = sym_addr - tr_addr - 12; + if (!grub_arm_jump24_check_offset (new_offset)) + return grub_util_error ("jump24 relocation out of range"); + + tr[0] = grub_host_to_target32 (0x46c04778); /* bx pc; nop */ + tr[1] = grub_host_to_target32 (((new_offset >> 2) & 0xffffff) | 0xea000000); /* b new_offset */ + tr += 2; + sym_addr = tr_addr | 1; + } + sym_addr -= offset; /* Thumb instructions can be 16-bit aligned */ - err = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr); + if (ELF_R_TYPE (info) == R_ARM_THM_JUMP19) + err = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr); + else + err = grub_arm_reloc_thm_call ((grub_uint16_t *) target, + sym_addr); if (err) 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); + if (sym_addr & 1) + { + grub_uint32_t tr_addr; + grub_int32_t new_offset; + tr_addr = (char *) tr - (char *) pe_target + - target_section_addr; + new_offset = sym_addr - tr_addr - 12; + + /* There is no immediate version of bx, only register one... */ + tr[0] = grub_host_to_target32 (0xe59fc004); /* ldr ip, [pc, #4] */ + tr[1] = grub_host_to_target32 (0xe08cc00f); /* add ip, ip, pc */ + tr[2] = grub_host_to_target32 (0xe12fff1c); /* bx ip */ + tr[3] = grub_host_to_target32 (new_offset | 1); + tr += 4; + sym_addr = tr_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 +1177,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; @@ -1280,7 +1405,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size, Elf_Off section_offset; Elf_Half section_entsize; grub_size_t kernel_size; - grub_size_t ia64jmp_off = 0, ia64_toff = 0, ia64_got_off = 0; + grub_size_t ia64jmp_off = 0, tramp_off = 0, ia64_got_off = 0; unsigned ia64jmpnum = 0; Elf_Shdr *symtab_section = 0; grub_size_t got = 0; @@ -1373,6 +1498,21 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size, break; } +#ifdef MKIMAGE_ELF32 + if (image_target->elf_target == EM_ARM) + { + grub_size_t tramp; + + *kernel_sz = ALIGN_UP (*kernel_sz, 16); + + tramp = arm_get_trampoline_size (e, sections, section_entsize, + num_sections, image_target); + + tramp_off = *kernel_sz; + *kernel_sz += ALIGN_UP (tramp, 16); + } +#endif + #ifdef MKIMAGE_ELF64 if (image_target->elf_target == EM_IA_64) { @@ -1382,7 +1522,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size, grub_ia64_dl_get_tramp_got_size (e, &tramp, &got); - ia64_toff = *kernel_sz; + tramp_off = *kernel_sz; *kernel_sz += ALIGN_UP (tramp, 16); ia64jmp_off = *kernel_sz; @@ -1424,7 +1564,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size, SUFFIX (relocate_addresses) (e, sections, section_addresses, section_entsize, num_sections, strtab, - out_img, ia64_toff, ia64_got_off, + out_img, tramp_off, ia64_got_off, image_target); *reloc_size = SUFFIX (make_reloc_section) (e, reloc_section, diff --git a/util/mkimage.c b/util/mkimage.c index 11d4a474b..ad12f8a76 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -834,6 +834,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