Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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: 34 additions & 14 deletions lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1357,21 +1357,24 @@ 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.
// In rare cases (.eh_frame pieces are reordered by a linker script), the
// relocations may be unordered.
template <class ELFT> void EhInputSection::split() {
const RelsOrRelas<ELFT> rels = relsOrRelas<ELFT>(/*supportsCrel=*/false);
const RelsOrRelas<ELFT> elfRels = relsOrRelas<ELFT>();
if (elfRels.areRelocsCrel())
preprocessRelocs<ELFT>(elfRels.crels);
else if (elfRels.areRelocsRel())
preprocessRelocs<ELFT>(elfRels.rels);
else
preprocessRelocs<ELFT>(elfRels.relas);
auto cmp = [](const Relocation &a, const Relocation &b) {
return a.offset < b.offset;
};
if (!llvm::is_sorted(rels, cmp))
llvm::stable_sort(rels, cmp);

// getReloc expects the relocations to be sorted by r_offset. See the comment
// in scanRelocs.
if (rels.areRelocsRel()) {
SmallVector<typename ELFT::Rel, 0> storage;
split<ELFT>(sortRels(rels.rels, storage));
} else {
SmallVector<typename ELFT::Rela, 0> storage;
split<ELFT>(sortRels(rels.relas, storage));
}
}

