From c36c73f681a0a5df49bacde4662a2b1b8058081a Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Fri, 22 Nov 2013 04:56:10 +0100 Subject: [PATCH] Add support for a new magic symbol _gp_disp on mips to handle PIC binaries. --- ChangeLog | 5 +++++ grub-core/genmoddep.awk | 2 +- grub-core/kern/mips/dl.c | 29 ++++++++++++++++++++--------- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 856185496..8fac253ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2013-11-22 Vladimir Serbinenko + + Add support for a new magic symbol _gp_disp on mips to handle PIC + binaries. + 2013-11-22 Vladimir Serbinenko Use $t9 for indirect calls from asm to C as PIC ABI requires. diff --git a/grub-core/genmoddep.awk b/grub-core/genmoddep.awk index 9ffea7309..dc555b93c 100644 --- a/grub-core/genmoddep.awk +++ b/grub-core/genmoddep.awk @@ -23,7 +23,7 @@ BEGIN { } else if ($1 == "undefined") { if ($3 in symtab) modtab[$2] = modtab[$2] " " symtab[$3]; - else if ($3 != "__gnu_local_gp") { + else if (and ($3 != "__gnu_local_gp", $3 != "_gp_disp")) { printf "%s in %s is not defined\n", $3, $2 >"/dev/stderr"; error++; } diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c index 36ca9a225..111241831 100644 --- a/grub-core/kern/mips/dl.c +++ b/grub-core/kern/mips/dl.c @@ -27,6 +27,7 @@ /* Dummy __gnu_local_gp. Resolved by linker. */ static char __gnu_local_gp_dummy; +static char _gp_disp_dummy; /* Check if EHDR is a valid ELF header. */ grub_err_t @@ -164,6 +165,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) { 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, @@ -172,9 +174,17 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset); sym = (Elf_Sym *) ((char *) mod->symtab + entsize * ELF_R_SYM (rel->r_info)); - if (sym->st_value == (grub_addr_t) &__gnu_local_gp_dummy) - sym->st_value = (grub_addr_t) gp; - + 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: @@ -190,7 +200,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) treated as signed. Hence add 0x8000 to compensate. */ value = (*(grub_uint16_t *) addr << 16) - + sym->st_value + 0x8000; + + sym_value + 0x8000; for (rel2 = rel + 1; rel2 < max; rel2++) if (ELF_R_SYM (rel2->r_info) == ELF_R_SYM (rel->r_info) @@ -211,13 +221,13 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) #ifdef GRUB_CPU_WORDS_BIGENDIAN addr += 2; #endif - *(grub_uint16_t *) addr += (sym->st_value) & 0xffff; + *(grub_uint16_t *) addr += sym_value & 0xffff; break; case R_MIPS_32: - *(grub_uint32_t *) addr += sym->st_value; + *(grub_uint32_t *) addr += sym_value; break; case R_MIPS_GPREL32: - *(grub_uint32_t *) addr = sym->st_value + *(grub_uint32_t *) addr = sym_value + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)gp; break; @@ -227,7 +237,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) grub_uint32_t raw; raw = (*(grub_uint32_t *) addr) & 0x3ffffff; value = raw << 2; - value += sym->st_value; + value += sym_value; raw = (value >> 2) & 0x3ffffff; *(grub_uint32_t *) addr = @@ -240,7 +250,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) #ifdef GRUB_CPU_WORDS_BIGENDIAN addr += 2; #endif - *gpptr = sym->st_value + *(grub_uint16_t *) addr; + *gpptr = sym_value + *(grub_uint16_t *) addr; *(grub_uint16_t *) addr = sizeof (grub_uint32_t) * (gpptr - gp); gpptr++; @@ -266,5 +276,6 @@ void grub_arch_dl_init_linker (void) { grub_dl_register_symbol ("__gnu_local_gp", &__gnu_local_gp_dummy, 0, 0); + grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0); }