summaryrefslogtreecommitdiffstats
path: root/sys/mips/mips/elf_machdep.c
diff options
context:
space:
mode:
authorgonzo <gonzo@FreeBSD.org>2012-01-08 05:44:19 +0000
committergonzo <gonzo@FreeBSD.org>2012-01-08 05:44:19 +0000
commit1c75fb6a4d6b5570918d1d2bf6b8b7c8eed7a2d8 (patch)
tree6b341b0d158eb66c0bd633b6d794e48347f1c62d /sys/mips/mips/elf_machdep.c
parentc8986499b7c4a4fec91ca4971176d0f7f98a9600 (diff)
downloadFreeBSD-src-1c75fb6a4d6b5570918d1d2bf6b8b7c8eed7a2d8.zip
FreeBSD-src-1c75fb6a4d6b5570918d1d2bf6b8b7c8eed7a2d8.tar.gz
Fix relocations for MIPS64:
- Use Elf32_Addr as default, the only field that is 64 bitw wide is R_MIPS_64 - Add R_MIPS_HIGHER and R_MIPS_HGHEST handlers - Handle R_MIPS_HI16 and R_MIPS_LO16 for both .rel and .rela sections
Diffstat (limited to 'sys/mips/mips/elf_machdep.c')
-rw-r--r--sys/mips/mips/elf_machdep.c99
1 files changed, 82 insertions, 17 deletions
diff --git a/sys/mips/mips/elf_machdep.c b/sys/mips/mips/elf_machdep.c
index 85ada0b..a147a7f 100644
--- a/sys/mips/mips/elf_machdep.c
+++ b/sys/mips/mips/elf_machdep.c
@@ -168,25 +168,41 @@ static int
elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
int type, int local, elf_lookup_fn lookup)
{
- Elf_Addr *where = (Elf_Addr *)NULL;
+ Elf32_Addr *where = (Elf32_Addr *)NULL;
Elf_Addr addr;
Elf_Addr addend = (Elf_Addr)0;
Elf_Word rtype = (Elf_Word)0, symidx;
- const Elf_Rel *rel;
+ const Elf_Rel *rel = NULL;
+ const Elf_Rela *rela = NULL;
/*
* Stash R_MIPS_HI16 info so we can use it when processing R_MIPS_LO16
*/
static Elf_Addr ahl;
- static Elf_Addr *where_hi16;
+ static Elf32_Addr *where_hi16;
switch (type) {
case ELF_RELOC_REL:
rel = (const Elf_Rel *)data;
- where = (Elf_Addr *) (relocbase + rel->r_offset);
- addend = *where;
+ where = (Elf32_Addr *) (relocbase + rel->r_offset);
rtype = ELF_R_TYPE(rel->r_info);
symidx = ELF_R_SYM(rel->r_info);
+ switch (rtype) {
+ case R_MIPS_64:
+ addend = *(Elf64_Addr *)where;
+ break;
+ default:
+ addend = *where;
+ break;
+ }
+
+ break;
+ case ELF_RELOC_RELA:
+ rela = (const Elf_Rela *)data;
+ where = (Elf32_Addr *) (relocbase + rela->r_offset);
+ addend = rela->r_addend;
+ rtype = ELF_R_TYPE(rela->r_info);
+ symidx = ELF_R_SYM(rela->r_info);
break;
default:
panic("unknown reloc type %d\n", type);
@@ -202,7 +218,7 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
return (-1);
addr += addend;
if (*where != addr)
- *where = addr;
+ *where = (Elf32_Addr)addr;
break;
case R_MIPS_26: /* ((A << 2) | (P & 0xf0000000) + S) >> 2 */
@@ -220,25 +236,73 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
*where |= addr & 0x03ffffff;
break;
+ case R_MIPS_64: /* S + A */
+ addr = lookup(lf, symidx, 1);
+ if (addr == 0)
+ return (-1);
+ addr += addend;
+ if (*(Elf64_Addr*)where != addr)
+ *(Elf64_Addr*)where = addr;
+ break;
+
case R_MIPS_HI16: /* ((AHL + S) - ((short)(AHL + S)) >> 16 */
- ahl = addend << 16;
- where_hi16 = where;
+ if (rela != NULL) {
+ addr = lookup(lf, symidx, 1);
+ if (addr == 0)
+ return (-1);
+ addr += addend;
+ *where &= 0xffff0000;
+ *where |= ((((long long) addr + 0x8000LL) >> 16) & 0xffff);
+ }
+ else {
+ ahl = addend << 16;
+ where_hi16 = where;
+ }
break;
case R_MIPS_LO16: /* AHL + S */
- ahl += (int16_t)addend;
+ if (rela != NULL) {
+ addr = lookup(lf, symidx, 1);
+ if (addr == 0)
+ return (-1);
+ addr += addend;
+ *where &= 0xffff0000;
+ *where |= addr & 0xffff;
+ }
+ else {
+ ahl += (int16_t)addend;
+ addr = lookup(lf, symidx, 1);
+ if (addr == 0)
+ return (-1);
+
+ addend &= 0xffff0000;
+ addend |= (uint16_t)(ahl + addr);
+ *where = addend;
+
+ addend = *where_hi16;
+ addend &= 0xffff0000;
+ addend |= ((ahl + addr) - (int16_t)(ahl + addr)) >> 16;
+ *where_hi16 = addend;
+ }
+
+ break;
+
+ case R_MIPS_HIGHER: /* %higher(A+S) */
addr = lookup(lf, symidx, 1);
if (addr == 0)
return (-1);
+ addr += addend;
+ *where &= 0xffff0000;
+ *where |= (((long long)addr + 0x80008000LL) >> 32) & 0xffff;
+ break;
- addend &= 0xffff0000;
- addend |= (uint16_t)(ahl + addr);
- *where = addend;
-
- addend = *where_hi16;
- addend &= 0xffff0000;
- addend |= ((ahl + addr) - (int16_t)(ahl + addr)) >> 16;
- *where_hi16 = addend;
+ case R_MIPS_HIGHEST: /* %highest(A+S) */
+ addr = lookup(lf, symidx, 1);
+ if (addr == 0)
+ return (-1);
+ addr += addend;
+ *where &= 0xffff0000;
+ *where |= (((long long)addr + 0x800080008000LL) >> 48) & 0xffff;
break;
default:
@@ -246,6 +310,7 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
rtype);
return (-1);
}
+
return(0);
}
OpenPOWER on IntegriCloud