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>
|
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.
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue