From b165c71bf58c20e37466739e6ce16f6fb29b473b Mon Sep 17 00:00:00 2001 From: Alexey Karyakin Date: Fri, 1 Aug 2025 11:34:30 -0700 Subject: [PATCH 1/3] [RISCV] Add support for TLSDESC Signed-off-by: Alexey Karyakin --- lib/Target/GNULDBackend.cpp | 6 + lib/Target/RISCV/RISCVGOT.cpp | 6 + lib/Target/RISCV/RISCVGOT.h | 20 +++ lib/Target/RISCV/RISCVLDBackend.cpp | 149 +++++++++++++++- lib/Target/RISCV/RISCVLDBackend.h | 24 ++- lib/Target/RISCV/RISCVRelocationHelper.h | 13 +- lib/Target/RISCV/RISCVRelocationInfo.cpp | 40 +++++ lib/Target/RISCV/RISCVRelocator.cpp | 74 +++++++- lib/Writers/ELFObjectWriter.cpp | 2 +- test/lit.cfg | 7 +- test/lld/ELF/Inputs/riscv-tlsdesc-relax/a.s | 63 +++++++ test/lld/ELF/Inputs/riscv-tlsdesc-relax/c.s | 4 + test/lld/ELF/Inputs/riscv-tlsdesc/a.s | 36 ++++ test/lld/ELF/Inputs/riscv-tlsdesc/a1.s | 14 ++ test/lld/ELF/Inputs/riscv-tlsdesc/c.s | 3 + test/lld/ELF/Inputs/riscv-tlsdesc/d.s | 14 ++ test/lld/ELF/riscv-tlsdesc-relax.test | 178 ++++++++++++++++++++ test/lld/ELF/riscv-tlsdesc.test | 156 +++++++++++++++++ 18 files changed, 792 insertions(+), 17 deletions(-) create mode 100644 test/lld/ELF/Inputs/riscv-tlsdesc-relax/a.s create mode 100644 test/lld/ELF/Inputs/riscv-tlsdesc-relax/c.s create mode 100644 test/lld/ELF/Inputs/riscv-tlsdesc/a.s create mode 100644 test/lld/ELF/Inputs/riscv-tlsdesc/a1.s create mode 100644 test/lld/ELF/Inputs/riscv-tlsdesc/c.s create mode 100644 test/lld/ELF/Inputs/riscv-tlsdesc/d.s create mode 100644 test/lld/ELF/riscv-tlsdesc-relax.test create mode 100644 test/lld/ELF/riscv-tlsdesc.test diff --git a/lib/Target/GNULDBackend.cpp b/lib/Target/GNULDBackend.cpp index 7cf1da7ae..4f6bbf29a 100644 --- a/lib/Target/GNULDBackend.cpp +++ b/lib/Target/GNULDBackend.cpp @@ -3938,6 +3938,12 @@ bool GNULDBackend::relax() { if (hasError) m_Module.setFailure(true); } + if (LinkerConfig::Object != config().codeGenType()) { + if (!setupProgramHdrs()) { + m_Module.setFailure(true); + return false; + } + } } if (!config().getDiagEngine()->diagnose()) { diff --git a/lib/Target/RISCV/RISCVGOT.cpp b/lib/Target/RISCV/RISCVGOT.cpp index a3c5ec8b4..eaa33f0f6 100644 --- a/lib/Target/RISCV/RISCVGOT.cpp +++ b/lib/Target/RISCV/RISCVGOT.cpp @@ -72,3 +72,9 @@ RISCVGOT *RISCVGOT::CreateIE(ELFSection *O, ResolveInfo *R, bool is32bit) { return make>(O, R); return make>(O, R); } + +RISCVGOT *RISCVGOT::CreateTLSDESC(ELFSection *O, ResolveInfo *R, bool is32bit) { + if (is32bit) + return make>(O, R); + return make>(O, R); +} diff --git a/lib/Target/RISCV/RISCVGOT.h b/lib/Target/RISCV/RISCVGOT.h index d35f074be..c938bb96f 100644 --- a/lib/Target/RISCV/RISCVGOT.h +++ b/lib/Target/RISCV/RISCVGOT.h @@ -67,6 +67,7 @@ class RISCVGOT : public GOT { static RISCVGOT *CreateGD(ELFSection *O, ResolveInfo *R, bool is32bit); static RISCVGOT *CreateLD(ELFSection *O, ResolveInfo *R, bool is32bit); static RISCVGOT *CreateIE(ELFSection *O, ResolveInfo *R, bool is32bit); + static RISCVGOT *CreateTLSDESC(ELFSection *O, ResolveInfo *R, bool is32bit); }; template @@ -150,6 +151,25 @@ class RISCVLDGOT : public RISCVTGOT { RISCVGOT *Other = nullptr; }; +template +class RISCVTLSDESCGOT : public RISCVTGOT { +public: + RISCVTLSDESCGOT(ELFSection *O, ResolveInfo *R) + : RISCVTGOT(GOT::TLS_DESC, O, R), + Other(make>(GOT::TLS_DESC, O, R)) {} + + RISCVGOT *getFirst() override { return this; } + + RISCVGOT *getNext() override { return Other; } + + static RISCVGOT *Create(ELFSection *O, ResolveInfo *R) { + return (make(O, R)); + } + +private: + RISCVGOT *Other; +}; + template class RISCVIEGOT : public RISCVTGOT { public: diff --git a/lib/Target/RISCV/RISCVLDBackend.cpp b/lib/Target/RISCV/RISCVLDBackend.cpp index 7fc3f7b22..7bee75d87 100644 --- a/lib/Target/RISCV/RISCVLDBackend.cpp +++ b/lib/Target/RISCV/RISCVLDBackend.cpp @@ -12,6 +12,7 @@ #include "RISCVLLVMExtern.h" #include "RISCVPLT.h" #include "RISCVRelaxationStats.h" +#include "RISCVRelocationHelper.h" #include "RISCVRelocationInternal.h" #include "RISCVRelocator.h" #include "RISCVStandaloneInfo.h" @@ -674,6 +675,104 @@ bool RISCVLDBackend::doRelaxationQCLi(Relocation *reloc, Relocator::DWord G) { return false; } +bool RISCVLDBackend::doRelaxationTLSDESC(Relocation &R, bool Relax) { + + Fragment *frag = R.targetRef()->frag(); + RegionFragmentEx *region = llvm::dyn_cast(frag); + if (!region) + return false; + + const StringRef RelaxType = "RISCV_TLSDESC"; + const ResolveInfo &Sym = *R.symInfo(); + size_t offset = R.targetRef()->offset(); + + // In executables, TLSDESC relocations are either removed, when the + // instruction is replaced with a NOP, or replaced with one of a + // different type. The conditions and the instruction substitution rules are + // the same whether or not relaxation is enabled. + auto attempt = [&]() -> bool { + bool Relaxed = Relax && config().options().getRISCVRelax(); + if (Relaxed) + relaxDeleteBytes(RelaxType, *region, offset, 4, Sym.name()); + else { + // Otherwise, the instruction is replaced with a NOP. + reportMissedRelaxation(RelaxType, *region, offset, 4, Sym.name()); + region->replaceInstruction(offset, &R, NOP, 4); + } + R.setType(llvm::ELF::R_RISCV_NONE); + return Relaxed; + }; + + // The first two instructions are always deleted. + if (R.type() == llvm::ELF::R_RISCV_TLSDESC_HI20 || + R.type() == llvm::ELF::R_RISCV_TLSDESC_LOAD_LO12) + return attempt(); + + // We need the base relocation to get the symbol and the original addend. + const Relocation *BaseReloc = getBaseReloc(R); + if (!BaseReloc) + return false; + + bool isLocalExec = !isSymbolPreemptible(*BaseReloc->symInfo()); + if (isLocalExec) { + // We only need the symbol value to determine if it fits in 12 bits so we + // can use the short form. Hopefully, it will not increase later. + int64_t S = getRelocator()->getSymValue(BaseReloc); + int64_t A = BaseReloc->addend(); + int64_t Value = S + A; + bool isShortForm = hi20(Value) == 0; + if (isShortForm) { + // LE short form is one ADDI instruction. + switch (R.type()) { + case llvm::ELF::R_RISCV_TLSDESC_ADD_LO12: + return attempt(); + case llvm::ELF::R_RISCV_TLSDESC_CALL: + // addi a0, zero, %lo(S) + R.setTargetData(itype(ADDI, X_A0, X_ZERO, 0)); + R.setType(llvm::ELF::R_RISCV_LO12_I); + break; + } + } else { + // LE long form is LUI + ADDI. + switch (R.type()) { + case llvm::ELF::R_RISCV_TLSDESC_ADD_LO12: + // lui a0, %hi(S) + R.setTargetData(utype(LUI, X_A0, 0)); + R.setType(llvm::ELF::R_RISCV_HI20); + // Inherit the original addend from the instruction we have deleted. + R.setAddend(BaseReloc->addend()); + break; + case llvm::ELF::R_RISCV_TLSDESC_CALL: + // addi a0, a0, %lo(S) + R.setTargetData(itype(ADDI, X_A0, X_A0, 0)); + R.setType(llvm::ELF::R_RISCV_LO12_I); + break; + } + } + } else { + // Initial executable: AUIPC + GOT LW/LD: + switch (R.type()) { + case llvm::ELF::R_RISCV_TLSDESC_ADD_LO12: + // auipc a0, %got_pcrel_hi(S) + R.setTargetData(utype(AUIPC, X_A0, 0)); + R.setType(llvm::ELF::R_RISCV_GOT_HI20); + // Inherit the original addend from the instruction we have deleted. + R.setAddend(BaseReloc->addend()); + break; + case llvm::ELF::R_RISCV_TLSDESC_CALL: + // lw/ld a0, a0+%pcrel_lo(S) + // The correspondence between this relocation and the corresponding HI20 + // stays the same even its type changes. + R.setTargetData( + itype(config().targets().is32Bits() ? LW : LD, X_A0, X_A0, 0)); + R.setType(llvm::ELF::R_RISCV_PCREL_LO12_I); + break; + } + } + + return true; +} + bool RISCVLDBackend::doRelaxationAlign(Relocation *pReloc) { FragmentRef *Ref = pReloc->targetRef(); Fragment *frag = Ref->frag(); @@ -913,12 +1012,18 @@ enum RelaxationPass { RELAXATION_CALL, // Must start at zero RELAXATION_PC, RELAXATION_LUI, + RELAXATION_TLSDESC, RELAXATION_ALIGN, RELAXATION_PASS_COUNT, // Number of passes }; void RISCVLDBackend::mayBeRelax(int relaxation_pass, bool &pFinished) { pFinished = true; + + // TLSDESC relaxations only apply to executables. + if (relaxation_pass == RELAXATION_TLSDESC && !config().isBuildingExecutable()) + return; + // RELAXATION_ALIGN pass, which is the last pass, will set pFinished to // false if it has made changes. It is needed to call createProgramHdrs() // again in the outer loop. Therefore, this function may be entered once more, @@ -980,6 +1085,26 @@ void RISCVLDBackend::mayBeRelax(int relaxation_pass, bool &pFinished) { doRelaxationLui(relocation, GP); break; } + case llvm::ELF::R_RISCV_TLSDESC_HI20: + case llvm::ELF::R_RISCV_TLSDESC_LOAD_LO12: + case llvm::ELF::R_RISCV_TLSDESC_ADD_LO12: + case llvm::ELF::R_RISCV_TLSDESC_CALL: + if (relaxation_pass == RELAXATION_TLSDESC) { + // In the TLSDESC relaxation sequence, only the instruction with + // R_RISCV_TLSDESC_HI20 can be marked with R_RISCV_RELAX to indicate + // that the whole sequence is relaxable. So the other three + // relocation types will inherit this knowledge from the + // R_RISCV_TLSDESC_HI20 relocation. + if (type != llvm::ELF::R_RISCV_TLSDESC_HI20) + if (const Relocation *HIReloc = getBaseReloc(*relocation)) + nextRelax = rs->getLink()->findRelocation( + HIReloc->targetRef()->offset(), llvm::ELF::R_RISCV_RELAX); + // Note that doRelaxationTLSDESC is used for both optimizations and + // relaxations, therefore this function should be called regardless + // of whether relaxations are enabled. + doRelaxationTLSDESC(*relocation, nextRelax); + } + break; case llvm::ELF::R_RISCV_ALIGN: { if (relaxation_pass == RELAXATION_ALIGN) if (doRelaxationAlign(relocation)) @@ -1145,12 +1270,20 @@ bool RISCVLDBackend::handleRelocation(ELFSection *pSection, m_GroupRelocs.insert(std::make_pair(reloc, offsetToReloc->second)); return true; } - // R_RISCV_PCREL_LO* relocations have the corresponding HI reloc as the - // syminfo, we need to find out the actual target by inspecting this reloc - // and set the appropriate relocation. + // R_RISCV_PCREL_LO* and TLSDESC relocations have the corresponding HI reloc + // as the syminfo, we need to find out the actual target by inspecting this + // reloc and set the appropriate relocation. case llvm::ELF::R_RISCV_PCREL_LO12_I: - case llvm::ELF::R_RISCV_PCREL_LO12_S: { - Relocation *hi_reloc = findHIRelocation(pSection, pSym.value()); + case llvm::ELF::R_RISCV_PCREL_LO12_S: + case llvm::ELF::R_RISCV_TLSDESC_LOAD_LO12: + case llvm::ELF::R_RISCV_TLSDESC_ADD_LO12: + case llvm::ELF::R_RISCV_TLSDESC_CALL: { + bool pcrel = pType == llvm::ELF::R_RISCV_PCREL_LO12_I || + pType == llvm::ELF::R_RISCV_PCREL_LO12_S; + Relocation *hi_reloc = + pcrel ? findHIRelocation(pSection, pSym.value()) + : pSection->findRelocation(pSym.value(), + llvm::ELF::R_RISCV_TLSDESC_HI20); if (!hi_reloc && pLastVisit) { config().raise(Diag::rv_hi20_not_found) << pSym.name() << getRISCVRelocName(pType) @@ -1180,7 +1313,7 @@ bool RISCVLDBackend::handleRelocation(ELFSection *pSection, reloc->setSymInfo(hi_reloc->symInfo()); pSection->addRelocation(reloc); } - if (pLastVisit) { + if (pcrel && pLastVisit) { // Disable GP Relaxation for this pair to mimic GNU m_DisableGPRelocs.insert(reloc); m_DisableGPRelocs.insert(hi_reloc); @@ -1482,6 +1615,10 @@ RISCVGOT *RISCVLDBackend::createGOT(GOT::GOTType T, ELFObjectFile *Obj, case GOT::TLS_IE: G = RISCVGOT::CreateIE(Obj->getGOT(), R, config().targets().is32Bits()); break; + case GOT::TLS_DESC: + G = RISCVGOT::CreateTLSDESC(Obj->getGOTPLT(), R, + config().targets().is32Bits()); + break; default: assert(0); break; diff --git a/lib/Target/RISCV/RISCVLDBackend.h b/lib/Target/RISCV/RISCVLDBackend.h index f2ffb9cd5..0cb4bcbc1 100644 --- a/lib/Target/RISCV/RISCVLDBackend.h +++ b/lib/Target/RISCV/RISCVLDBackend.h @@ -156,6 +156,11 @@ class RISCVLDBackend : public GNULDBackend { return DynRelocType::TPREL_LOCAL; return DynRelocType::TPREL_GLOBAL; } + if (X->type() == llvm::ELF::R_RISCV_TLSDESC) { + if (X->symInfo() && X->symInfo()->binding() == ResolveInfo::Local) + return DynRelocType::TLSDESC_LOCAL; + return DynRelocType::TLSDESC_GLOBAL; + } return DynRelocType::DEFAULT; } @@ -185,6 +190,19 @@ class RISCVLDBackend : public GNULDBackend { return reloc->second; } + const Relocation * + getNewBaseForTLSDESCRelaxation(const Relocation &BaseReloc) const { + auto It = m_HiToIELoadBase.find(&BaseReloc); + if (It != m_HiToIELoadBase.end()) + return It->second; + return nullptr; + } + + void setNewBaseForTLSDESCRelaxation(const Relocation &R) { + const Relocation *HIReloc = getBaseReloc(R); + m_HiToIELoadBase[HIReloc] = &R; + } + // Get the value of the symbol, using the PLT slot if one exists. Relocation::Address getSymbolValuePLT(const Relocation &R); @@ -212,7 +230,7 @@ class RISCVLDBackend : public GNULDBackend { bool doRelaxationLui(Relocation *R, Relocation::DWord G); bool doRelaxationQCLi(Relocation *R, Relocation::DWord G); - + bool doRelaxationTLSDESC(Relocation &reloc, bool Relax); bool doRelaxationAlign(Relocation *R); bool doRelaxationPC(Relocation *R, Relocation::DWord G); @@ -290,6 +308,10 @@ class RISCVLDBackend : public GNULDBackend { RISCVRelaxationStats *m_ModuleStats = nullptr; std::unordered_map> SectionRelocMap; + + // A map from HI relocations to the relocations that should be used as a base + // address for the load instruction during TLSDESC to IE optimization. + std::unordered_map m_HiToIELoadBase; }; } // namespace eld diff --git a/lib/Target/RISCV/RISCVRelocationHelper.h b/lib/Target/RISCV/RISCVRelocationHelper.h index 21d2a300d..135159fe2 100644 --- a/lib/Target/RISCV/RISCVRelocationHelper.h +++ b/lib/Target/RISCV/RISCVRelocationHelper.h @@ -10,16 +10,26 @@ namespace { enum Op { - ADDI = 0x13, + // Full instructions. + NOP = 0x13, + + // U-type opcodes. AUIPC = 0x17, + LUI = 0x37, + + // I-type opcodes. + ADDI = 0x13, JALR = 0x67, LD = 0x3003, LW = 0x2003, SRLI = 0x5013, + + // R-type opcodes. SUB = 0x40000033, }; enum Reg { + X_ZERO = 0, X_RA = 1, X_SP = 2, X_GP = 3, @@ -27,6 +37,7 @@ enum Reg { X_T0 = 5, X_T1 = 6, X_T2 = 7, + X_A0 = 10, X_T3 = 28 }; diff --git a/lib/Target/RISCV/RISCVRelocationInfo.cpp b/lib/Target/RISCV/RISCVRelocationInfo.cpp index 7df439980..217196479 100644 --- a/lib/Target/RISCV/RISCVRelocationInfo.cpp +++ b/lib/Target/RISCV/RISCVRelocationInfo.cpp @@ -502,6 +502,46 @@ RISCVRelocationMap RISCVRelocs = { /*.VerifyAlignment = */ false, /*.Signed = */ false, /*.Size = */ 32}}, + {llvm::ELF::R_RISCV_TLSDESC_HI20, + {/*.Name = */ "R_RISCV_TLSDESC_HI20", + /*.Type = */ llvm::ELF::R_RISCV_TLSDESC_HI20, + /*EncodingType = */ EncTy_U_HI20, + /*.Alignment = */ 0, + /*.shift = */ 20, + /*.VerifyRange = */ false, + /*.VerifyAlignment = */ false, + /*.Signed = */ true, + /*.Size = */ 32}}, + {llvm::ELF::R_RISCV_TLSDESC_LOAD_LO12, + {/*.Name = */ "R_RISCV_TLSDESC_LOAD_LO12", + /*.Type = */ llvm::ELF::R_RISCV_TLSDESC_LOAD_LO12, + /*EncodingType = */ EncTy_I, + /*.Alignment = */ 0, + /*.shift = */ 0, + /*.VerifyRange = */ false, + /*.VerifyAlignment = */ false, + /*.Signed = */ true, + /*.Size = */ 32}}, + {llvm::ELF::R_RISCV_TLSDESC_ADD_LO12, + {/*.Name = */ "R_RISCV_TLSDESC_ADD_LO12", + /*.Type = */ llvm::ELF::R_RISCV_TLSDESC_ADD_LO12, + /*EncodingType = */ EncTy_I, + /*.Alignment = */ 0, + /*.shift = */ 0, + /*.VerifyRange = */ false, + /*.VerifyAlignment = */ false, + /*.Signed = */ true, + /*.Size = */ 32}}, + {llvm::ELF::R_RISCV_TLSDESC_CALL, + {/*.Name = */ "R_RISCV_TLSDESC_CALL", + /*.Type = */ llvm::ELF::R_RISCV_TLSDESC_CALL, + /*EncodingType = */ EncTy_None, + /*.Alignment = */ 0, + /*.shift = */ 0, + /*.VerifyRange = */ false, + /*.VerifyAlignment = */ false, + /*.Signed = */ false, + /*.Size = */ 32}}, {llvm::ELF::R_RISCV_SET_ULEB128, {/*.Name = */ "R_RISCV_SET_ULEB128", /*.Type = */ llvm::ELF::R_RISCV_SET_ULEB128, diff --git a/lib/Target/RISCV/RISCVRelocator.cpp b/lib/Target/RISCV/RISCVRelocator.cpp index 8b4d363a6..a1cbcd164 100644 --- a/lib/Target/RISCV/RISCVRelocator.cpp +++ b/lib/Target/RISCV/RISCVRelocator.cpp @@ -129,6 +129,10 @@ RelocationDescMap RelocDescs = { PUBLIC_RELOC_DESC_ENTRY(R_RISCV_32_PCREL, applyRel), PUBLIC_RELOC_DESC_ENTRY(R_RISCV_SET_ULEB128, applyAdditive), PUBLIC_RELOC_DESC_ENTRY(R_RISCV_SUB_ULEB128, applyAdditive), + PUBLIC_RELOC_DESC_ENTRY(R_RISCV_TLSDESC_HI20, applyGOT), + PUBLIC_RELOC_DESC_ENTRY(R_RISCV_TLSDESC_LOAD_LO12, applyGOT), + PUBLIC_RELOC_DESC_ENTRY(R_RISCV_TLSDESC_ADD_LO12, applyGOT), + PUBLIC_RELOC_DESC_ENTRY(R_RISCV_TLSDESC_CALL, applyNone), PUBLIC_RELOC_DESC_ENTRY(R_RISCV_VENDOR, applyVendor), PUBLIC_RELOC_DESC_ENTRY(R_RISCV_CUSTOM192, unsupported), @@ -446,6 +450,54 @@ void RISCVRelocator::scanRelocation(Relocation &pReloc, eld::IRBuilder &pLinker, if (!section->isAlloc()) return; + ELFObjectFile *Obj = llvm::dyn_cast(&pInputFile); + + // Common relocation processing for both local and global symbols. + switch (pReloc.type()) { + case llvm::ELF::R_RISCV_TLSDESC_HI20: + case llvm::ELF::R_RISCV_TLSDESC_LOAD_LO12: + case llvm::ELF::R_RISCV_TLSDESC_ADD_LO12: + case llvm::ELF::R_RISCV_TLSDESC_CALL: { + std::lock_guard relocGuard(m_RelocMutex); + if (config().isBuildingExecutable()) { + // Non-preemptible symbols in executables will be optimized or relaxed, + // no GOT needed. + if (!m_Target.isSymbolPreemptible(*rsym)) + return; + + // RISC-V seems to dictate how each instruction in the sequence is + // transformed during IE/LE optimization. In particular, the instruction + // with R_RISCV_TLSDESC_ADD_LO12 is transformed to AUIPC, although it + // would be easier to keep the original AUIPC and remove the one with + // R_RISCV_TLSDESC_ADD_LO12. Therefore, the new load instruction will + // need a new relocation to indicate its base address. Reuse the + // existing R_RISCV_TLSDESC_ADD_LO12 as this is where the new auipc will + // be created. + if (pReloc.type() == llvm::ELF::R_RISCV_TLSDESC_ADD_LO12) + m_Target.setNewBaseForTLSDESCRelaxation(pReloc); + + if (rsym->reserved() & ReserveGOT) + return; + + RISCVGOT *G = m_Target.createGOT(GOT::TLS_IE, Obj, rsym); + G->setValueType(GOT::TLSStaticSymbolValue); + helper_DynRel_init(Obj, &pReloc, rsym, G, 0x0, + is32bit() ? llvm::ELF::R_RISCV_TLS_TPREL32 + : llvm::ELF::R_RISCV_TLS_TPREL64, + m_Target); + } else { + if (rsym->reserved() & ReserveGOT) + return; + RISCVGOT *G = m_Target.createGOT(GOT::TLS_DESC, Obj, rsym); + helper_DynRel_init(Obj, &pReloc, rsym, G->getFirst(), 0x0, + llvm::ELF::R_RISCV_TLSDESC, m_Target); + } + + rsym->setReserved(rsym->reserved() | ReserveGOT); + return; + } + } + if (rsym->isLocal()) // rsym is local scanLocalReloc(pInputFile, pReloc, pLinker, *section); else // rsym is external @@ -919,6 +971,10 @@ RISCVRelocator::Result applyRelLO(Relocation &pReloc, RISCVLDBackend &Backend, if (!HIReloc) return RISCVRelocator::BadReloc; + if (const Relocation *RelaxedBase = + Backend.getNewBaseForTLSDESCRelaxation(*HIReloc)) + HIReloc = RelaxedBase; + int64_t Value; if (HIReloc->type() == llvm::ELF::R_RISCV_GOT_HI20 || HIReloc->type() == llvm::ELF::R_RISCV_TLS_GD_HI20 || @@ -954,12 +1010,20 @@ RISCVRelocator::Result applyGOT(Relocation &pReloc, RISCVLDBackend &Backend, return Relocator::BadReloc; } - int64_t S = Backend.findEntryInGOT(pReloc.symInfo()) - ->getAddr(Backend.config().getDiagEngine()); - int64_t A = pReloc.addend(); - int64_t Result = S + A; + // Base relocation is used to find the symbol and addend. + const Relocation *BaseReloc = + pReloc.type() == llvm::ELF::R_RISCV_TLSDESC_LOAD_LO12 || + pReloc.type() == llvm::ELF::R_RISCV_TLSDESC_ADD_LO12 + ? Backend.getBaseReloc(pReloc) + : &pReloc; + if (!BaseReloc) + return RISCVRelocator::BadReloc; - Result -= pReloc.place(Backend.getModule()); + int64_t S = Backend.findEntryInGOT(BaseReloc->symInfo()) + ->getAddr(Backend.config().getDiagEngine()); + int64_t A = BaseReloc->addend(); + int64_t P = BaseReloc->place(Backend.getModule()); + int64_t Result = S + A - P; return ApplyReloc(pReloc, Result, pRelocDesc, Backend.config()); } diff --git a/lib/Writers/ELFObjectWriter.cpp b/lib/Writers/ELFObjectWriter.cpp index 6c33f4de6..19ab7259a 100644 --- a/lib/Writers/ELFObjectWriter.cpp +++ b/lib/Writers/ELFObjectWriter.cpp @@ -643,12 +643,12 @@ void ELFObjectWriter::emitRela(ELFSection *S, MemoryRegion &CurRegion, emitRelocation(*Rel, R->type(), RSym, ROffset, RAddend); break; } - case GNULDBackend::DynRelocType::TLSDESC_LOCAL: case GNULDBackend::DynRelocType::DTPMOD_LOCAL: case GNULDBackend::DynRelocType::DTPREL_LOCAL: emitRelocation(*Rel, R->type(), 0, ROffset, 0); break; + case GNULDBackend::DynRelocType::TLSDESC_LOCAL: case GNULDBackend::DynRelocType::TPREL_LOCAL: emitRelocation( *Rel, R->type(), 0, ROffset, diff --git a/test/lit.cfg b/test/lit.cfg index 388e6aded..8f561f88d 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -279,7 +279,6 @@ if config.test_target == 'X86': config.march = '' clang = 'clang -target x86_64-linux-gnu' clangxx = 'clang++' - llvmmc = 'llvm-mc' readelf = 'llvm-readelf' link = 'ld.eld -m elf_x86_64' ar = 'llvm-ar' @@ -314,6 +313,7 @@ if config.test_target == 'ARM': linkdriveropts = clangcommonopts + clangcommonxxopts linkdriverg0opts = linkdriveropts clangasopts = '' + llvmmc = f'{llvmmc} -triple=arm' if config.target_triple: if re.match(r'.*-android', config.target_triple): config.available_features.add("ndk-build") @@ -345,6 +345,7 @@ if config.test_target == 'AArch64': linkdriveropts = clangcommonopts + clangcommonxxopts linkdriverg0opts = linkdriveropts clangasopts = '-cc1as -triple aarch64' + llvmmc = f'{llvmmc} -triple=aarch64' if config.target_triple: if re.match(r'.*-android', config.target_triple): config.available_features.add("ndk-build") @@ -368,7 +369,7 @@ if config.test_target == 'RISCV64': linkopts = '--thread-count 4 --emit-relocs' if hasattr(config,'eld_option') and config.eld_option != 'default': linkopts = '--thread-count 4 ' + config.eld_option - llvmmc = 'llvm-mc' + llvmmc = f'{llvmmc} -triple=riscv64' readelf = 'llvm-readelf' ar = 'llvm-ar' nm = 'llvm-nm' @@ -397,7 +398,7 @@ if config.test_target == 'RISCV': linkopts = '--thread-count 4 -mtriple riscv32-unknown-elf --emit-relocs' if hasattr(config,'eld_option') and config.eld_option != 'default': linkopts = '-mtriple riscv32-unknown-elf --thread-count 4 ' + config.eld_option - llvmmc = 'llvm-mc' + llvmmc = f'{llvmmc} -triple=riscv32 --defsym ELF32=1' readelf = 'llvm-readelf' ar = 'llvm-ar' nm = 'llvm-nm' diff --git a/test/lld/ELF/Inputs/riscv-tlsdesc-relax/a.s b/test/lld/ELF/Inputs/riscv-tlsdesc-relax/a.s new file mode 100644 index 000000000..948b9810a --- /dev/null +++ b/test/lld/ELF/Inputs/riscv-tlsdesc-relax/a.s @@ -0,0 +1,63 @@ +.macro load dst, src +.ifdef ELF32 +lw \dst, \src +.else +ld \dst, \src +.endif +.endm + +.globl _start +_start: + .word 0 + +foo: +.Ltlsdesc_hi0: +.option norelax +## All 4 instructions have an R_RISCV_RELAX. +## Check that optimization/relaxation are not affected by irrelevant instructions. + auipc a2, %tlsdesc_hi(b) + .reloc .-4, R_RISCV_RELAX, 0 + c.add a7, a7 + load a3, %tlsdesc_load_lo(.Ltlsdesc_hi0)(a2) + .reloc .-4, R_RISCV_RELAX, 0 + c.add a7, a7 + addi a0, a2, %tlsdesc_add_lo(.Ltlsdesc_hi0) + .reloc .-4, R_RISCV_RELAX, 0 + c.add a7, a7 + jalr t0, 0(a3), %tlsdesc_call(.Ltlsdesc_hi0) + .reloc .-4, R_RISCV_RELAX, 0 + add a0, a0, tp +.option relax + + .word 0 + +.Ltlsdesc_hi1: +.option norelax +## AUIPC has an R_RISCV_RELAX. We perform relaxation, ignoring whether other +## instructions have R_RISCV_RELAX. + auipc a4, %tlsdesc_hi(b) + .reloc .-4, R_RISCV_RELAX, 0 + load a5, %tlsdesc_load_lo(.Ltlsdesc_hi1)(a4) + addi a0, a4, %tlsdesc_add_lo(.Ltlsdesc_hi1) + jalr t0, 0(a5), %tlsdesc_call(.Ltlsdesc_hi1) + add a0, a0, tp +.option relax + +.Ltlsdesc_hi2: +.option norelax +## AUIPC does not have R_RISCV_RELAX. No relaxation. + auipc a6, %tlsdesc_hi(b) + load a7, %tlsdesc_load_lo(.Ltlsdesc_hi2)(a6) + .reloc .-4, R_RISCV_RELAX, 0 + addi a0, a6, %tlsdesc_add_lo(.Ltlsdesc_hi2) + .reloc .-4, R_RISCV_RELAX, 0 + jalr t0, 0(a7), %tlsdesc_call(.Ltlsdesc_hi2) + add a0, a0, tp +.option relax + +.section .tbss +.globl a +.zero 8 +a: +.zero 2039+PAD ## Place b at 0x7ff+PAD + diff --git a/test/lld/ELF/Inputs/riscv-tlsdesc-relax/c.s b/test/lld/ELF/Inputs/riscv-tlsdesc-relax/c.s new file mode 100644 index 000000000..dace297a3 --- /dev/null +++ b/test/lld/ELF/Inputs/riscv-tlsdesc-relax/c.s @@ -0,0 +1,4 @@ +.tbss +.globl b +b: +.zero 4 diff --git a/test/lld/ELF/Inputs/riscv-tlsdesc/a.s b/test/lld/ELF/Inputs/riscv-tlsdesc/a.s new file mode 100644 index 000000000..c473fe663 --- /dev/null +++ b/test/lld/ELF/Inputs/riscv-tlsdesc/a.s @@ -0,0 +1,36 @@ +.macro load dst, src +.ifdef ELF32 +lw \dst, \src +.else +ld \dst, \src +.endif +.endm + +.Ltlsdesc_hi0: + auipc a0, %tlsdesc_hi(a) + load a1, %tlsdesc_load_lo(.Ltlsdesc_hi0)(a0) + addi a0, a0, %tlsdesc_add_lo(.Ltlsdesc_hi0) + jalr t0, 0(a1), %tlsdesc_call(.Ltlsdesc_hi0) + add a0, a0, tp + +.Ltlsdesc_hi1: + auipc a2, %tlsdesc_hi(b) + load a3, %tlsdesc_load_lo(.Ltlsdesc_hi1)(a2) + addi a0, a2, %tlsdesc_add_lo(.Ltlsdesc_hi1) + jalr t0, 0(a3), %tlsdesc_call(.Ltlsdesc_hi1) + add a0, a0, tp + +.Ltlsdesc_hi2: + auipc a4, %tlsdesc_hi(c) + load a5, %tlsdesc_load_lo(.Ltlsdesc_hi2)(a4) + addi a0, a4, %tlsdesc_add_lo(.Ltlsdesc_hi2) + jalr t0, 0(a5), %tlsdesc_call(.Ltlsdesc_hi2) + add a0, a0, tp + +.section .tbss +.globl a +.zero 8 +a: +.zero 2039 ## Place b at 0x7ff +b: +.zero 1 diff --git a/test/lld/ELF/Inputs/riscv-tlsdesc/a1.s b/test/lld/ELF/Inputs/riscv-tlsdesc/a1.s new file mode 100644 index 000000000..197705661 --- /dev/null +++ b/test/lld/ELF/Inputs/riscv-tlsdesc/a1.s @@ -0,0 +1,14 @@ +.macro load dst, src +.ifdef ELF32 +lw \dst, \src +.else +ld \dst, \src +.endif +.endm + +.Ltlsdesc_hi2: + auipc a4, %tlsdesc_hi(c) + load a6, %tlsdesc_load_lo(.Ltlsdesc_hi2)(a4) + addi a0, a4, %tlsdesc_add_lo(.Ltlsdesc_hi2) + jalr t0, 0(a6), %tlsdesc_call(.Ltlsdesc_hi2) + add a0, a0, tp diff --git a/test/lld/ELF/Inputs/riscv-tlsdesc/c.s b/test/lld/ELF/Inputs/riscv-tlsdesc/c.s new file mode 100644 index 000000000..261394ba9 --- /dev/null +++ b/test/lld/ELF/Inputs/riscv-tlsdesc/c.s @@ -0,0 +1,3 @@ +.tbss +.globl c +c: .zero 4 diff --git a/test/lld/ELF/Inputs/riscv-tlsdesc/d.s b/test/lld/ELF/Inputs/riscv-tlsdesc/d.s new file mode 100644 index 000000000..633e44253 --- /dev/null +++ b/test/lld/ELF/Inputs/riscv-tlsdesc/d.s @@ -0,0 +1,14 @@ +.macro load dst, src +.ifdef ELF32 +lw \dst, \src +.else +ld \dst, \src +.endif +.endm + +.Ltlsdesc_hi0: + auipc a0, %tlsdesc_hi(foo) + load a1, %tlsdesc_load_lo(.Ltlsdesc_hi0)(a0) + addi a0, a0, %tlsdesc_add_lo(.Ltlsdesc_hi0) + jalr t0, 0(a1), %tlsdesc_call(.Ltlsdesc_hi0) + add a1, a0, tp diff --git a/test/lld/ELF/riscv-tlsdesc-relax.test b/test/lld/ELF/riscv-tlsdesc-relax.test new file mode 100644 index 000000000..982e8d143 --- /dev/null +++ b/test/lld/ELF/riscv-tlsdesc-relax.test @@ -0,0 +1,178 @@ +REQUIRES: riscv32 || riscv64 + +RUN: %llvm-mc -filetype=obj --defsym PAD=0 -mattr=+c,+relax %p/Inputs/riscv-tlsdesc-relax/a.s -o %t.a.o +RUN: %llvm-mc -filetype=obj --defsym PAD=5000 -mattr=+c,+relax %p/Inputs/riscv-tlsdesc-relax/a.s -o %t.aa.o +RUN: %llvm-mc -filetype=obj -mattr=+c,+relax %p/Inputs/riscv-tlsdesc-relax/c.s -o %t.c.o +RUN: %link %linkopts -shared %t.c.o -o %t.c.so + +RUN: %link %linkopts -shared -z now --keep-labels --no-align-segments --section-start .text=0x1000 --section-start .got=0x20c0 %t.a.o %t.c.o -o %t.a.so +RUN: %readelf --dyn-relocations %t.a.so | FileCheck %s --check-prefix=GD-REL%xlen +RUN: %objdump --no-show-raw-insn -M no-aliases -h -d %t.a.so | FileCheck %s --check-prefix=GD --check-prefix=GD%xlen + +## Test the TLSDESC to LE optimization. Also check --emit-relocs. +RUN: %link %linkopts -z now --no-align-segments --section-start .text=0x11000 %t.a.o %t.c.o -o %t.a.le --emit-relocs +RUN: %readelf --dyn-relocations %t.a.le | FileCheck %s --match-full-lines --allow-empty --check-prefix=LE-REL +RUN: %objdump --no-show-raw-insn -M no-aliases -hdr %t.a.le | FileCheck %s --check-prefix=LE + +RUN: %link %linkopts -z now --no-align-segments --section-start .text=0x11000 %t.aa.o %t.c.o -o %t.aa.le +RUN: %objdump --no-show-raw-insn -M no-aliases -h -d %t.aa.le | FileCheck %s --check-prefix=LEA + +## Test the TLSDESC to IE optimization. +RUN: %link %linkopts -z now --no-align-segments --section-start .text=0x11000 --section-start .got=0x120e0 %t.a.o %t.c.so -o %t.a.ie +RUN: %readelf --dyn-relocations %t.a.ie | FileCheck %s --match-full-lines --check-prefix=IE-REL%xlen +RUN: %objdump --no-show-raw-insn -M no-aliases -h -d %t.a.ie | FileCheck %s --check-prefix=IE --check-prefix=IE%xlen + +## .got has 3 additional header slots before the TLS slot. + +GD4: .got {{0*}}14 {{0*}}20c0 DATA +GD8: .got {{0*}}28 {{0*}}20c0 DATA + +GD-REL4: {{0*}}20cc {{0*}}030c R_RISCV_TLSDESC {{0*}}7ff b + 0 +GD-REL8: {{0*}}20d8 {{0*}}030000000c R_RISCV_TLSDESC {{0*}}7ff b + 0 + +## Notes re. comparing the output to lld: +## - .got does not have the initial entry, so its size and all offsets are 8 +## bytes less when compared to lld. +## - lld keeps original relocation names after relaxation, which affects +## --emit-relocs output. Due to relaxations, these relocations may point +## to unrelated instructions. + +GD-LABEL: : +## &.got[c]-. = 0x20cc - 0x1004 = 0x10c8 +## &.got[c]-. = 0x20d8 - 0x1004 = 0x10d4 +GD: 1004: auipc a2, 0x1 +GD-NEXT: c.add a7, a7 +GD4-NEXT: lw a3, 0xc8(a2) +GD8-NEXT: ld a3, 0xd4(a2) +GD-NEXT: c.add a7, a7 +GD4-NEXT: addi a0, a2, 0xc8 +GD8-NEXT: addi a0, a2, 0xd4 +GD-NEXT: c.add a7, a7 +GD-NEXT: jalr t0, 0x0(a3) +GD-NEXT: c.add a0, tp + +GD-LABEL: <.Ltlsdesc_hi1>: +## &.got[c]-. = 0x20cc - 0x1020 = 0x10ac +## &.got[c]-. = 0x20d8 - 0x1020 = 0x10b8 +GD-NEXT: 1020: auipc a4, 0x1 +GD4-NEXT: lw a5, 0xac(a4) +GD8-NEXT: ld a5, 0xb8(a4) +GD4-NEXT: addi a0, a4, 0xac +GD8-NEXT: addi a0, a4, 0xb8 +GD-NEXT: jalr t0, 0x0(a5) +GD-NEXT: c.add a0, tp + +GD-LABEL: <.Ltlsdesc_hi2>: +## &.got[c]-. = 0x20cc - 0x1032 = 0x109a +## &.got[c]-. = 0x20d8 - 0x1032 = 0x10a6 +GD-NEXT: 1032: auipc a6, 0x1 +GD4-NEXT: lw a7, 0x9a(a6) +GD8-NEXT: ld a7, 0xa6(a6) +GD4-NEXT: addi a0, a6, 0x9a +GD8-NEXT: addi a0, a6, 0xa6 +GD-NEXT: jalr t0, 0x0(a7) +GD-NEXT: c.add a0, tp + +## No GOT slots and dynamic relocations in LE +LE-REL-NOT: R_RISCV + +LE-LABEL: : +LE-NEXT: c.add a7, a7 +LE-NEXT-LLD: R_RISCV_TLSDESC_HI20 b +LE-NEXT: R_RISCV_NONE b +LE-NEXT: R_RISCV_RELAX *ABS* +LE-NEXT: c.add a7, a7 +LE-NEXT-LLD: R_RISCV_TLSDESC_LOAD_LO12 .Ltlsdesc_hi0 +LE-NEXT: R_RISCV_NONE b +LE-NEXT: R_RISCV_RELAX *ABS* +LE-NEXT: 11008: c.add a7, a7 +LE-NEXT-LLD: R_RISCV_TLSDESC_ADD_LO12 .Ltlsdesc_hi0 +LE-NEXT: R_RISCV_NONE b +LE-NEXT: R_RISCV_RELAX *ABS* +LE-NEXT: addi a0, zero, 0x7ff +LE-NEXT-LLD: R_RISCV_TLSDESC_CALL .Ltlsdesc_hi0 +LE-NEXT: R_RISCV_LO12_I b +LE-NEXT: R_RISCV_RELAX *ABS* +LE-NEXT: c.add a0, tp + +LE-LABEL: <.Ltlsdesc_hi1>: +LE-NEXT: addi a0, zero, 0x7ff +LE-NEXT-LLD: R_RISCV_TLSDESC_HI20 b +LE-NEXT: R_RISCV_NONE b +LE-NEXT: R_RISCV_RELAX *ABS* +LE-NEXT-LLD: R_RISCV_TLSDESC_LOAD_LO12 .Ltlsdesc_hi1 +LE-NEXT-LLD: R_RISCV_TLSDESC_ADD_LO12 .Ltlsdesc_hi1 +LE-NEXT: R_RISCV_NONE b +LE-NEXT: R_RISCV_NONE b +LE-NEXT-LLD: R_RISCV_TLSDESC_CALL .Ltlsdesc_hi1 +LE-NEXT: R_RISCV_LO12_I b +LE-NEXT: c.add a0, tp +LE-LABEL: <.Ltlsdesc_hi2>: +LE-NEXT: addi zero, zero, 0x0 +LE-NEXT-LLD: R_RISCV_TLSDESC_HI20 b +LE-NEXT: R_RISCV_NONE b +LE-NEXT: addi zero, zero, 0x0 +LE-NEXT-LLD: R_RISCV_TLSDESC_LOAD_LO12 .Ltlsdesc_hi2 +LE-NEXT: R_RISCV_NONE b +LE-NEXT: R_RISCV_RELAX *ABS* +LE-NEXT: addi zero, zero, 0x0 +LE-NEXT-LLD: R_RISCV_TLSDESC_ADD_LO12 .Ltlsdesc_hi2 +LE-NEXT: R_RISCV_NONE b +LE-NEXT: R_RISCV_RELAX *ABS* +LE-NEXT: addi a0, zero, 0x7ff +LE-NEXT-LLD: R_RISCV_TLSDESC_CALL .Ltlsdesc_hi2 +LE-NEXT: R_RISCV_LO12_I b +LE-NEXT: c.add a0, tp + +LEA-LABEL: : +LEA-NEXT: c.add a7, a7 +LEA-NEXT: c.add a7, a7 +LEA-NEXT: 11008: lui a0, 0x2 +LEA-NEXT: c.add a7, a7 +LEA-NEXT: addi a0, a0, -0x479 +LEA-NEXT: c.add a0, tp + +LEA-LABEL: <.Ltlsdesc_hi1>: +LEA-NEXT: lui a0, 0x2 +LEA-NEXT: addi a0, a0, -0x479 +LEA-NEXT: c.add a0, tp +LEA-LABEL: <.Ltlsdesc_hi2>: +LEA-NEXT: addi zero, zero, 0x0 +LEA-NEXT: addi zero, zero, 0x0 +LEA-NEXT: lui a0, 0x2 +LEA-NEXT: addi a0, a0, -0x479 +LEA-NEXT: c.add a0, tp + +IE4: .got {{0*}}10 {{0*}}120e0 DATA +IE8: .got {{0*}}20 {{0*}}120e0 DATA + +IE-REL4: {{0*}}120e4 {{0*}}010a R_RISCV_TLS_TPREL32 {{0+}} b + 0 +IE-REL8: {{0*}}120e8 {{0*}}010000000b R_RISCV_TLS_TPREL64 {{0+}} b + 0 + +IE-LABEL: : +IE-NEXT: c.add a7, a7 +IE-NEXT: c.add a7, a7 +## &.got[c]-. = 0x120e4 - 0x11008 = 0x10dc +## &.got[c]-. = 0x120e8 - 0x11008 = 0x10e0 +IE-NEXT: 11008: auipc a0, 0x1 +IE-NEXT: c.add a7, a7 +IE4-NEXT: lw a0, 0xdc(a0) +IE8-NEXT: ld a0, 0xe0(a0) +IE-NEXT: c.add a0, tp + +IE-LABEL: <.Ltlsdesc_hi1>: +## &.got[c]-. = 0x120e4 - 0x11018 = 0x10cc +## &.got[c]-. = 0x120e8 - 0x11018 = 0x10d0 +IE-NEXT: 11018: auipc a0, 0x1 +IE4-NEXT: lw a0, 0xcc(a0) +IE8-NEXT: ld a0, 0xd0(a0) +IE-NEXT: c.add a0, tp +## &.got[c]-. = 0x120e4 - 0x1102a = 0x10ba +## &.got[c]-. = 0x120e8 - 0x1102a = 0x10be +IE-LABEL: <.Ltlsdesc_hi2>: +IE-NEXT: addi zero, zero, 0x0 +IE-NEXT: addi zero, zero, 0x0 +IE-NEXT: 1102a: auipc a0, 0x1 +IE4-NEXT: lw a0, 0xba(a0) +IE8-NEXT: ld a0, 0xbe(a0) +IE-NEXT: c.add a0, tp diff --git a/test/lld/ELF/riscv-tlsdesc.test b/test/lld/ELF/riscv-tlsdesc.test new file mode 100644 index 000000000..ba210f908 --- /dev/null +++ b/test/lld/ELF/riscv-tlsdesc.test @@ -0,0 +1,156 @@ +REQUIRES: riscv32 || riscv64 + +RUN: %llvm-mc -filetype=obj %p/Inputs/riscv-tlsdesc/a.s -o %t.a.o +RUN: %llvm-mc -filetype=obj %p/Inputs/riscv-tlsdesc/c.s -o %t.c.o +RUN: %link %linkopts -shared %t.c.o -o %t.c.so + +RUN: %link %linkopts -shared --no-align-segments --section-start .text=0x1800 --section-start .got=0x2880 -z now %t.a.o %t.c.o -o %t.a.so +RUN: llvm-readobj -r -x .got %t.a.so | FileCheck -D#%x,XLEN=%xlen --check-prefix=GD-RELA --check-prefix=GD-RELA-GOT%xlen %s +RUN: %objdump --no-show-raw-insn -M no-aliases -h -d %t.a.so | FileCheck %s --check-prefix=GD --check-prefix=GD%xlen + +RUN: %link %linkopts --no-emit-relocs --keep-labels --no-align-segments --section-start .text=0x1800 -z now %t.a.o %t.c.o -o %t.a.le +RUN: %readelf -r %t.a.le | FileCheck --check-prefix=NOREL %s +RUN: %objdump --no-show-raw-insn -M no-aliases -h -d %t.a.le | FileCheck %s --check-prefix=LE + +RUN: %link %linkopts --no-emit-relocs --keep-labels --no-align-segments --section-start .text=0x1800 --section-start .got=0x2880 -z now %t.a.o %t.c.so -o %t.a.ie +RUN: llvm-readobj -r %t.a.ie | FileCheck --check-prefix=IE-RELA%xlen %s +RUN: %objdump --no-show-raw-insn -M no-aliases -h -d %t.a.ie | FileCheck %s --check-prefix=IE --check-prefix=IE%xlen + +## Prior to https://github.com/llvm/llvm-project/pull/85817 the local TLSDESC +## labels would be marked STT_TLS, resulting in an error "has an STT_TLS symbol but doesn't have an SHF_TLS section" + +RUN: %llvm-mc -filetype=obj %p/Inputs/riscv-tlsdesc/d.s -o %t.d.o +RUN: %link %linkopts --no-emit-relocs -shared -o %t.d.so %t.d.o --fatal-warnings + +## The output has a TLS reference but no TLS section. +RUN: %llvm-mc -filetype=obj %p/Inputs/riscv-tlsdesc/a1.s -o %t.a1.o +RUN: %link %linkopts --no-emit-relocs --keep-labels --no-align-segments --section-start .text=0x1250 --section-start .got=0x233c -pie %t.a1.o %t.c.so -o %t.a1 +RUN: %objdump --no-show-raw-insn -M no-aliases -Rd %t.a1 | FileCheck -D#%x,XLEN=%xlen %s --check-prefix=IEA --check-prefix=IEA%xlen + +## .got has 3 additional header slots before the TLS slot. + +## The order of symbols and GOT slots is different than produced by lld. +GD-RELA: .rela.dyn +GD-RELA-NEXT: 0x{{0*}}[[#%X,0x2880+mul(XLEN,5)]] R_RISCV_TLSDESC - 0x7FF{{$}} +GD-RELA-NEXT: 0x{{0*}}[[#%X,0x2880+mul(XLEN,7)]] R_RISCV_TLSDESC c 0x0{{$}} +GD-RELA-NEXT: 0x{{0*}}[[#%X,0x2880+mul(XLEN,3)]] R_RISCV_TLSDESC a 0x0{{$}} +GD-RELA-NEXT: } +GD-RELA-GOT4: Hex dump of section '.got': +GD-RELA-GOT4-NEXT: 0x{{0*}}[[#%x,0x2880]] 00000000 00000000 00000000 00000000 +GD-RELA-GOT4-NEXT: 0x{{0*}}[[#%x,0x2890]] 00000000 00000000 +GD-RELA-GOT8: Hex dump of section '.got': +GD-RELA-GOT8-NEXT: 0x{{0*}}[[#%x,0x2880]] 00000000 00000000 00000000 00000000 +GD-RELA-GOT8-NEXT: 0x{{0*}}[[#%x,0x2890]] 00000000 00000000 00000000 00000000 +GD-RELA-GOT8-NEXT: 0x{{0*}}[[#%x,0x28A0]] 00000000 00000000 00000000 00000000 + +## .got does not have the intial entry, so its size and all offsets are 8 bytes less than produced by lld. + +GD4: .got {{0*}}24 {{0*}}2880 DATA +GD8: .got {{0*}}48 {{0*}}2880 DATA + +## &.got[a]-. = 0x2880+3*4 - 0x1800 = 0x108c +## &.got[a]-. = 0x2880+3*8 - 0x1800 = 0x1098 +GD: 1800: auipc a0, 0x1 +GD4-NEXT: lw a1, 0x8c(a0) +GD8-NEXT: ld a1, 0x98(a0) +GD4-NEXT: addi a0, a0, 0x8c +GD8-NEXT: addi a0, a0, 0x98 +GD-NEXT: jalr t0, 0x0(a1) +GD-NEXT: add a0, a0, tp + +## riscv32: &.got[b]-. = 0x2880+5*4 - 0x1814 = 0x1080 +## riscv64: &.got[b]-. = 0x2880+5*8 - 0x1814 = 0x1094 +GD: 1814: auipc a2, 0x1 +GD4-NEXT: lw a3, 0x80(a2) +GD8-NEXT: ld a3, 0x94(a2) +GD4-NEXT: addi a0, a2, 0x80 +GD8-NEXT: addi a0, a2, 0x94 +GD-NEXT: jalr t0, 0x0(a3) +GD-NEXT: add a0, a0, tp + +## riscv32: &.got[c]-. = 0x2880+7*4 - 0x1828 = 0x1074 +## riscv64: &.got[c]-. = 0x2880+7*8 - 0x1828 = 0x1090 +GD: 1828: auipc a4, 0x1 +GD4-NEXT: lw a5, 0x74(a4) +GD8-NEXT: ld a5, 0x90(a4) +GD4-NEXT: addi a0, a4, 0x74 +GD8-NEXT: addi a0, a4, 0x90 +GD-NEXT: jalr t0, 0x0(a5) +GD-NEXT: add a0, a0, tp + +NOREL: no relocations + +LE-LABEL: <.Ltlsdesc_hi0>: +## st_value(a) = 8 +LE-NEXT: addi zero, zero, 0x0 +LE-NEXT: addi zero, zero, 0x0 +LE-NEXT: addi zero, zero, 0x0 +LE-NEXT: addi a0, zero, 0x8 +LE-NEXT: add a0, a0, tp +## st_value(b) = 2047 +LE-LABEL: <.Ltlsdesc_hi1>: +LE-NEXT: addi zero, zero, 0x0 +LE-NEXT: addi zero, zero, 0x0 +LE-NEXT: addi zero, zero, 0x0 +LE-NEXT: addi a0, zero, 0x7ff +LE-NEXT: add a0, a0, tp +## st_value(c) = 2048 +LE-LABEL: <.Ltlsdesc_hi2>: +LE-NEXT: addi zero, zero, 0x0 +LE-NEXT: addi zero, zero, 0x0 +LE-NEXT: lui a0, 0x1 +LE-NEXT: addi a0, a0, -0x800 +LE-NEXT: add a0, a0, tp + +IE-RELA4: .rela.dyn { +IE-RELA4-NEXT: 0x{{0*}}2884 R_RISCV_TLS_TPREL32 c 0x0{{$}} +IE-RELA4-NEXT: } + +IE-RELA8: .rela.dyn { +IE-RELA8-NEXT: 0x{{0*}}2888 R_RISCV_TLS_TPREL64 c 0x0{{$}} +IE-RELA8-NEXT: } + +IE4: .got {{0*}}10 {{0*}}2880 DATA +IE8: .got {{0*}}20 {{0*}}2880 DATA + +## a and b are optimized to use LE. c is optimized to IE. + +## TODO: Test case for hi != 0 + +IE-LABEL: <.Ltlsdesc_hi0>: +## st_value(a) = 8 +IE-NEXT: addi zero, zero, 0x0 +IE-NEXT: addi zero, zero, 0x0 +IE-NEXT: addi zero, zero, 0x0 +IE-NEXT: addi a0, zero, 0x8 +IE-NEXT: add a0, a0, tp +## st_value(b) = 2047 +IE-LABEL: <.Ltlsdesc_hi1>: +IE-NEXT: addi zero, zero, 0x0 +IE-NEXT: addi zero, zero, 0x0 +IE-NEXT: addi zero, zero, 0x0 +IE-NEXT: addi a0, zero, 0x7ff +IE-NEXT: add a0, a0, tp +## riscv32: &.got[c]-. = 0x2880+4 - 0x1830 = 0x1054 +## riscv64: &.got[c]-. = 0x2880+8 - 0x1830 = 0x1054 +IE-LABEL: <.Ltlsdesc_hi2>: +IE-NEXT: addi zero, zero, 0x0 +IE-NEXT: addi zero, zero, 0x0 +IE-NEXT: 1830: auipc a0, 0x1 +IE4-NEXT: lw a0, 0x54(a0) +IE8-NEXT: ld a0, 0x58(a0) +IE-NEXT: add a0, a0, tp + +IEA: OFFSET TYPE VALUE +IEA4-NEXT: {{0*}}2340 R_RISCV_TLS_TPREL[[#%d,mul(XLEN,8)]] c +IEA8-NEXT: {{0*}}2348 R_RISCV_TLS_TPREL[[#%d,mul(XLEN,8)]] c +IEA-EMPTY: +## riscv32: &.got[c]-. = 0x2340 - 0x1258 = 0x10e8 +## &.got[c]-. = 0x2348 - 0x1258 = 0x10f0 +IEA-LABEL: <.Ltlsdesc_hi2>: +IEA-NEXT: addi zero, zero, 0x0 +IEA-NEXT: addi zero, zero, 0x0 +IEA-NEXT: 1258: auipc a0, 0x1 +IEA4-NEXT: lw a0, 0xe8(a0) +IEA8-NEXT: ld a0, 0xf0(a0) +IEA-NEXT: add a0, a0, tp From ed813f440b202323b03521d17b66f497221bd79d Mon Sep 17 00:00:00 2001 From: Alexey Karyakin Date: Wed, 13 Aug 2025 17:18:59 -0700 Subject: [PATCH 2/3] NFC: Support tests written in the yaml language Add '.yaml' to config.suffixes and fix tests that had non-test yaml files outside of their Inputs directory. Signed-off-by: Alexey Karyakin --- test/AArch64/standalone/BSS_none_bss/BSS.test | 7 ------- .../standalone/BSS_none_bss/{t.yaml => BSS_none_bss.yaml} | 8 ++++++++ test/ARM/standalone/TwoSizeof/{t.yaml => SizeOf.yaml} | 5 +++++ test/ARM/standalone/TwoSizeof/Sizeof.test | 4 ---- test/lit.cfg | 2 +- 5 files changed, 14 insertions(+), 12 deletions(-) delete mode 100644 test/AArch64/standalone/BSS_none_bss/BSS.test rename test/AArch64/standalone/BSS_none_bss/{t.yaml => BSS_none_bss.yaml} (92%) rename test/ARM/standalone/TwoSizeof/{t.yaml => SizeOf.yaml} (94%) delete mode 100644 test/ARM/standalone/TwoSizeof/Sizeof.test diff --git a/test/AArch64/standalone/BSS_none_bss/BSS.test b/test/AArch64/standalone/BSS_none_bss/BSS.test deleted file mode 100644 index 8ff156dfc..000000000 --- a/test/AArch64/standalone/BSS_none_bss/BSS.test +++ /dev/null @@ -1,7 +0,0 @@ -RUN: %yaml2obj %p/t.yaml -o %t.o -RUN: %link %linkopts -march aarch64 -T %p/t.t %t.o -o %t.out -RUN: llvm-objdump -s %t.out | %filecheck %s - -CHECK: 0000 00000000 00000000 00474343 3a202863 -CHECK: 0020 726f2d31 2e31332e 312d342e 382d3230 -CHECK: 0050 3000 diff --git a/test/AArch64/standalone/BSS_none_bss/t.yaml b/test/AArch64/standalone/BSS_none_bss/BSS_none_bss.yaml similarity index 92% rename from test/AArch64/standalone/BSS_none_bss/t.yaml rename to test/AArch64/standalone/BSS_none_bss/BSS_none_bss.yaml index 56e524de9..4b378cd95 100644 --- a/test/AArch64/standalone/BSS_none_bss/t.yaml +++ b/test/AArch64/standalone/BSS_none_bss/BSS_none_bss.yaml @@ -1,3 +1,11 @@ +# RUN: %yaml2obj %s -o %t.o +# RUN: %link %linkopts -march aarch64 -T %p/t.t %t.o -o %t.out +# RUN: llvm-objdump -s %t.out | %filecheck %s + +# CHECK: 0000 00000000 00000000 00474343 3a202863 +# CHECK: 0020 726f2d31 2e31332e 312d342e 382d3230 +# CHECK: 0050 3000 + --- !ELF FileHeader: Class: ELFCLASS64 diff --git a/test/ARM/standalone/TwoSizeof/t.yaml b/test/ARM/standalone/TwoSizeof/SizeOf.yaml similarity index 94% rename from test/ARM/standalone/TwoSizeof/t.yaml rename to test/ARM/standalone/TwoSizeof/SizeOf.yaml index 00dfcf0f1..fdb56b0b6 100644 --- a/test/ARM/standalone/TwoSizeof/t.yaml +++ b/test/ARM/standalone/TwoSizeof/SizeOf.yaml @@ -1,3 +1,8 @@ +# RUN: %yaml2obj %s -o %t.o +# RUN: %not %link %linkopts -march=arm -static -T %p/t.t %t.o -o %t.out 2>&1 | %filecheck %s + +# CHECK: Invalid Size + --- !ELF FileHeader: Class: ELFCLASS32 diff --git a/test/ARM/standalone/TwoSizeof/Sizeof.test b/test/ARM/standalone/TwoSizeof/Sizeof.test deleted file mode 100644 index cb18ef7d8..000000000 --- a/test/ARM/standalone/TwoSizeof/Sizeof.test +++ /dev/null @@ -1,4 +0,0 @@ -RUN: %yaml2obj %p/t.yaml -o %t.o -RUN: %not %link %linkopts -march=arm -static -T %p/t.t %t.o -o %t.out 2>&1 | %filecheck %s - -CHECK: Invalid Size diff --git a/test/lit.cfg b/test/lit.cfg index 8f561f88d..d97a4387f 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -43,7 +43,7 @@ execute_external = (platform.system() != 'Windows' config.test_format = eld_test_formats.get_test_format(config, execute_external) # suffixes: A list of file extensions to treat as test files. -config.suffixes = ['.test', '.s', '.ll'] +config.suffixes = ['.test', '.s', '.ll', '.yaml'] # excludes: A list of directories to exclude from the testsuite. The 'Inputs' # subdirectories contain auxiliary inputs for various tests in their parent From 04957ed5a1af787ebd57a1a8df91bef8f722baeb Mon Sep 17 00:00:00 2001 From: Alexey Karyakin Date: Wed, 13 Aug 2025 16:31:44 -0700 Subject: [PATCH 3/3] [RISC-V] Sort relocations The RISC-V psABI is silent on where R_RISCV_RELAX has to be in the relocation table relative to the affected relocation, as long as it is "at the same position". It is also not clear where exactly `.reloc` assembly directive must place the relocation. Therefore, sort all the RISC-V relocations. They were already sorted, but not always. Signed-off-by: Alexey Karyakin --- lib/Target/RISCV/RISCVLDBackend.cpp | 8 +-- .../RelocationOrder/RelocationOrder.yaml | 54 +++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 test/RISCV/standalone/Relaxation/RelocationOrder/RelocationOrder.yaml diff --git a/lib/Target/RISCV/RISCVLDBackend.cpp b/lib/Target/RISCV/RISCVLDBackend.cpp index 7bee75d87..f4d2bcdcb 100644 --- a/lib/Target/RISCV/RISCVLDBackend.cpp +++ b/lib/Target/RISCV/RISCVLDBackend.cpp @@ -1428,9 +1428,6 @@ bool RISCVLDBackend::handlePendingRelocations(ELFSection *section) { return false; } - if (m_PendingRelocations.empty()) - return true; - for (auto &r : m_PendingRelocations) if (!handleRelocation(std::get<0>(r), std::get<1>(r), *(std::get<2>(r)), std::get<3>(r), std::get<4>(r), /*pLastVisit*/ true)) @@ -1438,6 +1435,11 @@ bool RISCVLDBackend::handlePendingRelocations(ELFSection *section) { // Sort the relocation table, in offset order, since the pending relocations // that got added at end of the relocation table may not be in offset order. + // Another reason to sort relocations is to make sure R_RISCV_RELAX are + // adjacent to the relocation they affect. The psABI is not clear if + // R_RISCV_RELAX must be adjacent, but using `.reloc` in assembly may generate + // those that are not. Note that because of this, this function must not have + // earlier non-error exists. std::stable_sort(section->getRelocations().begin(), section->getRelocations().end(), [](Relocation *A, Relocation *B) { diff --git a/test/RISCV/standalone/Relaxation/RelocationOrder/RelocationOrder.yaml b/test/RISCV/standalone/Relaxation/RelocationOrder/RelocationOrder.yaml new file mode 100644 index 000000000..0a6cd7cc8 --- /dev/null +++ b/test/RISCV/standalone/Relaxation/RelocationOrder/RelocationOrder.yaml @@ -0,0 +1,54 @@ +# REQUIRES: riscv32 + +## R_RISCV_RELAX for the first call is not adjacent to its R_RISCV_CALL_PLT relocation. +## Check that it still gets relaxed. + +# RUN: %yaml2obj %s -o %t.o +# RUN: %link %linkopts -o %t.out %t.o +# RUN: %objdump -d %t.out | %filecheck %s + +# CHECK:
: +# CHECK-NEXT: 3ff5 jal 0x{{[[:xdigit:]]+}} + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_RISCV + Flags: [ EF_RISCV_RVC ] + SectionHeaderStringTable: .strtab +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x4 + Content: 6780000097000000E78000001300000097000000E7800000 + - Name: .rela.text + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x4 + Info: .text + Relocations: + - Offset: 0x4 + Symbol: f + Type: R_RISCV_CALL_PLT + - Offset: 0x10 + Symbol: f + Type: R_RISCV_CALL_PLT + - Offset: 0x4 + Type: R_RISCV_RELAX + - Type: SectionHeaderTable + Sections: + - Name: .strtab + - Name: .text + - Name: .rela.text + - Name: .symtab +Symbols: + - Name: f + Section: .text + - Name: main + Section: .text + Value: 0x4 +...