Skip to content

[ELF] Support relocatable files using CREL with explicit addends #98115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
3 changes: 2 additions & 1 deletion lld/ELF/DWARF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 7 additions & 1 deletion lld/ELF/ICF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,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:
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,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.
Expand Down
67 changes: 53 additions & 14 deletions lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 10 additions & 4 deletions lld/ELF/InputSection.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is the comment above now stale? I'd expect only one member to be non-empty now.

Copy link
Member Author

Choose a reason for hiding this comment

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

I believe the comment has been stale before this patch. Updated

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); \
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/LinkerScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 11 additions & 1 deletion lld/ELF/MarkLive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add a comment that we assume all input Crel relocations will have an explicit addend?

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,
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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);
Expand Down
Loading
Loading