Support trampoline jumps on powerpc.

* grub-core/kern/dl.c (grub_dl_load_segments) [__powerpc__]: Follow
	__ia64__ path.
	(grub_dl_load_segments): Set mod->sz.
	(grub_dl_flush_cache): Flush whole space occupied by module, not just
	segments.
	* grub-core/kern/ia64/dl.c (nopm): Make const while on it.
	(jump): Likewise.
	* grub-core/kern/powerpc/dl.c (grub_arch_dl_get_tramp_got_size): New
	function.
	(trampoline): New struct.
	(trampoline_template): New const.
	(grub_arch_dl_relocate_symbols): Create trampolines on overflow.
	* include/grub/dl.h (grub_dl): Add sz element.
	[__powerpc__]: Follow __ia64__.
	(GRUB_ARCH_DL_TRAMP_ALIGN): Define on ppc.
	(GRUB_ARCH_DL_GOT_ALIGN): Likewise.
	(GRUB_ARCH_DL_TRAMP_SIZE): Likewise.
	(grub_arch_dl_get_tramp_got_size) [__powerpc__]: New proto.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-11-08 11:23:56 +01:00
parent 3084ede4c7
commit ed64e9e279
5 changed files with 120 additions and 18 deletions

View file

@ -37,6 +37,65 @@ grub_arch_dl_check_header (void *ehdr)
return GRUB_ERR_NONE;
}
void
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;
Elf_Word entsize;
unsigned i;
*tramp = 0;
*got = 0;
/* Find a symbol table. */
for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff);
i < e->e_shnum;
i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize))
if (s->sh_type == SHT_SYMTAB)
break;
if (i == e->e_shnum)
return;
entsize = s->sh_entsize;
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_RELA)
{
const Elf_Rela *rel, *max;
for (rel = (const Elf_Rela *) ((const char *) e + s->sh_offset),
max = rel + s->sh_size / s->sh_entsize;
rel < max;
rel++)
if (ELF_R_TYPE (rel->r_info) == R_PPC_REL24)
(*tramp)++;
}
return;
}
/* For low-endian reverse lis and addr_high as well as ori and addr_low. */
struct trampoline
{
grub_uint32_t lis;
grub_uint32_t ori;
grub_uint32_t mtctr;
grub_uint32_t bctr;
};
static const struct trampoline trampoline_template =
{
0x3c000000,
0x60000000,
0x7c0903a6,
0x4e800420,
};
/* Relocate symbols. */
grub_err_t
@ -46,6 +105,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
Elf_Shdr *s;
Elf_Word entsize;
unsigned i;
struct trampoline *tptr = mod->tramp;
/* Find a symbol table. */
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
@ -106,7 +166,20 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
Elf_Sword delta = value - (Elf_Word) addr;
if (delta << 6 >> 6 != delta)
return grub_error (GRUB_ERR_BAD_MODULE, "relocation overflow");
{
COMPILE_TIME_ASSERT (sizeof (struct trampoline)
== GRUB_ARCH_DL_TRAMP_SIZE);
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;
}