template <class ELFT, class RelTy>
void EhInputSection::split(ArrayRef<RelTy> rels) {
ArrayRef<uint8_t> d = content();
const char *msg = nullptr;
unsigned relI = 0;
Expand All @@ -1397,10 +1400,10 @@ void EhInputSection::split(ArrayRef<RelTy> rels) {
// Find the first relocation that points to [off,off+size). Relocations
// have been sorted by r_offset.
const uint64_t off = d.data() - content().data();
while (relI != rels.size() && rels[relI].r_offset < off)
while (relI != rels.size() && rels[relI].offset < off)
++relI;
unsigned firstRel = -1;
if (relI != rels.size() && rels[relI].r_offset < off + size)
if (relI != rels.size() && rels[relI].offset < off + size)
firstRel = relI;
(id == 0 ? cies : fdes).emplace_back(off, this, size, firstRel);
d = d.slice(size);
Expand All @@ -1410,6 +1413,23 @@ void EhInputSection::split(ArrayRef<RelTy> rels) {
<< getObjMsg(d.data() - content().data());
}

template <class ELFT, class RelTy>
void EhInputSection::preprocessRelocs(Relocs<RelTy> elfRels) {
Ctx &ctx = file->ctx;
rels.reserve(elfRels.size());
for (auto rel : elfRels) {
uint64_t offset = rel.r_offset;
Symbol &sym = file->getSymbol(rel.getSymbol(ctx.arg.isMips64EL));
RelType type = rel.getType(ctx.arg.isMips64EL);
RelExpr expr = ctx.target->getRelExpr(type, sym, content().data() + offset);
int64_t addend =
RelTy::HasAddend
? getAddend<ELFT>(rel)
: ctx.target->getImplicitAddend(content().data() + offset, type);
rels.push_back({expr, type, offset, addend, &sym});
}
}

// Return the offset in an output section for a given input offset.
uint64_t EhInputSection::getParentOffset(uint64_t offset) const {
auto it = partition_point(
Expand Down
6 changes: 5 additions & 1 deletion lld/ELF/InputSection.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,14 +394,18 @@ class EhInputSection : public InputSectionBase {
StringRef name);
static bool classof(const SectionBase *s) { return s->kind() == EHFrame; }
template <class ELFT> void split();
template <class ELFT, class RelTy> void split(ArrayRef<RelTy> rels);
template <class ELFT, class RelTy> void preprocessRelocs(Relocs<RelTy> rels);

// Splittable sections are handled as a sequence of data
// rather than a single large blob of data.
SmallVector<EhSectionPiece, 0> cies, fdes;

SyntheticSection *getParent() const;
uint64_t getParentOffset(uint64_t offset) const;

// Preprocessed relocations in uniform format to avoid REL/RELA/CREL
// relocation format handling throughout the codebase.
SmallVector<Relocation, 0> rels;
};

// This is a section that is added directly to an output section
Expand Down
64 changes: 36 additions & 28 deletions lld/ELF/MarkLive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,9 @@ template <class ELFT, bool TrackWhyLive> class MarkLive {
void mark();

template <class RelTy>
void resolveReloc(InputSectionBase &sec, RelTy &rel, bool fromFDE);
void resolveReloc(InputSectionBase &sec, const RelTy &rel, bool fromFDE);

template <class RelTy>
void scanEhFrameSection(EhInputSection &eh, ArrayRef<RelTy> rels);
void scanEhFrameSection(EhInputSection &eh);

Ctx &ctx;
// The index of the partition that we are currently processing.
Expand Down Expand Up @@ -115,23 +114,38 @@ static uint64_t getAddend(Ctx &, InputSectionBase &sec,
template <class ELFT, bool TrackWhyLive>
template <class RelTy>
void MarkLive<ELFT, TrackWhyLive>::resolveReloc(InputSectionBase &sec,
RelTy &rel, bool fromFDE) {
const RelTy &rel,
bool fromFDE) {
// If a symbol is referenced in a live section, it is used.
Symbol &sym = sec.file->getRelocTargetSym(rel);
sym.used = true;
Symbol *sym;
if constexpr (std::is_same_v<RelTy, Relocation>) {
assert(isa<EhInputSection>(sec));
sym = rel.sym;
} else {
sym = &sec.file->getRelocTargetSym(rel);
}
sym->used = true;

LiveReason reason;
if (TrackWhyLive)
reason = {SecOffset(&sec, rel.r_offset), "referenced by"};
if (TrackWhyLive) {
if constexpr (std::is_same_v<RelTy, Relocation>)
reason = {SecOffset(&sec, rel.offset), "referenced by"};
else
reason = {SecOffset(&sec, rel.r_offset), "referenced by"};
}

if (auto *d = dyn_cast<Defined>(&sym)) {
if (auto *d = dyn_cast<Defined>(sym)) {
auto *relSec = dyn_cast_or_null<InputSectionBase>(d->section);
if (!relSec)
return;

uint64_t offset = d->value;
if (d->isSection())
offset += getAddend<ELFT>(ctx, sec, rel);
if (d->isSection()) {
if constexpr (std::is_same_v<RelTy, Relocation>)
offset += rel.addend;
else
offset += getAddend<ELFT>(ctx, sec, rel);
}

// fromFDE being true means this is referenced by a FDE in a .eh_frame
// piece. The relocation points to the described function or to a LSDA. We
Expand All @@ -141,8 +155,9 @@ void MarkLive<ELFT, TrackWhyLive>::resolveReloc(InputSectionBase &sec,
// associated text section is live, the LSDA will be retained due to section
// group/SHF_LINK_ORDER rules (b) if the associated text section should be
// discarded, marking the LSDA will unnecessarily retain the text section.
if (!(fromFDE && ((relSec->flags & (SHF_EXECINSTR | SHF_LINK_ORDER)) ||
relSec->nextInSectionGroup))) {
if (!(std::is_same_v<RelTy, Relocation> && fromFDE &&
((relSec->flags & (SHF_EXECINSTR | SHF_LINK_ORDER)) ||
relSec->nextInSectionGroup))) {
Symbol *canonicalSym = d;
if (TrackWhyLive && d->isSection()) {
// This is expensive, so ideally this would be deferred until it's known
Expand All @@ -159,15 +174,15 @@ void MarkLive<ELFT, TrackWhyLive>::resolveReloc(InputSectionBase &sec,
return;
}

if (auto *ss = dyn_cast<SharedSymbol>(&sym)) {
if (auto *ss = dyn_cast<SharedSymbol>(sym)) {
if (!ss->isWeak()) {
cast<SharedFile>(ss->file)->isNeeded = true;
if (TrackWhyLive)
whyLive.try_emplace(&sym, reason);
whyLive.try_emplace(sym, reason);
}
}

for (InputSectionBase *sec : cNamedSections.lookup(sym.getName()))
for (InputSectionBase *sec : cNamedSections.lookup(sym->getName()))
enqueue(sec, /*offset=*/0, /*sym=*/nullptr, reason);
}

Expand All @@ -186,9 +201,8 @@ void MarkLive<ELFT, TrackWhyLive>::resolveReloc(InputSectionBase &sec,
// the gc pass. With that we would be able to also gc some sections holding
// LSDAs and personality functions if we found that they were unused.
template <class ELFT, bool TrackWhyLive>
template <class RelTy>
void MarkLive<ELFT, TrackWhyLive>::scanEhFrameSection(EhInputSection &eh,
ArrayRef<RelTy> rels) {
void MarkLive<ELFT, TrackWhyLive>::scanEhFrameSection(EhInputSection &eh) {
ArrayRef<Relocation> rels = eh.rels;
for (const EhSectionPiece &cie : eh.cies)
if (cie.firstRelocation != unsigned(-1))
resolveReloc(eh, rels[cie.firstRelocation], false);
Expand All @@ -198,7 +212,7 @@ void MarkLive<ELFT, TrackWhyLive>::scanEhFrameSection(EhInputSection &eh,
continue;
uint64_t pieceEnd = fde.inputOff + fde.size;
for (size_t j = firstRelI, end2 = rels.size();
j < end2 && rels[j].r_offset < pieceEnd; ++j)
j < end2 && rels[j].offset < pieceEnd; ++j)
resolveReloc(eh, rels[j], true);
}
}
Expand Down Expand Up @@ -360,14 +374,8 @@ void MarkLive<ELFT, TrackWhyLive>::run() {
// that point to .eh_frames. Otherwise, the garbage collector would drop
// 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>(/*supportsCrel=*/false);
if (rels.areRelocsRel())
scanEhFrameSection(*eh, rels.rels);
else if (rels.relas.size())
scanEhFrameSection(*eh, rels.relas);
}
for (EhInputSection *eh : ctx.ehInputSections)
scanEhFrameSection(*eh);
for (InputSectionBase *sec : ctx.inputSections) {
if (sec->flags & SHF_GNU_RETAIN) {
enqueue(sec, /*offset=*/0, /*sym=*/nullptr, {std::nullopt, "retained"});
Expand Down
69 changes: 44 additions & 25 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,22 +405,17 @@ namespace {
class OffsetGetter {
public:
OffsetGetter() = default;
explicit OffsetGetter(InputSectionBase &sec) {
if (auto *eh = dyn_cast<EhInputSection>(&sec)) {
cies = eh->cies;
fdes = eh->fdes;
i = cies.begin();
j = fdes.begin();
}
explicit OffsetGetter(EhInputSection &sec) {
cies = sec.cies;
fdes = sec.fdes;
i = cies.begin();
j = fdes.begin();
}

// Translates offsets in input sections to offsets in output sections.
// Given offset must increase monotonically. We assume that Piece is
// sorted by inputOff.
uint64_t get(Ctx &ctx, uint64_t off) {
if (cies.empty())
return off;

while (j != fdes.end() && j->inputOff <= off)
++j;
auto it = j;
Expand Down Expand Up @@ -450,13 +445,12 @@ class OffsetGetter {
class RelocationScanner {
public:
RelocationScanner(Ctx &ctx) : ctx(ctx) {}
template <class ELFT>
void scanSection(InputSectionBase &s, bool isEH = false);
template <class ELFT> void scanSection(InputSectionBase &s);
template <class ELFT> void scanEhSection(EhInputSection &s);

private:
Ctx &ctx;
InputSectionBase *sec;
OffsetGetter getter;

// End of relocations, used by Mips/PPC64.
const void *end = nullptr;
Expand All @@ -473,6 +467,9 @@ class RelocationScanner {

template <class ELFT, class RelTy>
void scanOne(typename Relocs<RelTy>::const_iterator &i);
template <class ELFT, class RelIt>
Copy link
Collaborator

Choose a reason for hiding this comment

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

We've now got scanOne, processAux and now scanOneAux. Could we write a small comment explaining what each one is supposed to do? I think we may be able to do without scanOne.

Copy link
Member Author

Choose a reason for hiding this comment

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

Created #161229 to rewrite the file-level comment and give an overview.

void scanOneAux(RelIt &i, RelExpr expr, RelType type, uint64_t offset,
Symbol &sym, int64_t addend);
template <class ELFT, class RelTy> void scan(Relocs<RelTy> rels);
};
} // namespace
Expand Down Expand Up @@ -1511,9 +1508,7 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
}
}
// Get an offset in an output section this relocation is applied to.
uint64_t offset = getter.get(ctx, rel.r_offset);
if (offset == uint64_t(-1))
return;
uint64_t offset = rel.r_offset;

RelExpr expr =
ctx.target->getRelExpr(type, sym, sec->content().data() + offset);
Expand Down Expand Up @@ -1574,6 +1569,13 @@ void RelocationScanner::scanOne(typename Relocs<RelTy>::const_iterator &i) {
}
}

scanOneAux<ELFT>(i, expr, type, offset, sym, addend);
}

template <class ELFT, class RelIt>
void RelocationScanner::scanOneAux(RelIt &i, RelExpr expr, RelType type,
Copy link
Collaborator

Choose a reason for hiding this comment

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

IIUC this is the common tail between scanEhSection and scanSection.

As .ehframe sections describe code sections, I wouldn't expect to see any GOT generating or TLS relocations. Could we call processAux directly from scanEhSection instead of calling scanOneAux. Then we could fold this part back into scanOne.

Copy link
Member Author

Choose a reason for hiding this comment

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

Called process (renamed from processAux) directly now.

uint64_t offset, Symbol &sym,
int64_t addend) {
// If the relocation does not emit a GOT or GOTPLT entry but its computation
// uses their addresses, we need GOT or GOTPLT to be created.
//
Expand Down Expand Up @@ -1649,13 +1651,10 @@ void RelocationScanner::scan(Relocs<RelTy> rels) {
if (ctx.arg.emachine == EM_PPC64)
checkPPC64TLSRelax<RelTy>(*sec, rels);

// For EhInputSection, OffsetGetter expects the relocations to be sorted by
// r_offset. In rare cases (.eh_frame pieces are reordered by a linker
// script), the relocations may be unordered.
// On SystemZ, all sections need to be sorted by r_offset, to allow TLS
// relaxation to be handled correctly - see SystemZ::getTlsGdRelaxSkip.
SmallVector<RelTy, 0> storage;
if (isa<EhInputSection>(sec) || ctx.arg.emachine == EM_S390)
if (ctx.arg.emachine == EM_S390)
rels = sortRels(rels, storage);

if constexpr (RelTy::IsCrel) {
Expand All @@ -1680,11 +1679,9 @@ void RelocationScanner::scan(Relocs<RelTy> rels) {
});
}

template <class ELFT>
void RelocationScanner::scanSection(InputSectionBase &s, bool isEH) {
template <class ELFT> void RelocationScanner::scanSection(InputSectionBase &s) {
sec = &s;
getter = OffsetGetter(s);
const RelsOrRelas<ELFT> rels = s.template relsOrRelas<ELFT>(!isEH);
const RelsOrRelas<ELFT> rels = s.template relsOrRelas<ELFT>();
if (rels.areRelocsCrel())
scan<ELFT>(rels.crels);
else if (rels.areRelocsRel())
Expand All @@ -1693,6 +1690,28 @@ void RelocationScanner::scanSection(InputSectionBase &s, bool isEH) {
scan<ELFT>(rels.relas);
}

template <class ELFT> void RelocationScanner::scanEhSection(EhInputSection &s) {
sec = &s;
OffsetGetter getter(s);
auto rels = s.rels;
s.relocations.reserve(rels.size());
for (auto it = rels.begin(); it != rels.end();) {
auto i = it++;
// Ignore R_*_NONE and other marker relocations.
if (i->expr == R_NONE)
continue;
uint64_t offset = getter.get(ctx, i->offset);
Symbol *sym = i->sym;
// Skip if the relocation offset is within a dead piece.
if (offset == uint64_t(-1))
continue;
if (sym->isUndefined() &&
maybeReportUndefined(ctx, cast<Undefined>(*sym), *sec, offset))
continue;
scanOneAux<ELFT>(it, i->expr, i->type, offset, *sym, i->addend);
}
}

template <class ELFT> void elf::scanRelocations(Ctx &ctx) {
// Scan all relocations. Each relocation goes through a series of tests to
// determine if it needs special treatment, such as creating GOT, PLT,
Expand Down Expand Up @@ -1725,7 +1744,7 @@ template <class ELFT> void elf::scanRelocations(Ctx &ctx) {
RelocationScanner scanner(ctx);
for (Partition &part : ctx.partitions) {
for (EhInputSection *sec : part.ehFrame->sections)
scanner.template scanSection<ELFT>(*sec, /*isEH=*/true);
scanner.template scanEhSection<ELFT>(*sec);
if (part.armExidx && part.armExidx->isLive())
for (InputSection *sec : part.armExidx->exidxSections)
if (sec->isLive())
Expand Down
Loading