summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/Target.cpp53
-rw-r--r--lld/test/ELF/mips-64-rels.s46
2 files changed, 85 insertions, 14 deletions
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index 1cfe1e3019a..ccb33132c69 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -1244,6 +1244,9 @@ template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
template <class ELFT>
RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type,
const SymbolBody &S) const {
+ if (ELFT::Is64Bits)
+ // See comment in the calculateMips64RelChain.
+ Type &= 0xff;
switch (Type) {
default:
return R_ABS;
@@ -1443,14 +1446,44 @@ uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf,
}
}
+static std::pair<uint32_t, uint64_t> calculateMips64RelChain(uint32_t Type,
+ uint64_t Val) {
+ // MIPS N64 ABI packs multiple relocations into the single relocation
+ // record. In general, all up to three relocations can have arbitrary
+ // types. In fact, Clang and GCC uses only a few combinations. For now,
+ // we support two of them. That is allow to pass at least all LLVM
+ // test suite cases.
+ // <any relocation> / R_MIPS_SUB / R_MIPS_HI16 | R_MIPS_LO16
+ // <any relocation> / R_MIPS_64 / R_MIPS_NONE
+ // The first relocation is a 'real' relocation which is calculated
+ // using the corresponding symbol's value. The second and the third
+ // relocations used to modify result of the first one: extend it to
+ // 64-bit, extract high or low part etc. For details, see part 2.9 Relocation
+ // at the https://dmz-portal.mips.com/mw/images/8/82/007-4658-001.pdf
+ uint32_t Type2 = (Type >> 8) & 0xff;
+ uint32_t Type3 = (Type >> 16) & 0xff;
+ if (Type2 == R_MIPS_NONE && Type3 == R_MIPS_NONE)
+ return std::make_pair(Type, Val);
+ if (Type2 == R_MIPS_64 && Type3 == R_MIPS_NONE)
+ return std::make_pair(Type2, Val);
+ if (Type2 == R_MIPS_SUB && (Type3 == R_MIPS_HI16 || Type3 == R_MIPS_LO16))
+ return std::make_pair(Type3, -Val);
+ error("unsupported relocations combination " + Twine(Type));
+ return std::make_pair(Type & 0xff, Val);
+}
+
template <class ELFT>
void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
uint64_t Val) const {
const endianness E = ELFT::TargetEndianness;
// Thread pointer and DRP offsets from the start of TLS data area.
// https://www.linux-mips.org/wiki/NPTL
- const uint32_t TPOffset = 0x7000;
- const uint32_t DTPOffset = 0x8000;
+ if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16)
+ Val -= 0x8000;
+ else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16)
+ Val -= 0x7000;
+ if (ELFT::Is64Bits)
+ std::tie(Type, Val) = calculateMips64RelChain(Type, Val);
switch (Type) {
case R_MIPS_32:
case R_MIPS_GPREL32:
@@ -1472,10 +1505,14 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
case R_MIPS_GOT_OFST:
case R_MIPS_LO16:
case R_MIPS_PCLO16:
+ case R_MIPS_TLS_DTPREL_LO16:
+ case R_MIPS_TLS_TPREL_LO16:
writeMipsLo16<E>(Loc, Val);
break;
case R_MIPS_HI16:
case R_MIPS_PCHI16:
+ case R_MIPS_TLS_DTPREL_HI16:
+ case R_MIPS_TLS_TPREL_HI16:
writeMipsHi16<E>(Loc, Val);
break;
case R_MIPS_JALR:
@@ -1496,18 +1533,6 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
case R_MIPS_PC32:
applyMipsPcReloc<E, 32, 0>(Loc, Type, Val);
break;
- case R_MIPS_TLS_DTPREL_HI16:
- writeMipsHi16<E>(Loc, Val - DTPOffset);
- break;
- case R_MIPS_TLS_DTPREL_LO16:
- writeMipsLo16<E>(Loc, Val - DTPOffset);
- break;
- case R_MIPS_TLS_TPREL_HI16:
- writeMipsHi16<E>(Loc, Val - TPOffset);
- break;
- case R_MIPS_TLS_TPREL_LO16:
- writeMipsLo16<E>(Loc, Val - TPOffset);
- break;
default:
fatal("unrecognized reloc " + Twine(Type));
}
diff --git a/lld/test/ELF/mips-64-rels.s b/lld/test/ELF/mips-64-rels.s
new file mode 100644
index 00000000000..7126afc1e59
--- /dev/null
+++ b/lld/test/ELF/mips-64-rels.s
@@ -0,0 +1,46 @@
+# Check handling multiple MIPS N64 ABI relocations packed
+# into the single relocation record.
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t.exe
+# RUN: llvm-objdump -d -s -t %t.exe | FileCheck %s
+# RUN: llvm-readobj -r %t.exe | FileCheck -check-prefix=REL %s
+
+# REQUIRES: mips
+
+# CHECK: __start:
+# CHECK-NEXT: 20000: 3c 1c 00 01 lui $gp, 1
+# ^-- 0x20000 - 0x37ff0
+# ^-- 0 - 0xfffffffffffe8010
+# ^-- %hi(0x17ff0)
+# CHECK: loc:
+# CHECK-NEXT: 20004: 67 9c 7f f0 daddiu $gp, $gp, 32752
+# ^-- 0x20000 - 0x37ff0
+# ^-- 0 - 0xfffffffffffe8010
+# ^-- %lo(0x17ff0)
+
+# CHECK: Contents of section .rodata:
+# CHECK-NEXT: 10190 ffffffff fffe8014
+# ^-- 0x20004 - 0x37ff0 = 0xfffffffffffe8014
+
+# CHECK: 0000000000020004 .text 00000000 loc
+# CHECK: 0000000000037ff0 .got 00000000 .hidden _gp
+# CHECK: 0000000000020000 .text 00000000 __start
+
+# REL: Relocations [
+# REL-NEXT: ]
+
+ .text
+ .global __start
+__start:
+ lui $gp,%hi(%neg(%gp_rel(__start))) # R_MIPS_GPREL16
+ # R_MIPS_SUB
+ # R_MIPS_HI16
+loc:
+ daddiu $gp,$gp,%lo(%neg(%gp_rel(__start))) # R_MIPS_GPREL16
+ # R_MIPS_SUB
+ # R_MIPS_LO16
+
+ .section .rodata,"a",@progbits
+ .gpdword(loc) # R_MIPS_GPREL32
+ # R_MIPS_64
OpenPOWER on IntegriCloud