From 96d6b8d370e653386982b808f10a2a67849f73f1 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 29 Jan 2014 10:44:46 +1100 Subject: [PATCH 17/23] Add ppc64 relocations Add ppc64 relocations Signed-off-by: Ram Pai From: Anton Blanchard --- grub-core/kern/powerpc/dl.c | 185 ++++++++++++++++++++++++++++++++++++++++---- include/grub/elf.h | 3 + 2 files changed, 174 insertions(+), 14 deletions(-) diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c index 7677e5a..cc496d3 100644 --- a/grub-core/kern/powerpc/dl.c +++ b/grub-core/kern/powerpc/dl.c @@ -23,6 +23,20 @@ #include #include +#if defined( __powerpc64__ ) || defined( __powerpc64le__ ) +#define ELFCLASSXX ELFCLASS64 +#define ELFMACHINEXX EM_PPC64 +#else +#define ELFCLASSXX ELFCLASS32 +#define ELFMACHINEXX EM_PPC +#endif + +#if defined( __powerpc64le__ ) +#define ELFDATA2XSB ELFDATA2LSB +#else +#define ELFDATA2XSB ELFDATA2MSB +#endif + /* Check if EHDR is a valid ELF header. */ grub_err_t grub_arch_dl_check_header (void *ehdr) @@ -30,14 +44,86 @@ grub_arch_dl_check_header (void *ehdr) Elf_Ehdr *e = ehdr; /* Check the magic numbers. */ - if (e->e_ident[EI_CLASS] != ELFCLASS32 - || e->e_ident[EI_DATA] != ELFDATA2MSB - || e->e_machine != EM_PPC) + if (e->e_ident[EI_CLASS] != ELFCLASSXX + || e->e_ident[EI_DATA] != ELFDATA2XSB + || e->e_machine != ELFMACHINEXX) return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); return GRUB_ERR_NONE; } + + + +#if defined( __powerpc64le__ ) +struct trampoline +{ + grub_uint32_t std; + grub_uint32_t addis; + grub_uint32_t addi; + grub_uint32_t mtctr; + grub_uint32_t bctr; +}; + +static const struct trampoline trampoline_template = + { + 0xf8410018, /* std r2,24(r1) */ + 0x3d800000, /* addis r12,0,0 */ + 0x398c0000, /* addi r12,r12,0 */ + 0x7d8903a6, /* mtctr r12 */ + 0x4e800420, /* bctr */ + }; + +#define PPC_NOP 0x60000000 +#define RESTORE_TOC 0xe8410018 /* ld r2,24(r1) */ + +#define STO_PPC64_LOCAL_BIT 5 +#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT) + +static unsigned long grub_arch_dl_get_toc (grub_dl_t mod, void *ehdr) +{ + unsigned long i = (unsigned long)grub_dl_find_section_addr(mod, ehdr, ".toc"); + if (!i) + return 0; + + return i; +} + +static inline unsigned int +ppc64_decode_local_entry(unsigned int other) +{ + return ((1 << other) >> 2) << 2; +} + +#define PPC64_LOCAL_ENTRY_OFFSET(other) \ + ppc64_decode_local_entry (((other) & STO_PPC64_LOCAL_MASK) \ + >> STO_PPC64_LOCAL_BIT) + + + +#elif defined( __powerpc64__ ) + +#error "NOT IMPLEMENTED YET" + +static int grub_arch_dl_is_in_opd (grub_dl_t mod, void *ehdr, unsigned long addr) +{ + unsigned long start, end; + Elf_Shdr *s = grub_dl_find_section(ehdr, ".opd"); + + if (!s) + return 0; + + start = (unsigned long)grub_dl_find_section_addr(mod, ehdr, ".opd"); + end = start + s->sh_size; + + if ((start <= addr) && (addr < end)) + return 1; + else + return 0; +} + +#else + /* For low-endian reverse lis and addr_high as well as ori and addr_low. */ struct trampoline { @@ -47,7 +133,7 @@ struct trampoline grub_uint32_t bctr; }; -static const struct trampoline trampoline_template = +static const struct trampoline trampoline_template = { 0x3d800000, 0x618c0000, @@ -55,6 +141,8 @@ static const struct trampoline trampoline_template = 0x4e800420, }; +#endif + #pragma GCC diagnostic ignored "-Wcast-align" grub_err_t @@ -74,14 +162,13 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, 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) == GRUB_ELF_R_PPC_REL24) (*tramp)++; - } *tramp *= sizeof (struct trampoline); @@ -89,12 +176,15 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, return GRUB_ERR_NONE; } +#define PPC_LO(v) ((v) & 0xffff) +#define PPC_HI(v) (((v) >> 16) & 0xffff) +#define PPC_HA(v) PPC_HI ((v) + 0x8000) + /* Relocate symbols. */ grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, Elf_Shdr *s, grub_dl_segment_t seg) { -#ifdef powerpc Elf_Rela *rel, *max; for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset), @@ -104,7 +194,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, { Elf_Word *addr; Elf_Sym *sym; - grub_uint32_t value; + Elf_Addr value; if (seg->size < rel->r_offset) return grub_error (GRUB_ERR_BAD_MODULE, @@ -119,6 +209,76 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, value = sym->st_value + rel->r_addend; switch (ELF_R_TYPE (rel->r_info)) { +#ifdef __powerpc64le__ + case GRUB_ELF_R_PPC_REL24: + { + struct trampoline *tptr = mod->trampptr; + Elf_Sword delta; + if (sym->st_shndx == SHN_UNDEF) + { + grub_memcpy (tptr, &trampoline_template, sizeof (*tptr)); + + tptr->addis |= PPC_HA(value); + tptr->addi |= PPC_LO(value); + + mod->trampptr = tptr + 1; + delta = (grub_uint8_t *) tptr - (grub_uint8_t *) addr; + + if (*(addr+1) != PPC_NOP) + return grub_error (GRUB_ERR_BAD_MODULE, + "Missing NOP after PPC_REL24 got %x", *(addr+1)); + *(addr+1) = RESTORE_TOC; + } else + delta = (grub_uint8_t *)value - (grub_uint8_t *) addr + + PPC64_LOCAL_ENTRY_OFFSET(sym->st_other); + + + if (delta << 6 >> 6 != delta) + return grub_error (GRUB_ERR_BAD_MODULE, + "relocation overflow"); + + *(Elf_Word *) (addr) = (*addr & ~0x03fffffc) | (delta & 0x03fffffc); + } + break; + + case GRUB_ELF_R_PPC64_ADDR64: + *(Elf_Xword *) addr = value; + break; + + case GRUB_ELF_R_PPC64_TOC: + *(Elf_Xword *) addr = grub_arch_dl_get_toc(mod, ehdr); + break; + + case GRUB_ELF_R_PPC64_TOC16_HA: + value -= grub_arch_dl_get_toc(mod, ehdr); + *(Elf_Half *) addr = PPC_HA(value); + break; + + case GRUB_ELF_R_PPC64_TOC16_LO: + value -= grub_arch_dl_get_toc(mod, ehdr); + *(Elf_Half *) addr = PPC_LO(value); + break; + + case GRUB_ELF_R_PPC64_TOC16_LO_DS: + value -= grub_arch_dl_get_toc(mod, ehdr); + if (value & 3) + return grub_error (GRUB_ERR_BAD_MODULE, + "bad TOC16_LO_DS relocation"); + + *(Elf_Half *) addr = ((*(Elf_Half *) addr) & ~0xfffc) | (value & 0xfffc); + break; + + case GRUB_ELF_R_PPC64_REL16_HA: + value -= (unsigned long) addr; + *(Elf_Half *) addr = PPC_HA(value); + break; + + case GRUB_ELF_R_PPC64_REL16_LO: + value -= (unsigned long) addr; + *(Elf_Half *) addr = PPC_LO(value); + break; +#else + case GRUB_ELF_R_PPC_ADDR16_LO: *(Elf_Half *) addr = value; break; @@ -137,7 +297,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, tptr->ori |= ((value) & 0xffff); mod->trampptr = tptr + 1; } - + if (delta << 6 >> 6 != delta) return grub_error (GRUB_ERR_BAD_MODULE, "relocation overflow"); @@ -156,6 +316,8 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, case GRUB_ELF_R_PPC_REL32: *addr = value - (Elf_Word) addr; break; +#endif + default: return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, N_("relocation 0x%x is not implemented yet"), @@ -164,9 +326,4 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, } return GRUB_ERR_NONE; -#else - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - N_("relocation is not implemented yet for module=%llx ehdr=%llx Elf_Shdr=%llx seg=%llx"), - mod, ehdr, s, seg); -#endif } diff --git a/include/grub/elf.h b/include/grub/elf.h index bee7583..224d164 100644 --- a/include/grub/elf.h +++ b/include/grub/elf.h @@ -1998,6 +1998,9 @@ typedef Elf32_Addr Elf32_Conflict; #define GRUB_ELF_R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ #define GRUB_ELF_R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ +#define GRUB_ELF_R_PPC64_REL16_LO 250 +#define GRUB_ELF_R_PPC64_REL16_HA 252 + /* This is a phony reloc to handle any old fashioned TOC16 references that may still be in object files. */ #define GRUB_ELF_R_PPC_TOC16 255 -- 1.8.3.1