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-06 Vladimir Serbinenko <phcoder@gmail.com>
+
+ 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 <phcoder@gmail.com>
* util/grub-mkimagexx.c: Fix reloc section generation for ia64.
#include <grub/i18n.h>
#include <grub/arm/reloc.h>
-/*************************************************
- * Runtime dynamic linker with helper functions. *
- *************************************************/
-static grub_err_t
-do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
+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_dl_segment_t seg;
- Elf_Rel *rel;
- Elf_Sym *sym;
- int i, entnum;
+ grub_uint16_t template[8];
+ grub_uint32_t neg_addr;
+};
+
+#pragma GCC diagnostic ignored "-Wcast-align"
- entnum = relhdr->sh_size / sizeof (Elf_Rel);
+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;
- /* 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"));
+ *tramp = 0;
+ *got = 0;
- rel = (Elf_Rel *) ((grub_addr_t) e + relhdr->sh_offset);
+ 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. *
+ *************************************************/
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+ Elf_Shdr *s, grub_dl_segment_t seg)
+{
+ Elf_Rel *rel, *max;
- /* 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:
{
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;
}
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));
}
}
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;
-}
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;
}
* 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;
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;
return GRUB_ERR_NONE;
}
-/*
- * R_ARM_THM_JUMP19
- *
- * Relocate conditional Thumb (T32) B<c>.W
- */
-grub_err_t
-grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
+grub_int32_t
+grub_arm_thm_jump19_get_offset (grub_uint16_t *target)
{
grub_int32_t offset;
- grub_uint32_t insword, insmask;
+ grub_uint32_t insword;
/* Extract instruction word in alignment-safe manner */
- insword = grub_le_to_cpu16 ((*target)) << 16
- | grub_le_to_cpu16 (*(target + 1));
- insmask = 0xfbc0d000;
+ insword = (grub_le_to_cpu16 (*target) << 16)
+ | (grub_le_to_cpu16(*(target + 1)));
/* Extract and sign extend offset */
offset = ((insword >> 26) & 1) << 19
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
| (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;
+}
/***********************************************************
* 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;
}
#include <grub/i18n.h>
#include <grub/cpu/reloc.h>
+struct trampoline
+{
+#define LDR 0x58000050
+#define BR 0xd61f0200
+ grub_uint32_t ldr; /* ldr x16, 8 */
+ grub_uint32_t br; /* br x16 */
+ grub_uint64_t addr;
+};
+
/*
* Check if EHDR is a valid ELF header.
*/
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;
-
- /* 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++)
+ Elf_Rel *rel, *max;
+
+ 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:
{
break;
case R_AARCH64_CALL26:
case R_AARCH64_JUMP26:
- retval = grub_arm64_reloc_xxxx26 (place, sym_addr);
- 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;
+ 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;
- 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;
- };
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ N_("relocation 0x%x is not implemented yet"),
+ ELF_R_TYPE (rel->r_info));
}
}
-#undef FIRST_SHDR
-#undef NEXT_SHDR
-
return GRUB_ERR_NONE;
}
#include <grub/i18n.h>
#include <grub/arm64/reloc.h>
-static grub_ssize_t
-sign_compress_offset (grub_ssize_t offset, int bitpos)
-{
- return offset & ((1LL << (bitpos + 1)) - 1);
-}
-
/*
* grub_arm64_reloc_xxxx26():
*
* JUMP26/CALL26 relocations for B and BL instructions.
*/
-grub_err_t
-grub_arm64_reloc_xxxx26 (grub_uint32_t *place, Elf64_Addr adjust)
+int
+grub_arm_64_check_xxxx26_offset (grub_int64_t offset)
{
- grub_uint32_t insword, insmask;
- grub_ssize_t offset;
const grub_ssize_t offset_low = -(1 << 27), offset_high = (1 << 27) - 1;
- insword = grub_le_to_cpu32 (*place);
- insmask = 0xfc000000;
-
- offset = adjust;
-#ifndef GRUB_UTIL
- offset -= (grub_addr_t) place;
-#endif
-
if ((offset < offset_low) || (offset > offset_high))
- {
- return grub_error (GRUB_ERR_BAD_MODULE,
- N_("CALL26 Relocation out of range"));
- }
+ return 0;
+ return 1;
+}
+
+void
+grub_arm64_set_xxxx26_offset (grub_uint32_t *place, grub_int64_t offset)
+{
+ const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfc000000);
grub_dprintf ("dl", " reloc_xxxx64 %p %c= 0x%llx\n",
place, offset > 0 ? '+' : '-',
offset < 0 ? (long long) -(unsigned long long) offset : offset);
- offset = sign_compress_offset (offset, 27) >> 2;
-
- *place = grub_cpu_to_le32 ((insword & insmask) | offset);
-
- return GRUB_ERR_NONE;
+ *place &= insmask;
+ *place |= grub_cpu_to_le32 (offset >> 2) & ~insmask;
}
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;
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;
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
#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;
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)
|| 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);
}
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;
}
/* 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;
-
- /* 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 (i == e->e_shnum)
- return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
- entsize = s->sh_entsize;
-
- 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)
- {
- 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));
- }
- }
- }
- }
+ Elf_Rel *rel, *max;
+
+ 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 (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
+ + mod->symsize * 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 - (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;
}
/* 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;
-
- gp = (grub_uint64_t *) mod->base;
- gpptr = (grub_uint64_t *) mod->got;
- tr = mod->tramp;
-
- /* 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 (i == e->e_shnum)
- return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
- entsize = s->sh_entsize;
-
- 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 *) 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;
+
+ 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
+ + mod->symsize * 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:
{
- Elf_Rela *rel, *max;
-
- 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));
- }
- }
+ 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;
+
+ 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;
}
{
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))
/* 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 (i == e->e_shnum)
- return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
- entsize = s->sh_entsize;
-
- /* 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;
-
- if (i == e->e_shnum)
- return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found");
-
- 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)
+ if (!mod->reginfo)
+ {
+ unsigned i;
+ Elf_Shdr *ri;
+
+ /* 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);
+ }
+
+ gp0 = mod->reginfo[5];
+ Elf_Rel *rel, *max;
+
+ 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 (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
+ + 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:
+ );
+ break;
+ }
+ *(grub_uint16_t *) addr = (value >> 16) & 0xffff;
+ }
+ break;
+ case R_MIPS_LO16:
#ifdef GRUB_CPU_WORDS_BIGENDIAN
- addr += 2;
+ 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_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:
- /* FIXME: reuse*/
+ *(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
- addr += 2;
+ + 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;
}
- }
+ 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;
}
/* 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;
-
- /* 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 (i == e->e_shnum)
- return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
- entsize = s->sh_entsize;
-
- 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 *) 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 (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
+ + mod->symsize * 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_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;
}
/* 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;
-
- /* 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 (i == e->e_shnum)
- return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
- entsize = s->sh_entsize;
-
- 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));
- }
- }
- }
- }
+ Elf_Rela *rel, *max;
+
+ 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 (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
+ + mod->symsize * 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));
+ }
+ }
return GRUB_ERR_NONE;
}
/* 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;
-
- /* 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;
-
- if (i == e->e_shnum)
- return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
-
- entsize = s->sh_entsize;
-
- 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;
-
- /* Find the target segment. */
- for (seg = mod->segment; seg; seg = seg->next)
- if (seg->section == s->sh_info)
- break;
-
- if (seg)
+ Elf64_Rela *rel, *max;
+
+ 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 (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
+ + mod->symsize * 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:
{
- 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;
+ 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;
- 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_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;
}
#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
#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
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;
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
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 */
}
#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
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;
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:
*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:
+ case R_ARM_THM_JUMP19:
{
grub_err_t err;
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_call ((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_THM_JUMP19:
+
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
{
grub_err_t err;
- grub_util_info (" THM_JUMP19:\toffset=%d\t(0x%08x)",
- sym_addr, sym_addr);
+ 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;
-
- /* Thumb instructions can be 16-bit aligned */
- err = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr);
+ 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;
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;
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;
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)
{
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;
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,
struct grub_pe32_fixup_block b;
};
+/*
+ * R_ARM_THM_CALL/THM_JUMP24
+ *
+ * Relocate Thumb (T32) instruction set relative branches:
+ * B.W, BL and BLX
+ */
+static grub_err_t
+grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
+{
+ grub_int32_t offset;
+
+ offset = grub_arm_thm_call_get_offset (target);
+
+ grub_dprintf ("dl", " sym_addr = 0x%08x", sym_addr);
+
+ offset += sym_addr;
+
+ grub_dprintf("dl", " BL*: target=%p, sym_addr=0x%08x, offset=%d\n",
+ target, sym_addr, offset);
+
+ /* Keep traditional (pre-Thumb2) limits on blx. In any case if the kernel
+ is bigger than 2M (currently under 150K) then we probably have a problem
+ somewhere else. */
+ if (offset < -0x200000 || offset >= 0x200000)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("THM_CALL Relocation out of range."));
+
+ grub_dprintf ("dl", " relative destination = %p",
+ (char *) target + offset);
+
+ return grub_arm_thm_call_set_offset (target, offset);
+}
+
+/*
+ * R_ARM_THM_JUMP19
+ *
+ * Relocate conditional Thumb (T32) B<c>.W
+ */
+static grub_err_t
+grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
+{
+ grub_int32_t offset;
+
+ if (!(sym_addr & 1))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("Relocation targeting wrong execution state"));
+
+ offset = grub_arm_thm_jump19_get_offset (target);
+
+ /* Adjust and re-truncate offset */
+ offset += sym_addr;
+
+ if (!grub_arm_thm_jump19_check_offset (offset))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("THM_JUMP19 Relocation out of range."));
+
+ grub_arm_thm_jump19_set_offset (target, offset);
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * R_ARM_JUMP24
+ *
+ * Relocate ARM (A32) B
+ */
+static grub_err_t
+grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
+{
+ grub_int32_t offset;
+
+ if (sym_addr & 1)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("Relocation targeting wrong execution state"));
+
+ offset = grub_arm_jump24_get_offset (target);
+ offset += sym_addr;
+
+ if (!grub_arm_jump24_check_offset (offset))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("JUMP24 Relocation out of range."));
+
+
+ grub_arm_jump24_set_offset (target, offset);
+
+ return GRUB_ERR_NONE;
+}
+
#pragma GCC diagnostic ignored "-Wcast-align"
#define MKIMAGE_ELF32 1