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)
This commit is contained in:
Vladimir Serbinenko 2013-12-06 09:18:55 +01:00
parent a846dd4b3a
commit 8c534b85f1
18 changed files with 1097 additions and 987 deletions

View file

@ -1,3 +1,10 @@
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> 2013-12-05 Vladimir Serbinenko <phcoder@gmail.com>
* util/grub-mkimagexx.c: Fix reloc section generation for ia64. * util/grub-mkimagexx.c: Fix reloc section generation for ia64.

View file

@ -25,45 +25,113 @@
#include <grub/i18n.h> #include <grub/i18n.h>
#include <grub/arm/reloc.h> #include <grub/arm/reloc.h>
struct trampoline_arm
{
#define ARM_LOAD_IP 0xe59fc000
#define ARM_BX 0xe12fff1c
#define ARM_MOV_PC 0xe1a0f00c
grub_uint32_t load_ip; /* ldr ip, [pc] */
grub_uint32_t bx; /* bx ip or mov pc, ip*/
grub_uint32_t addr;
};
static grub_uint16_t thumb_template[8] =
{
0x468c, /* mov ip, r1 */
0x4903, /* ldr r1, [pc, #12] ; (10 <.text+0x10>) */
/* Exchange R1 and IP in limited Thumb instruction set.
IP gets negated but we compensate it by C code. */
/* R1 IP */
/* -A R1 */
0x4461, /* add r1, ip */ /* R1-A R1 */
0x4249, /* negs r1, r1 */ /* A-R1 R1 */
0x448c, /* add ip, r1 */ /* A-R1 A */
0x4249, /* negs r1, r1 */ /* R1-A A */
0x4461, /* add r1, ip */ /* R1 A */
0x4760 /* bx ip */
};
struct trampoline_thumb
{
grub_uint16_t template[8];
grub_uint32_t neg_addr;
};
#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. * * Runtime dynamic linker with helper functions. *
*************************************************/ *************************************************/
static grub_err_t grub_err_t
do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod) 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, *max;
Elf_Rel *rel;
Elf_Sym *sym;
int i, entnum;
entnum = relhdr->sh_size / sizeof (Elf_Rel); for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
max = (Elf_Rel *) ((char *) rel + s->sh_size);
/* Find the target segment for this relocation section. */ rel < max;
for (seg = mod->segment ; seg ; seg = seg->next) rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
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++)
{ {
Elf_Addr *target, sym_addr; Elf_Addr *target, sym_addr;
int relsym, reltype;
grub_err_t retval; 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, return grub_error (GRUB_ERR_BAD_MODULE,
"reloc offset is out of the segment"); "reloc offset is out of the segment");
relsym = ELF_R_SYM (rel[i].r_info); target = (void *) ((char *) seg->addr + rel->r_offset);
reltype = ELF_R_TYPE (rel[i].r_info); sym = (Elf_Sym *) ((char *) mod->symtab
target = (void *) ((grub_addr_t) seg->addr + rel[i].r_offset); + 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_ABS32:
{ {
@ -76,16 +144,58 @@ do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
case R_ARM_CALL: case R_ARM_CALL:
case R_ARM_JUMP24: case R_ARM_JUMP24:
{ {
retval = grub_arm_reloc_jump24 (target, sym_addr); grub_int32_t offset;
if (retval != GRUB_ERR_NONE)
return retval; 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; break;
case R_ARM_THM_CALL: case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24: case R_ARM_THM_JUMP24:
{ {
/* Thumb instructions can be 16-bit aligned */ /* 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) if (retval != GRUB_ERR_NONE)
return retval; return retval;
} }
@ -98,15 +208,37 @@ do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP19:
{ {
/* Thumb instructions can be 16-bit aligned */ /* Thumb instructions can be 16-bit aligned */
retval = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr); grub_int32_t offset;
if (retval != GRUB_ERR_NONE)
return retval; 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; break;
default: default:
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
N_("relocation 0x%x is 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; 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;
}

View file

@ -38,8 +38,6 @@ grub_arm_reloc_abs32 (Elf32_Word *target, Elf32_Addr sym_addr)
tmp = grub_le_to_cpu32 (*target); tmp = grub_le_to_cpu32 (*target);
tmp += sym_addr; tmp += sym_addr;
*target = grub_cpu_to_le32 (tmp); *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; 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. * * little-endian, requiring some additional fiddling. *
********************************************************************/ ********************************************************************/
/* grub_int32_t
* R_ARM_THM_CALL/THM_JUMP24 grub_arm_thm_call_get_offset (grub_uint16_t *target)
*
* 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 offset, offset_low, offset_high; grub_uint32_t sign, j1, j2;
grub_uint32_t sign, j1, j2, is_blx; grub_uint32_t insword;
grub_uint32_t insword, insmask; grub_int32_t offset;
/* Extract instruction word in alignment-safe manner */ /* Extract instruction word in alignment-safe manner */
insword = (grub_le_to_cpu16 (*target) << 16) insword = (grub_le_to_cpu16 (*target) << 16)
| (grub_le_to_cpu16(*(target + 1))); | (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 */ /* Extract bitfields from instruction words */
sign = (insword >> 26) & 1; 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)) if (offset & (1 << 24))
offset -= (1 << 25); offset -= (1 << 25);
grub_dprintf ("dl", " sym_addr = 0x%08x", sym_addr); return offset;
}
offset += sym_addr; grub_err_t
#ifndef GRUB_UTIL grub_arm_thm_call_set_offset (grub_uint16_t *target, grub_int32_t offset)
offset -= (grub_uint32_t) target; {
#endif 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", /* Extract instruction word in alignment-safe manner */
is_blx ? "BLX" : "BL", target, sym_addr, offset); insword = (grub_le_to_cpu16 (*target) << 16)
| (grub_le_to_cpu16(*(target + 1)));
if ((offset < offset_low) || (offset > offset_high)) if (((insword >> 12) & 0xd) == 0xc)
return grub_error (GRUB_ERR_BAD_MODULE, is_blx = 1;
N_("THM_CALL Relocation out of range.")); else
is_blx = 0;
grub_dprintf ("dl", " relative destination = %p", if (!is_blx && !(offset & 1))
(char *) target + offset); 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 */ /* Reassemble instruction word */
sign = (offset >> 24) & 1; 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; return GRUB_ERR_NONE;
} }
/* grub_int32_t
* R_ARM_THM_JUMP19 grub_arm_thm_jump19_get_offset (grub_uint16_t *target)
*
* 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 offset; grub_int32_t offset;
grub_uint32_t insword, insmask; grub_uint32_t insword;
/* Extract instruction word in alignment-safe manner */ /* Extract instruction word in alignment-safe manner */
insword = grub_le_to_cpu16 ((*target)) << 16 insword = (grub_le_to_cpu16 (*target) << 16)
| grub_le_to_cpu16 (*(target + 1)); | (grub_le_to_cpu16(*(target + 1)));
insmask = 0xfbc0d000;
/* Extract and sign extend offset */ /* Extract and sign extend offset */
offset = ((insword >> 26) & 1) << 19 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)) if (offset & (1 << 20))
offset -= (1 << 21); offset -= (1 << 21);
/* Adjust and re-truncate offset */ return offset;
offset += sym_addr; }
#ifndef GRUB_UTIL
offset -= (grub_uint32_t) target; void
#endif grub_arm_thm_jump19_set_offset (grub_uint16_t *target, grub_int32_t offset)
if ((offset > 1048574) || (offset < -1048576)) {
return grub_error (GRUB_ERR_BAD_MODULE, grub_uint32_t insword;
N_("THM_JUMP19 Relocation out of range.")); const grub_uint32_t insmask = 0xfbc0d000;
offset >>= 1; offset >>= 1;
offset &= 0xfffff; 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 */ /* Reassemble instruction word and write back */
insword &= insmask; insword &= insmask;
insword |= ((offset >> 19) & 1) << 26 insword |= ((offset >> 19) & 1) << 26
@ -177,9 +162,15 @@ grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
| (offset & 0x7ff); | (offset & 0x7ff);
*target = grub_cpu_to_le16 (insword >> 16); *target = grub_cpu_to_le16 (insword >> 16);
*(target + 1) = grub_cpu_to_le16 (insword & 0xffff); *(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. * * ARM instructions are 32-bit in size and 32-bit aligned. *
***********************************************************/ ***********************************************************/
/* grub_int32_t
* R_ARM_JUMP24 grub_arm_jump24_get_offset (grub_uint32_t *target)
*
* Relocate ARM (A32) B
*/
grub_err_t
grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
{ {
grub_uint32_t insword;
grub_int32_t offset; grub_int32_t offset;
grub_uint32_t insword;
if (sym_addr & 1)
return grub_error (GRUB_ERR_BAD_MODULE,
N_("Relocation targeting wrong execution state"));
insword = grub_le_to_cpu32 (*target); insword = grub_le_to_cpu32 (*target);
offset = (insword & 0x00ffffff) << 2; offset = (insword & 0x00ffffff) << 2;
if (offset & 0x02000000) if (offset & 0x02000000)
offset -= 0x04000000; offset -= 0x04000000;
offset += sym_addr; return offset;
#ifndef GRUB_UTIL }
offset -= (grub_uint32_t) target;
#endif 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 &= 0xff000000;
insword |= (offset >> 2) & 0x00ffffff; insword |= (offset >> 2) & 0x00ffffff;
*target = grub_cpu_to_le32 (insword); *target = grub_cpu_to_le32 (insword);
return GRUB_ERR_NONE;
} }

View file

@ -25,6 +25,15 @@
#include <grub/i18n.h> #include <grub/i18n.h>
#include <grub/cpu/reloc.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. * Check if EHDR is a valid ELF header.
*/ */
@ -42,59 +51,76 @@ grub_arch_dl_check_header (void *ehdr)
return GRUB_ERR_NONE; 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 * Unified function for both REL and RELA
*/ */
static grub_err_t grub_err_t
do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod) grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
Elf_Shdr *s, grub_dl_segment_t seg)
{ {
grub_err_t retval; Elf_Rel *rel, *max;
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 (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
for (segment = mod->segment ; segment != 0 ; segment = segment->next) max = (Elf_Rel *) ((char *) rel + s->sh_size);
if (segment->section == relhdr->sh_info) rel < max;
break; rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
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_Sym *sym;
void *place; 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, return grub_error (GRUB_ERR_BAD_MODULE,
"reloc offset is out of the segment"); "reloc offset is out of the segment");
symidx = ELF_R_SYM (rel->r_info); sym = (Elf_Sym *) ((char *) mod->symtab
reltype = ELF_R_TYPE (rel->r_info); + mod->symsize * ELF_R_SYM (rel->r_info));
sym_addr = symbol[symidx].st_value; sym_addr = sym->st_value;
if (relhdr->sh_type == SHT_RELA) if (s->sh_type == SHT_RELA)
sym_addr += rela->r_addend; sym_addr += ((Elf_Rela *) rel)->r_addend;
place = (void *) ((grub_addr_t) segment->addr + rel->r_offset); place = (void *) ((grub_addr_t) seg->addr + rel->r_offset);
switch (reltype) switch (ELF_R_TYPE (rel->r_info))
{ {
case R_AARCH64_ABS64: case R_AARCH64_ABS64:
{ {
@ -108,92 +134,32 @@ do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
break; break;
case R_AARCH64_CALL26: case R_AARCH64_CALL26:
case R_AARCH64_JUMP26: 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; break;
default: default:
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
N_("relocation 0x%x is not implemented yet"), N_("relocation 0x%x is not implemented yet"),
reltype); ELF_R_TYPE (rel->r_info));
}
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;
};
} }
} }
#undef FIRST_SHDR
#undef NEXT_SHDR
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }

