/* 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
{
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,
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:
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)
#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;
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 =
#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++;
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);
}