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:
parent
a846dd4b3a
commit
8c534b85f1
18 changed files with 1097 additions and 987 deletions
|
@ -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>
|
||||
|
||||
* util/grub-mkimagexx.c: Fix reloc section generation for ia64.
|
||||
|
|
|
@ -25,45 +25,113 @@
|
|||
#include <grub/i18n.h>
|
||||
#include <grub/arm/reloc.h>
|
||||
|
||||
struct trampoline_arm
|
||||
{
|
||||
#define ARM_LOAD_IP 0xe59fc000
|
||||
#define ARM_BX 0xe12fff1c
|
||||
#define ARM_MOV_PC 0xe1a0f00c
|
||||
grub_uint32_t load_ip; /* ldr ip, [pc] */
|
||||
grub_uint32_t bx; /* bx ip or mov pc, ip*/
|
||||
grub_uint32_t addr;
|
||||
};
|
||||
|
||||
static grub_uint16_t thumb_template[8] =
|
||||
{
|
||||
0x468c, /* mov ip, r1 */
|
||||
0x4903, /* ldr r1, [pc, #12] ; (10 <.text+0x10>) */
|
||||
/* Exchange R1 and IP in limited Thumb instruction set.
|
||||
IP gets negated but we compensate it by C code. */
|
||||
/* R1 IP */
|
||||
/* -A R1 */
|
||||
0x4461, /* add r1, ip */ /* R1-A R1 */
|
||||
0x4249, /* negs r1, r1 */ /* A-R1 R1 */
|
||||
0x448c, /* add ip, r1 */ /* A-R1 A */
|
||||
0x4249, /* negs r1, r1 */ /* R1-A A */
|
||||
0x4461, /* add r1, ip */ /* R1 A */
|
||||
0x4760 /* bx ip */
|
||||
};
|
||||
|
||||
struct trampoline_thumb
|
||||
{
|
||||
grub_uint16_t template[8];
|
||||
grub_uint32_t neg_addr;
|
||||
};
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||
|
||||
grub_err_t
|
||||
grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
|
||||
grub_size_t *got)
|
||||
{
|
||||
const Elf_Ehdr *e = ehdr;
|
||||
const Elf_Shdr *s;
|
||||
unsigned i;
|
||||
|
||||
*tramp = 0;
|
||||
*got = 0;
|
||||
|
||||
for (i = 0, s = (const Elf_Shdr *) ((grub_addr_t) e + e->e_shoff);
|
||||
i < e->e_shnum;
|
||||
i++, s = (const Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize))
|
||||
if (s->sh_type == SHT_REL)
|
||||
{
|
||||
const Elf_Rel *rel, *max;
|
||||
|
||||
for (rel = (const Elf_Rel *) ((grub_addr_t) e + s->sh_offset),
|
||||
max = rel + s->sh_size / s->sh_entsize;
|
||||
rel < max;
|
||||
rel++)
|
||||
switch (ELF_R_TYPE (rel->r_info))
|
||||
{
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_JUMP24:
|
||||
{
|
||||
*tramp += sizeof (struct trampoline_arm);
|
||||
break;
|
||||
}
|
||||
case R_ARM_THM_CALL:
|
||||
case R_ARM_THM_JUMP24:
|
||||
case R_ARM_THM_JUMP19:
|
||||
{
|
||||
*tramp += sizeof (struct trampoline_thumb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grub_dprintf ("dl", "trampoline size %x\n", *tramp);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
/*************************************************
|
||||
* Runtime dynamic linker with helper functions. *
|
||||
*************************************************/
|
||||
static grub_err_t
|
||||
do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||||
Elf_Shdr *s, grub_dl_segment_t seg)
|
||||
{
|
||||
grub_dl_segment_t seg;
|
||||
Elf_Rel *rel;
|
||||
Elf_Sym *sym;
|
||||
int i, entnum;
|
||||
Elf_Rel *rel, *max;
|
||||
|
||||
entnum = relhdr->sh_size / sizeof (Elf_Rel);
|
||||
|
||||
/* Find the target segment for this relocation section. */
|
||||
for (seg = mod->segment ; seg ; seg = seg->next)
|
||||
if (seg->section == relhdr->sh_info)
|
||||
break;
|
||||
if (!seg)
|
||||
return grub_error (GRUB_ERR_EOF, N_("relocation segment not found"));
|
||||
|
||||
rel = (Elf_Rel *) ((grub_addr_t) e + relhdr->sh_offset);
|
||||
|
||||
/* Step through all relocations */
|
||||
for (i = 0, sym = mod->symtab; i < entnum; i++)
|
||||
for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
|
||||
max = (Elf_Rel *) ((char *) rel + s->sh_size);
|
||||
rel < max;
|
||||
rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
|
||||
{
|
||||
Elf_Addr *target, sym_addr;
|
||||
int relsym, reltype;
|
||||
grub_err_t retval;
|
||||
Elf_Sym *sym;
|
||||
|
||||
if (seg->size < rel[i].r_offset)
|
||||
if (seg->size < rel->r_offset)
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
"reloc offset is out of the segment");
|
||||
relsym = ELF_R_SYM (rel[i].r_info);
|
||||
reltype = ELF_R_TYPE (rel[i].r_info);
|
||||
target = (void *) ((grub_addr_t) seg->addr + rel[i].r_offset);
|
||||
target = (void *) ((char *) seg->addr + rel->r_offset);
|
||||
sym = (Elf_Sym *) ((char *) mod->symtab
|
||||
+ mod->symsize * ELF_R_SYM (rel->r_info));
|
||||
|
||||
sym_addr = sym[relsym].st_value;
|
||||
sym_addr = sym->st_value;
|
||||
|
||||
switch (reltype)
|
||||
switch (ELF_R_TYPE (rel->r_info))
|
||||
{
|
||||
case R_ARM_ABS32:
|
||||
{
|
||||
|
@ -76,16 +144,58 @@ do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
|
|||
case R_ARM_CALL:
|
||||
case R_ARM_JUMP24:
|
||||
{
|
||||
retval = grub_arm_reloc_jump24 (target, sym_addr);
|
||||
if (retval != GRUB_ERR_NONE)
|
||||
return retval;
|
||||
grub_int32_t offset;
|
||||
|
||||
sym_addr += grub_arm_jump24_get_offset (target);
|
||||
offset = sym_addr - (grub_uint32_t) target;
|
||||
|
||||
if ((sym_addr & 1) || !grub_arm_jump24_check_offset (offset))
|
||||
{
|
||||
struct trampoline_arm *tp = mod->trampptr;
|
||||
mod->trampptr = tp + 1;
|
||||
tp->load_ip = ARM_LOAD_IP;
|
||||
tp->bx = (sym_addr & 1) ? ARM_BX : ARM_MOV_PC;
|
||||
tp->addr = sym_addr + 8;
|
||||
offset = (grub_uint8_t *) tp - (grub_uint8_t *) target - 8;
|
||||
}
|
||||
if (!grub_arm_jump24_check_offset (offset))
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
"trampoline out of range");
|
||||
grub_arm_jump24_set_offset (target, offset);
|
||||
}
|
||||
break;
|
||||
case R_ARM_THM_CALL:
|
||||
case R_ARM_THM_JUMP24:
|
||||
{
|
||||
/* Thumb instructions can be 16-bit aligned */
|
||||
retval = grub_arm_reloc_thm_call ((grub_uint16_t *) target, sym_addr);
|
||||
grub_int32_t offset;
|
||||
|
||||
sym_addr += grub_arm_thm_call_get_offset ((grub_uint16_t *) target);
|
||||
|
||||
grub_dprintf ("dl", " sym_addr = 0x%08x\n", sym_addr);
|
||||
|
||||
offset = sym_addr - (grub_uint32_t) target;
|
||||
|
||||
grub_dprintf("dl", " BL*: target=%p, sym_addr=0x%08x, offset=%d\n",
|
||||
target, sym_addr, offset);
|
||||
|
||||
if (!(sym_addr & 1) || (offset < -0x200000 || offset >= 0x200000))
|
||||
{
|
||||
struct trampoline_thumb *tp = mod->trampptr;
|
||||
mod->trampptr = tp + 1;
|
||||
grub_memcpy (tp->template, thumb_template, sizeof (tp->template));
|
||||
tp->neg_addr = -sym_addr - 4;
|
||||
offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1;
|
||||
}
|
||||
|
||||
if (offset < -0x200000 || offset >= 0x200000)
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
"trampoline out of range");
|
||||
|
||||
grub_dprintf ("dl", " relative destination = %p\n",
|
||||
(char *) target + offset);
|
||||
|
||||
retval = grub_arm_thm_call_set_offset ((grub_uint16_t *) target, offset);
|
||||
if (retval != GRUB_ERR_NONE)
|
||||
return retval;
|
||||
}
|
||||
|
@ -98,15 +208,37 @@ do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
|
|||
case R_ARM_THM_JUMP19:
|
||||
{
|
||||
/* Thumb instructions can be 16-bit aligned */
|
||||
retval = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr);
|
||||
if (retval != GRUB_ERR_NONE)
|
||||
return retval;
|
||||
grub_int32_t offset;
|
||||
|
||||
if (!(sym_addr & 1))
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
N_("Relocation targeting wrong execution state"));
|
||||
|
||||
sym_addr += grub_arm_thm_jump19_get_offset ((grub_uint16_t *) target);
|
||||
|
||||
offset = sym_addr - (grub_uint32_t) target;
|
||||
|
||||
if (!grub_arm_thm_jump19_check_offset (offset)
|
||||
|| !(sym_addr & 1))
|
||||
{
|
||||
struct trampoline_thumb *tp = mod->gotptr;
|
||||
mod->gotptr = tp + 1;
|
||||
grub_memcpy (tp->template, thumb_template, sizeof (tp->template));
|
||||
tp->neg_addr = -sym_addr - 4;
|
||||
offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1;
|
||||
}
|
||||
|
||||
if (!grub_arm_thm_jump19_check_offset (offset))
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
"trampoline out of range");
|
||||
|
||||
grub_arm_thm_jump19_set_offset ((grub_uint16_t *) target, offset);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
||||
N_("relocation 0x%x is not implemented yet"),
|
||||
reltype);
|
||||
ELF_R_TYPE (rel->r_info));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,77 +262,3 @@ grub_arch_dl_check_header (void *ehdr)
|
|||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that provided ELF header contains reference to a symbol table
|
||||
*/
|
||||
static int
|
||||
has_symtab (Elf_Ehdr * e)
|
||||
{
|
||||
int i;
|
||||
Elf_Shdr *s;
|
||||
|
||||
for (i = 0, s = (Elf_Shdr *) ((grub_uint32_t) e + e->e_shoff);
|
||||
i < e->e_shnum;
|
||||
i++, s = (Elf_Shdr *) ((grub_uint32_t) s + e->e_shentsize))
|
||||
if (s->sh_type == SHT_SYMTAB)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* grub_arch_dl_relocate_symbols():
|
||||
* Only externally visible function in this file.
|
||||
* Locates the relocations section of the ELF object, and calls
|
||||
* do_relocations() to deal with it.
|
||||
*/
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
||||
{
|
||||
Elf_Ehdr *e = ehdr;
|
||||
Elf_Shdr *s;
|
||||
unsigned i;
|
||||
|
||||
if (!has_symtab (e))
|
||||
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
|
||||
|
||||
#define FIRST_SHDR(x) ((Elf_Shdr *) ((grub_addr_t)(x) + (x)->e_shoff))
|
||||
#define NEXT_SHDR(x, y) ((Elf_Shdr *) ((grub_addr_t)(y) + (x)->e_shentsize))
|
||||
|
||||
for (i = 0, s = FIRST_SHDR (e); i < e->e_shnum; i++, s = NEXT_SHDR (e, s))
|
||||
{
|
||||
grub_err_t ret;
|
||||
|
||||
switch (s->sh_type)
|
||||
{
|
||||
case SHT_REL:
|
||||
{
|
||||
/* Relocations, no addends */
|
||||
ret = do_relocations (s, e, mod);
|
||||
if (ret != GRUB_ERR_NONE)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case SHT_NULL:
|
||||
case SHT_PROGBITS:
|
||||
case SHT_SYMTAB:
|
||||
case SHT_STRTAB:
|
||||
case SHT_NOBITS:
|
||||
case SHT_ARM_ATTRIBUTES:
|
||||
break;
|
||||
case SHT_RELA:
|
||||
default:
|
||||
{
|
||||
grub_dprintf ("dl", "unhandled section_type: %d (0x%08x)\n",
|
||||
s->sh_type, s->sh_type);
|
||||
return GRUB_ERR_NOT_IMPLEMENTED_YET;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#undef FIRST_SHDR
|
||||
#undef NEXT_SHDR
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
|
|
@ -38,8 +38,6 @@ grub_arm_reloc_abs32 (Elf32_Word *target, Elf32_Addr sym_addr)
|
|||
tmp = grub_le_to_cpu32 (*target);
|
||||
tmp += sym_addr;
|
||||
*target = grub_cpu_to_le32 (tmp);
|
||||
grub_dprintf ("dl", " %s: reloc_abs32 0x%08x => 0x%08x", __FUNCTION__,
|
||||
(unsigned int) sym_addr, (unsigned int) tmp);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
@ -51,37 +49,16 @@ grub_arm_reloc_abs32 (Elf32_Word *target, Elf32_Addr sym_addr)
|
|||
* little-endian, requiring some additional fiddling. *
|
||||
********************************************************************/
|
||||
|
||||
/*
|
||||
* R_ARM_THM_CALL/THM_JUMP24
|
||||
*
|
||||
* Relocate Thumb (T32) instruction set relative branches:
|
||||
* B.W, BL and BLX
|
||||
*/
|
||||
grub_err_t
|
||||
grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
|
||||
grub_int32_t
|
||||
grub_arm_thm_call_get_offset (grub_uint16_t *target)
|
||||
{
|
||||
grub_int32_t offset, offset_low, offset_high;
|
||||
grub_uint32_t sign, j1, j2, is_blx;
|
||||
grub_uint32_t insword, insmask;
|
||||
grub_uint32_t sign, j1, j2;
|
||||
grub_uint32_t insword;
|
||||
grub_int32_t offset;
|
||||
|
||||
/* Extract instruction word in alignment-safe manner */
|
||||
insword = (grub_le_to_cpu16 (*target) << 16)
|
||||
| (grub_le_to_cpu16(*(target + 1)));
|
||||
insmask = 0xf800d000;
|
||||
|
||||
/* B.W/BL or BLX? Affects range and expected target state */
|
||||
if (((insword >> 12) & 0xd) == 0xc)
|
||||
is_blx = 1;
|
||||
else
|
||||
is_blx = 0;
|
||||
|
||||
/* If BLX, target symbol must be ARM (target address LSB == 0) */
|
||||
if (is_blx && (sym_addr & 1))
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
N_("Relocation targeting wrong execution state"));
|
||||
|
||||
offset_low = -16777216;
|
||||
offset_high = is_blx ? 16777212 : 16777214;
|
||||
|
||||
/* Extract bitfields from instruction words */
|
||||
sign = (insword >> 26) & 1;
|
||||
|
@ -95,22 +72,32 @@ grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
|
|||
if (offset & (1 << 24))
|
||||
offset -= (1 << 25);
|
||||
|
||||
grub_dprintf ("dl", " sym_addr = 0x%08x", sym_addr);
|
||||
return offset;
|
||||
}
|
||||
|
||||
offset += sym_addr;
|
||||
#ifndef GRUB_UTIL
|
||||
offset -= (grub_uint32_t) target;
|
||||
#endif
|
||||
grub_err_t
|
||||
grub_arm_thm_call_set_offset (grub_uint16_t *target, grub_int32_t offset)
|
||||
{
|
||||
grub_uint32_t sign, j1, j2;
|
||||
const grub_uint32_t insmask = 0xf800d000;
|
||||
grub_uint32_t insword;
|
||||
int is_blx;
|
||||
|
||||
grub_dprintf("dl", " %s: target=%p, sym_addr=0x%08x, offset=%d\n",
|
||||
is_blx ? "BLX" : "BL", target, sym_addr, offset);
|
||||
/* Extract instruction word in alignment-safe manner */
|
||||
insword = (grub_le_to_cpu16 (*target) << 16)
|
||||
| (grub_le_to_cpu16(*(target + 1)));
|
||||
|
||||
if ((offset < offset_low) || (offset > offset_high))
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
N_("THM_CALL Relocation out of range."));
|
||||
if (((insword >> 12) & 0xd) == 0xc)
|
||||
is_blx = 1;
|
||||
else
|
||||
is_blx = 0;
|
||||
|
||||
grub_dprintf ("dl", " relative destination = %p",
|
||||
(char *) target + offset);
|
||||
if (!is_blx && !(offset & 1))
|
||||
return grub_error (GRUB_ERR_BAD_MODULE, "bl/b.w targettting ARM");
|
||||
|
||||
/* Transform blx into bl if necessarry. */
|
||||
if (is_blx && (offset & 1))
|
||||
insword |= (1 << 12);
|
||||
|
||||
/* Reassemble instruction word */
|
||||
sign = (offset >> 24) & 1;
|
||||
|
@ -130,21 +117,15 @@ grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
|
|||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* R_ARM_THM_JUMP19
|
||||
*
|
||||
* Relocate conditional Thumb (T32) B<c>.W
|
||||
*/
|
||||
grub_err_t
|
||||
grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
|
||||
grub_int32_t
|
||||
grub_arm_thm_jump19_get_offset (grub_uint16_t *target)
|
||||
{
|
||||
grub_int32_t offset;
|
||||
grub_uint32_t insword, insmask;
|
||||
grub_uint32_t insword;
|
||||
|
||||
/* Extract instruction word in alignment-safe manner */
|
||||
insword = grub_le_to_cpu16 ((*target)) << 16
|
||||
| grub_le_to_cpu16 (*(target + 1));
|
||||
insmask = 0xfbc0d000;
|
||||
insword = (grub_le_to_cpu16 (*target) << 16)
|
||||
| (grub_le_to_cpu16(*(target + 1)));
|
||||
|
||||
/* Extract and sign extend offset */
|
||||
offset = ((insword >> 26) & 1) << 19
|
||||
|
@ -156,18 +137,22 @@ grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
|
|||
if (offset & (1 << 20))
|
||||
offset -= (1 << 21);
|
||||
|
||||
/* Adjust and re-truncate offset */
|
||||
offset += sym_addr;
|
||||
#ifndef GRUB_UTIL
|
||||
offset -= (grub_uint32_t) target;
|
||||
#endif
|
||||
if ((offset > 1048574) || (offset < -1048576))
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
N_("THM_JUMP19 Relocation out of range."));
|
||||
return offset;
|
||||
}
|
||||
|
||||
void
|
||||
grub_arm_thm_jump19_set_offset (grub_uint16_t *target, grub_int32_t offset)
|
||||
{
|
||||
grub_uint32_t insword;
|
||||
const grub_uint32_t insmask = 0xfbc0d000;
|
||||
|
||||
offset >>= 1;
|
||||
offset &= 0xfffff;
|
||||
|
||||
/* Extract instruction word in alignment-safe manner */
|
||||
insword = grub_le_to_cpu16 ((*target)) << 16
|
||||
| grub_le_to_cpu16 (*(target + 1));
|
||||
|
||||
/* Reassemble instruction word and write back */
|
||||
insword &= insmask;
|
||||
insword |= ((offset >> 19) & 1) << 26
|
||||
|
@ -177,9 +162,15 @@ grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
|
|||
| (offset & 0x7ff);
|
||||
*target = grub_cpu_to_le16 (insword >> 16);
|
||||
*(target + 1) = grub_cpu_to_le16 (insword & 0xffff);
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
grub_arm_thm_jump19_check_offset (grub_int32_t offset)
|
||||
{
|
||||
if ((offset > 1048574) || (offset < -1048576))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************
|
||||
|
@ -188,35 +179,38 @@ grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
|
|||
* ARM instructions are 32-bit in size and 32-bit aligned. *
|
||||
***********************************************************/
|
||||
|
||||
/*
|
||||
* R_ARM_JUMP24
|
||||
*
|
||||
* Relocate ARM (A32) B
|
||||
*/
|
||||
grub_err_t
|
||||
grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
|
||||
grub_int32_t
|
||||
grub_arm_jump24_get_offset (grub_uint32_t *target)
|
||||
{
|
||||
grub_uint32_t insword;
|
||||
grub_int32_t offset;
|
||||
|
||||
if (sym_addr & 1)
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
N_("Relocation targeting wrong execution state"));
|
||||
grub_uint32_t insword;
|
||||
|
||||
insword = grub_le_to_cpu32 (*target);
|
||||
|
||||
offset = (insword & 0x00ffffff) << 2;
|
||||
if (offset & 0x02000000)
|
||||
offset -= 0x04000000;
|
||||
offset += sym_addr;
|
||||
#ifndef GRUB_UTIL
|
||||
offset -= (grub_uint32_t) target;
|
||||
#endif
|
||||
return offset;
|
||||
}
|
||||
|
||||
int
|
||||
grub_arm_jump24_check_offset (grub_int32_t offset)
|
||||
{
|
||||
if (offset >= 0x02000000 || offset < -0x02000000)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
grub_arm_jump24_set_offset (grub_uint32_t *target,
|
||||
grub_int32_t offset)
|
||||
{
|
||||
grub_uint32_t insword;
|
||||
|
||||
insword = grub_le_to_cpu32 (*target);
|
||||
|
||||
insword &= 0xff000000;
|
||||
insword |= (offset >> 2) & 0x00ffffff;
|
||||
|
||||
*target = grub_cpu_to_le32 (insword);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,15 @@
|
|||
#include <grub/i18n.h>
|
||||
#include <grub/cpu/reloc.h>
|
||||
|
||||
struct trampoline
|
||||
{
|
||||
#define LDR 0x58000050
|
||||
#define BR 0xd61f0200
|
||||
grub_uint32_t ldr; /* ldr x16, 8 */
|
||||
grub_uint32_t br; /* br x16 */
|
||||
grub_uint64_t addr;
|
||||
};
|
||||
|
||||
/*
|
||||
* Check if EHDR is a valid ELF header.
|
||||
*/
|
||||
|
@ -42,59 +51,76 @@ grub_arch_dl_check_header (void *ehdr)
|
|||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||
|
||||
grub_err_t
|
||||
grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
|
||||
grub_size_t *got)
|
||||
{
|
||||
const Elf_Ehdr *e = ehdr;
|
||||
const Elf_Shdr *s;
|
||||
unsigned i;
|
||||
|
||||
*tramp = 0;
|
||||
*got = 0;
|
||||
|
||||
for (i = 0, s = (const Elf_Shdr *) ((grub_addr_t) e + e->e_shoff);
|
||||
i < e->e_shnum;
|
||||
i++, s = (const Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize))
|
||||
if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA)
|
||||
{
|
||||
const Elf_Rel *rel, *max;
|
||||
|
||||
for (rel = (const Elf_Rel *) ((grub_addr_t) e + s->sh_offset),
|
||||
max = rel + s->sh_size / s->sh_entsize;
|
||||
rel < max;
|
||||
rel = (const Elf_Rel *) ((grub_addr_t) rel + s->sh_entsize))
|
||||
switch (ELF_R_TYPE (rel->r_info))
|
||||
{
|
||||
case R_AARCH64_CALL26:
|
||||
case R_AARCH64_JUMP26:
|
||||
{
|
||||
*tramp += sizeof (struct trampoline);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unified function for both REL and RELA
|
||||
*/
|
||||
static grub_err_t
|
||||
do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||||
Elf_Shdr *s, grub_dl_segment_t seg)
|
||||
{
|
||||
grub_err_t retval;
|
||||
grub_dl_segment_t segment;
|
||||
Elf_Rel *rel;
|
||||
Elf_Rela *rela;
|
||||
Elf_Sym *symbol;
|
||||
int i, entnum;
|
||||
unsigned long long entsize;
|
||||
Elf_Rel *rel, *max;
|
||||
|
||||
/* Find the target segment for this relocation section. */
|
||||
for (segment = mod->segment ; segment != 0 ; segment = segment->next)
|
||||
if (segment->section == relhdr->sh_info)
|
||||
break;
|
||||
if (!segment)
|
||||
return grub_error (GRUB_ERR_EOF, N_("relocation segment not found"));
|
||||
|
||||
rel = (Elf_Rel *) ((grub_addr_t) e + relhdr->sh_offset);
|
||||
rela = (Elf_Rela *) rel;
|
||||
if (relhdr->sh_type == SHT_RELA)
|
||||
entsize = sizeof (Elf_Rela);
|
||||
else
|
||||
entsize = sizeof (Elf_Rel);
|
||||
|
||||
entnum = relhdr->sh_size / entsize;
|
||||
retval = GRUB_ERR_NONE;
|
||||
|
||||
grub_dprintf("dl", "Processing %d relocation entries.\n", entnum);
|
||||
|
||||
/* Step through all relocations */
|
||||
for (i = 0, symbol = mod->symtab; i < entnum; i++)
|
||||
for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
|
||||
max = (Elf_Rel *) ((char *) rel + s->sh_size);
|
||||
rel < max;
|
||||
rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
|
||||
{
|
||||
Elf_Sym *sym;
|
||||
void *place;
|
||||
grub_uint64_t sym_addr, symidx, reltype;
|
||||
grub_uint64_t sym_addr;
|
||||
|
||||
if (rel->r_offset >= segment->size)
|
||||
if (rel->r_offset >= seg->size)
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
"reloc offset is out of the segment");
|
||||
|
||||
symidx = ELF_R_SYM (rel->r_info);
|
||||
reltype = ELF_R_TYPE (rel->r_info);
|
||||
sym = (Elf_Sym *) ((char *) mod->symtab
|
||||
+ mod->symsize * ELF_R_SYM (rel->r_info));
|
||||
|
||||
sym_addr = symbol[symidx].st_value;
|
||||
if (relhdr->sh_type == SHT_RELA)
|
||||
sym_addr += rela->r_addend;
|
||||
sym_addr = sym->st_value;
|
||||
if (s->sh_type == SHT_RELA)
|
||||
sym_addr += ((Elf_Rela *) rel)->r_addend;
|
||||
|
||||
place = (void *) ((grub_addr_t) segment->addr + rel->r_offset);
|
||||
place = (void *) ((grub_addr_t) seg->addr + rel->r_offset);
|
||||
|
||||
switch (reltype)
|
||||
switch (ELF_R_TYPE (rel->r_info))
|
||||
{
|
||||
case R_AARCH64_ABS64:
|
||||
{
|
||||
|
@ -108,92 +134,32 @@ do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod)
|
|||
break;
|
||||
case R_AARCH64_CALL26:
|
||||
case R_AARCH64_JUMP26:
|
||||
retval = grub_arm64_reloc_xxxx26 (place, sym_addr);
|
||||
{
|
||||
grub_int64_t offset = sym_addr - (grub_uint64_t) place;
|
||||
|
||||
if (!grub_arm_64_check_xxxx26_offset (offset))
|
||||
{
|
||||
struct trampoline *tp = mod->trampptr;
|
||||
mod->trampptr = tp + 1;
|
||||
tp->ldr = LDR;
|
||||
tp->br = BR;
|
||||
tp->addr = sym_addr;
|
||||
offset = (grub_uint8_t *) tp - (grub_uint8_t *) place;
|
||||
}
|
||||
|
||||
if (!grub_arm_64_check_xxxx26_offset (offset))
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
N_("Trampoline out of range"));
|
||||
|
||||
grub_arm64_set_xxxx26_offset (place, offset);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
||||
N_("relocation 0x%x is not implemented yet"),
|
||||
reltype);
|
||||
}
|
||||
|
||||
if (retval != GRUB_ERR_NONE)
|
||||
break;
|
||||
|
||||
rel = (Elf_Rel *) ((grub_addr_t) rel + entsize);
|
||||
rela++;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that provided ELF header contains reference to a symbol table
|
||||
*/
|
||||
static int
|
||||
has_symtab (Elf_Ehdr * e)
|
||||
{
|
||||
int i;
|
||||
Elf_Shdr *s;
|
||||
|
||||
for (i = 0, s = (Elf_Shdr *) ((grub_addr_t) e + e->e_shoff);
|
||||
i < e->e_shnum;
|
||||
i++, s = (Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize))
|
||||
if (s->sh_type == SHT_SYMTAB)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* grub_arch_dl_relocate_symbols():
|
||||
* Locates the relocations section of the ELF object, and calls
|
||||
* do_relX() to deal with it.
|
||||
*/
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
||||
{
|
||||
Elf_Ehdr *e = ehdr;
|
||||
Elf_Shdr *s;
|
||||
unsigned i;
|
||||
|
||||
if (!has_symtab (e))
|
||||
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
|
||||
|
||||
#define FIRST_SHDR(x) ((Elf_Shdr *) ((grub_addr_t)(x) + (x)->e_shoff))
|
||||
#define NEXT_SHDR(x, y) ((Elf_Shdr *) ((grub_addr_t)(y) + (x)->e_shentsize))
|
||||
|
||||
for (i = 0, s = FIRST_SHDR (e); i < e->e_shnum; i++, s = NEXT_SHDR (e, s))
|
||||
{
|
||||
grub_err_t ret;
|
||||
|
||||
switch (s->sh_type)
|
||||
{
|
||||
case SHT_REL:
|
||||
case SHT_RELA:
|
||||
{
|
||||
ret = do_relX (s, e, mod);
|
||||
if (ret != GRUB_ERR_NONE)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case SHT_ARM_ATTRIBUTES:
|
||||
case SHT_NOBITS:
|
||||
case SHT_NULL:
|
||||
case SHT_PROGBITS:
|
||||
case SHT_SYMTAB:
|
||||
case SHT_STRTAB:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
grub_dprintf ("dl", "unhandled section_type: %d (0x%08x)\n",
|
||||
s->sh_type, s->sh_type);
|
||||
return GRUB_ERR_NOT_IMPLEMENTED_YET;
|
||||
};
|
||||
ELF_R_TYPE (rel->r_info));
|
||||
}
|
||||
}
|
||||
|
||||
#undef FIRST_SHDR
|
||||
#undef NEXT_SHDR
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
|
|
@ -25,46 +25,31 @@
|
|||
#include <grub/i18n.h>
|
||||
#include <grub/arm64/reloc.h>
|
||||
|
||||
static grub_ssize_t
|
||||
sign_compress_offset (grub_ssize_t offset, int bitpos)
|
||||
{
|
||||
return offset & ((1LL << (bitpos + 1)) - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* grub_arm64_reloc_xxxx26():
|
||||
*
|
||||
* JUMP26/CALL26 relocations for B and BL instructions.
|
||||
*/
|
||||
|
||||
grub_err_t
|
||||
grub_arm64_reloc_xxxx26 (grub_uint32_t *place, Elf64_Addr adjust)
|
||||
int
|
||||
grub_arm_64_check_xxxx26_offset (grub_int64_t offset)
|
||||
{
|
||||
grub_uint32_t insword, insmask;
|
||||
grub_ssize_t offset;
|
||||
const grub_ssize_t offset_low = -(1 << 27), offset_high = (1 << 27) - 1;
|
||||
|
||||
insword = grub_le_to_cpu32 (*place);
|
||||
insmask = 0xfc000000;
|
||||
|
||||
offset = adjust;
|
||||
#ifndef GRUB_UTIL
|
||||
offset -= (grub_addr_t) place;
|
||||
#endif
|
||||
|
||||
if ((offset < offset_low) || (offset > offset_high))
|
||||
{
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
N_("CALL26 Relocation out of range"));
|
||||
}
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
grub_arm64_set_xxxx26_offset (grub_uint32_t *place, grub_int64_t offset)
|
||||
{
|
||||
const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfc000000);
|
||||
|
||||
grub_dprintf ("dl", " reloc_xxxx64 %p %c= 0x%llx\n",
|
||||
place, offset > 0 ? '+' : '-',
|
||||
offset < 0 ? (long long) -(unsigned long long) offset : offset);
|
||||
|
||||
offset = sign_compress_offset (offset, 27) >> 2;
|
||||
|
||||
*place = grub_cpu_to_le32 ((insword & insmask) | offset);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
*place &= insmask;
|
||||
*place |= grub_cpu_to_le32 (offset >> 2) & ~insmask;
|
||||
}
|
||||
|
|
|
@ -229,7 +229,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
|||
unsigned i;
|
||||
Elf_Shdr *s;
|
||||
grub_size_t tsize = 0, talign = 1;
|
||||
#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__)
|
||||
#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
|
||||
grub_size_t tramp;
|
||||
grub_size_t got;
|
||||
grub_err_t err;
|
||||
|
@ -245,7 +245,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
|||
talign = s->sh_addralign;
|
||||
}
|
||||
|
||||
#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__)
|
||||
#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
|
||||
err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -314,12 +314,14 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
|||
mod->segment = seg;
|
||||
}
|
||||
}
|
||||
#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__)
|
||||
#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
|
||||
ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN);
|
||||
mod->tramp = ptr;
|
||||
mod->trampptr = ptr;
|
||||
ptr += tramp;
|
||||
ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_GOT_ALIGN);
|
||||
mod->got = ptr;
|
||||
mod->gotptr = ptr;
|
||||
ptr += got;
|
||||
#endif
|
||||
|
||||
|
@ -352,6 +354,7 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
|
|||
#else
|
||||
mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset);
|
||||
#endif
|
||||
mod->symsize = s->sh_entsize;
|
||||
sym = mod->symtab;
|
||||
size = s->sh_size;
|
||||
entsize = s->sh_entsize;
|
||||
|
@ -561,6 +564,37 @@ grub_dl_flush_cache (grub_dl_t mod)
|
|||
grub_arch_sync_caches (mod->base, mod->sz);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
||||
{
|
||||
Elf_Ehdr *e = ehdr;
|
||||
Elf_Shdr *s;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
|
||||
i < e->e_shnum;
|
||||
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
|
||||
if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA)
|
||||
{
|
||||
grub_dl_segment_t seg;
|
||||
grub_err_t err;
|
||||
|
||||
/* Find the target segment. */
|
||||
for (seg = mod->segment; seg; seg = seg->next)
|
||||
if (seg->section == s->sh_info)
|
||||
break;
|
||||
|
||||
if (seg)
|
||||
{
|
||||
err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
/* Load a module from core memory. */
|
||||
grub_dl_t
|
||||
grub_dl_load_core_noinit (void *addr, grub_size_t size)
|
||||
|
@ -607,7 +641,7 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
|
|||
|| grub_dl_resolve_dependencies (mod, e)
|
||||
|| grub_dl_load_segments (mod, e)
|
||||
|| grub_dl_resolve_symbols (mod, e)
|
||||
|| grub_arch_dl_relocate_symbols (mod, e))
|
||||
|| grub_dl_relocate_symbols (mod, e))
|
||||
{
|
||||
mod->fini = 0;
|
||||
grub_dl_unload (mod);
|
||||
|
|
|
@ -39,10 +39,13 @@ grub_arch_dl_check_header (void *ehdr)
|
|||
}
|
||||
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||||
Elf_Shdr *s, grub_dl_segment_t seg)
|
||||
{
|
||||
(void) mod;
|
||||
(void) ehdr;
|
||||
(void) s;
|
||||
(void) seg;
|
||||
return GRUB_ERR_BAD_MODULE;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,45 +40,15 @@ grub_arch_dl_check_header (void *ehdr)
|
|||
|
||||
/* Relocate symbols. */
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||||
Elf_Shdr *s, grub_dl_segment_t seg)
|
||||
{
|
||||
Elf_Ehdr *e = ehdr;
|
||||
Elf_Shdr *s;
|
||||
Elf_Word entsize;
|
||||
unsigned i;
|
||||
|
||||
/* 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;
|
||||
for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
|
||||
max = (Elf_Rel *) ((char *) rel + s->sh_size);
|
||||
rel < max;
|
||||
rel++)
|
||||
rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
|
||||
{
|
||||
Elf_Word *addr;
|
||||
Elf_Sym *sym;
|
||||
|
@ -89,7 +59,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
|
||||
addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
|
||||
sym = (Elf_Sym *) ((char *) mod->symtab
|
||||
+ entsize * ELF_R_SYM (rel->r_info));
|
||||
+ mod->symsize * ELF_R_SYM (rel->r_info));
|
||||
|
||||
switch (ELF_R_TYPE (rel->r_info))
|
||||
{
|
||||
|
@ -98,8 +68,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
break;
|
||||
|
||||
case R_386_PC32:
|
||||
*addr += (sym->st_value - (Elf_Word) seg->addr
|
||||
- rel->r_offset);
|
||||
*addr += (sym->st_value - (grub_addr_t) addr);
|
||||
break;
|
||||
default:
|
||||
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
||||
|
@ -107,8 +76,6 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
ELF_R_TYPE (rel->r_info));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
|
|
@ -46,51 +46,15 @@ grub_arch_dl_check_header (void *ehdr)
|
|||
|
||||
/* Relocate symbols. */
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||||
Elf_Shdr *s, grub_dl_segment_t seg)
|
||||
{
|
||||
Elf_Ehdr *e = ehdr;
|
||||
Elf_Shdr *s;
|
||||
Elf_Word entsize;
|
||||
unsigned i;
|
||||
grub_uint64_t *gp, *gpptr;
|
||||
struct grub_ia64_trampoline *tr;
|
||||
|
||||
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 *) e + s->sh_offset),
|
||||
max = rel + s->sh_size / s->sh_entsize;
|
||||
for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset),
|
||||
max = (Elf_Rela *) ((char *) rel + s->sh_size);
|
||||
rel < max;
|
||||
rel++)
|
||||
rel = (Elf_Rela *) ((char *) rel + s->sh_entsize))
|
||||
{
|
||||
grub_addr_t addr;
|
||||
Elf_Sym *sym;
|
||||
|
@ -102,7 +66,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
|
||||
addr = (grub_addr_t) seg->addr + rel->r_offset;
|
||||
sym = (Elf_Sym *) ((char *) mod->symtab
|
||||
+ entsize * ELF_R_SYM (rel->r_info));
|
||||
+ mod->symsize * ELF_R_SYM (rel->r_info));
|
||||
|
||||
/* On the PPC the value does not have an explicit
|
||||
addend, add it. */
|
||||
|
@ -113,9 +77,10 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
case R_IA64_PCREL21B:
|
||||
{
|
||||
grub_uint64_t noff;
|
||||
struct grub_ia64_trampoline *tr = mod->trampptr;
|
||||
grub_ia64_make_trampoline (tr, value);
|
||||
noff = ((char *) tr - (char *) (addr & ~3)) >> 4;
|
||||
tr++;
|
||||
mod->trampptr = tr + 1;
|
||||
|
||||
if (noff & ~MASK19)
|
||||
return grub_error (GRUB_ERR_BAD_OS,
|
||||
|
@ -134,7 +99,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
*(grub_uint64_t *) addr += value - addr;
|
||||
break;
|
||||
case R_IA64_GPREL22:
|
||||
grub_ia64_add_value_to_slot_21 (addr, value - (grub_addr_t) gp);
|
||||
grub_ia64_add_value_to_slot_21 (addr, value - (grub_addr_t) mod->base);
|
||||
break;
|
||||
|
||||
case R_IA64_LTOFF22X:
|
||||
|
@ -142,11 +107,13 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
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) gp);
|
||||
gpptr++;
|
||||
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;
|
||||
|
@ -156,8 +123,5 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
ELF_R_TYPE (rel->r_info));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
|
|
@ -58,23 +58,13 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
|
|||
{
|
||||
const Elf_Ehdr *e = ehdr;
|
||||
const Elf_Shdr *s;
|
||||
unsigned i;
|
||||
/* FIXME: suboptimal. */
|
||||
grub_size_t gp_size = 0;
|
||||
unsigned i;
|
||||
|
||||
*tramp = 0;
|
||||
*got = 0;
|
||||
|
||||
/* Find a symbol table. */
|
||||
for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff);
|
||||
i < e->e_shnum;
|
||||
i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize))
|
||||
if (s->sh_type == SHT_SYMTAB)
|
||||
break;
|
||||
|
||||
if (i == e->e_shnum)
|
||||
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
|
||||
|
||||
for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff);
|
||||
i < e->e_shnum;
|
||||
i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize))
|
||||
|
@ -106,62 +96,35 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
|
|||
|
||||
/* Relocate symbols. */
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||||
Elf_Shdr *s, grub_dl_segment_t seg)
|
||||
{
|
||||
Elf_Ehdr *e = ehdr;
|
||||
Elf_Shdr *s;
|
||||
Elf_Word entsize;
|
||||
unsigned i;
|
||||
/* FIXME: suboptimal. */
|
||||
grub_uint32_t *gp, *gpptr;
|
||||
grub_uint32_t gp0;
|
||||
Elf_Ehdr *e = ehdr;
|
||||
|
||||
/* Find a symbol table. */
|
||||
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
|
||||
i < e->e_shnum;
|
||||
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
|
||||
if (s->sh_type == SHT_SYMTAB)
|
||||
break;
|
||||
|
||||
if (i == e->e_shnum)
|
||||
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
|
||||
|
||||
entsize = s->sh_entsize;
|
||||
if (!mod->reginfo)
|
||||
{
|
||||
unsigned i;
|
||||
Elf_Shdr *ri;
|
||||
|
||||
/* Find reginfo. */
|
||||
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
|
||||
for (i = 0, ri = (Elf_Shdr *) ((char *) ehdr + e->e_shoff);
|
||||
i < e->e_shnum;
|
||||
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
|
||||
if (s->sh_type == SHT_MIPS_REGINFO)
|
||||
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 = ((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)
|
||||
{
|
||||
gp0 = mod->reginfo[5];
|
||||
Elf_Rel *rel, *max;
|
||||
|
||||
for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
|
||||
max = rel + s->sh_size / s->sh_entsize;
|
||||
max = (Elf_Rel *) ((char *) rel + s->sh_size);
|
||||
rel < max;
|
||||
rel++)
|
||||
rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
|
||||
{
|
||||
grub_uint8_t *addr;
|
||||
Elf_Sym *sym;
|
||||
|
@ -173,13 +136,13 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
|
||||
addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset);
|
||||
sym = (Elf_Sym *) ((char *) mod->symtab
|
||||
+ entsize * ELF_R_SYM (rel->r_info));
|
||||
+ 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) gp;
|
||||
sym_value = (grub_addr_t) mod->got;
|
||||
else if (sym_value == (grub_addr_t) &_gp_disp_dummy)
|
||||
{
|
||||
sym_value = (grub_addr_t) gp - (grub_addr_t) addr;
|
||||
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. */
|
||||
|
@ -228,7 +191,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
break;
|
||||
case R_MIPS_GPREL32:
|
||||
*(grub_uint32_t *) addr = sym_value
|
||||
+ *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)gp;
|
||||
+ *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)mod->got;
|
||||
break;
|
||||
|
||||
case R_MIPS_26:
|
||||
|
@ -270,15 +233,18 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
*(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 - gp);
|
||||
gpptr++;
|
||||
= sizeof (grub_uint32_t) * (gpptr - (grub_uint32_t *) mod->got);
|
||||
mod->gotptr = gpptr + 1;
|
||||
break;
|
||||
}
|
||||
case R_MIPS_JALR:
|
||||
break;
|
||||
default:
|
||||
|
@ -290,8 +256,6 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
|
|
@ -101,46 +101,15 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
|
|||
|
||||
/* Relocate symbols. */
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||||
Elf_Shdr *s, grub_dl_segment_t seg)
|
||||
{
|
||||
Elf_Ehdr *e = ehdr;
|
||||
Elf_Shdr *s;
|
||||
Elf_Word entsize;
|
||||
unsigned i;
|
||||
struct trampoline *tptr = mod->tramp;
|
||||
|
||||
/* 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;
|
||||
for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset),
|
||||
max = (Elf_Rela *) ((char *) rel + s->sh_size);
|
||||
rel < max;
|
||||
rel++)
|
||||
rel = (Elf_Rela *) ((char *) rel + s->sh_entsize))
|
||||
{
|
||||
Elf_Word *addr;
|
||||
Elf_Sym *sym;
|
||||
|
@ -152,7 +121,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
|
||||
addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
|
||||
sym = (Elf_Sym *) ((char *) mod->symtab
|
||||
+ entsize * ELF_R_SYM (rel->r_info));
|
||||
+ mod->symsize * ELF_R_SYM (rel->r_info));
|
||||
|
||||
/* On the PPC the value does not have an explicit
|
||||
addend, add it. */
|
||||
|
@ -169,12 +138,13 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
|
||||
if (delta << 6 >> 6 != delta)
|
||||
{
|
||||
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);
|
||||
tptr++;
|
||||
mod->trampptr = tptr + 1;
|
||||
}
|
||||
|
||||
if (delta << 6 >> 6 != delta)
|
||||
|
@ -202,8 +172,6 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
ELF_R_TYPE (rel->r_info));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
|
|
@ -42,45 +42,15 @@ grub_arch_dl_check_header (void *ehdr)
|
|||
|
||||
/* Relocate symbols. */
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||||
Elf_Shdr *s, grub_dl_segment_t seg)
|
||||
{
|
||||
Elf_Ehdr *e = ehdr;
|
||||
Elf_Shdr *s;
|
||||
Elf_Word entsize;
|
||||
unsigned i;
|
||||
|
||||
/* 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;
|
||||
for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset),
|
||||
max = (Elf_Rela *) ((char *) rel + s->sh_size);
|
||||
rel < max;
|
||||
rel++)
|
||||
rel = (Elf_Rela *) ((char *) rel + s->sh_entsize))
|
||||
{
|
||||
Elf_Word *addr;
|
||||
Elf_Sym *sym;
|
||||
|
@ -92,7 +62,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
|
||||
addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
|
||||
sym = (Elf_Sym *) ((char *) mod->symtab
|
||||
+ entsize * ELF_R_SYM (rel->r_info));
|
||||
+ mod->symsize * ELF_R_SYM (rel->r_info));
|
||||
|
||||
value = sym->st_value + rel->r_addend;
|
||||
switch (ELF_R_TYPE (rel->r_info) & 0xff)
|
||||
|
@ -140,8 +110,6 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
ELF_R_TYPE (rel->r_info));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
|
|
@ -40,45 +40,15 @@ grub_arch_dl_check_header (void *ehdr)
|
|||
|
||||
/* Relocate symbols. */
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||||
Elf_Shdr *s, grub_dl_segment_t seg)
|
||||
{
|
||||
Elf64_Ehdr *e = ehdr;
|
||||
Elf64_Shdr *s;
|
||||
Elf64_Word entsize;
|
||||
unsigned i;
|
||||
|
||||
/* 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 *) e + s->sh_offset),
|
||||
max = rel + s->sh_size / s->sh_entsize;
|
||||
for (rel = (Elf64_Rela *) ((char *) ehdr + s->sh_offset),
|
||||
max = (Elf64_Rela *) ((char *) rel + s->sh_size);
|
||||
rel < max;
|
||||
rel++)
|
||||
rel = (Elf64_Rela *) ((char *) rel + s->sh_entsize))
|
||||
{
|
||||
Elf64_Word *addr32;
|
||||
Elf64_Xword *addr64;
|
||||
|
@ -91,7 +61,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
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));
|
||||
+ mod->symsize * ELF_R_SYM (rel->r_info));
|
||||
|
||||
switch (ELF_R_TYPE (rel->r_info))
|
||||
{
|
||||
|
@ -133,8 +103,6 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
|
|||
ELF_R_TYPE (rel->r_info));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
|
|
@ -20,8 +20,27 @@
|
|||
#define GRUB_ARM_RELOC_H 1
|
||||
|
||||
grub_err_t grub_arm_reloc_abs32 (grub_uint32_t *addr, Elf32_Addr sym_addr);
|
||||
grub_err_t grub_arm_reloc_jump24 (grub_uint32_t *addr, Elf32_Addr sym_addr);
|
||||
grub_err_t grub_arm_reloc_thm_call (grub_uint16_t *addr, Elf32_Addr sym_addr);
|
||||
grub_err_t grub_arm_reloc_thm_jump19 (grub_uint16_t *addr, Elf32_Addr sym_addr);
|
||||
|
||||
int
|
||||
grub_arm_thm_call_check_offset (grub_int32_t offset, int is_thumb);
|
||||
grub_int32_t
|
||||
grub_arm_thm_call_get_offset (grub_uint16_t *target);
|
||||
grub_err_t
|
||||
grub_arm_thm_call_set_offset (grub_uint16_t *target, grub_int32_t offset);
|
||||
|
||||
grub_int32_t
|
||||
grub_arm_thm_jump19_get_offset (grub_uint16_t *target);
|
||||
void
|
||||
grub_arm_thm_jump19_set_offset (grub_uint16_t *target, grub_int32_t offset);
|
||||
int
|
||||
grub_arm_thm_jump19_check_offset (grub_int32_t offset);
|
||||
|
||||
grub_int32_t
|
||||
grub_arm_jump24_get_offset (grub_uint32_t *target);
|
||||
int
|
||||
grub_arm_jump24_check_offset (grub_int32_t offset);
|
||||
void
|
||||
grub_arm_jump24_set_offset (grub_uint32_t *target,
|
||||
grub_int32_t offset);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#ifndef GRUB_ARM64_RELOC_H
|
||||
#define GRUB_ARM64_RELOC_H 1
|
||||
|
||||
grub_err_t grub_arm64_reloc_xxxx26 (grub_uint32_t *target, Elf64_Addr sym_addr);
|
||||
int grub_arm_64_check_xxxx26_offset (grub_int64_t offset);
|
||||
void
|
||||
grub_arm64_set_xxxx26_offset (grub_uint32_t *place, grub_int64_t offset);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -177,11 +177,17 @@ struct grub_dl
|
|||
grub_dl_dep_t dep;
|
||||
grub_dl_segment_t segment;
|
||||
Elf_Sym *symtab;
|
||||
grub_size_t symsize;
|
||||
void (*init) (struct grub_dl *mod);
|
||||
void (*fini) (void);
|
||||
#if defined (__ia64__) || defined (__powerpc__) || defined (__mips__)
|
||||
#if !defined (__i386__) && !defined (__x86_64__) && !defined (__sparc__)
|
||||
void *got;
|
||||
void *gotptr;
|
||||
void *tramp;
|
||||
void *trampptr;
|
||||
#endif
|
||||
#ifdef __mips__
|
||||
grub_uint32_t *reginfo;
|
||||
#endif
|
||||
void *base;
|
||||
grub_size_t sz;
|
||||
|
@ -232,7 +238,11 @@ grub_err_t grub_dl_register_symbol (const char *name, void *addr,
|
|||
int isfunc, grub_dl_t mod);
|
||||
|
||||
grub_err_t grub_arch_dl_check_header (void *ehdr);
|
||||
grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr);
|
||||
#ifndef GRUB_UTIL
|
||||
grub_err_t
|
||||
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||||
Elf_Shdr *s, grub_dl_segment_t seg);
|
||||
#endif
|
||||
|
||||
#if defined (_mips)
|
||||
#define GRUB_LINKER_HAVE_INIT 1
|
||||
|
@ -256,11 +266,16 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
|
|||
grub_size_t *got);
|
||||
#endif
|
||||
|
||||
#if defined (__powerpc__) || defined (__mips__)
|
||||
#if defined (__powerpc__) || defined (__mips__) || defined (__arm__)
|
||||
#define GRUB_ARCH_DL_TRAMP_ALIGN 4
|
||||
#define GRUB_ARCH_DL_GOT_ALIGN 4
|
||||
#endif
|
||||
|
||||
#if defined (__aarch64__)
|
||||
#define GRUB_ARCH_DL_TRAMP_ALIGN 8
|
||||
#define GRUB_ARCH_DL_GOT_ALIGN 8
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* ! GRUB_DL_H */
|
||||
|
|
|
@ -493,6 +493,85 @@ SUFFIX (count_funcs) (Elf_Ehdr *e, Elf_Shdr *symtab_section,
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef MKIMAGE_ELF32
|
||||
/* Deal with relocation information. This function relocates addresses
|
||||
within the virtual address space starting from 0. So only relative
|
||||
addresses can be fully resolved. Absolute addresses must be relocated
|
||||
again by a PE32 relocator when loaded. */
|
||||
static grub_size_t
|
||||
arm_get_trampoline_size (Elf_Ehdr *e,
|
||||
Elf_Shdr *sections,
|
||||
Elf_Half section_entsize,
|
||||
Elf_Half num_sections,
|
||||
const struct grub_install_image_target_desc *image_target)
|
||||
{
|
||||
Elf_Half i;
|
||||
Elf_Shdr *s;
|
||||
grub_size_t ret = 0;
|
||||
|
||||
for (i = 0, s = sections;
|
||||
i < num_sections;
|
||||
i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
|
||||
if ((s->sh_type == grub_host_to_target32 (SHT_REL)) ||
|
||||
(s->sh_type == grub_host_to_target32 (SHT_RELA)))
|
||||
{
|
||||
Elf_Rela *r;
|
||||
Elf_Word rtab_size, r_size, num_rs;
|
||||
Elf_Off rtab_offset;
|
||||
Elf_Shdr *symtab_section;
|
||||
Elf_Word j;
|
||||
|
||||
symtab_section = (Elf_Shdr *) ((char *) sections
|
||||
+ (grub_target_to_host32 (s->sh_link)
|
||||
* section_entsize));
|
||||
|
||||
rtab_size = grub_target_to_host (s->sh_size);
|
||||
r_size = grub_target_to_host (s->sh_entsize);
|
||||
rtab_offset = grub_target_to_host (s->sh_offset);
|
||||
num_rs = rtab_size / r_size;
|
||||
|
||||
for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset);
|
||||
j < num_rs;
|
||||
j++, r = (Elf_Rela *) ((char *) r + r_size))
|
||||
{
|
||||
Elf_Addr info;
|
||||
Elf_Addr sym_addr;
|
||||
|
||||
info = grub_target_to_host (r->r_info);
|
||||
sym_addr = SUFFIX (get_symbol_address) (e, symtab_section,
|
||||
ELF_R_SYM (info), image_target);
|
||||
|
||||
sym_addr += (s->sh_type == grub_target_to_host32 (SHT_RELA)) ?
|
||||
grub_target_to_host (r->r_addend) : 0;
|
||||
|
||||
switch (ELF_R_TYPE (info))
|
||||
{
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_V4BX:
|
||||
break;
|
||||
case R_ARM_THM_CALL:
|
||||
case R_ARM_THM_JUMP24:
|
||||
case R_ARM_THM_JUMP19:
|
||||
if (!(sym_addr & 1))
|
||||
ret += 8;
|
||||
break;
|
||||
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_JUMP24:
|
||||
if (sym_addr & 1)
|
||||
ret += 16;
|
||||
break;
|
||||
|
||||
default:
|
||||
grub_util_error (_("relocation 0x%x is not implemented yet!"), ELF_R_TYPE (info));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Deal with relocation information. This function relocates addresses
|
||||
within the virtual address space starting from 0. So only relative
|
||||
addresses can be fully resolved. Absolute addresses must be relocated
|
||||
|
@ -512,6 +591,8 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
|
|||
struct grub_ia64_trampoline *tr = (void *) (pe_target + tramp_off);
|
||||
grub_uint64_t *gpptr = (void *) (pe_target + got_off);
|
||||
#define MASK19 ((1 << 19) - 1)
|
||||
#else
|
||||
grub_uint32_t *tr = (void *) (pe_target + tramp_off);
|
||||
#endif
|
||||
|
||||
for (i = 0, s = sections;
|
||||
|
@ -731,13 +812,13 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
|
|||
case R_AARCH64_JUMP26:
|
||||
case R_AARCH64_CALL26:
|
||||
{
|
||||
grub_err_t err;
|
||||
sym_addr -= offset;
|
||||
sym_addr -= SUFFIX (entry_point);
|
||||
err = grub_arm64_reloc_xxxx26((grub_uint32_t *)target,
|
||||
if (!grub_arm_64_check_xxxx26_offset (sym_addr))
|
||||
grub_util_error ("%s", _("CALL26 Relocation out of range"));
|
||||
|
||||
grub_arm64_set_xxxx26_offset((grub_uint32_t *)target,
|
||||
sym_addr);
|
||||
if (err)
|
||||
grub_util_error ("%s", grub_errmsg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -764,32 +845,74 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
|
|||
*target = grub_host_to_target32 (grub_target_to_host32 (*target) + sym_addr);
|
||||
}
|
||||
break;
|
||||
/* Happens when compiled with -march=armv4.
|
||||
Since currently we need at least armv5, keep bx as-is.
|
||||
*/
|
||||
case R_ARM_V4BX:
|
||||
break;
|
||||
case R_ARM_THM_CALL:
|
||||
case R_ARM_THM_JUMP24:
|
||||
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 */
|
||||
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);
|
||||
sym_addr -= offset;
|
||||
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;
|
||||
|
||||
/* Thumb instructions can be 16-bit aligned */
|
||||
err = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr);
|
||||
/* There is no immediate version of bx, only register one... */
|
||||
tr[0] = grub_host_to_target32 (0xe59fc004); /* ldr ip, [pc, #4] */
|
||||
tr[1] = grub_host_to_target32 (0xe08cc00f); /* add ip, ip, pc */
|
||||
tr[2] = grub_host_to_target32 (0xe12fff1c); /* bx ip */
|
||||
tr[3] = grub_host_to_target32 (new_offset | 1);
|
||||
tr += 4;
|
||||
sym_addr = tr_addr;
|
||||
}
|
||||
sym_addr -= offset;
|
||||
err = grub_arm_reloc_jump24 (target,
|
||||
sym_addr);
|
||||
if (err)
|
||||
grub_util_error ("%s", grub_errmsg);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
grub_util_error (_("relocation 0x%x is not implemented yet!"), ELF_R_TYPE (info));
|
||||
break;
|
||||
|
@ -1054,11 +1177,13 @@ SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out,
|
|||
case EM_ARM:
|
||||
switch (ELF_R_TYPE (info))
|
||||
{
|
||||
case R_ARM_V4BX:
|
||||
/* Relative relocations do not require fixup entries. */
|
||||
case R_ARM_JUMP24:
|
||||
case R_ARM_THM_CALL:
|
||||
case R_ARM_THM_JUMP19:
|
||||
case R_ARM_THM_JUMP24:
|
||||
case R_ARM_CALL:
|
||||
{
|
||||
Elf_Addr addr;
|
||||
|
||||
|
@ -1280,7 +1405,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size,
|
|||
Elf_Off section_offset;
|
||||
Elf_Half section_entsize;
|
||||
grub_size_t kernel_size;
|
||||
grub_size_t ia64jmp_off = 0, ia64_toff = 0, ia64_got_off = 0;
|
||||
grub_size_t ia64jmp_off = 0, tramp_off = 0, ia64_got_off = 0;
|
||||
unsigned ia64jmpnum = 0;
|
||||
Elf_Shdr *symtab_section = 0;
|
||||
grub_size_t got = 0;
|
||||
|
@ -1373,6 +1498,21 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size,
|
|||
break;
|
||||
}
|
||||
|
||||
#ifdef MKIMAGE_ELF32
|
||||
if (image_target->elf_target == EM_ARM)
|
||||
{
|
||||
grub_size_t tramp;
|
||||
|
||||
*kernel_sz = ALIGN_UP (*kernel_sz, 16);
|
||||
|
||||
tramp = arm_get_trampoline_size (e, sections, section_entsize,
|
||||
num_sections, image_target);
|
||||
|
||||
tramp_off = *kernel_sz;
|
||||
*kernel_sz += ALIGN_UP (tramp, 16);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MKIMAGE_ELF64
|
||||
if (image_target->elf_target == EM_IA_64)
|
||||
{
|
||||
|
@ -1382,7 +1522,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size,
|
|||
|
||||
grub_ia64_dl_get_tramp_got_size (e, &tramp, &got);
|
||||
|
||||
ia64_toff = *kernel_sz;
|
||||
tramp_off = *kernel_sz;
|
||||
*kernel_sz += ALIGN_UP (tramp, 16);
|
||||
|
||||
ia64jmp_off = *kernel_sz;
|
||||
|
@ -1424,7 +1564,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size,
|
|||
SUFFIX (relocate_addresses) (e, sections, section_addresses,
|
||||
section_entsize,
|
||||
num_sections, strtab,
|
||||
out_img, ia64_toff, ia64_got_off,
|
||||
out_img, tramp_off, ia64_got_off,
|
||||
image_target);
|
||||
|
||||
*reloc_size = SUFFIX (make_reloc_section) (e, reloc_section,
|
||||
|
|
|
@ -834,6 +834,94 @@ struct fixup_block_list
|
|||
struct grub_pe32_fixup_block b;
|
||||
};
|
||||
|
||||
/*
|
||||
* R_ARM_THM_CALL/THM_JUMP24
|
||||
*
|
||||
* Relocate Thumb (T32) instruction set relative branches:
|
||||
* B.W, BL and BLX
|
||||
*/
|
||||
static grub_err_t
|
||||
grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
|
||||
{
|
||||
grub_int32_t offset;
|
||||
|
||||
offset = grub_arm_thm_call_get_offset (target);
|
||||
|
||||
grub_dprintf ("dl", " sym_addr = 0x%08x", sym_addr);
|
||||
|
||||
offset += sym_addr;
|
||||
|
||||
grub_dprintf("dl", " BL*: target=%p, sym_addr=0x%08x, offset=%d\n",
|
||||
target, sym_addr, offset);
|
||||
|
||||
/* Keep traditional (pre-Thumb2) limits on blx. In any case if the kernel
|
||||
is bigger than 2M (currently under 150K) then we probably have a problem
|
||||
somewhere else. */
|
||||
if (offset < -0x200000 || offset >= 0x200000)
|
||||
return grub_error (GRUB_ERR_BAD_MODULE,
|
||||
N_("THM_CALL Relocation out of range."));
|
||||
|
||||
grub_dprintf ("dl", " relative destination = %p",
|
||||
(char *) target + offset);
|
||||
|
||||
return grub_arm_thm_call_set_offset (target, offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* R_ARM_THM_JUMP19
|
||||
*
|
||||
* Relocate conditional Thumb (T32) B<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
|
||||
|
|
Loading…
Reference in a new issue