View file

@ -25,46 +25,31 @@
#include <grub/i18n.h> #include <grub/i18n.h>
#include <grub/arm64/reloc.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(): * grub_arm64_reloc_xxxx26():
* *
* JUMP26/CALL26 relocations for B and BL instructions. * JUMP26/CALL26 relocations for B and BL instructions.
*/ */
grub_err_t int
grub_arm64_reloc_xxxx26 (grub_uint32_t *place, Elf64_Addr adjust) 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; 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)) if ((offset < offset_low) || (offset > offset_high))
{ return 0;
return grub_error (GRUB_ERR_BAD_MODULE, return 1;
N_("CALL26 Relocation out of range")); }
}
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", grub_dprintf ("dl", " reloc_xxxx64 %p %c= 0x%llx\n",
place, offset > 0 ? '+' : '-', place, offset > 0 ? '+' : '-',
offset < 0 ? (long long) -(unsigned long long) offset : offset); offset < 0 ? (long long) -(unsigned long long) offset : offset);
offset = sign_compress_offset (offset, 27) >> 2; *place &= insmask;
*place |= grub_cpu_to_le32 (offset >> 2) & ~insmask;
*place = grub_cpu_to_le32 ((insword & insmask) | offset);
return GRUB_ERR_NONE;
} }

View file

@ -229,7 +229,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
unsigned i; unsigned i;
Elf_Shdr *s; Elf_Shdr *s;
grub_size_t tsize = 0, talign = 1; 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 tramp;
grub_size_t got; grub_size_t got;
grub_err_t err; grub_err_t err;
@ -245,7 +245,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
talign = s->sh_addralign; 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); err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got);
if (err) if (err)
return err; return err;
@ -314,12 +314,14 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
mod->segment = seg; 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); ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN);
mod->tramp = ptr; mod->tramp = ptr;
mod->trampptr = ptr;
ptr += tramp; ptr += tramp;
ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_GOT_ALIGN); ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_GOT_ALIGN);
mod->got = ptr; mod->got = ptr;
mod->gotptr = ptr;
ptr += got; ptr += got;
#endif #endif
@ -352,6 +354,7 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
#else #else
mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset); mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset);
#endif #endif
mod->symsize = s->sh_entsize;
sym = mod->symtab; sym = mod->symtab;
size = s->sh_size; size = s->sh_size;
entsize = s->sh_entsize; entsize = s->sh_entsize;
@ -561,6 +564,37 @@ grub_dl_flush_cache (grub_dl_t mod)
grub_arch_sync_caches (mod->base, mod->sz); 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. */ /* Load a module from core memory. */
grub_dl_t grub_dl_t
grub_dl_load_core_noinit (void *addr, grub_size_t size) 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_resolve_dependencies (mod, e)
|| grub_dl_load_segments (mod, e) || grub_dl_load_segments (mod, e)
|| grub_dl_resolve_symbols (mod, e) || grub_dl_resolve_symbols (mod, e)
|| grub_arch_dl_relocate_symbols (mod, e)) || grub_dl_relocate_symbols (mod, e))
{ {
mod->fini = 0; mod->fini = 0;
grub_dl_unload (mod); grub_dl_unload (mod);

View file

@ -39,10 +39,13 @@ grub_arch_dl_check_header (void *ehdr)
} }
grub_err_t 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) mod;
(void) ehdr; (void) ehdr;
(void) s;
(void) seg;
return GRUB_ERR_BAD_MODULE; return GRUB_ERR_BAD_MODULE;
} }

View file

@ -40,75 +40,42 @@ grub_arch_dl_check_header (void *ehdr)
/* Relocate symbols. */ /* Relocate symbols. */
grub_err_t 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_Rel *rel, *max;
Elf_Shdr *s;
Elf_Word entsize;
unsigned i;
/* Find a symbol table. */ for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); max = (Elf_Rel *) ((char *) rel + s->sh_size);
i < e->e_shnum; rel < max;
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
if (s->sh_type == SHT_SYMTAB) {
break; Elf_Word *addr;
Elf_Sym *sym;
if (i == e->e_shnum) if (seg->size < rel->r_offset)
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); 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); switch (ELF_R_TYPE (rel->r_info))
i < e->e_shnum; {
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) case R_386_32:
if (s->sh_type == SHT_REL) *addr += sym->st_value;
{ break;
grub_dl_segment_t seg;
/* Find the target segment. */ case R_386_PC32:
for (seg = mod->segment; seg; seg = seg->next) *addr += (sym->st_value - (grub_addr_t) addr);
if (seg->section == s->sh_info) break;
break; default:
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
if (seg) N_("relocation 0x%x is not implemented yet"),
{ ELF_R_TYPE (rel->r_info));
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));
}
}
}
}
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }

View file

@ -46,118 +46,82 @@ grub_arch_dl_check_header (void *ehdr)
/* Relocate symbols. */ /* Relocate symbols. */
grub_err_t 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_Rela *rel, *max;
Elf_Shdr *s;
Elf_Word entsize;
unsigned i;
grub_uint64_t *gp, *gpptr;
struct grub_ia64_trampoline *tr;
gp = (grub_uint64_t *) mod->base; for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset),
gpptr = (grub_uint64_t *) mod->got; max = (Elf_Rela *) ((char *) rel + s->sh_size);
tr = mod->tramp; 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. */ if (seg->size < (rel->r_offset & ~3))
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); return grub_error (GRUB_ERR_BAD_MODULE,
i < e->e_shnum; "reloc offset is out of the segment");
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
if (s->sh_type == SHT_SYMTAB)
break;
if (i == e->e_shnum) addr = (grub_addr_t) seg->addr + rel->r_offset;
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); 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); switch (ELF_R_TYPE (rel->r_info))
i < e->e_shnum; {
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) case R_IA64_PCREL21B:
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; 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), if (noff & ~MASK19)
max = rel + s->sh_size / s->sh_entsize; return grub_error (GRUB_ERR_BAD_OS,
rel < max; "trampoline offset too big (%lx)", noff);
rel++) grub_ia64_add_value_to_slot_20b (addr, noff);
{
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));
}
}
} }
} 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; return GRUB_ERR_NONE;
} }

View file

@ -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_Ehdr *e = ehdr;
const Elf_Shdr *s; const Elf_Shdr *s;
unsigned i;
/* FIXME: suboptimal. */ /* FIXME: suboptimal. */
grub_size_t gp_size = 0; grub_size_t gp_size = 0;
unsigned i;
*tramp = 0; *tramp = 0;
*got = 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); for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff);
i < e->e_shnum; i < e->e_shnum;
i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) 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. */ /* Relocate symbols. */
grub_err_t 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; grub_uint32_t gp0;
Elf_Ehdr *e = ehdr;
/* Find a symbol table. */ if (!mod->reginfo)
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); {
i < e->e_shnum; unsigned i;
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) Elf_Shdr *ri;
if (s->sh_type == SHT_SYMTAB)
break;
if (i == e->e_shnum) /* Find reginfo. */
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); 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 (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); max = (Elf_Rel *) ((char *) rel + s->sh_size);
i < e->e_shnum; rel < max;
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
if (s->sh_type == SHT_MIPS_REGINFO) {
break; grub_uint8_t *addr;
Elf_Sym *sym;
grub_uint32_t sym_value;
if (i == e->e_shnum) if (seg->size < rel->r_offset)
return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found"); return grub_error (GRUB_ERR_BAD_MODULE,
"reloc offset is out of the segment");
gp0 = ((grub_uint32_t *)((char *) e + s->sh_offset))[5]; addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset);
sym = (Elf_Sym *) ((char *) mod->symtab
gpptr = gp = mod->got; + mod->symsize * ELF_R_SYM (rel->r_info));
sym_value = sym->st_value;
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); if (sym_value == (grub_addr_t) &__gnu_local_gp_dummy)
i < e->e_shnum; sym_value = (grub_addr_t) mod->got;
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) else if (sym_value == (grub_addr_t) &_gp_disp_dummy)
if (s->sh_type == SHT_REL) {
{ sym_value = (grub_addr_t) mod->got - (grub_addr_t) addr;
grub_dl_segment_t seg; if (ELF_R_TYPE (rel->r_info) == R_MIPS_LO16)
/* ABI mandates +4 even if partner lui doesn't
/* Find the target segment. */ immediately precede addiu. */
for (seg = mod->segment; seg; seg = seg->next) sym_value += 4;
if (seg->section == s->sh_info) }
break; switch (ELF_R_TYPE (rel->r_info))
{
if (seg) case R_MIPS_HI16:
{ {
Elf_Rel *rel, *max; grub_uint32_t value;
Elf_Rel *rel2;
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;
#ifdef GRUB_CPU_WORDS_BIGENDIAN #ifdef GRUB_CPU_WORDS_BIGENDIAN
addr += 2; addr += 2;
#endif #endif
/* Handle partner lo16 relocation. Lower part is /* Handle partner lo16 relocation. Lower part is
treated as signed. Hence add 0x8000 to compensate. treated as signed. Hence add 0x8000 to compensate.
*/ */
value = (*(grub_uint16_t *) addr << 16) value = (*(grub_uint16_t *) addr << 16)
+ sym_value + 0x8000; + sym_value + 0x8000;
for (rel2 = rel + 1; rel2 < max; rel2++) for (rel2 = rel + 1; rel2 < max; rel2++)
if (ELF_R_SYM (rel2->r_info) if (ELF_R_SYM (rel2->r_info)
== ELF_R_SYM (rel->r_info) == ELF_R_SYM (rel->r_info)
&& ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
{ {
value += *(grub_int16_t *) value += *(grub_int16_t *)
((char *) seg->addr + rel2->r_offset ((char *) seg->addr + rel2->r_offset
#ifdef GRUB_CPU_WORDS_BIGENDIAN #ifdef GRUB_CPU_WORDS_BIGENDIAN
+ 2 + 2
#endif #endif
); );
break; break;
} }
*(grub_uint16_t *) addr = (value >> 16) & 0xffff; *(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;
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; return GRUB_ERR_NONE;
} }

View file

@ -101,109 +101,77 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
/* Relocate symbols. */ /* Relocate symbols. */
grub_err_t 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_Rela *rel, *max;
Elf_Shdr *s;
Elf_Word entsize;
unsigned i;
struct trampoline *tptr = mod->tramp;
/* Find a symbol table. */ for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset),
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); max = (Elf_Rela *) ((char *) rel + s->sh_size);
i < e->e_shnum; rel < max;
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) rel = (Elf_Rela *) ((char *) rel + s->sh_entsize))
if (s->sh_type == SHT_SYMTAB) {
break; Elf_Word *addr;
Elf_Sym *sym;
grub_uint32_t value;
if (i == e->e_shnum) if (seg->size < rel->r_offset)
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); 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); /* On the PPC the value does not have an explicit
i < e->e_shnum; addend, add it. */
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) value = sym->st_value + rel->r_addend;
if (s->sh_type == SHT_RELA) switch (ELF_R_TYPE (rel->r_info))
{ {
grub_dl_segment_t seg; case GRUB_ELF_R_PPC_ADDR16_LO:
*(Elf_Half *) addr = value;
break;
/* Find the target segment. */ case GRUB_ELF_R_PPC_REL24:
for (seg = mod->segment; seg; seg = seg->next)
if (seg->section == s->sh_info)
break;
if (seg)
{ {
Elf_Rela *rel, *max; Elf_Sword delta = value - (Elf_Word) addr;
for (rel = (Elf_Rela *) ((char *) e + s->sh_offset), if (delta << 6 >> 6 != delta)
max = rel + s->sh_size / s->sh_entsize;
rel < max;
rel++)
{ {
Elf_Word *addr; struct trampoline *tptr = mod->trampptr;
Elf_Sym *sym; grub_memcpy (tptr, &trampoline_template,
grub_uint32_t value; sizeof (*tptr));
delta = (grub_uint8_t *) tptr - (grub_uint8_t *) addr;
if (seg->size < rel->r_offset) tptr->lis |= (((value) >> 16) & 0xffff);
return grub_error (GRUB_ERR_BAD_MODULE, tptr->ori |= ((value) & 0xffff);
"reloc offset is out of the segment"); mod->trampptr = tptr + 1;
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));
}
} }
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; return GRUB_ERR_NONE;
} }

View file

@ -42,106 +42,74 @@ grub_arch_dl_check_header (void *ehdr)
/* Relocate symbols. */ /* Relocate symbols. */
grub_err_t 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_Rela *rel, *max;
Elf_Shdr *s;
Elf_Word entsize;
unsigned i;
/* Find a symbol table. */ for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset),
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); max = (Elf_Rela *) ((char *) rel + s->sh_size);
i < e->e_shnum; rel < max;
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) rel = (Elf_Rela *) ((char *) rel + s->sh_entsize))
if (s->sh_type == SHT_SYMTAB) {
break; Elf_Word *addr;
Elf_Sym *sym;
Elf_Addr value;
if (i == e->e_shnum) if (seg->size < rel->r_offset)
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); 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); value = sym->st_value + rel->r_addend;
i < e->e_shnum; switch (ELF_R_TYPE (rel->r_info) & 0xff)
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) {
if (s->sh_type == SHT_RELA) case R_SPARC_32: /* 3 V-word32 */
{ if (value & 0xFFFFFFFF00000000)
grub_dl_segment_t seg; return grub_error (GRUB_ERR_BAD_MODULE,
"address out of 32 bits range");
/* Find the target segment. */ *addr = value;
for (seg = mod->segment; seg; seg = seg->next) break;
if (seg->section == s->sh_info) case R_SPARC_WDISP30: /* 7 V-disp30 */
break; if (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) &&
(((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000)
if (seg) != 0xFFFFFFFF00000000))
{ return grub_error (GRUB_ERR_BAD_MODULE,
Elf_Rela *rel, *max; "displacement out of 30 bits range");
*addr = (*addr & 0xC0000000) |
for (rel = (Elf_Rela *) ((char *) e + s->sh_offset), (((grub_int32_t) ((value - (Elf_Addr) addr) >> 2)) &
max = rel + s->sh_size / s->sh_entsize; 0x3FFFFFFF);
rel < max; break;
rel++) case R_SPARC_HH22: /* 9 V-imm22 */
{ *addr = (*addr & 0xFFC00000) | ((value >> 42) & 0x3FFFFF);
Elf_Word *addr; break;
Elf_Sym *sym; case R_SPARC_HM10: /* 12 T-simm13 */
Elf_Addr value; *addr = (*addr & 0xFFFFFC00) | ((value >> 32) & 0x3FF);
break;
if (seg->size < rel->r_offset) case R_SPARC_HI22: /* 9 V-imm22 */
return grub_error (GRUB_ERR_BAD_MODULE, *addr = (*addr & 0xFFC00000) | ((value >> 10) & 0x3FFFFF);
"reloc offset is out of the segment"); break;
case R_SPARC_LO10: /* 12 T-simm13 */
addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset); *addr = (*addr & 0xFFFFFC00) | (value & 0x3FF);
sym = (Elf_Sym *) ((char *) mod->symtab break;
+ entsize * ELF_R_SYM (rel->r_info)); case R_SPARC_64: /* 32 V-xwords64 */
*(Elf_Xword *) addr = value;
value = sym->st_value + rel->r_addend; break;
switch (ELF_R_TYPE (rel->r_info) & 0xff) case R_SPARC_OLO10:
{ *addr = (*addr & ~0x1fff)
case R_SPARC_32: /* 3 V-word32 */ | (((value & 0x3ff) +
if (value & 0xFFFFFFFF00000000) (ELF_R_TYPE (rel->r_info) >> 8))
return grub_error (GRUB_ERR_BAD_MODULE, & 0x1fff);
"address out of 32 bits range"); break;
*addr = value; default:
break; return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
case R_SPARC_WDISP30: /* 7 V-disp30 */ N_("relocation 0x%x is not implemented yet"),
if (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) && ELF_R_TYPE (rel->r_info));
(((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; return GRUB_ERR_NONE;
} }

View file

@ -40,101 +40,69 @@ grub_arch_dl_check_header (void *ehdr)
/* Relocate symbols. */ /* Relocate symbols. */
grub_err_t 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_Rela *rel, *max;
Elf64_Shdr *s;
Elf64_Word entsize;
unsigned i;
/* Find a symbol table. */ for (rel = (Elf64_Rela *) ((char *) ehdr + s->sh_offset),
for (i = 0, s = (Elf64_Shdr *) ((char *) e + e->e_shoff); max = (Elf64_Rela *) ((char *) rel + s->sh_size);
i < e->e_shnum; rel < max;
i++, s = (Elf64_Shdr *) ((char *) s + e->e_shentsize)) rel = (Elf64_Rela *) ((char *) rel + s->sh_entsize))
if (s->sh_type == SHT_SYMTAB) {
break; Elf64_Word *addr32;
Elf64_Xword *addr64;
Elf64_Sym *sym;
if (i == e->e_shnum) if (seg->size < rel->r_offset)
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); 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); switch (ELF_R_TYPE (rel->r_info))
i < e->e_shnum; {
i++, s = (Elf64_Shdr *) ((char *) s + e->e_shentsize)) case R_X86_64_64:
if (s->sh_type == SHT_RELA) *addr64 += rel->r_addend + sym->st_value;
{ break;
grub_dl_segment_t seg;
/* Find the target segment. */ case R_X86_64_PC32:
for (seg = mod->segment; seg; seg = seg->next)
if (seg->section == s->sh_info)
break;
if (seg)
{ {
Elf64_Rela *rel, *max; grub_int64_t value;
value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value -
for (rel = (Elf64_Rela *) ((char *) e + s->sh_offset), (Elf64_Xword) seg->addr - rel->r_offset;
max = rel + s->sh_size / s->sh_entsize; if (value != (grub_int32_t) value)
rel < max; return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range");
rel++) *addr32 = value;
{
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));
}
}
} }
} 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; return GRUB_ERR_NONE;
} }

View file

@ -20,8 +20,27 @@
#define GRUB_ARM_RELOC_H 1 #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_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); int
grub_err_t grub_arm_reloc_thm_jump19 (grub_uint16_t *addr, Elf32_Addr sym_addr); 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 #endif

View file

@ -19,6 +19,8 @@
#ifndef GRUB_ARM64_RELOC_H #ifndef GRUB_ARM64_RELOC_H
#define GRUB_ARM64_RELOC_H 1 #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 #endif

View file

@ -177,11 +177,17 @@ struct grub_dl
grub_dl_dep_t dep; grub_dl_dep_t dep;
grub_dl_segment_t segment; grub_dl_segment_t segment;
Elf_Sym *symtab; Elf_Sym *symtab;
grub_size_t symsize;
void (*init) (struct grub_dl *mod); void (*init) (struct grub_dl *mod);
void (*fini) (void); void (*fini) (void);
#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__) #if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
void *got; void *got;
void *gotptr;
void *tramp; void *tramp;
void *trampptr;
#endif
#ifdef __mips__
grub_uint32_t *reginfo;
#endif #endif
void *base; void *base;
grub_size_t sz; 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); int isfunc, grub_dl_t mod);
grub_err_t grub_arch_dl_check_header (void *ehdr); 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) #if defined (_mips)
#define GRUB_LINKER_HAVE_INIT 1 #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); grub_size_t *got);
#endif #endif
#if defined (__powerpc__) || defined (__mips__) #if defined (__powerpc__) || defined (__mips__) || defined (__arm__)
#define GRUB_ARCH_DL_TRAMP_ALIGN 4 #define GRUB_ARCH_DL_TRAMP_ALIGN 4
#define GRUB_ARCH_DL_GOT_ALIGN 4 #define GRUB_ARCH_DL_GOT_ALIGN 4
#endif #endif
#if defined (__aarch64__)
#define GRUB_ARCH_DL_TRAMP_ALIGN 8
#define GRUB_ARCH_DL_GOT_ALIGN 8
#endif
#endif #endif
#endif /* ! GRUB_DL_H */ #endif /* ! GRUB_DL_H */

