[lld] [ELF] Support relocatable files using CREL (PR #98115)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 8 23:14:11 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lld
@llvm/pr-subscribers-lld-elf
Author: Fangrui Song (MaskRay)
<details>
<summary>Changes</summary>
Introduce `RelsOrRelas::crels` to iterate over SHT_CREL sections and
update users to check `crels`.
(The decoding performance is critical and error checking is difficult.
Follow `skipLeb` and `R_*LEB128` handling, do not use
`llvm::decodeULEB128`, whichs compiles to a lot of code.)
A few users (e.g. .eh_frame, LLDDwarfObj, s390x) require random access. Pass
`/*supportsCrel=*/false` to `relsOrRelas` to allocate a buffer and
convert CREL to RELA (`relas` instead of `crels` will be used). Since
allocating a buffer increases, the conversion is only performed when
absolutely necessary.
---
Non-alloc SHT_CREL sections may be created in -r and --emit-relocs
links. SHT_CREL and SHT_RELA components need reencoding since
r_offset/r_symidx/r_type/r_addend may change. (r_type may change because
relocations referencing a symbol in a discarded section are converted to
`R_*_NONE`).
* SHT_CREL components: decode with `RelsOrRelas` and re-encode (`OutputSection::finalizeNonAllocCrel`)
* SHT_RELA components: convert to CREL (`relToCrel`). An output section can only have one relocation section.
* SHT_REL components: print an error for now.
---
**I'll land the `struct Relocs` change separately, once it looks reasonable, and then rebase this patch**
Link: https://discourse.llvm.org/t/rfc-crel-a-compact-relocation-format-for-elf/77600
---
Patch is 48.27 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/98115.diff
23 Files Affected:
- (modified) lld/ELF/DWARF.cpp (+2-1)
- (modified) lld/ELF/ICF.cpp (+16-10)
- (modified) lld/ELF/InputFiles.cpp (+1)
- (modified) lld/ELF/InputFiles.h (+1)
- (modified) lld/ELF/InputSection.cpp (+57-16)
- (modified) lld/ELF/InputSection.h (+9-5)
- (modified) lld/ELF/LinkerScript.cpp (+2)
- (modified) lld/ELF/MarkLive.cpp (+10-1)
- (modified) lld/ELF/OutputSections.cpp (+133-3)
- (modified) lld/ELF/OutputSections.h (+6)
- (modified) lld/ELF/Relocations.cpp (+44-38)
- (modified) lld/ELF/Relocations.h (+97-2)
- (modified) lld/ELF/SyntheticSections.cpp (+8-4)
- (modified) lld/ELF/SyntheticSections.h (+1-1)
- (modified) lld/ELF/Writer.cpp (+11-2)
- (added) lld/test/ELF/crel-rel-mixed.s (+22)
- (added) lld/test/ELF/crel.s (+81)
- (modified) lld/test/ELF/debug-names.s (+1-1)
- (modified) lld/test/ELF/gc-sections.s (+4)
- (modified) lld/test/ELF/icf1.s (+3)
- (modified) lld/test/ELF/icf4.s (+1-1)
- (added) lld/test/ELF/relocatable-crel-32.s (+71)
- (added) lld/test/ELF/relocatable-crel.s (+107)
``````````diff
diff --git a/lld/ELF/DWARF.cpp b/lld/ELF/DWARF.cpp
index 5d58e0c60a952..517d26810a378 100644
--- a/lld/ELF/DWARF.cpp
+++ b/lld/ELF/DWARF.cpp
@@ -136,7 +136,8 @@ template <class ELFT>
std::optional<RelocAddrEntry>
LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &s, uint64_t pos) const {
auto &sec = static_cast<const LLDDWARFSection &>(s);
- const RelsOrRelas<ELFT> rels = sec.sec->template relsOrRelas<ELFT>();
+ const RelsOrRelas<ELFT> rels =
+ sec.sec->template relsOrRelas<ELFT>(/*supportsCrel=*/false);
if (rels.areRelocsRel())
return findAux(*sec.sec, pos, rels.rels);
return findAux(*sec.sec, pos, rels.relas);
diff --git a/lld/ELF/ICF.cpp b/lld/ELF/ICF.cpp
index bfc605c793a92..44e8a71cc6286 100644
--- a/lld/ELF/ICF.cpp
+++ b/lld/ELF/ICF.cpp
@@ -103,12 +103,12 @@ template <class ELFT> class ICF {
void segregate(size_t begin, size_t end, uint32_t eqClassBase, bool constant);
template <class RelTy>
- bool constantEq(const InputSection *a, ArrayRef<RelTy> relsA,
- const InputSection *b, ArrayRef<RelTy> relsB);
+ bool constantEq(const InputSection *a, Relocs<RelTy> relsA,
+ const InputSection *b, Relocs<RelTy> relsB);
template <class RelTy>
- bool variableEq(const InputSection *a, ArrayRef<RelTy> relsA,
- const InputSection *b, ArrayRef<RelTy> relsB);
+ bool variableEq(const InputSection *a, Relocs<RelTy> relsA,
+ const InputSection *b, Relocs<RelTy> relsB);
bool equalsConstant(const InputSection *a, const InputSection *b);
bool equalsVariable(const InputSection *a, const InputSection *b);
@@ -235,8 +235,8 @@ void ICF<ELFT>::segregate(size_t begin, size_t end, uint32_t eqClassBase,
// Compare two lists of relocations.
template <class ELFT>
template <class RelTy>
-bool ICF<ELFT>::constantEq(const InputSection *secA, ArrayRef<RelTy> ra,
- const InputSection *secB, ArrayRef<RelTy> rb) {
+bool ICF<ELFT>::constantEq(const InputSection *secA, Relocs<RelTy> ra,
+ const InputSection *secB, Relocs<RelTy> rb) {
if (ra.size() != rb.size())
return false;
auto rai = ra.begin(), rae = ra.end(), rbi = rb.begin();
@@ -324,6 +324,8 @@ bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) {
const RelsOrRelas<ELFT> ra = a->template relsOrRelas<ELFT>();
const RelsOrRelas<ELFT> rb = b->template relsOrRelas<ELFT>();
+ if (ra.areRelocsCrel())
+ return constantEq(a, ra.crels, b, rb.crels);
return ra.areRelocsRel() || rb.areRelocsRel()
? constantEq(a, ra.rels, b, rb.rels)
: constantEq(a, ra.relas, b, rb.relas);
@@ -333,8 +335,8 @@ bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) {
// relocations point to the same section in terms of ICF.
template <class ELFT>
template <class RelTy>
-bool ICF<ELFT>::variableEq(const InputSection *secA, ArrayRef<RelTy> ra,
- const InputSection *secB, ArrayRef<RelTy> rb) {
+bool ICF<ELFT>::variableEq(const InputSection *secA, Relocs<RelTy> ra,
+ const InputSection *secB, Relocs<RelTy> rb) {
assert(ra.size() == rb.size());
auto rai = ra.begin(), rae = ra.end(), rbi = rb.begin();
@@ -374,6 +376,8 @@ template <class ELFT>
bool ICF<ELFT>::equalsVariable(const InputSection *a, const InputSection *b) {
const RelsOrRelas<ELFT> ra = a->template relsOrRelas<ELFT>();
const RelsOrRelas<ELFT> rb = b->template relsOrRelas<ELFT>();
+ if (ra.areRelocsCrel())
+ return variableEq(a, ra.crels, b, rb.crels);
return ra.areRelocsRel() || rb.areRelocsRel()
? variableEq(a, ra.rels, b, rb.rels)
: variableEq(a, ra.relas, b, rb.relas);
@@ -441,7 +445,7 @@ void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> fn) {
// hash.
template <class RelTy>
static void combineRelocHashes(unsigned cnt, InputSection *isec,
- ArrayRef<RelTy> rels) {
+ Relocs<RelTy> rels) {
uint32_t hash = isec->eqClass[cnt % 2];
for (RelTy rel : rels) {
Symbol &s = isec->file->getRelocTargetSym(rel);
@@ -505,7 +509,9 @@ template <class ELFT> void ICF<ELFT>::run() {
for (unsigned cnt = 0; cnt != 2; ++cnt) {
parallelForEach(sections, [&](InputSection *s) {
const RelsOrRelas<ELFT> rels = s->template relsOrRelas<ELFT>();
- if (rels.areRelocsRel())
+ if (rels.areRelocsCrel())
+ combineRelocHashes(cnt, s, rels.crels);
+ else if (rels.areRelocsRel())
combineRelocHashes(cnt, s, rels.rels);
else
combineRelocHashes(cnt, s, rels.relas);
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index 03ff4eadfe670..f1c0eb292361b 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -834,6 +834,7 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
case SHT_STRTAB:
case SHT_REL:
case SHT_RELA:
+ case SHT_CREL:
case SHT_NULL:
break;
case SHT_PROGBITS:
diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h
index 0617f41e1e13a..91d9273b48dfe 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -84,6 +84,7 @@ class InputFile {
assert(fileKind == ObjKind || fileKind == BinaryKind);
return sections;
}
+ void setSection(size_t i, InputSectionBase *s) { sections[i] = s; }
// Returns object file symbols. It is a runtime error to call this
// function on files of other types.
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index 4420be77f6685..30816f13bab7c 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -133,21 +133,56 @@ void InputSectionBase::decompress() const {
compressed = false;
}
-template <class ELFT> RelsOrRelas<ELFT> InputSectionBase::relsOrRelas() const {
+template <class ELFT>
+RelsOrRelas<ELFT> InputSectionBase::relsOrRelas(bool supportsCrel) const {
if (relSecIdx == 0)
return {};
RelsOrRelas<ELFT> ret;
- typename ELFT::Shdr shdr =
- cast<ELFFileBase>(file)->getELFShdrs<ELFT>()[relSecIdx];
+ auto *f = cast<ObjFile<ELFT>>(file);
+ typename ELFT::Shdr shdr = f->template getELFShdrs<ELFT>()[relSecIdx];
+ if (shdr.sh_type == SHT_CREL) {
+ // Return an iterator if supported by caller.
+ if (supportsCrel) {
+ ret.crels = Relocs<typename ELFT::Crel>(
+ (const uint8_t *)f->mb.getBufferStart() + shdr.sh_offset);
+ return ret;
+ }
+ InputSectionBase *const &relSec = f->getSections()[relSecIdx];
+ // Otherwise, allocate a buffer to hold the decoded RELA relocations. When
+ // called for the first time, relSec is null (without --emit-relocs) or an
+ // InputSection with zero eqClass[0].
+ if (!relSec || !cast<InputSection>(relSec)->eqClass[0]) {
+ auto *sec = makeThreadLocal<InputSection>(*f, shdr, name);
+ f->setSection(relSecIdx, sec);
+ cast<InputSection>(sec)->eqClass[0] = SHT_RELA;
+
+ RelocsCrel<ELFT::Is64Bits> entries(sec->content_);
+ sec->size = entries.size();
+ auto *relas = makeThreadLocalN<typename ELFT::Rela>(entries.size());
+ sec->content_ = reinterpret_cast<uint8_t *>(relas);
+ size_t i = 0;
+ for (Elf_Crel_Impl<ELFT::Is64Bits> r : entries) {
+ relas[i].r_offset = r.r_offset;
+ relas[i].setSymbolAndType(r.r_symidx, r.r_type, false);
+ relas[i++].r_addend = r.r_addend;
+ }
+ }
+ ret.relas = {ArrayRef(
+ reinterpret_cast<const typename ELFT::Rela *>(relSec->content_),
+ relSec->size)};
+ return ret;
+ }
+
+ const void *content = f->mb.getBufferStart() + shdr.sh_offset;
+ size_t size = shdr.sh_size;
if (shdr.sh_type == SHT_REL) {
- ret.rels = ArrayRef(reinterpret_cast<const typename ELFT::Rel *>(
- file->mb.getBufferStart() + shdr.sh_offset),
- shdr.sh_size / sizeof(typename ELFT::Rel));
+ ret.rels = {ArrayRef(reinterpret_cast<const typename ELFT::Rel *>(content),
+ size / sizeof(typename ELFT::Rel))};
} else {
assert(shdr.sh_type == SHT_RELA);
- ret.relas = ArrayRef(reinterpret_cast<const typename ELFT::Rela *>(
- file->mb.getBufferStart() + shdr.sh_offset),
- shdr.sh_size / sizeof(typename ELFT::Rela));
+ ret.relas = {
+ ArrayRef(reinterpret_cast<const typename ELFT::Rela *>(content),
+ size / sizeof(typename ELFT::Rela))};
}
return ret;
}
@@ -906,7 +941,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
// So, we handle relocations for non-alloc sections directly in this
// function as a performance optimization.
template <class ELFT, class RelTy>
-void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
+void InputSection::relocateNonAlloc(uint8_t *buf, Relocs<RelTy> rels) {
const unsigned bits = sizeof(typename ELFT::uint) * 8;
const TargetInfo &target = *elf::target;
const auto emachine = config->emachine;
@@ -1069,7 +1104,9 @@ void InputSectionBase::relocate(uint8_t *buf, uint8_t *bufEnd) {
// For a relocatable link, also call relocateNonAlloc() to rewrite applicable
// locations with tombstone values.
const RelsOrRelas<ELFT> rels = sec->template relsOrRelas<ELFT>();
- if (rels.areRelocsRel())
+ if (rels.areRelocsCrel())
+ sec->relocateNonAlloc<ELFT>(buf, rels.crels);
+ else if (rels.areRelocsRel())
sec->relocateNonAlloc<ELFT>(buf, rels.rels);
else
sec->relocateNonAlloc<ELFT>(buf, rels.relas);
@@ -1247,7 +1284,7 @@ SyntheticSection *EhInputSection::getParent() const {
// .eh_frame is a sequence of CIE or FDE records.
// This function splits an input section into records and returns them.
template <class ELFT> void EhInputSection::split() {
- const RelsOrRelas<ELFT> rels = relsOrRelas<ELFT>();
+ const RelsOrRelas<ELFT> rels = relsOrRelas<ELFT>(/*supportsCrel=*/false);
// getReloc expects the relocations to be sorted by r_offset. See the comment
// in scanRelocs.
if (rels.areRelocsRel()) {
@@ -1413,10 +1450,14 @@ template void InputSection::writeTo<ELF32BE>(uint8_t *);
template void InputSection::writeTo<ELF64LE>(uint8_t *);
template void InputSection::writeTo<ELF64BE>(uint8_t *);
-template RelsOrRelas<ELF32LE> InputSectionBase::relsOrRelas<ELF32LE>() const;
-template RelsOrRelas<ELF32BE> InputSectionBase::relsOrRelas<ELF32BE>() const;
-template RelsOrRelas<ELF64LE> InputSectionBase::relsOrRelas<ELF64LE>() const;
-template RelsOrRelas<ELF64BE> InputSectionBase::relsOrRelas<ELF64BE>() const;
+template RelsOrRelas<ELF32LE>
+InputSectionBase::relsOrRelas<ELF32LE>(bool) const;
+template RelsOrRelas<ELF32BE>
+InputSectionBase::relsOrRelas<ELF32BE>(bool) const;
+template RelsOrRelas<ELF64LE>
+InputSectionBase::relsOrRelas<ELF64LE>(bool) const;
+template RelsOrRelas<ELF64BE>
+InputSectionBase::relsOrRelas<ELF64BE>(bool) const;
template MergeInputSection::MergeInputSection(ObjFile<ELF32LE> &,
const ELF32LE::Shdr &, StringRef);
diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h
index ec12235f842a9..ecfff284b1388 100644
--- a/lld/ELF/InputSection.h
+++ b/lld/ELF/InputSection.h
@@ -37,9 +37,11 @@ LLVM_LIBRARY_VISIBILITY extern std::vector<Partition> partitions;
// Returned by InputSectionBase::relsOrRelas. At least one member is empty.
template <class ELFT> struct RelsOrRelas {
- ArrayRef<typename ELFT::Rel> rels;
- ArrayRef<typename ELFT::Rela> relas;
+ Relocs<typename ELFT::Rel> rels;
+ Relocs<typename ELFT::Rela> relas;
+ Relocs<typename ELFT::Crel> crels;
bool areRelocsRel() const { return rels.size(); }
+ bool areRelocsCrel() const { return crels.size(); }
};
// This is the base class of all sections that lld handles. Some are sections in
@@ -200,7 +202,8 @@ class InputSectionBase : public SectionBase {
// used by --gc-sections.
InputSectionBase *nextInSectionGroup = nullptr;
- template <class ELFT> RelsOrRelas<ELFT> relsOrRelas() const;
+ template <class ELFT>
+ RelsOrRelas<ELFT> relsOrRelas(bool supportsCrel = true) const;
// InputSections that are dependent on us (reverse dependency for GC)
llvm::TinyPtrVector<InputSection *> dependentSections;
@@ -407,7 +410,7 @@ class InputSection : public InputSectionBase {
InputSectionBase *getRelocatedSection() const;
template <class ELFT, class RelTy>
- void relocateNonAlloc(uint8_t *buf, llvm::ArrayRef<RelTy> rels);
+ void relocateNonAlloc(uint8_t *buf, Relocs<RelTy> rels);
// Points to the canonical section. If ICF folds two sections, repl pointer of
// one section points to the other.
@@ -474,7 +477,8 @@ class SyntheticSection : public InputSection {
};
inline bool isStaticRelSecType(uint32_t type) {
- return type == llvm::ELF::SHT_RELA || type == llvm::ELF::SHT_REL;
+ return type == llvm::ELF::SHT_RELA || type == llvm::ELF::SHT_CREL ||
+ type == llvm::ELF::SHT_REL;
}
inline bool isDebugSection(const InputSectionBase &sec) {
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index e2208da18dce0..055fa21d44ca6 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -61,6 +61,8 @@ static StringRef getOutputSectionName(const InputSectionBase *s) {
assert(config->relocatable && (rel->flags & SHF_LINK_ORDER));
return s->name;
}
+ if (s->type == SHT_CREL)
+ return saver().save(".crel" + out->name);
if (s->type == SHT_RELA)
return saver().save(".rela" + out->name);
return saver().save(".rel" + out->name);
diff --git a/lld/ELF/MarkLive.cpp b/lld/ELF/MarkLive.cpp
index 45431e44a6c8c..e188b93e7e2b9 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -85,6 +85,12 @@ static uint64_t getAddend(InputSectionBase &sec,
return rel.r_addend;
}
+template <class ELFT>
+static uint64_t getAddend(InputSectionBase &sec,
+ const typename ELFT::Crel &rel) {
+ return rel.r_addend;
+}
+
template <class ELFT>
template <class RelTy>
void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
@@ -239,7 +245,8 @@ template <class ELFT> void MarkLive<ELFT>::run() {
// all of them. We also want to preserve personality routines and LSDA
// referenced by .eh_frame sections, so we scan them for that here.
for (EhInputSection *eh : ctx.ehInputSections) {
- const RelsOrRelas<ELFT> rels = eh->template relsOrRelas<ELFT>();
+ const RelsOrRelas<ELFT> rels =
+ eh->template relsOrRelas<ELFT>(/*supportsCrel=*/false);
if (rels.areRelocsRel())
scanEhFrameSection(*eh, rels.rels);
else if (rels.relas.size())
@@ -310,6 +317,8 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
resolveReloc(sec, rel, false);
for (const typename ELFT::Rela &rel : rels.relas)
resolveReloc(sec, rel, false);
+ for (const typename ELFT::Crel &rel : rels.crels)
+ resolveReloc(sec, rel, false);
for (InputSectionBase *isec : sec.dependentSections)
enqueue(isec, 0);
diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index 60de10061c53d..709ede116c16f 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -18,6 +18,7 @@
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Config/llvm-config.h" // LLVM_ENABLE_ZLIB
#include "llvm/Support/Compression.h"
+#include "llvm/Support/LEB128.h"
#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TimeProfiler.h"
@@ -115,7 +116,19 @@ void OutputSection::recordSection(InputSectionBase *isec) {
// other InputSections.
void OutputSection::commitSection(InputSection *isec) {
if (LLVM_UNLIKELY(type != isec->type)) {
- if (hasInputSections || typeIsSet) {
+ if (!hasInputSections && !typeIsSet) {
+ type = isec->type;
+ } else if (isStaticRelSecType(type) && isStaticRelSecType(isec->type) &&
+ (type == SHT_CREL) != (isec->type == SHT_CREL)) {
+ // Combine mixed SHT_REL[A] and SHT_CREL to SHT_CREL.
+ type = SHT_CREL;
+ if (type == SHT_REL) {
+ if (name.consume_front(".rel"))
+ name = saver().save(".crel" + name);
+ } else if (name.consume_front(".rela")) {
+ name = saver().save(".crel" + name);
+ }
+ } else {
if (typeIsSet || !canMergeToProgbits(type) ||
!canMergeToProgbits(isec->type)) {
// The (NOLOAD) changes the section type to SHT_NOBITS, the intention is
@@ -133,8 +146,6 @@ void OutputSection::commitSection(InputSection *isec) {
}
if (!typeIsSet)
type = SHT_PROGBITS;
- } else {
- type = isec->type;
}
}
if (!hasInputSections) {
@@ -470,6 +481,11 @@ void OutputSection::writeTo(uint8_t *buf, parallel::TaskGroup &tg) {
llvm::TimeTraceScope timeScope("Write sections", name);
if (type == SHT_NOBITS)
return;
+ if (type == SHT_CREL && !(flags & SHF_ALLOC)) {
+ buf += encodeULEB128(crelHeader, buf);
+ memcpy(buf, crelBody.data(), crelBody.size());
+ return;
+ }
// If the section is compressed due to
// --compress-debug-section/--compress-sections, the content is already known.
@@ -592,6 +608,113 @@ static void finalizeShtGroup(OutputSection *os, InputSection *section) {
os->size = (1 + seen.size()) * sizeof(uint32_t);
}
+template <class uint>
+LLVM_ATTRIBUTE_ALWAYS_INLINE static void
+encodeOneCrel(raw_svector_ostream &os, uint &outOffset, uint32_t &outSymidx,
+ uint32_t &outType, uint &outAddend, uint offset,
+ const Symbol &sym, uint32_t type, uint addend) {
+ const auto deltaOffset = static_cast<uint64_t>(offset - outOffset);
+ outOffset = offset;
+ int64_t symidx = in.symTab->getSymbolIndex(sym);
+ if (sym.type == STT_SECTION) {
+ auto *d = dyn_cast<Defined>(&sym);
+ if (d) {
+ SectionBase *section = d->section;
+ assert(section->isLive());
+ addend = sym.getVA(addend) - section->getOutputSection()->addr;
+ } else {
+ // Encode R_*_NONE(symidx=0).
+ symidx = type = addend = 0;
+ }
+ }
+
+ // Similar to llvm::ELF::encodeCrel.
+ uint8_t b = deltaOffset * 8 + (outSymidx != symidx) +
+ (outType != type ? 2 : 0) + (outAddend != addend ? 4 : 0);
+ if (deltaOffset < 0x10) {
+ os << char(b);
+ } else {
+ os << char(b | 0x80);
+ encodeULEB128(deltaOffset >> 4, os);
+ }
+ if (b & 1) {
+ encodeSLEB128(static_cast<int32_t>(symidx - outSymidx), os);
+ outSymidx = symidx;
+ }
+ if (b & 2) {
+ encodeSLEB128(static_cast<int32_t>(type - outType), os);
+ outType = type;
+ }
+ if (b & 4) {
+ encodeSLEB128(std::make_signed_t<uint>(addend - outAddend), os);
+ outAddend = addend;
+ }
+}
+
+template <class ELFT>
+static size_t relToCrel(raw_svector_ostream &os, typename ELFT::uint &outOffset,
+ uint32_t &outSymidx, uint32_t &outType,
+ typename ELFT::uint &outAddend, InputSection *relSec,
+ InputSectionBase *sec) {
+ const auto &file = *cast<ELFFileBase>(relSec->file);
+ if (relSec->type == SHT_REL) {
+ errorOrWarn(toString(relSec) + ": REL cannot be converted to CREL");
+ return 0;
+ }
+ auto rels = relSec->getDataAs<typename ELFT::Rela>();
+ for (auto rel : rels) {
+ encodeOneCrel<typename ELFT::uint>(
+ os, outOffset, outSymidx, outType, outAddend, sec->getVA(rel.r_offset),
+ file.getRelocTargetSym(rel), rel.getType(config->isMips64EL),
+ getAddend<ELFT>(rel));
+ }
+ return rels.size();
+}
+
+// Compute the content of a non-alloc CREL section due to -r or --emit-relocs.
+// Input CREL sections are decoded while REL[A] need to be converted.
+template <bool is64> void OutputSection::finalizeNonAllocCrel() {
+ using uint = typename Elf_Crel_Impl<is64>::uint;
+ raw_svector_ostream os(crelBody);
+ uint64_t totalCount = 0;
+ uint outOffset = 0, outAddend = 0;
+ uint32_t outSymidx = 0, outType = 0;
+ assert(commands.size() == 1);
+ auto *isd = cast<InputSectionDescription>(commands[0]);
+ for (InputSection *relSec : isd->sections) {
+ const auto &file = *cast<ELFFileBase>(relSec->file);
+ InputSectionBase *sec = relSec->getRelocatedSection();
+ if (relSec->type == SHT_CREL) {
+ RelocsCrel<is64> entries(relSec->content_);
+ totalCount += entries.size();
+ for (Elf_Crel_Impl<is64> r : entries) {
+ ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/98115
More information about the llvm-commits
mailing list