EhReader(InputSectionBase *s, ArrayRef<uint8_t> d) : isec(s), d(d) {}
size_t readEhRecordSize();
uint8_t getFdeEncoding();
+ bool hasLSDA();
private:
template <class P> void failOn(const P *loc, const Twine &msg) {
StringRef readString();
void skipLeb128();
void skipAugP();
+ StringRef getAugmentation();
InputSectionBase *isec;
ArrayRef<uint8_t> d;
return EhReader(p->sec, p->data()).getFdeEncoding();
}
-uint8_t EhReader::getFdeEncoding() {
+bool elf::hasLSDA(const EhSectionPiece &p) {
+ return EhReader(p.sec, p.data()).hasLSDA();
+}
+
+StringRef EhReader::getAugmentation() {
skipBytes(8);
int version = readByte();
if (version != 1 && version != 3)
readByte();
else
skipLeb128();
+ return aug;
+}
+uint8_t EhReader::getFdeEncoding() {
// We only care about an 'R' value, but other records may precede an 'R'
// record. Unfortunately records are not in TLV (type-length-value) format,
// so we need to teach the linker how to skip records for each type.
+ StringRef aug = getAugmentation();
for (char c : aug) {
if (c == 'R')
return readByte();
}
return DW_EH_PE_absptr;
}
+
+bool EhReader::hasLSDA() {
+ StringRef aug = getAugmentation();
+ for (char c : aug) {
+ if (c == 'L')
+ return true;
+ if (c == 'z')
+ skipLeb128();
+ else if (c == 'P')
+ skipAugP();
+ else if (c == 'R')
+ readByte();
+ else
+ failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
+ }
+ return false;
+}
size_t readEhRecordSize(InputSectionBase *s, size_t off);
uint8_t getFdeEncoding(EhSectionPiece *p);
+bool hasLSDA(const EhSectionPiece &p);
} // namespace elf
} // namespace lld
#include "ICF.h"
#include "Config.h"
+#include "EhFrame.h"
#include "LinkerScript.h"
#include "OutputSections.h"
#include "SymbolTable.h"
for (Symbol *sym : symtab->symbols())
sym->isPreemptible = computeIsPreemptible(*sym);
+ // Two text sections may have identical content and relocations but different
+ // LSDA, e.g. the two functions may have catch blocks of different types. If a
+ // text section is referenced by a .eh_frame FDE with LSDA, it is not
+ // eligible. This is implemented by iterating over CIE/FDE and setting
+ // eqClass[0] to the referenced text section from a live FDE.
+ //
+ // If two .gcc_except_table have identical semantics (usually identical
+ // content with PC-relative encoding), we will lose folding opportunity.
+ for (Partition &part : partitions)
+ part.ehFrame->iterateFDEWithLSDA<ELFT>(
+ [&](InputSection &s) { s.eqClass[0] = 1; });
+
// Collect sections to merge.
for (InputSectionBase *sec : inputSections) {
auto *s = cast<InputSection>(sec);
- if (isEligible(s))
+ if (isEligible(s) && s->eqClass[0] == 0)
sections.push_back(s);
}
parallelForEach(
sections, [&](InputSection *s) { s->eqClass[0] = xxHash64(s->data()); });
+ // Perform 2 rounds of relocation hash propagation. 2 is an empirical value to
+ // reduce the average sizes of equivalence classes, i.e. segregate() which has
+ // a large time complexity will have less work to do.
for (unsigned cnt = 0; cnt != 2; ++cnt) {
parallelForEach(sections, [&](InputSection *s) {
if (s->areRelocsRela)
unsigned firstRelocation)
: inputOff(off), sec(sec), size(size), firstRelocation(firstRelocation) {}
- ArrayRef<uint8_t> data() {
+ ArrayRef<uint8_t> data() const {
return {sec->data().data() + this->inputOff, size};
}
return rec;
}
-// There is one FDE per function. Returns true if a given FDE
-// points to a live function.
+// There is one FDE per function. Returns a non-null pointer to the function
+// symbol if the given FDE points to a live function.
template <class ELFT, class RelTy>
-bool EhFrameSection::isFdeLive(EhSectionPiece &fde, ArrayRef<RelTy> rels) {
+Defined *EhFrameSection::isFdeLive(EhSectionPiece &fde, ArrayRef<RelTy> rels) {
auto *sec = cast<EhInputSection>(fde.sec);
unsigned firstRelI = fde.firstRelocation;
// corresponding FDEs, which results in creating bad .eh_frame sections.
// To deal with that, we ignore such FDEs.
if (firstRelI == (unsigned)-1)
- return false;
+ return nullptr;
const RelTy &rel = rels[firstRelI];
Symbol &b = sec->template getFile<ELFT>()->getRelocTargetSym(rel);
// FDEs for garbage-collected or merged-by-ICF sections, or sections in
// another partition, are dead.
if (auto *d = dyn_cast<Defined>(&b))
- if (SectionBase *sec = d->section)
- return sec->partition == partition;
- return false;
+ if (d->section && d->section->partition == partition)
+ return d;
+ return nullptr;
}
// .eh_frame is a sequence of CIE or FDE records. In general, there
dependentSections.push_back(ds);
}
+// Used by ICF<ELFT>::handleLSDA(). This function is very similar to
+// EhFrameSection::addRecords().
+template <class ELFT, class RelTy>
+void EhFrameSection::iterateFDEWithLSDAAux(
+ EhInputSection &sec, ArrayRef<RelTy> rels, DenseSet<size_t> &ciesWithLSDA,
+ llvm::function_ref<void(InputSection &)> fn) {
+ for (EhSectionPiece &piece : sec.pieces) {
+ // Skip ZERO terminator.
+ if (piece.size == 4)
+ continue;
+
+ size_t offset = piece.inputOff;
+ uint32_t id =
+ endian::read32<ELFT::TargetEndianness>(piece.data().data() + 4);
+ if (id == 0) {
+ if (hasLSDA(piece))
+ ciesWithLSDA.insert(offset);
+ continue;
+ }
+ uint32_t cieOffset = offset + 4 - id;
+ if (ciesWithLSDA.count(cieOffset) == 0)
+ continue;
+
+ // The CIE has a LSDA argument. Call fn with d's section.
+ if (Defined *d = isFdeLive<ELFT>(piece, rels))
+ if (auto *s = dyn_cast_or_null<InputSection>(d->section))
+ fn(*s);
+ }
+}
+
+template <class ELFT>
+void EhFrameSection::iterateFDEWithLSDA(
+ llvm::function_ref<void(InputSection &)> fn) {
+ DenseSet<uint64_t> ciesWithLSDA;
+ for (EhInputSection *sec : sections) {
+ ciesWithLSDA.clear();
+ if (sec->areRelocsRela)
+ iterateFDEWithLSDAAux<ELFT>(*sec, sec->template relas<ELFT>(),
+ ciesWithLSDA, fn);
+ else
+ iterateFDEWithLSDAAux<ELFT>(*sec, sec->template rels<ELFT>(),
+ ciesWithLSDA, fn);
+ }
+}
+
static void writeCieFde(uint8_t *buf, ArrayRef<uint8_t> d) {
memcpy(buf, d.data(), d.size());
template class elf::MipsOptionsSection<ELF64LE>;
template class elf::MipsOptionsSection<ELF64BE>;
+template void EhFrameSection::iterateFDEWithLSDA<ELF32LE>(
+ function_ref<void(InputSection &)>);
+template void EhFrameSection::iterateFDEWithLSDA<ELF32BE>(
+ function_ref<void(InputSection &)>);
+template void EhFrameSection::iterateFDEWithLSDA<ELF64LE>(
+ function_ref<void(InputSection &)>);
+template void EhFrameSection::iterateFDEWithLSDA<ELF64BE>(
+ function_ref<void(InputSection &)>);
+
template class elf::MipsReginfoSection<ELF32LE>;
template class elf::MipsReginfoSection<ELF32BE>;
template class elf::MipsReginfoSection<ELF64LE>;
#include "DWARF.h"
#include "EhFrame.h"
#include "InputSection.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Support/Endian.h"
std::vector<FdeData> getFdeData() const;
ArrayRef<CieRecord *> getCieRecords() const { return cieRecords; }
+ template <class ELFT>
+ void iterateFDEWithLSDA(llvm::function_ref<void(InputSection &)> fn);
private:
// This is used only when parsing EhInputSection. We keep it here to avoid
template <class ELFT, class RelTy>
void addRecords(EhInputSection *s, llvm::ArrayRef<RelTy> rels);
- template <class ELFT>
- void addSectionAux(EhInputSection *s);
+ template <class ELFT> void addSectionAux(EhInputSection *s);
+ template <class ELFT, class RelTy>
+ void iterateFDEWithLSDAAux(EhInputSection &sec, ArrayRef<RelTy> rels,
+ llvm::DenseSet<size_t> &ciesWithLSDA,
+ llvm::function_ref<void(InputSection &)> fn);
template <class ELFT, class RelTy>
CieRecord *addCie(EhSectionPiece &piece, ArrayRef<RelTy> rels);
template <class ELFT, class RelTy>
- bool isFdeLive(EhSectionPiece &piece, ArrayRef<RelTy> rels);
+ Defined *isFdeLive(EhSectionPiece &piece, ArrayRef<RelTy> rels);
uint64_t getFdePc(uint8_t *buf, size_t off, uint8_t enc) const;
--- /dev/null
+# REQUIRES: x86
+## Test that text sections with LSDA are not folded.
+
+## Test REL.
+# RUN: llvm-mc -filetype=obj -triple=i386 %s -o %t1.o
+# RUN: ld.lld --icf=all %t1.o -o /dev/null --print-icf-sections | FileCheck %s --implicit-check-not=removing
+## Test RELA.
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t2.o
+# RUN: ld.lld --icf=all %t2.o -o /dev/null --print-icf-sections | FileCheck %s --implicit-check-not=removing
+
+# CHECK: selected section {{.*}}.o:(.text.Z1cv)
+# CHECK-NEXT: removing identical section {{.*}}.o:(.text.Z1dv)
+
+.globl _Z1av, _Z1bv, _Z1cv, _Z1dv
+.section .text.Z1av,"ax",@progbits
+_Z1av:
+ .cfi_startproc
+ .cfi_lsda 27, .Lexception0
+ ret
+ .cfi_endproc
+
+.section .text.Z1bv,"ax",@progbits
+_Z1bv:
+ .cfi_startproc
+ .cfi_lsda 27, .Lexception0
+ ret
+ .cfi_endproc
+
+.section .text.Z1cv,"ax",@progbits
+_Z1cv:
+ .cfi_startproc
+ ret
+ .cfi_endproc
+
+.section .text.Z1dv,"ax",@progbits
+_Z1dv:
+ .cfi_startproc
+ ret
+ .cfi_endproc
+
+.section .gcc_except_table,"a",@progbits
+## The actual content does not matter.
+.Lexception0: