diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index 5ed89e47c672e..b3a0f7e804020 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -8,6 +8,7 @@ #include "InputFiles.h" #include "OutputSections.h" +#include "RISCVInternalRelocations.h" #include "RelocScan.h" #include "Symbols.h" #include "SyntheticSections.h" @@ -345,8 +346,15 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s, case R_RISCV_SUB_ULEB128: return RE_RISCV_LEB128; default: - Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v - << ") against symbol " << &s; + if (type.v & INTERNAL_RISCV_VENDOR_MASK) { + Err(ctx) << getErrorLoc(ctx, loc) + << "unsupported vendor-specific relocation " << type + << " against symbol " << &s; + return R_NONE; + } + Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" + << (type.v & ~INTERNAL_RISCV_VENDOR_MASK) << ") against symbol " + << &s; return R_NONE; } } @@ -859,7 +867,7 @@ static bool relax(Ctx &ctx, int pass, InputSection &sec) { std::fill_n(aux.relocTypes.get(), relocs.size(), R_RISCV_NONE); aux.writes.clear(); - for (auto [i, r] : llvm::enumerate(relocs)) { + for (auto [i, r] : llvm::enumerate(riscv_vendor_relocs(relocs))) { const uint64_t loc = secAddr + r.offset - delta; uint32_t &cur = aux.relocDeltas[i], remove = 0; switch (r.type) { @@ -1503,12 +1511,18 @@ void RISCV::scanSectionImpl(InputSectionBase &sec, Relocs rels) { rvVendor = sym.getName(); continue; } else if (!rvVendor.empty()) { - Err(ctx) << getErrorLoc(ctx, loc) - << "unknown vendor-specific relocation (" << type.v - << ") in namespace '" << rvVendor << "' against symbol '" << &sym - << "'"; + uint32_t VendorFlag = getRISCVVendorRelMarker(rvVendor); + if (!VendorFlag) { + Err(ctx) << getErrorLoc(ctx, loc) + << "unknown vendor-specific relocation (" << type.v + << ") in namespace '" << rvVendor << "' against symbol '" + << &sym << "'"; + rvVendor = ""; + continue; + } + rvVendor = ""; - continue; + type.v |= VendorFlag; } rs.scan(it, type, rs.getAddend(*it, type)); @@ -1533,3 +1547,21 @@ template void RISCV::scanSection1(InputSectionBase &sec) { void RISCV::scanSection(InputSectionBase &sec) { invokeELFT(scanSection1, sec); } + +namespace lld::elf { +uint32_t getRISCVVendorRelMarker(StringRef rvVendor) { + return StringSwitch(rvVendor) + .Case("QUALCOMM", INTERNAL_RISCV_VENDOR_QUALCOMM) + .Case("ANDES", INTERNAL_RISCV_VENDOR_ANDES) + .Default(0); +} + +std::optional getRISCVVendorString(RelType ty) { + if ((ty.v & INTERNAL_RISCV_VENDOR_MASK) == INTERNAL_RISCV_VENDOR_QUALCOMM) + return "QUALCOMM"; + if ((ty.v & INTERNAL_RISCV_VENDOR_MASK) == INTERNAL_RISCV_VENDOR_ANDES) + return "ANDES"; + return std::nullopt; +} + +} // namespace lld::elf \ No newline at end of file diff --git a/lld/ELF/Arch/RISCVInternalRelocations.h b/lld/ELF/Arch/RISCVInternalRelocations.h new file mode 100644 index 0000000000000..3153dd0a3f9e7 --- /dev/null +++ b/lld/ELF/Arch/RISCVInternalRelocations.h @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_ELF_ARCH_RISCVINTERNALRELOCATIONS_H +#define LLD_ELF_ARCH_RISCVINTERNALRELOCATIONS_H + +#include "Relocations.h" +#include "Symbols.h" + +namespace lld::elf { + +// Bit 8 of RelType is used to indicate linker-internal relocations that are +// not vendor-specific. +// These are internal relocation numbers for GP/X0 relaxation. They aren't part +// of the psABI spec. +constexpr uint32_t INTERNAL_R_RISCV_GPREL_I = 256; +constexpr uint32_t INTERNAL_R_RISCV_GPREL_S = 257; +constexpr uint32_t INTERNAL_R_RISCV_X0REL_I = 258; +constexpr uint32_t INTERNAL_R_RISCV_X0REL_S = 259; + +// Bits 9 -> 31 of RelType are used to indicate vendor-specific relocations. +constexpr uint32_t INTERNAL_RISCV_VENDOR_MASK = 0xFFFFFFFF << 9; +constexpr uint32_t INTERNAL_RISCV_VENDOR_QUALCOMM = 1 << 9; +constexpr uint32_t INTERNAL_RISCV_VENDOR_ANDES = 2 << 9; + +constexpr uint32_t INTERNAL_RISCV_QC_ABS20_U = + INTERNAL_RISCV_VENDOR_QUALCOMM | llvm::ELF::R_RISCV_QC_ABS20_U; +constexpr uint32_t INTERNAL_RISCV_QC_E_BRANCH = + INTERNAL_RISCV_VENDOR_QUALCOMM | llvm::ELF::R_RISCV_QC_E_BRANCH; +constexpr uint32_t INTERNAL_RISCV_QC_E_32 = + INTERNAL_RISCV_VENDOR_QUALCOMM | llvm::ELF::R_RISCV_QC_E_32; +constexpr uint32_t INTERNAL_RISCV_QC_E_CALL_PLT = + INTERNAL_RISCV_VENDOR_QUALCOMM | llvm::ELF::R_RISCV_QC_E_CALL_PLT; + +constexpr uint32_t INTERNAL_RISCV_NDS_BRANCH_10 = + INTERNAL_RISCV_VENDOR_ANDES | llvm::ELF::R_RISCV_NDS_BRANCH_10; + +uint32_t getRISCVVendorRelMarker(llvm::StringRef rvVendor); +std::optional getRISCVVendorString(RelType ty); + +class vendor_reloc_iterator { +public: + using iterator_category = std::forward_iterator_tag; + using value_type = Relocation; + using difference_type = std::ptrdiff_t; + using pointer = Relocation *; + using reference = Relocation; // returned by value + + vendor_reloc_iterator(MutableArrayRef::iterator i, + MutableArrayRef::iterator e) + : it(i), end(e) {} + + // Dereference + Relocation operator*() const { + Relocation r = *it; + r.type.v |= rvVendorFlag; + return r; + } + + struct vendor_reloc_proxy { + Relocation r; + const Relocation *operator->() const { return &r; } + }; + + vendor_reloc_proxy operator->() const { + return vendor_reloc_proxy{this->operator*()}; + } + + vendor_reloc_iterator &operator++() { + ++it; + if (it != end && it->type == llvm::ELF::R_RISCV_VENDOR) { + rvVendorFlag = getRISCVVendorRelMarker(it->sym->getName()); + ++it; + } else { + rvVendorFlag = 0; + } + return *this; + } + + vendor_reloc_iterator operator++(int) { + vendor_reloc_iterator tmp(*this); + ++(*this); + return tmp; + } + + bool operator==(const vendor_reloc_iterator &other) const { + return it == other.it; + } + bool operator!=(const vendor_reloc_iterator &other) const { + return it != other.it; + } + + Relocation *getUnderlyingRelocation() const { return &*it; } + +private: + MutableArrayRef::iterator it; + MutableArrayRef::iterator end; + uint32_t rvVendorFlag = 0; +}; + +inline auto riscv_vendor_relocs(MutableArrayRef arr) { + return llvm::make_range(vendor_reloc_iterator(arr.begin(), arr.end()), + vendor_reloc_iterator(arr.end(), arr.end())); +} + +} // namespace lld::elf + +#endif \ No newline at end of file diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index d21376fd3ee47..75d50de95394f 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -1644,8 +1644,11 @@ void elf::postScanRelocations(Ctx &ctx) { } assert(ctx.symAux.size() == 1); - for (Symbol *sym : ctx.symtab->getSymbols()) - fn(*sym); + // When RISCV vendor-specific relocations are used in the GOT, a new marker + // symbol may be introduced during this iteration, so we have to use an + // invalidation-safe loop. + for (size_t i = 0; i < ctx.symtab->getSymbols().size(); ++i) + fn(*ctx.symtab->getSymbols()[i]); // Local symbols may need the aforementioned non-preemptible ifunc and GOT // handling. They don't need regular PLT. diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 19b08152ae081..2e2f0d16c16e6 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "SyntheticSections.h" +#include "Arch/RISCVInternalRelocations.h" #include "Config.h" #include "DWARF.h" #include "EhFrame.h" @@ -1703,6 +1704,18 @@ void DynamicReloc::finalize(Ctx &ctx, SymbolTableBaseSection *symt) { isFinal = true; // Catch errors } +size_t RelocationBaseSection::getSize() const { + size_t size = relocs.size() * entsize; + if (ctx.arg.emachine == EM_RISCV) { + for (const auto &reloc : relocs) { + if (reloc.type.v & INTERNAL_RISCV_VENDOR_MASK) { + size += entsize; + } + } + } + return size; +} + void RelocationBaseSection::computeRels() { SymbolTableBaseSection *symTab = getPartition(ctx).dynSymTab.get(); parallelForEach(relocs, [&ctx = ctx, symTab](DynamicReloc &rel) { @@ -1725,6 +1738,47 @@ void RelocationBaseSection::computeRels() { return std::tie(a.r_sym, a.r_offset) < std::tie(b.r_sym, b.r_offset); }); } + // Insert R_RISCV_VENDOR relocations very late, so that it doesn't interfere + // with relocation sorting above. + if (ctx.arg.emachine == EM_RISCV) { + SmallVector processedRelocs; + processedRelocs.reserve(relocs.size()); + for (auto reloc : relocs) { + auto vendorString = getRISCVVendorString(reloc.type); + if (vendorString) { + // Symbol *vendorSym = ctx.symtab->find(*vendorString); + auto *vendorSym = ctx.symtab->find(*vendorString); + if (!vendorSym || !vendorSym->isDefined()) { + vendorSym = ctx.symtab->addSymbol(Defined{ctx, nullptr, *vendorString, + STB_GLOBAL, STV_HIDDEN, + STT_NOTYPE, 0, 0, nullptr}); + symTab->addSymbol(vendorSym); + } + vendorSym->isUsedInRegularObj = true; + vendorSym->isExported = true; + processedRelocs.push_back({llvm::ELF::R_RISCV_VENDOR, reloc.inputSec, + reloc.offsetInSec, true, *vendorSym, 0, + R_ABS}); + processedRelocs.back().finalize(ctx, symTab); + } + + reloc.type.v &= ~INTERNAL_RISCV_VENDOR_MASK; + processedRelocs.push_back(reloc); + } + + relocs = std::move(processedRelocs); + } +} + +void RelocationBaseSection::maybeAddRISCVendorRelocation( + const DynamicReloc &reloc, SmallVector &outRelocs) { + auto riscvVendorString = getRISCVVendorString(reloc.type); + if (ctx.arg.emachine == llvm::ELF::EM_RISCV && riscvVendorString) { + Symbol &vendorSym = *ctx.symtab->addSymbol(Defined{ + ctx, ctx.internalFile, *riscvVendorString, llvm::ELF::STB_GLOBAL, + llvm::ELF::STV_HIDDEN, llvm::ELF::STT_NOTYPE, 0, 0, nullptr}); + vendorSym.isUsedInRegularObj = true; + } } template diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index 66c866d7e8cde..d5365242b85ce 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -478,6 +478,7 @@ class RelocationBaseSection : public SyntheticSection { /// This overload can be used if the addends are written directly instead of /// using relocations on the input section (e.g. MipsGotSection::writeTo()). template void addReloc(const DynamicReloc &reloc) { + maybeAddRISCVendorRelocation(reloc, relocs); relocs.push_back(reloc); } /// Add a dynamic relocation against \p sym with an optional addend. @@ -518,7 +519,7 @@ class RelocationBaseSection : public SyntheticSection { return !relocs.empty() || llvm::any_of(relocsVec, [](auto &v) { return !v.empty(); }); } - size_t getSize() const override { return relocs.size() * this->entsize; } + size_t getSize() const override; size_t getRelativeRelocCount() const { return numRelativeRelocs; } void mergeRels(); void partitionRels(); @@ -529,6 +530,8 @@ class RelocationBaseSection : public SyntheticSection { protected: void computeRels(); + void maybeAddRISCVendorRelocation(const DynamicReloc &reloc, + SmallVector &outRelocs); // Used when parallel relocation scanning adds relocations. The elements // will be moved into relocs by mergeRel(). SmallVector, 0> relocsVec; @@ -538,6 +541,8 @@ class RelocationBaseSection : public SyntheticSection { template <> inline void RelocationBaseSection::addReloc(const DynamicReloc &reloc) { + maybeAddRISCVendorRelocation(reloc, + relocsVec[llvm::parallel::getThreadIndex()]); relocsVec[llvm::parallel::getThreadIndex()].push_back(reloc); } diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index 89e4dbeed3109..3fc3e3f16e9e0 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -24,6 +24,7 @@ //===----------------------------------------------------------------------===// #include "Target.h" +#include "Arch/RISCVInternalRelocations.h" #include "InputFiles.h" #include "OutputSections.h" #include "RelocScan.h" @@ -40,6 +41,14 @@ using namespace lld::elf; std::string elf::toStr(Ctx &ctx, RelType type) { StringRef s = getELFRelocationTypeName(ctx.arg.emachine, type); + if (ctx.arg.emachine == EM_RISCV && s == "Unknown") { + auto VendorString = getRISCVVendorString(type); + if (VendorString) + s = getRISCVVendorRelocationTypeName(type & ~INTERNAL_RISCV_VENDOR_MASK, + *VendorString); + if (s == "Unknown") + return ("Unknown vendor-specific (" + Twine(type) + ")").str(); + } if (s == "Unknown") return ("Unknown (" + Twine(type) + ")").str(); return std::string(s); diff --git a/lld/test/ELF/riscv-vendor-relocations.s b/lld/test/ELF/riscv-vendor-relocations.s index b0f3c4a30d060..f121adec95cd0 100644 --- a/lld/test/ELF/riscv-vendor-relocations.s +++ b/lld/test/ELF/riscv-vendor-relocations.s @@ -8,12 +8,19 @@ TARGET: nop -.global INVALID_VENDOR +.local INVALID_VENDOR +.local QUALCOMM +.local ANDES .reloc 1f, R_RISCV_VENDOR, INVALID_VENDOR+0 .reloc 1f, R_RISCV_VENDOR, INVALID_VENDOR+0 .reloc 1f, R_RISCV_CUSTOM255, TARGET -1: - nop - # CHECK: error: {{.*}}:(.text+0x4): malformed consecutive R_RISCV_VENDOR relocations # CHECK: error: {{.*}}:(.text+0x4): unknown vendor-specific relocation (255) in namespace 'INVALID_VENDOR' against symbol 'TARGET' +.reloc 1f, R_RISCV_VENDOR, QUALCOMM+0 +.reloc 1f, R_RISCV_CUSTOM192, TARGET +# CHECK: error: {{.*}}:(.text+0x4): unsupported vendor-specific relocation R_RISCV_QC_ABS20_U against symbol TARGET +.reloc 1f, R_RISCV_VENDOR, ANDES+0 +.reloc 1f, R_RISCV_CUSTOM241, TARGET +# CHECK: error: {{.*}}:(.text+0x4): unsupported vendor-specific relocation R_RISCV_NDS_BRANCH_10 against symbol TARGET +1: + nop