diff options
author | Simon Atanasyan <simon@atanasyan.com> | 2016-05-08 14:08:40 +0000 |
---|---|---|
committer | Simon Atanasyan <simon@atanasyan.com> | 2016-05-08 14:08:40 +0000 |
commit | 8c8a5b5f81fbb88671e6553710d1544da8c6bb86 (patch) | |
tree | dfc0fd1456039919725fecacfec95bec47b5e444 | |
parent | eac58d8f688722371d8af33f3cd46de5fdf4f26d (diff) | |
download | bcm5719-llvm-8c8a5b5f81fbb88671e6553710d1544da8c6bb86.tar.gz bcm5719-llvm-8c8a5b5f81fbb88671e6553710d1544da8c6bb86.zip |
[ELF][MIPS] Handling 'packed' N64 ABI relocations
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
https://dmz-portal.mips.com/mw/images/8/82/007-4658-001.pdf
llvm-svn: 268876
-rw-r--r-- | lld/ELF/Target.cpp | 53 | ||||
-rw-r--r-- | lld/test/ELF/mips-64-rels.s | 46 |
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 |