Index: lld/ELF/Arch/SPARCV9.cpp =================================================================== --- lld/ELF/Arch/SPARCV9.cpp +++ lld/ELF/Arch/SPARCV9.cpp @@ -25,6 +25,12 @@ SPARCV9(); RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; + RelExpr adjustGotOffExpr(RelType type, const Symbol &sym, int64_t addend, + const uint8_t *loc) const override; + RelType getDynRel(RelType type) const; + void relaxGot(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void writeGotHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const override; void relocate(uint8_t *loc, const Relocation &rel, @@ -45,6 +51,43 @@ defaultCommonPageSize = 8192; defaultMaxPageSize = 0x100000; defaultImageBase = 0x100000; + + // .got[0] = _DYNAMIC + gotBaseSymInGotPlt = false; + gotHeaderEntriesNum = 1; + + needsGotPlt = false; +} + +void SPARCV9::writeGotHeader(uint8_t *buf) const { + write64(buf, mainPart->dynamic->getVA()); +} + +RelType SPARCV9::getDynRel(RelType type) const { + if (type == R_SPARC_64) + return type; + return R_SPARC_NONE; +} + +RelExpr SPARCV9::adjustGotOffExpr(RelType type, const Symbol &sym, + int64_t addend, const uint8_t *loc) const { + return sym.isLocal() ? R_RELAX_GOT_OFF : R_GOT_OFF; +} + +void SPARCV9::relaxGot(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + switch (rel.type) { + case R_SPARC_GOTDATA_OP_LOX10: + case R_SPARC_GOTDATA_OP_HIX22: + relocate(loc, rel, val); + break; + case R_SPARC_GOTDATA_OP: + // ldx [%rs1 + %rs2], %rd -> add %rs1, %rs2, %rd + write32(loc, (read32(loc) & 0x3e07c01f) | 0x80000000); + break; + default: + llvm_unreachable("unexpected relocation kind"); + } } RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, @@ -69,7 +112,7 @@ case R_SPARC_WDISP30: return R_PC; case R_SPARC_GOT10: - return R_GOT_OFF; + case R_SPARC_GOT13: case R_SPARC_GOT22: return R_GOT_OFF; case R_SPARC_WPLT30: @@ -79,6 +122,10 @@ case R_SPARC_TLS_LE_HIX22: case R_SPARC_TLS_LE_LOX10: return R_TPREL; + case R_SPARC_GOTDATA_OP_LOX10: + case R_SPARC_GOTDATA_OP_HIX22: + case R_SPARC_GOTDATA_OP: + return R_GOT_OFF; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); @@ -127,6 +174,11 @@ checkInt(loc, val, 21, rel); write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff)); break; + case R_SPARC_GOT13: + // V-simm13 + checkInt(loc, val, 13, rel); + write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff)); + break; case R_SPARC_GOT10: case R_SPARC_PC10: // T-simm10 @@ -167,6 +219,23 @@ // T-imm22 write32be(loc, (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff)); break; + case R_SPARC_GOTDATA_OP_LOX10: + if ((int64_t)val < 0) + write32be(loc, + (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1C00); + else + write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff)); + break; + case R_SPARC_GOTDATA_OP_HIX22: + if ((int64_t)val < 0) + write32be(loc, + (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff)); + else + write32be(loc, + (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); + break; + case R_SPARC_GOTDATA_OP: + break; case R_SPARC_TLS_LE_LOX10: // T-simm13 write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1C00); @@ -176,7 +245,7 @@ } } -void SPARCV9::writePlt(uint8_t *buf, const Symbol & /*sym*/, +void SPARCV9::writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const { const uint8_t pltData[] = { 0x03, 0x00, 0x00, 0x00, // sethi (. - .PLT0), %g1 Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -926,7 +926,7 @@ // Otherwise use the psABI defined relocation entry format. uint16_t m = config->emachine; return m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON || m == EM_PPC || - m == EM_PPC64 || m == EM_RISCV || m == EM_X86_64; + m == EM_PPC64 || m == EM_RISCV || m == EM_SPARCV9 || m == EM_X86_64; } static void parseClangOption(StringRef opt, const Twine &msg) { Index: lld/ELF/InputSection.cpp =================================================================== --- lld/ELF/InputSection.cpp +++ lld/ELF/InputSection.cpp @@ -705,6 +705,7 @@ return in.gotPlt->getVA() + a - p; case R_GOTREL: case R_PPC64_RELAX_TOC: + case R_RELAX_GOT_OFF: return sym.getVA(a) - in.got->getVA(); case R_GOTPLTREL: return sym.getVA(a) - in.gotPlt->getVA(); @@ -1032,6 +1033,7 @@ for (const Relocation &rel : relocations) { if (rel.expr == R_NONE) continue; + uint64_t offset = rel.offset; uint8_t *bufLoc = buf + offset; RelType type = rel.type; @@ -1047,6 +1049,7 @@ switch (expr) { case R_RELAX_GOT_PC: case R_RELAX_GOT_PC_NOPIC: + case R_RELAX_GOT_OFF: target->relaxGot(bufLoc, rel, targetVA); break; case R_PPC64_RELAX_GOT_PC: { Index: lld/ELF/Relocations.h =================================================================== --- lld/ELF/Relocations.h +++ lld/ELF/Relocations.h @@ -47,6 +47,7 @@ R_PLT_PC, R_RELAX_GOT_PC, R_RELAX_GOT_PC_NOPIC, + R_RELAX_GOT_OFF, R_RELAX_TLS_GD_TO_IE, R_RELAX_TLS_GD_TO_IE_ABS, R_RELAX_TLS_GD_TO_IE_GOT_OFF, Index: lld/ELF/Relocations.cpp =================================================================== --- lld/ELF/Relocations.cpp +++ lld/ELF/Relocations.cpp @@ -125,6 +125,7 @@ } namespace { +typedef unsigned __int128 uint128_t; // Build a bitmask with one bit set for each RelExpr. // // Constexpr function arguments can't be used in static asserts, so we @@ -132,16 +133,16 @@ // But function template partial specializations don't exist (needed // for base case of the recursion), so we need a dummy struct. template struct RelExprMaskBuilder { - static inline uint64_t build() { return 0; } + static inline uint128_t build() { return 0; } }; // Specialization for recursive case. template struct RelExprMaskBuilder { - static inline uint64_t build() { - static_assert(0 <= Head && Head < 64, - "RelExpr is too large for 64-bit mask!"); - return (uint64_t(1) << Head) | RelExprMaskBuilder::build(); + static inline uint128_t build() { + static_assert(0 <= Head && Head < 128, + "RelExpr is too large for 128-bit mask!"); + return (uint128_t(1) << Head) | RelExprMaskBuilder::build(); } }; } // namespace @@ -151,9 +152,9 @@ // RelExpr's as a constant bit mask and test for membership with a // couple cheap bitwise operations. template bool oneof(RelExpr expr) { - assert(0 <= expr && (int)expr < 64 && - "RelExpr is too large for 64-bit mask!"); - return (uint64_t(1) << expr) & RelExprMaskBuilder::build(); + assert(0 <= expr && (int)expr < 128 && + "RelExpr is too large for 128-bit mask!"); + return (uint128_t(1) << expr) & RelExprMaskBuilder::build(); } // This function is similar to the `handleTlsRelocation`. MIPS does not @@ -390,7 +391,8 @@ static bool isRelExpr(RelExpr expr) { return oneof(expr); + R_RISCV_PC_INDIRECT, R_PPC64_RELAX_GOT_PC, R_RELAX_GOT_OFF>( + expr); } // Returns true if a given relocation can be computed at link-time. @@ -1068,11 +1070,21 @@ template static void addPltEntry(PltSection *plt, GotPltSection *gotPlt, - RelocationBaseSection *rel, RelType type, Symbol &sym) { - plt->addEntry(sym); - gotPlt->addEntry(sym); - rel->addReloc( - {type, gotPlt, sym.getGotPltOffset(), !sym.isPreemptible, &sym, 0}); + RelocationBaseSection *rel, bool indirect, + Symbol &sym) { + RelType type = indirect ? target->iRelativeRel : target->pltRel; + + if (target->needsGotPlt) { + plt->addEntry(sym); + gotPlt->addEntry(sym); + // The relocation is applied to the .got.plt entry. + rel->addReloc( + {type, gotPlt, sym.getGotPltOffset(), !sym.isPreemptible, &sym, 0}); + } else { + plt->addEntry(sym); + // The relocation is applied to the .plt entry. + rel->addReloc({type, plt, sym.getPltOffset(), !sym.isPreemptible, &sym, 0}); + } } static void addGotEntry(Symbol &sym) { @@ -1245,7 +1257,7 @@ "' cannot be preempted; recompile with -fPIE" + getLocation(sec, sym, offset)); if (!sym.isInPlt()) - addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym); + addPltEntry(in.plt, in.gotPlt, in.relaPlt, false, sym); if (!sym.isDefined()) { replaceWithDefined( sym, in.plt, @@ -1363,7 +1375,7 @@ // runtime, because the main executable is always at the beginning of a search // list. We can leverage that fact. if (!sym.isPreemptible && (!sym.isGnuIFunc() || config->zIfuncNoplt)) { - if (expr != R_GOT_PC) { + if (expr != R_GOT_PC && expr != R_GOT_OFF) { // The 0x8000 bit of r_addend of R_PPC_PLTREL24 is used to choose call // stub type. It should be ignored if optimized to R_PC. if (config->emachine == EM_PPC && expr == R_PPC32_PLTREL) @@ -1376,7 +1388,10 @@ type == R_HEX_GD_PLT_B32_PCREL_X))) expr = fromPlt(expr); } else if (!isAbsoluteValue(sym)) { - expr = target->adjustGotPcExpr(type, addend, relocatedAddr); + if (expr == R_GOT_PC) + expr = target->adjustGotPcExpr(type, addend, relocatedAddr); + else if (expr == R_GOT_OFF) + expr = target->adjustGotOffExpr(type, sym, addend, relocatedAddr); } } @@ -1419,7 +1434,7 @@ if (!sym.isGnuIFunc() || sym.isPreemptible) { // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol. if (needsPlt(expr) && !sym.isInPlt()) - addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym); + addPltEntry(in.plt, in.gotPlt, in.relaPlt, false, sym); // Create a GOT slot if a relocation needs GOT. if (needsGot(expr)) { @@ -1489,8 +1504,7 @@ // that's really needed to create the IRELATIVE is the section and value, // so ideally we should just need to copy those. auto *directSym = make(cast(sym)); - addPltEntry(in.iplt, in.igotPlt, in.relaIplt, target->iRelativeRel, - *directSym); + addPltEntry(in.iplt, in.igotPlt, in.relaIplt, true, *directSym); sym.pltIndex = directSym->pltIndex; } if (needsGot(expr)) { @@ -2113,8 +2127,7 @@ for (Relocation &rel : isec->relocations) if (rel.sym->type == llvm::ELF::STT_TLS && rel.expr == R_PLT_PC) { if (needEntry) { - addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel, - *sym); + addPltEntry(in.plt, in.gotPlt, in.relaPlt, false, *sym); needEntry = false; } rel.sym = sym; Index: lld/ELF/Symbols.h =================================================================== --- lld/ELF/Symbols.h +++ lld/ELF/Symbols.h @@ -199,6 +199,7 @@ uint64_t getGotPltOffset() const; uint64_t getGotPltVA() const; uint64_t getPltVA() const; + uint64_t getPltOffset() const; uint64_t getSize() const; OutputSection *getOutputSection() const; Index: lld/ELF/Symbols.cpp =================================================================== --- lld/ELF/Symbols.cpp +++ lld/ELF/Symbols.cpp @@ -190,6 +190,12 @@ return outVA; } +uint64_t Symbol::getPltOffset() const { + if (isInIplt) + return pltIndex * target->ipltEntrySize; + return target->pltHeaderSize + pltIndex * target->pltEntrySize; +} + uint64_t Symbol::getSize() const { if (const auto *dr = dyn_cast(this)) return dr->size; Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -1624,11 +1624,17 @@ if (in.relaPlt == this) { getParent()->flags |= ELF::SHF_INFO_LINK; - getParent()->info = in.gotPlt->getParent()->sectionIndex; + if (target->needsGotPlt) + getParent()->info = in.gotPlt->getParent()->sectionIndex; + else + getParent()->info = in.plt->getParent()->sectionIndex; } if (in.relaIplt == this) { getParent()->flags |= ELF::SHF_INFO_LINK; - getParent()->info = in.igotPlt->getParent()->sectionIndex; + if (target->needsGotPlt) + getParent()->info = in.igotPlt->getParent()->sectionIndex; + else + getParent()->info = in.iplt->getParent()->sectionIndex; } } @@ -2572,6 +2578,11 @@ name = ".glink"; alignment = 4; } + + // The PLT needs to be writable on SPARC as the dynamic linker will + // modify the instructions in the PLT entries. + if (config->emachine == EM_SPARCV9) + this->flags |= SHF_WRITE; } void IpltSection::writeTo(uint8_t *buf) { Index: lld/ELF/Target.h =================================================================== --- lld/ELF/Target.h +++ lld/ELF/Target.h @@ -134,6 +134,8 @@ // On PPC ELF V2 abi, the first entry in the .got is the .TOC. unsigned gotHeaderEntriesNum = 0; + bool needsGotPlt = true; + bool needsThunks = false; // A 4-byte field corresponding to one or more trap instructions, used to pad @@ -152,6 +154,8 @@ virtual RelExpr adjustTlsExpr(RelType type, RelExpr expr) const; virtual RelExpr adjustGotPcExpr(RelType type, int64_t addend, const uint8_t *loc) const; + virtual RelExpr adjustGotOffExpr(RelType type, const Symbol &sym, + int64_t addend, const uint8_t *loc) const; virtual void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const; virtual void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, Index: lld/ELF/Target.cpp =================================================================== --- lld/ELF/Target.cpp +++ lld/ELF/Target.cpp @@ -159,6 +159,12 @@ return R_GOT_PC; } +RelExpr TargetInfo::adjustGotOffExpr(RelType type, const Symbol &sym, + int64_t addend, + const uint8_t *data) const { + return R_GOT_OFF; +} + void TargetInfo::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const { llvm_unreachable("Should not have claimed to be relaxable"); Index: lld/test/ELF/sparcv9-reloc-got.s =================================================================== --- /dev/null +++ lld/test/ELF/sparcv9-reloc-got.s @@ -0,0 +1,24 @@ +# REQUIRES: sparc +# RUN: llvm-mc -filetype=obj -triple=sparcv9 %s -o %t.o +# RUN: ld.lld -shared %t.o -o %t.so +# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck %s +# RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=RELOC %s + +# 13-bit PIC + +# CHECK: ldx [%l7+8], %o0 +ldx [%l7 + %got13(x)], %o0 + +# 32-bit PIC + +# CHECK-NEXT: sethi 0, %o1 +# CHECK-NEXT: add %o1, 16, %o1 +# CHECK-NEXT: ldx [%l7+%o1], %o0 +sethi %got22(y), %o1 +add %o1, %got10(y), %o1 +ldx [%l7 + %o1], %o0 + +# RELOC: .rela.dyn { +# RELOC-NEXT: R_SPARC_GLOB_DAT x 0x0 +# RELOC-NEXT: R_SPARC_GLOB_DAT y 0x0 +# RELOC-NEXT: }