Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 40 additions & 8 deletions lld/ELF/Arch/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "InputFiles.h"
#include "OutputSections.h"
#include "RISCVInternalRelocations.h"
#include "RelocScan.h"
#include "Symbols.h"
#include "SyntheticSections.h"
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -1503,12 +1511,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 = 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<ELFT, RelTy>(it, type, rs.getAddend<ELFT>(*it, type));
Expand All @@ -1533,3 +1547,21 @@ template <class ELFT> void RISCV::scanSection1(InputSectionBase &sec) {
void RISCV::scanSection(InputSectionBase &sec) {
invokeELFT(scanSection1, sec);
}

namespace lld::elf {
uint32_t getRISCVVendorRelMarker(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_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
113 changes: 113 additions & 0 deletions lld/ELF/Arch/RISCVInternalRelocations.h
Original file line number Diff line number Diff line change
@@ -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<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 = 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<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
7 changes: 5 additions & 2 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
54 changes: 54 additions & 0 deletions lld/ELF/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
//===----------------------------------------------------------------------===//

#include "SyntheticSections.h"
#include "Arch/RISCVInternalRelocations.h"
#include "Config.h"
#include "DWARF.h"
#include "EhFrame.h"
Expand Down Expand Up @@ -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) {
Expand All @@ -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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ctx, ctx.internalFile, *riscvVendorString, llvm::ELF::STB_GLOBAL,
ctx, ctx.internalFile, *riscvVendorString, llvm::ELF::STB_LOCAL,

I think we've said these need to be local, but maybe that's not right for dynamic relocs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I recalled that dynamic relocations could only point to global symbols, but I'm failing to find that requirement in the ELF spec.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oracle docs seem to agree with you:

The .dynsym table begins with the standard NULL symbol, followed by the files global symbols. STT_FILE symbols are typically not present in this symbol table. STT_SECTION symbols might be present if required by relocation entries.

From: https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-79797.html#stlac

I will go back to the ABI about this. We made them local to echo mapping symbols, but evidently that doesn't work for vendor-specific dynamic relocations.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Can you share the ABI discussion link once you bring it there?

llvm::ELF::STV_HIDDEN, llvm::ELF::STT_NOTYPE, 0, 0, nullptr});
vendorSym.isUsedInRegularObj = true;
}
}

template <class ELFT>
Expand Down
7 changes: 6 additions & 1 deletion lld/ELF/SyntheticSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand All @@ -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;
Expand All @@ -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);
}

Expand Down
9 changes: 9 additions & 0 deletions lld/ELF/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
//===----------------------------------------------------------------------===//

#include "Target.h"
#include "Arch/RISCVInternalRelocations.h"
#include "InputFiles.h"
#include "OutputSections.h"
#include "RelocScan.h"
Expand All @@ -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);
Expand Down
15 changes: 11 additions & 4 deletions lld/test/ELF/riscv-vendor-relocations.s
Original file line number Diff line number Diff line change
Expand Up @@ -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