[lld] [wip][LLD] Support RISCV vendor-specific relocations. (PR #168497)
Owen Anderson via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 18 18:54:00 PST 2025
https://github.com/resistor updated https://github.com/llvm/llvm-project/pull/168497
>From 4e2e28e05a25d18ad26ae661326bd14e85001c9c Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Tue, 18 Nov 2025 01:19:17 -0600
Subject: [PATCH 1/2] [LLD] Support RISCV vendor-specific relocations.
---
lld/ELF/Arch/RISCV.cpp | 37 ++++++--
lld/ELF/Arch/RISCVInternalRelocations.h | 113 ++++++++++++++++++++++++
lld/ELF/Relocations.cpp | 7 +-
lld/ELF/SyntheticSections.cpp | 54 +++++++++++
lld/ELF/SyntheticSections.h | 7 +-
lld/ELF/Target.cpp | 7 ++
6 files changed, 216 insertions(+), 9 deletions(-)
create mode 100644 lld/ELF/Arch/RISCVInternalRelocations.h
diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 5ed89e47c672e..5aa71db508446 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"
@@ -859,7 +860,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 +1504,18 @@ void RISCV::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> 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 = getRISCVVendorRelType(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<ELFT, RelTy>(it, type, rs.getAddend<ELFT>(*it, type));
@@ -1533,3 +1540,21 @@ template <class ELFT> void RISCV::scanSection1(InputSectionBase &sec) {
void RISCV::scanSection(InputSectionBase &sec) {
invokeELFT(scanSection1, sec);
}
+
+namespace lld::elf {
+uint32_t getRISCVVendorRelType(StringRef rvVendor) {
+ return StringSwitch<uint32_t>(rvVendor)
+ .Case("QUALCOMM", INTERNAL_RISCV_VENDOR_QUALCOMM)
+ .Case("ANDES", INTERNAL_RISCV_VENDOR_ANDES)
+ .Default(0);
+}
+
+std::optional<StringRef> getRISCVVendorString(RelType ty) {
+ if (ty.v & INTERNAL_RISCV_VENDOR_QUALCOMM)
+ return "QUALCOMM";
+ if (ty.v & 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..da9e8b9feef0d
--- /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 = 1 << 10;
+
+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 getRISCVVendorRelType(llvm::StringRef rvVendor);
+std::optional<llvm::StringRef> 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<Relocation>::iterator i,
+ MutableArrayRef<Relocation>::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 = getRISCVVendorRelType(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<Relocation>::iterator it;
+ MutableArrayRef<Relocation>::iterator end;
+ uint32_t rvVendorFlag = 0;
+};
+
+inline auto riscv_vendor_relocs(MutableArrayRef<Relocation> 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<DynamicReloc, 0> 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<DynamicReloc, 0> &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 <class ELFT>
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 <bool shard = false> 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<DynamicReloc, 0> &outRelocs);
// Used when parallel relocation scanning adds relocations. The elements
// will be moved into relocs by mergeRel().
SmallVector<SmallVector<DynamicReloc, 0>, 0> relocsVec;
@@ -538,6 +541,8 @@ class RelocationBaseSection : public SyntheticSection {
template <>
inline void RelocationBaseSection::addReloc<true>(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..9f87cc7d5f49c 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,12 @@ 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 (" + Twine(type) + ")").str();
return std::string(s);
>From ec1a0a5db39af1948a39b1e56fb94cca64a808b5 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Tue, 18 Nov 2025 20:53:12 -0600
Subject: [PATCH 2/2] Treat the vendor field of RelType as a single contiguous
field rather than a mask.
---
lld/ELF/Arch/RISCV.cpp | 4 ++--
lld/ELF/Arch/RISCVInternalRelocations.h | 2 +-
lld/ELF/Target.cpp | 2 ++
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 5aa71db508446..d21aac7a38303 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -1550,9 +1550,9 @@ uint32_t getRISCVVendorRelType(StringRef rvVendor) {
}
std::optional<StringRef> getRISCVVendorString(RelType ty) {
- if (ty.v & INTERNAL_RISCV_VENDOR_QUALCOMM)
+ if ((ty.v & INTERNAL_RISCV_VENDOR_MASK) == INTERNAL_RISCV_VENDOR_QUALCOMM)
return "QUALCOMM";
- if (ty.v & INTERNAL_RISCV_VENDOR_ANDES)
+ if ((ty.v & INTERNAL_RISCV_VENDOR_MASK) == INTERNAL_RISCV_VENDOR_ANDES)
return "ANDES";
return std::nullopt;
}
diff --git a/lld/ELF/Arch/RISCVInternalRelocations.h b/lld/ELF/Arch/RISCVInternalRelocations.h
index da9e8b9feef0d..0e9d80bba0a04 100644
--- a/lld/ELF/Arch/RISCVInternalRelocations.h
+++ b/lld/ELF/Arch/RISCVInternalRelocations.h
@@ -26,7 +26,7 @@ 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 = 1 << 10;
+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;
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index 9f87cc7d5f49c..3fc3e3f16e9e0 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -46,6 +46,8 @@ std::string elf::toStr(Ctx &ctx, RelType 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();
More information about the llvm-commits
mailing list