Add support for a new magic symbol _gp_disp on mips to handle PIC

binaries.
This commit is contained in:
Vladimir Serbinenko 2013-11-22 04:56:10 +01:00
parent 6f4a19f59f
commit c36c73f681
3 changed files with 26 additions and 10 deletions

View file

@ -1,3 +1,8 @@
2013-11-22 Vladimir Serbinenko <phcoder@gmail.com>
Add support for a new magic symbol _gp_disp on mips to handle PIC
binaries.
2013-11-22 Vladimir Serbinenko <phcoder@gmail.com> 2013-11-22 Vladimir Serbinenko <phcoder@gmail.com>
Use $t9 for indirect calls from asm to C as PIC ABI requires. Use $t9 for indirect calls from asm to C as PIC ABI requires.

View file

@ -23,7 +23,7 @@ BEGIN {
} else if ($1 == "undefined") { } else if ($1 == "undefined") {
if ($3 in symtab) if ($3 in symtab)
modtab[$2] = modtab[$2] " " symtab[$3]; 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"; printf "%s in %s is not defined\n", $3, $2 >"/dev/stderr";
error++; error++;
} }

View file

@ -27,6 +27,7 @@
/* Dummy __gnu_local_gp. Resolved by linker. */ /* Dummy __gnu_local_gp. Resolved by linker. */
static char __gnu_local_gp_dummy; static char __gnu_local_gp_dummy;
static char _gp_disp_dummy;
/* Check if EHDR is a valid ELF header. */ /* Check if EHDR is a valid ELF header. */
grub_err_t grub_err_t
@ -164,6 +165,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
{ {
grub_uint8_t *addr; grub_uint8_t *addr;
Elf_Sym *sym; Elf_Sym *sym;
grub_uint32_t sym_value;
if (seg->size < rel->r_offset) if (seg->size < rel->r_offset)
return grub_error (GRUB_ERR_BAD_MODULE, 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); addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset);
sym = (Elf_Sym *) ((char *) mod->symtab sym = (Elf_Sym *) ((char *) mod->symtab
+ entsize * ELF_R_SYM (rel->r_info)); + entsize * ELF_R_SYM (rel->r_info));
if (sym->st_value == (grub_addr_t) &__gnu_local_gp_dummy) sym_value = sym->st_value;
sym->st_value = (grub_addr_t) gp; 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)) switch (ELF_R_TYPE (rel->r_info))
{ {
case R_MIPS_HI16: 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. treated as signed. Hence add 0x8000 to compensate.
*/ */
value = (*(grub_uint16_t *) addr << 16) value = (*(grub_uint16_t *) addr << 16)
+ sym->st_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)
@ -211,13 +221,13 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
#ifdef GRUB_CPU_WORDS_BIGENDIAN #ifdef GRUB_CPU_WORDS_BIGENDIAN
addr += 2; addr += 2;
#endif #endif
*(grub_uint16_t *) addr += (sym->st_value) & 0xffff; *(grub_uint16_t *) addr += sym_value & 0xffff;
break; break;
case R_MIPS_32: case R_MIPS_32:
*(grub_uint32_t *) addr += sym->st_value; *(grub_uint32_t *) addr += sym_value;
break; break;
case R_MIPS_GPREL32: 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; + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)gp;
break; break;
@ -227,7 +237,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
grub_uint32_t raw; grub_uint32_t raw;
raw = (*(grub_uint32_t *) addr) & 0x3ffffff; raw = (*(grub_uint32_t *) addr) & 0x3ffffff;
value = raw << 2; value = raw << 2;
value += sym->st_value; value += sym_value;
raw = (value >> 2) & 0x3ffffff; raw = (value >> 2) & 0x3ffffff;
*(grub_uint32_t *) addr = *(grub_uint32_t *) addr =
@ -240,7 +250,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
#ifdef GRUB_CPU_WORDS_BIGENDIAN #ifdef GRUB_CPU_WORDS_BIGENDIAN
addr += 2; addr += 2;
#endif #endif
*gpptr = sym->st_value + *(grub_uint16_t *) addr; *gpptr = sym_value + *(grub_uint16_t *) addr;
*(grub_uint16_t *) addr *(grub_uint16_t *) addr
= sizeof (grub_uint32_t) * (gpptr - gp); = sizeof (grub_uint32_t) * (gpptr - gp);
gpptr++; gpptr++;
@ -266,5 +276,6 @@ void
grub_arch_dl_init_linker (void) grub_arch_dl_init_linker (void)
{ {
grub_dl_register_symbol ("__gnu_local_gp", &__gnu_local_gp_dummy, 0, 0); grub_dl_register_symbol ("__gnu_local_gp", &__gnu_local_gp_dummy, 0, 0);
grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0);
} }