[llvm-branch-commits] [lld] release/19.x: [ELF] Support relocatable files using CREL with explicit addends (PR #101532)
Tobias Hieta via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Aug 2 00:26:45 PDT 2024
https://github.com/tru updated https://github.com/llvm/llvm-project/pull/101532
>From 142499d9a21309c7c5bacf34c35bb42fbffb7a8f Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Thu, 1 Aug 2024 10:22:03 -0700
Subject: [PATCH] [ELF] Support relocatable files using CREL with explicit
addends
... using the temporary section type code 0x40000020
(`clang -c -Wa,--crel,--allow-experimental-crel`). LLVM will change the
code and break compatibility (Clang and lld of different versions are
not guaranteed to cooperate, unlike other features). CREL with implicit
addends are not supported.
---
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.
SHT_REL to SHT_CREL conversion for -r/--emit-relocs is complex and
unsupported yet.
Link: https://discourse.llvm.org/t/rfc-crel-a-compact-relocation-format-for-elf/77600
Pull Request: https://github.com/llvm/llvm-project/pull/98115
(cherry picked from commit 0af07c078798b7c427e2981377781b5cc555a568)
---
lld/ELF/DWARF.cpp | 3 +-
lld/ELF/ICF.cpp | 8 +-
lld/ELF/InputFiles.cpp | 1 +
lld/ELF/InputFiles.h | 1 +
lld/ELF/InputSection.cpp | 67 ++++++++---
lld/ELF/InputSection.h | 14 ++-
lld/ELF/LinkerScript.cpp | 2 +
lld/ELF/MarkLive.cpp | 12 +-
lld/ELF/OutputSections.cpp | 132 ++++++++++++++++++++-
lld/ELF/OutputSections.h | 6 +
lld/ELF/Relocations.cpp | 38 ++++--
lld/ELF/Relocations.h | 92 ++++++++++++++
lld/ELF/SyntheticSections.cpp | 6 +-
lld/ELF/Writer.cpp | 13 +-
lld/test/ELF/crel-rel-mixed.s | 22 ++++
lld/test/ELF/crel.s | 90 ++++++++++++++
lld/test/ELF/debug-names.s | 2 +-
lld/test/ELF/gc-sections.s | 4 +
lld/test/ELF/icf1.s | 3 +
lld/test/ELF/icf4.s | 2 +-
lld/test/ELF/linkerscript/nocrossrefs.test | 4 +-
lld/test/ELF/relocatable-crel-32.s | 71 +++++++++++
lld/test/ELF/relocatable-crel.s | 107 +++++++++++++++++
23 files changed, 656 insertions(+), 44 deletions(-)
create mode 100644 lld/test/ELF/crel-rel-mixed.s
create mode 100644 lld/test/ELF/crel.s
create mode 100644 lld/test/ELF/relocatable-crel-32.s
create mode 100644 lld/test/ELF/relocatable-crel.s
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 a6b52d78fa806..44e8a71cc6286 100644
--- a/lld/ELF/ICF.cpp
+++ b/lld/ELF/ICF.cpp
@@ -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);
@@ -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);
@@ -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..8566baf61e1ab 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 cacheDecodedCrel(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 7857d857488c0..570e485455bad 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->cacheDecodedCrel(relSecIdx, sec);
+ sec->type = SHT_RELA;
+ sec->eqClass[0] = SHT_RELA;
+
+ RelocsCrel<ELFT::Is64Bits> entries(sec->content_);
+ sec->size = entries.size() * sizeof(typename ELFT::Rela);
+ auto *relas = makeThreadLocalN<typename ELFT::Rela>(entries.size());
+ sec->content_ = reinterpret_cast<uint8_t *>(relas);
+ for (auto [i, r] : llvm::enumerate(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 / sizeof(typename ELFT::Rela))};
+ 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;
}
@@ -1248,7 +1283,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()) {
@@ -1414,10 +1449,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 c89a545e1543f..6659530a9c9c2 100644
--- a/lld/ELF/InputSection.h
+++ b/lld/ELF/InputSection.h
@@ -35,17 +35,21 @@ class OutputSection;
LLVM_LIBRARY_VISIBILITY extern std::vector<Partition> partitions;
-// Returned by InputSectionBase::relsOrRelas. At least one member is empty.
+// Returned by InputSectionBase::relsOrRelas. At most one member is empty.
template <class ELFT> struct RelsOrRelas {
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(); }
};
#define invokeOnRelocs(sec, f, ...) \
{ \
const RelsOrRelas<ELFT> rs = (sec).template relsOrRelas<ELFT>(); \
- if (rs.areRelocsRel()) \
+ if (rs.areRelocsCrel()) \
+ f(__VA_ARGS__, rs.crels); \
+ else if (rs.areRelocsRel()) \
f(__VA_ARGS__, rs.rels); \
else \
f(__VA_ARGS__, rs.relas); \
@@ -209,7 +213,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;
@@ -483,7 +488,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..16e5883c2002c 100644
--- a/lld/ELF/MarkLive.cpp
+++ b/lld/ELF/MarkLive.cpp
@@ -85,6 +85,13 @@ static uint64_t getAddend(InputSectionBase &sec,
return rel.r_addend;
}
+// Currently, we assume all input CREL relocations have an explicit 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 +246,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 +318,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..29f18f89274f3 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.
@@ -505,6 +521,12 @@ void OutputSection::writeTo(uint8_t *buf, parallel::TaskGroup &tg) {
if (nonZeroFiller)
fill(buf, sections.empty() ? size : sections[0]->outSecOff, filler);
+ if (type == SHT_CREL && !(flags & SHF_ALLOC)) {
+ buf += encodeULEB128(crelHeader, buf);
+ memcpy(buf, crelBody.data(), crelBody.size());
+ return;
+ }
+
auto fn = [=](size_t begin, size_t end) {
size_t numSections = sections.size();
for (size_t i = begin; i != end; ++i) {
@@ -592,6 +614,103 @@ 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, Elf_Crel<sizeof(uint) == 8> &out,
+ uint offset, const Symbol &sym, uint32_t type, uint addend) {
+ const auto deltaOffset = static_cast<uint64_t>(offset - out.r_offset);
+ out.r_offset = 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 + (out.r_symidx != symidx) +
+ (out.r_type != type ? 2 : 0) +
+ (uint(out.r_addend) != 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 - out.r_symidx), os);
+ out.r_symidx = symidx;
+ }
+ if (b & 2) {
+ encodeSLEB128(static_cast<int32_t>(type - out.r_type), os);
+ out.r_type = type;
+ }
+ if (b & 4) {
+ encodeSLEB128(std::make_signed_t<uint>(addend - out.r_addend), os);
+ out.r_addend = addend;
+ }
+}
+
+template <class ELFT>
+static size_t relToCrel(raw_svector_ostream &os, Elf_Crel<ELFT::Is64Bits> &out,
+ InputSection *relSec, InputSectionBase *sec) {
+ const auto &file = *cast<ELFFileBase>(relSec->file);
+ if (relSec->type == SHT_REL) {
+ // REL conversion is complex and unsupported yet.
+ 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, out, 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;
+ Elf_Crel<is64> out{};
+ 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) {
+ encodeOneCrel<uint>(os, out, uint(sec->getVA(r.r_offset)),
+ file.getSymbol(r.r_symidx), r.r_type, r.r_addend);
+ }
+ continue;
+ }
+
+ // Convert REL[A] to CREL.
+ if constexpr (is64) {
+ totalCount += config->isLE ? relToCrel<ELF64LE>(os, out, relSec, sec)
+ : relToCrel<ELF64BE>(os, out, relSec, sec);
+ } else {
+ totalCount += config->isLE ? relToCrel<ELF32LE>(os, out, relSec, sec)
+ : relToCrel<ELF32BE>(os, out, relSec, sec);
+ }
+ }
+
+ crelHeader = totalCount * 8 + 4;
+ size = getULEB128Size(crelHeader) + crelBody.size();
+}
+
void OutputSection::finalize() {
InputSection *first = getFirstInputSection(this);
@@ -628,6 +747,13 @@ void OutputSection::finalize() {
InputSectionBase *s = first->getRelocatedSection();
info = s->getOutputSection()->sectionIndex;
flags |= SHF_INFO_LINK;
+ // Finalize the content of non-alloc CREL.
+ if (type == SHT_CREL) {
+ if (config->is64)
+ finalizeNonAllocCrel<true>();
+ else
+ finalizeNonAllocCrel<false>();
+ }
}
// Returns true if S is in one of the many forms the compiler driver may pass
diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h
index 78fede48a23f2..8c0c52f34ac9f 100644
--- a/lld/ELF/OutputSections.h
+++ b/lld/ELF/OutputSections.h
@@ -84,6 +84,11 @@ class OutputSection final : public SectionBase {
Expr alignExpr;
Expr lmaExpr;
Expr subalignExpr;
+
+ // Used by non-alloc SHT_CREL to hold the header and content byte stream.
+ uint64_t crelHeader = 0;
+ SmallVector<char, 0> crelBody;
+
SmallVector<SectionCommand *, 0> commands;
SmallVector<StringRef, 0> phdrs;
std::optional<std::array<uint8_t, 4>> filler;
@@ -106,6 +111,7 @@ class OutputSection final : public SectionBase {
// DATA_RELRO_END.
bool relro = false;
+ template <bool is64> void finalizeNonAllocCrel();
void finalize();
template <class ELFT>
void writeTo(uint8_t *buf, llvm::parallel::TaskGroup &tg);
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 9a799cd286135..e19b1e6c8efb8 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -1441,10 +1441,11 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
uint32_t symIndex = rel.getSymbol(config->isMips64EL);
Symbol &sym = sec->getFile<ELFT>()->getSymbol(symIndex);
RelType type;
- if constexpr (ELFT::Is64Bits) {
+ if constexpr (ELFT::Is64Bits || RelTy::IsCrel) {
type = rel.getType(config->isMips64EL);
++i;
} else {
+ // CREL is unsupported for MIPS N32.
if (config->mipsN32Abi) {
type = getMipsN32RelType(i);
} else {
@@ -1497,15 +1498,18 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
if ((type == R_PPC64_TLSGD && expr == R_TLSDESC_CALL) ||
(type == R_PPC64_TLSLD && expr == R_TLSLD_HINT)) {
- if (i == end) {
- errorOrWarn("R_PPC64_TLSGD/R_PPC64_TLSLD may not be the last "
- "relocation" +
- getLocation(*sec, sym, offset));
- return;
+ // Skip the error check for CREL, which does not set `end`.
+ if constexpr (!RelTy::IsCrel) {
+ if (i == end) {
+ errorOrWarn("R_PPC64_TLSGD/R_PPC64_TLSLD may not be the last "
+ "relocation" +
+ getLocation(*sec, sym, offset));
+ return;
+ }
}
- // Offset the 4-byte aligned R_PPC64_TLSGD by one byte in the NOTOC case,
- // so we can discern it later from the toc-case.
+ // Offset the 4-byte aligned R_PPC64_TLSGD by one byte in the NOTOC
+ // case, so we can discern it later from the toc-case.
if (i->getType(/*isMips64EL=*/false) == R_PPC64_REL24_NOTOC)
++offset;
}
@@ -1545,7 +1549,7 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
// instructions are generated by very old IBM XL compilers. Work around the
// issue by disabling GD/LD to IE/LE relaxation.
template <class RelTy>
-static void checkPPC64TLSRelax(InputSectionBase &sec, ArrayRef<RelTy> rels) {
+static void checkPPC64TLSRelax(InputSectionBase &sec, Relocs<RelTy> rels) {
// Skip if sec is synthetic (sec.file is null) or if sec has been marked.
if (!sec.file || sec.file->ppc64DisableTLSRelax)
return;
@@ -1593,9 +1597,15 @@ void RelocationScanner::scan(Relocs<RelTy> rels) {
if (isa<EhInputSection>(sec) || config->emachine == EM_S390)
rels = sortRels(rels, storage);
- end = static_cast<const void *>(rels.end());
- for (auto i = rels.begin(); i != end;)
- scanOne<ELFT, RelTy>(i);
+ if constexpr (RelTy::IsCrel) {
+ for (auto i = rels.begin(); i != rels.end();)
+ scanOne<ELFT, RelTy>(i);
+ } else {
+ // The non-CREL code path has additional check for PPC64 TLS.
+ end = static_cast<const void *>(rels.end());
+ for (auto i = rels.begin(); i != end;)
+ scanOne<ELFT, RelTy>(i);
+ }
// Sort relocations by offset for more efficient searching for
// R_RISCV_PCREL_HI20 and R_PPC64_ADDR64.
@@ -1611,7 +1621,9 @@ template <class ELFT> void RelocationScanner::scanSection(InputSectionBase &s) {
sec = &s;
getter = OffsetGetter(s);
const RelsOrRelas<ELFT> rels = s.template relsOrRelas<ELFT>();
- if (rels.areRelocsRel())
+ if (rels.areRelocsCrel())
+ scan<ELFT>(rels.crels);
+ else if (rels.areRelocsRel())
scan<ELFT>(rels.rels);
else
scan<ELFT>(rels.relas);
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index 77d8d52ca3d3f..aaa4581490a28 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -12,6 +12,7 @@
#include "lld/Common/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Object/ELFTypes.h"
#include <vector>
namespace lld::elf {
@@ -205,11 +206,91 @@ class ThunkCreator {
uint32_t pass = 0;
};
+// Decode LEB128 without error checking. Only used by performance critical code
+// like RelocsCrel.
+inline uint64_t readLEB128(const uint8_t *&p, uint64_t leb) {
+ uint64_t acc = 0, shift = 0, byte;
+ do {
+ byte = *p++;
+ acc |= (byte - 128 * (byte >= leb)) << shift;
+ shift += 7;
+ } while (byte >= 128);
+ return acc;
+}
+inline uint64_t readULEB128(const uint8_t *&p) { return readLEB128(p, 128); }
+inline int64_t readSLEB128(const uint8_t *&p) { return readLEB128(p, 64); }
+
+// This class implements a CREL iterator that does not allocate extra memory.
+template <bool is64> struct RelocsCrel {
+ using uint = std::conditional_t<is64, uint64_t, uint32_t>;
+ struct const_iterator {
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = llvm::object::Elf_Crel_Impl<is64>;
+ using difference_type = ptrdiff_t;
+ using pointer = value_type *;
+ using reference = const value_type &;
+ uint32_t count;
+ uint8_t flagBits, shift;
+ const uint8_t *p;
+ llvm::object::Elf_Crel_Impl<is64> crel{};
+ const_iterator(size_t hdr, const uint8_t *p)
+ : count(hdr / 8), flagBits(hdr & 4 ? 3 : 2), shift(hdr % 4), p(p) {
+ if (count)
+ step();
+ }
+ void step() {
+ // See object::decodeCrel.
+ const uint8_t b = *p++;
+ crel.r_offset += b >> flagBits << shift;
+ if (b >= 0x80)
+ crel.r_offset +=
+ ((readULEB128(p) << (7 - flagBits)) - (0x80 >> flagBits)) << shift;
+ if (b & 1)
+ crel.r_symidx += readSLEB128(p);
+ if (b & 2)
+ crel.r_type += readSLEB128(p);
+ if (b & 4 && flagBits == 3)
+ crel.r_addend += static_cast<uint>(readSLEB128(p));
+ }
+ llvm::object::Elf_Crel_Impl<is64> operator*() const { return crel; };
+ const llvm::object::Elf_Crel_Impl<is64> *operator->() const {
+ return &crel;
+ }
+ // For llvm::enumerate.
+ bool operator==(const const_iterator &r) const { return count == r.count; }
+ bool operator!=(const const_iterator &r) const { return count != r.count; }
+ const_iterator &operator++() {
+ if (--count)
+ step();
+ return *this;
+ }
+ // For RelocationScanner::scanOne.
+ void operator+=(size_t n) {
+ for (; n; --n)
+ operator++();
+ }
+ };
+
+ size_t hdr = 0;
+ const uint8_t *p = nullptr;
+
+ constexpr RelocsCrel() = default;
+ RelocsCrel(const uint8_t *p) : hdr(readULEB128(p)) { this->p = p; }
+ size_t size() const { return hdr / 8; }
+ const_iterator begin() const { return {hdr, p}; }
+ const_iterator end() const { return {0, nullptr}; }
+};
+
template <class RelTy> struct Relocs : ArrayRef<RelTy> {
Relocs() = default;
Relocs(ArrayRef<RelTy> a) : ArrayRef<RelTy>(a) {}
};
+template <bool is64>
+struct Relocs<llvm::object::Elf_Crel_Impl<is64>> : RelocsCrel<is64> {
+ using RelocsCrel<is64>::RelocsCrel;
+};
+
// Return a int64_t to make sure we get the sign extension out of the way as
// early as possible.
template <class ELFT>
@@ -220,6 +301,10 @@ template <class ELFT>
static inline int64_t getAddend(const typename ELFT::Rela &rel) {
return rel.r_addend;
}
+template <class ELFT>
+static inline int64_t getAddend(const typename ELFT::Crel &rel) {
+ return rel.r_addend;
+}
template <typename RelTy>
inline Relocs<RelTy> sortRels(Relocs<RelTy> rels,
@@ -235,6 +320,13 @@ inline Relocs<RelTy> sortRels(Relocs<RelTy> rels,
return rels;
}
+template <bool is64>
+inline Relocs<llvm::object::Elf_Crel_Impl<is64>>
+sortRels(Relocs<llvm::object::Elf_Crel_Impl<is64>> rels,
+ SmallVector<llvm::object::Elf_Crel_Impl<is64>, 0> &storage) {
+ return {};
+}
+
// Returns true if Expr refers a GOT entry. Note that this function returns
// false for TLS variables even though they need GOT, because TLS variables uses
// GOT differently than the regular variables.
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index b40ff0bc3cb03..41053c6472751 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -455,7 +455,8 @@ template <class ELFT>
void EhFrameSection::addSectionAux(EhInputSection *sec) {
if (!sec->isLive())
return;
- const RelsOrRelas<ELFT> rels = sec->template relsOrRelas<ELFT>();
+ const RelsOrRelas<ELFT> rels =
+ sec->template relsOrRelas<ELFT>(/*supportsCrel=*/false);
if (rels.areRelocsRel())
addRecords<ELFT>(sec, rels.rels);
else
@@ -489,7 +490,8 @@ void EhFrameSection::iterateFDEWithLSDA(
DenseSet<size_t> ciesWithLSDA;
for (EhInputSection *sec : sections) {
ciesWithLSDA.clear();
- const RelsOrRelas<ELFT> rels = sec->template relsOrRelas<ELFT>();
+ const RelsOrRelas<ELFT> rels =
+ sec->template relsOrRelas<ELFT>(/*supportsCrel=*/false);
if (rels.areRelocsRel())
iterateFDEWithLSDAAux<ELFT>(*sec, rels.rels, ciesWithLSDA, fn);
else
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 5cffdb771a738..8e3a746a08eb2 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -401,10 +401,19 @@ template <class ELFT> static void markUsedLocalSymbols() {
InputSection *isec = dyn_cast_or_null<InputSection>(s);
if (!isec)
continue;
- if (isec->type == SHT_REL)
+ if (isec->type == SHT_REL) {
markUsedLocalSymbolsImpl(f, isec->getDataAs<typename ELFT::Rel>());
- else if (isec->type == SHT_RELA)
+ } else if (isec->type == SHT_RELA) {
markUsedLocalSymbolsImpl(f, isec->getDataAs<typename ELFT::Rela>());
+ } else if (isec->type == SHT_CREL) {
+ // The is64=true variant also works with ELF32 since only the r_symidx
+ // member is used.
+ for (Elf_Crel_Impl<true> r : RelocsCrel<true>(isec->content_)) {
+ Symbol &sym = file->getSymbol(r.r_symidx);
+ if (sym.isLocal())
+ sym.used = true;
+ }
+ }
}
}
}
diff --git a/lld/test/ELF/crel-rel-mixed.s b/lld/test/ELF/crel-rel-mixed.s
new file mode 100644
index 0000000000000..a69fa1c09b436
--- /dev/null
+++ b/lld/test/ELF/crel-rel-mixed.s
@@ -0,0 +1,22 @@
+# REQUIRES: arm
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=armv7a -crel a.s -o a.o
+# RUN: llvm-mc -filetype=obj -triple=armv7a b.s -o b.o
+# RUN: not ld.lld -r a.o b.o 2>&1 | FileCheck %s --check-prefix=ERR
+
+# ERR: error: b.o:(.rel.text): REL cannot be converted to CREL
+
+#--- a.s
+.global _start, foo
+_start:
+ bl foo
+ bl .text.foo
+
+.section .text.foo,"ax"
+foo:
+ nop
+
+#--- b.s
+.globl fb
+fb:
+ bl fb
diff --git a/lld/test/ELF/crel.s b/lld/test/ELF/crel.s
new file mode 100644
index 0000000000000..d7c87be9a5402
--- /dev/null
+++ b/lld/test/ELF/crel.s
@@ -0,0 +1,90 @@
+# REQUIRES: x86
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64 -crel a.s -o a.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 -crel b.s -o b.o
+# RUN: ld.lld -pie a.o b.o -o out
+# RUN: llvm-objdump -d out | FileCheck %s
+# RUN: llvm-readelf -Srs out | FileCheck %s --check-prefix=RELOC
+
+# CHECK: <_start>:
+# CHECK-NEXT: callq {{.*}} <foo>
+# CHECK-NEXT: callq {{.*}} <foo>
+# CHECK-EMPTY:
+# CHECK-NEXT: <foo>:
+# CHECK-NEXT: leaq {{.*}} # 0x27c
+# CHECK-NEXT: leaq {{.*}} # 0x278
+
+# RELOC: .data PROGBITS {{0*}}[[#%x,DATA:]]
+
+# RELOC: {{0*}}[[#DATA+8]] 0000000000000008 R_X86_64_RELATIVE [[#%x,DATA+0x8000000000000000]]
+
+# RUN: ld.lld -pie --emit-relocs a.o b.o -o out1
+# RUN: llvm-objdump -dr out1 | FileCheck %s --check-prefix=CHECKE
+# RUN: llvm-readelf -Sr out1 | FileCheck %s --check-prefix=RELOCE
+
+# CHECKE: <_start>:
+# CHECKE-NEXT: callq {{.*}} <foo>
+# CHECKE-NEXT: R_X86_64_PLT32 foo-0x4
+# CHECKE-NEXT: callq {{.*}} <foo>
+# CHECKE-NEXT: R_X86_64_PLT32 .text+0x6
+# CHECKE-EMPTY:
+# CHECKE-NEXT: <foo>:
+# CHECKE-NEXT: leaq {{.*}}
+# CHECKE-NEXT: R_X86_64_PC32 .L.str-0x4
+# CHECKE-NEXT: leaq {{.*}}
+# CHECKE-NEXT: R_X86_64_PC32 .L.str1-0x4
+
+# RELOCE: .rodata PROGBITS {{0*}}[[#%x,RO:]]
+# RELOCE: .eh_frame PROGBITS {{0*}}[[#%x,EHFRAME:]]
+# RELOCE: .data PROGBITS {{0*}}[[#%x,DATA:]]
+
+# RELOCE: Relocation section '.crel.data' at offset {{.*}} contains 2 entries:
+# RELOCE-NEXT: Offset Info Type Symbol's Value Symbol's Name + Addend
+# RELOCE-NEXT: {{0*}}[[#DATA+8]] {{.*}} R_X86_64_64 {{.*}} .data - 8000000000000000
+# RELOCE-NEXT: {{0*}}[[#DATA+24]]{{.*}} R_X86_64_64 {{.*}} .data - 1
+# RELOCE: Relocation section '.crel.eh_frame' at offset {{.*}} contains 2 entries:
+# RELOCE-NEXT: Offset Info Type Symbol's Value Symbol's Name + Addend
+# RELOCE-NEXT: {{0*}}[[#EHFRAME+32]] {{.*}} R_X86_64_PC32 {{.*}} .text + 0
+# RELOCE-NEXT: {{0*}}[[#EHFRAME+52]] {{.*}} R_X86_64_PC32 {{.*}} .text + a
+# RELOCE: Relocation section '.crel.rodata' at offset {{.*}} contains 4 entries:
+# RELOCE-NEXT: Offset Info Type Symbol's Value Symbol's Name + Addend
+# RELOCE-NEXT: {{0*}}[[#RO+8]] {{.*}} R_X86_64_PC32 {{.*}} foo + 0
+# RELOCE-NEXT: {{0*}}[[#RO+23]] {{.*}} R_X86_64_PC32 {{.*}} foo + 3f
+# RELOCE-NEXT: {{0*}}[[#RO+39]] {{.*}} R_X86_64_PC64 {{.*}} foo + 7f
+# RELOCE-NEXT: {{0*}}[[#RO+47]] {{.*}} R_X86_64_PC32 {{.*}} _start - 1f81
+
+#--- a.s
+.global _start, foo
+_start:
+ .cfi_startproc # Test .eh_frame
+ call foo
+ call .text.foo
+ .cfi_endproc
+
+.section .text.foo,"ax"
+foo:
+ .cfi_startproc
+ leaq .L.str(%rip), %rsi
+ leaq .L.str1(%rip), %rsi
+ .cfi_endproc
+
+.section .rodata.str1.1,"aMS", at progbits,1
+.L.str:
+ .asciz "abc"
+.L.str1:
+ .asciz "def"
+
+.data
+.quad 0
+.quad .data - 0x8000000000000000
+.quad 0
+.quad .data - 1
+
+#--- b.s
+.section .rodata,"a"
+.long foo - .
+.space 15-4
+.long foo - . + 63 # offset+=15
+.space 16-4
+.quad foo - . + 127 # offset+=16
+.long _start - . - 8065
diff --git a/lld/test/ELF/debug-names.s b/lld/test/ELF/debug-names.s
index 888dd9007ed12..1bbb07b065e33 100644
--- a/lld/test/ELF/debug-names.s
+++ b/lld/test/ELF/debug-names.s
@@ -10,7 +10,7 @@
# REQUIRES: x86
# RUN: rm -rf %t && split-file %s %t && cd %t
-# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 --crel a.s -o a.o
# RUN: llvm-mc -filetype=obj -triple=x86_64 b.s -o b.o
# RUN: ld.lld --debug-names --no-debug-names a.o b.o -o out0
diff --git a/lld/test/ELF/gc-sections.s b/lld/test/ELF/gc-sections.s
index 94adc8210b4bc..31e00d495146a 100644
--- a/lld/test/ELF/gc-sections.s
+++ b/lld/test/ELF/gc-sections.s
@@ -8,6 +8,10 @@
# RUN: ld.lld --export-dynamic --gc-sections %t -o %t2
# RUN: llvm-readobj --sections --symbols %t2 | FileCheck -check-prefix=GC2 %s
+# RUN: llvm-mc -filetype=obj -triple=x86_64 --crel %s -o %t.o
+# RUN: ld.lld --gc-sections --print-gc-sections %t.o -o %t2 | FileCheck --check-prefix=GC1-DISCARD %s
+# RUN: llvm-readobj --sections --symbols %t2 | FileCheck -check-prefix=GC1 %s
+
# NOGC: Name: .eh_frame
# NOGC: Name: .text
# NOGC: Name: .init
diff --git a/lld/test/ELF/icf1.s b/lld/test/ELF/icf1.s
index 5c6e667d53c78..9682b06f4606f 100644
--- a/lld/test/ELF/icf1.s
+++ b/lld/test/ELF/icf1.s
@@ -3,6 +3,9 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: ld.lld %t -o /dev/null --icf=all --print-icf-sections | FileCheck %s
+# RUN: llvm-mc -filetype=obj -triple=x86_64 --crel %s -o %t
+# RUN: ld.lld %t -o /dev/null --icf=all --print-icf-sections | FileCheck %s
+
# CHECK: selected section {{.*}}:(.text.f1)
# CHECK: removing identical section {{.*}}:(.text.f2)
diff --git a/lld/test/ELF/icf4.s b/lld/test/ELF/icf4.s
index ff13a7ebff3da..310577a55c0d8 100644
--- a/lld/test/ELF/icf4.s
+++ b/lld/test/ELF/icf4.s
@@ -1,6 +1,6 @@
# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: llvm-mc --crel -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: ld.lld %t -o /dev/null --icf=all --print-icf-sections | count 0
.globl _start, f1, f2
diff --git a/lld/test/ELF/linkerscript/nocrossrefs.test b/lld/test/ELF/linkerscript/nocrossrefs.test
index f13d50a03be87..5eb56190fe63b 100644
--- a/lld/test/ELF/linkerscript/nocrossrefs.test
+++ b/lld/test/ELF/linkerscript/nocrossrefs.test
@@ -2,6 +2,7 @@
# RUN: rm -rf %t && split-file %s %t && cd %t
# RUN: llvm-mc --triple=x86_64 -filetype=obj a.s -o a.o
+# RUN: llvm-mc --triple=x86_64 -filetype=obj -crel a.s -o ac.o
# RUN: llvm-mc --triple=x86_64 -filetype=obj data.s -o data.o
# RUN: ld.lld a.o data.o -T 0.t 2>&1 | FileCheck %s --check-prefix=CHECK0 --implicit-check-not=warning:
@@ -9,7 +10,8 @@
# CHECK0-NEXT: warning: 0.t:4: ignored with fewer than 2 output sections
# RUN: not ld.lld a.o data.o -T 1.t 2>&1 | FileCheck %s --check-prefix=CHECK1 --implicit-check-not=error:
-# CHECK1: error: a.o:(.text.start+0x11): prohibited cross reference from '.text' to 'data' in '.data'
+# RUN: not ld.lld ac.o data.o -T 1.t 2>&1 | FileCheck %s --check-prefix=CHECK1 --implicit-check-not=error:
+# CHECK1: error: a{{.?}}.o:(.text.start+0x11): prohibited cross reference from '.text' to 'data' in '.data'
## .text and .text1 are in two NOCROSSREFS commands. Violations are reported twice.
# RUN: not ld.lld --threads=1 a.o data.o -T 2.t 2>&1 | FileCheck %s --check-prefix=CHECK2 --implicit-check-not=error:
diff --git a/lld/test/ELF/relocatable-crel-32.s b/lld/test/ELF/relocatable-crel-32.s
new file mode 100644
index 0000000000000..8fbf236d77452
--- /dev/null
+++ b/lld/test/ELF/relocatable-crel-32.s
@@ -0,0 +1,71 @@
+# REQUIRES: ppc
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=powerpc -crel a.s -o a.o
+# RUN: llvm-mc -filetype=obj -triple=powerpc -crel b.s -o b.o
+# RUN: ld.lld -r b.o a.o -o out
+# RUN: llvm-readobj -r out | FileCheck %s --check-prefixes=CHECK,CRELFOO
+
+# RUN: llvm-mc -filetype=obj -triple=powerpc a.s -o a1.o
+# RUN: ld.lld -r b.o a1.o -o out1
+# RUN: llvm-readobj -r out1 | FileCheck %s --check-prefixes=CHECK,RELAFOO
+# RUN: ld.lld -r a1.o b.o -o out2
+# RUN: llvm-readobj -r out2 | FileCheck %s --check-prefixes=CHECK2
+
+# CHECK: Relocations [
+# CHECK-NEXT: Section (2) .crel.text {
+# CHECK-NEXT: 0x0 R_PPC_REL24 fb 0x0
+# CHECK-NEXT: 0x4 R_PPC_REL24 foo 0x0
+# CHECK-NEXT: 0x8 R_PPC_REL24 .text.foo 0x0
+# CHECK-NEXT: 0xE R_PPC_ADDR16_HA .rodata.str1.1 0x4
+# CHECK-NEXT: 0x12 R_PPC_ADDR16_LO .rodata.str1.1 0x4
+# CHECK-NEXT: 0x16 R_PPC_ADDR16_HA .rodata.str1.1 0x0
+# CHECK-NEXT: 0x1A R_PPC_ADDR16_LO .rodata.str1.1 0x0
+# CHECK-NEXT: }
+# CRELFOO-NEXT: Section (4) .crel.text.foo {
+# RELAFOO-NEXT: Section (4) .rela.text.foo {
+# CHECK-NEXT: 0x0 R_PPC_REL24 g 0x0
+# CHECK-NEXT: 0x4 R_PPC_REL24 g 0x0
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+# CHECK2: Relocations [
+# CHECK2-NEXT: Section (2) .crel.text {
+# CHECK2-NEXT: 0x0 R_PPC_REL24 foo 0x0
+# CHECK2-NEXT: 0x4 R_PPC_REL24 .text.foo 0x0
+# CHECK2-NEXT: 0xA R_PPC_ADDR16_HA .rodata.str1.1 0x4
+# CHECK2-NEXT: 0xE R_PPC_ADDR16_LO .rodata.str1.1 0x4
+# CHECK2-NEXT: 0x12 R_PPC_ADDR16_HA .rodata.str1.1 0x0
+# CHECK2-NEXT: 0x16 R_PPC_ADDR16_LO .rodata.str1.1 0x0
+# CHECK2-NEXT: 0x18 R_PPC_REL24 fb 0x0
+# CHECK2-NEXT: }
+# CHECK2-NEXT: Section (4) .rela.text.foo {
+# CHECK2-NEXT: 0x0 R_PPC_REL24 g 0x0
+# CHECK2-NEXT: 0x4 R_PPC_REL24 g 0x0
+# CHECK2-NEXT: }
+# CHECK2-NEXT: ]
+
+#--- a.s
+.global _start, foo
+_start:
+ bl foo
+ bl .text.foo
+ lis 3, .L.str at ha
+ la 3, .L.str at l(3)
+ lis 3, .L.str1 at ha
+ la 3, .L.str1 at l(3)
+
+.section .text.foo,"ax"
+foo:
+ bl g
+ bl g
+
+.section .rodata.str1.1,"aMS", at progbits,1
+.L.str:
+ .asciz "abc"
+.L.str1:
+ .asciz "def"
+
+#--- b.s
+.globl fb
+fb:
+ bl fb
diff --git a/lld/test/ELF/relocatable-crel.s b/lld/test/ELF/relocatable-crel.s
new file mode 100644
index 0000000000000..6e97c3e24d66c
--- /dev/null
+++ b/lld/test/ELF/relocatable-crel.s
@@ -0,0 +1,107 @@
+# REQUIRES: x86
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64 -crel a.s -o a.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 -crel b.s -o b.o
+# RUN: ld.lld -r b.o a.o -o out
+# RUN: llvm-readobj -r out | FileCheck %s --check-prefixes=CHECK,CRELFOO
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a1.o
+# RUN: ld.lld -r b.o a1.o -o out1
+# RUN: llvm-readobj -r out1 | FileCheck %s --check-prefixes=CHECK,RELAFOO
+# RUN: ld.lld -r a1.o b.o -o out2
+# RUN: llvm-readobj -r out2 | FileCheck %s --check-prefixes=CHECK2
+
+# CHECK: Relocations [
+# CHECK-NEXT: .crel.text {
+# CHECK-NEXT: 0x1 R_X86_64_PLT32 fb 0xFFFFFFFFFFFFFFFC
+# CHECK-NEXT: 0x9 R_X86_64_PLT32 foo 0xFFFFFFFFFFFFFFFC
+# CHECK-NEXT: 0xE R_X86_64_PLT32 .text.foo 0xFFFFFFFFFFFFFFFC
+# CHECK-NEXT: }
+# CHECK-NEXT: .crel.rodata {
+# CHECK-NEXT: 0x0 R_X86_64_PC32 foo 0x0
+# CHECK-NEXT: 0xF R_X86_64_PC32 foo 0x3F
+# CHECK-NEXT: 0x1F R_X86_64_PC64 foo 0x7F
+# CHECK-NEXT: 0x27 R_X86_64_PC32 _start 0xFFFFFFFFFFFFE07F
+# CHECK-COUNT-12: R_X86_64_32 _start 0x0
+# CHECK-NEXT: }
+# CRELFOO-NEXT: .crel.text.foo {
+# RELAFOO-NEXT: .rela.text.foo {
+# CHECK-NEXT: 0x3 R_X86_64_PC32 .L.str 0xFFFFFFFFFFFFFFFC
+# CHECK-NEXT: 0xA R_X86_64_PC32 .L.str1 0xFFFFFFFFFFFFFFFC
+# CHECK-NEXT: 0xF R_X86_64_PLT32 g 0xFFFFFFFFFFFFFFFC
+# CHECK-NEXT: 0x14 R_X86_64_PLT32 g 0xFFFFFFFFFFFFFFFC
+# CHECK-NEXT: }
+# CRELFOO-NEXT: .crel.data {
+# RELAFOO-NEXT: .rela.data {
+# CHECK-NEXT: 0x8 R_X86_64_64 _start 0x8000000000000000
+# CHECK-NEXT: 0x18 R_X86_64_64 _start 0xFFFFFFFFFFFFFFFF
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+# CHECK2: Relocations [
+# CHECK2-NEXT: .crel.text {
+# CHECK2-NEXT: 0x1 R_X86_64_PLT32 foo 0xFFFFFFFFFFFFFFFC
+# CHECK2-NEXT: 0x6 R_X86_64_PLT32 .text.foo 0xFFFFFFFFFFFFFFFC
+# CHECK2-NEXT: 0xD R_X86_64_PLT32 fb 0xFFFFFFFFFFFFFFFC
+# CHECK2-NEXT: }
+# CHECK2-NEXT: .rela.text.foo {
+# CHECK2-NEXT: 0x3 R_X86_64_PC32 .L.str 0xFFFFFFFFFFFFFFFC
+# CHECK2-NEXT: 0xA R_X86_64_PC32 .L.str1 0xFFFFFFFFFFFFFFFC
+# CHECK2-NEXT: 0xF R_X86_64_PLT32 g 0xFFFFFFFFFFFFFFFC
+# CHECK2-NEXT: 0x14 R_X86_64_PLT32 g 0xFFFFFFFFFFFFFFFC
+# CHECK2-NEXT: }
+# CHECK2-NEXT: .rela.data {
+# CHECK2-NEXT: 0x8 R_X86_64_64 _start 0x8000000000000000
+# CHECK2-NEXT: 0x18 R_X86_64_64 _start 0xFFFFFFFFFFFFFFFF
+# CHECK2-NEXT: }
+# CHECK2-NEXT: .crel.rodata {
+# CHECK2-NEXT: 0x0 R_X86_64_PC32 foo 0x0
+# CHECK2-NEXT: 0xF R_X86_64_PC32 foo 0x3F
+# CHECK2-NEXT: 0x1F R_X86_64_PC64 foo 0x7F
+# CHECK2-NEXT: 0x27 R_X86_64_PC32 _start 0xFFFFFFFFFFFFE07F
+# CHECK2-COUNT-12: R_X86_64_32 _start 0x0
+# CHECK2-NEXT: }
+# CHECK2-NEXT: ]
+
+#--- a.s
+.global _start, foo
+_start:
+ call foo
+ call .text.foo
+
+.section .text.foo,"ax"
+foo:
+ leaq .L.str(%rip), %rsi
+ leaq .L.str1(%rip), %rsi
+ call g
+ call g
+
+.section .rodata.str1.1,"aMS", at progbits,1
+.L.str:
+ .asciz "abc"
+.L.str1:
+ .asciz "def"
+
+.data
+.quad 0
+.quad _start - 0x8000000000000000
+.quad 0
+.quad _start - 1
+
+#--- b.s
+.globl fb
+fb:
+ call fb
+
+.section .rodata,"a"
+.long foo - .
+.space 15-4
+.long foo - . + 63 # offset+=15
+.space 16-4
+.quad foo - . + 127 # offset+=16
+.long _start - . - 8065
+
+## Ensure .crel.rodata contains 16 relocations so that getULEB128Size(crelHeader) > 1.
+.rept 12
+.long _start
+.endr
More information about the llvm-branch-commits
mailing list