View file

@ -493,6 +493,85 @@ SUFFIX (count_funcs) (Elf_Ehdr *e, Elf_Shdr *symtab_section,
} }
#endif #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 /* Deal with relocation information. This function relocates addresses
within the virtual address space starting from 0. So only relative within the virtual address space starting from 0. So only relative
addresses can be fully resolved. Absolute addresses must be relocated 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); struct grub_ia64_trampoline *tr = (void *) (pe_target + tramp_off);
grub_uint64_t *gpptr = (void *) (pe_target + got_off); grub_uint64_t *gpptr = (void *) (pe_target + got_off);
#define MASK19 ((1 << 19) - 1) #define MASK19 ((1 << 19) - 1)
#else
grub_uint32_t *tr = (void *) (pe_target + tramp_off);
#endif #endif
for (i = 0, s = sections; 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_JUMP26:
case R_AARCH64_CALL26: case R_AARCH64_CALL26:
{ {
grub_err_t err;
sym_addr -= offset; sym_addr -= offset;
sym_addr -= SUFFIX (entry_point); 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); sym_addr);
if (err)
grub_util_error ("%s", grub_errmsg);
} }
break; break;
default: 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); *target = grub_host_to_target32 (grub_target_to_host32 (*target) + sym_addr);
} }
break; 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_CALL:
case R_ARM_THM_JUMP24: 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: case R_ARM_THM_JUMP19:
{ {
grub_err_t err; grub_err_t err;
grub_util_info (" THM_JUMP19:\toffset=%d\t(0x%08x)", grub_util_info (" THM_JUMP24:\ttarget=0x%08lx\toffset=(0x%08x)", (unsigned long) target, sym_addr);
sym_addr, sym_addr); if (!(sym_addr & 1))
sym_addr -= offset; {
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 */ /* 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) if (err)
grub_util_error ("%s", grub_errmsg); grub_util_error ("%s", grub_errmsg);
} }
break; 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: default:
grub_util_error (_("relocation 0x%x is not implemented yet!"), ELF_R_TYPE (info)); grub_util_error (_("relocation 0x%x is not implemented yet!"), ELF_R_TYPE (info));
break; break;
@ -1054,11 +1177,13 @@ SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out,
case EM_ARM: case EM_ARM:
switch (ELF_R_TYPE (info)) switch (ELF_R_TYPE (info))
{ {
case R_ARM_V4BX:
/* Relative relocations do not require fixup entries. */ /* Relative relocations do not require fixup entries. */
case R_ARM_JUMP24: case R_ARM_JUMP24:
case R_ARM_THM_CALL: case R_ARM_THM_CALL:
case R_ARM_THM_JUMP19: case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24: case R_ARM_THM_JUMP24:
case R_ARM_CALL:
{ {
Elf_Addr addr; Elf_Addr addr;
@ -1280,7 +1405,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size,
Elf_Off section_offset; Elf_Off section_offset;
Elf_Half section_entsize; Elf_Half section_entsize;
grub_size_t kernel_size; 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; unsigned ia64jmpnum = 0;
Elf_Shdr *symtab_section = 0; Elf_Shdr *symtab_section = 0;
grub_size_t got = 0; grub_size_t got = 0;
@ -1373,6 +1498,21 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size,
break; 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 #ifdef MKIMAGE_ELF64
if (image_target->elf_target == EM_IA_64) 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); grub_ia64_dl_get_tramp_got_size (e, &tramp, &got);
ia64_toff = *kernel_sz; tramp_off = *kernel_sz;
*kernel_sz += ALIGN_UP (tramp, 16); *kernel_sz += ALIGN_UP (tramp, 16);
ia64jmp_off = *kernel_sz; 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, SUFFIX (relocate_addresses) (e, sections, section_addresses,
section_entsize, section_entsize,
num_sections, strtab, num_sections, strtab,
out_img, ia64_toff, ia64_got_off, out_img, tramp_off, ia64_got_off,
image_target); image_target);
*reloc_size = SUFFIX (make_reloc_section) (e, reloc_section, *reloc_size = SUFFIX (make_reloc_section) (e, reloc_section,

View file

@ -834,6 +834,94 @@ struct fixup_block_list
struct grub_pe32_fixup_block b; 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" #pragma GCC diagnostic ignored "-Wcast-align"
#define MKIMAGE_ELF32 1 #define MKIMAGE_ELF32 1