aboutsummaryrefslogtreecommitdiff
path: root/lld/MachO/UnwindInfoSection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lld/MachO/UnwindInfoSection.cpp')
-rw-r--r--lld/MachO/UnwindInfoSection.cpp248
1 files changed, 119 insertions, 129 deletions
diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp
index 8b1e357499aa..2c4a469578de 100644
--- a/lld/MachO/UnwindInfoSection.cpp
+++ b/lld/MachO/UnwindInfoSection.cpp
@@ -28,6 +28,7 @@
using namespace llvm;
using namespace llvm::MachO;
+using namespace llvm::support::endian;
using namespace lld;
using namespace lld::macho;
@@ -95,12 +96,49 @@ using namespace lld::macho;
// TODO(gkm): prune __eh_frame entries superseded by __unwind_info, PR50410
// TODO(gkm): how do we align the 2nd-level pages?
-template <class Ptr> struct CompactUnwindEntry {
- Ptr functionAddress;
+// The offsets of various fields in the on-disk representation of each compact
+// unwind entry.
+struct CompactUnwindOffsets {
+ uint32_t functionAddress;
+ uint32_t functionLength;
+ uint32_t encoding;
+ uint32_t personality;
+ uint32_t lsda;
+
+ CompactUnwindOffsets(size_t wordSize) {
+ if (wordSize == 8)
+ init<uint64_t>();
+ else {
+ assert(wordSize == 4);
+ init<uint32_t>();
+ }
+ }
+
+private:
+ template <class Ptr> void init() {
+ functionAddress = offsetof(Layout<Ptr>, functionAddress);
+ functionLength = offsetof(Layout<Ptr>, functionLength);
+ encoding = offsetof(Layout<Ptr>, encoding);
+ personality = offsetof(Layout<Ptr>, personality);
+ lsda = offsetof(Layout<Ptr>, lsda);
+ }
+
+ template <class Ptr> struct Layout {
+ Ptr functionAddress;
+ uint32_t functionLength;
+ compact_unwind_encoding_t encoding;
+ Ptr personality;
+ Ptr lsda;
+ };
+};
+
+// LLD's internal representation of a compact unwind entry.
+struct CompactUnwindEntry {
+ uint64_t functionAddress;
uint32_t functionLength;
compact_unwind_encoding_t encoding;
- Ptr personality;
- Ptr lsda;
+ Symbol *personality;
+ InputSection *lsda;
};
using EncodingMap = DenseMap<compact_unwind_encoding_t, size_t>;
@@ -114,26 +152,32 @@ struct SecondLevelPage {
EncodingMap localEncodingIndexes;
};
-template <class Ptr>
+// UnwindInfoSectionImpl allows us to avoid cluttering our header file with a
+// lengthy definition of UnwindInfoSection.
class UnwindInfoSectionImpl final : public UnwindInfoSection {
public:
- void prepareRelocations(ConcatInputSection *) override;
- void relocateCompactUnwind(std::vector<CompactUnwindEntry<Ptr>> &);
- Reloc *findLsdaReloc(ConcatInputSection *) const;
- void encodePersonalities();
+ UnwindInfoSectionImpl() : cuOffsets(target->wordSize) {}
+ uint64_t getSize() const override { return unwindInfoSize; }
+ void prepareRelocations() override;
void finalize() override;
void writeTo(uint8_t *buf) const override;
private:
+ void prepareRelocations(ConcatInputSection *);
+ void relocateCompactUnwind(std::vector<CompactUnwindEntry> &);
+ void encodePersonalities();
+
+ uint64_t unwindInfoSize = 0;
+ std::vector<decltype(symbols)::value_type> symbolsVec;
+ CompactUnwindOffsets cuOffsets;
std::vector<std::pair<compact_unwind_encoding_t, size_t>> commonEncodings;
EncodingMap commonEncodingIndexes;
// The entries here will be in the same order as their originating symbols
// in symbolsVec.
- std::vector<CompactUnwindEntry<Ptr>> cuEntries;
+ std::vector<CompactUnwindEntry> cuEntries;
// Indices into the cuEntries vector.
std::vector<size_t> cuIndices;
- // Indices of personality functions within the GOT.
- std::vector<Ptr> personalities;
+ std::vector<Symbol *> personalities;
SmallDenseMap<std::pair<InputSection *, uint64_t /* addend */>, Symbol *>
personalityTable;
// Indices into cuEntries for CUEs with a non-null LSDA.
@@ -149,15 +193,6 @@ UnwindInfoSection::UnwindInfoSection()
align = 4;
}
-void UnwindInfoSection::prepareRelocations() {
- // This iteration needs to be deterministic, since prepareRelocations may add
- // entries to the GOT. Hence the use of a MapVector for
- // UnwindInfoSection::symbols.
- for (const Defined *d : make_second_range(symbols))
- if (d->unwindEntry)
- prepareRelocations(d->unwindEntry);
-}
-
// Record function symbols that may need entries emitted in __unwind_info, which
// stores unwind data for address ranges.
//
@@ -183,13 +218,22 @@ void UnwindInfoSection::addSymbol(const Defined *d) {
}
}
+void UnwindInfoSectionImpl::prepareRelocations() {
+ // This iteration needs to be deterministic, since prepareRelocations may add
+ // entries to the GOT. Hence the use of a MapVector for
+ // UnwindInfoSection::symbols.
+ for (const Defined *d : make_second_range(symbols))
+ if (d->unwindEntry &&
+ d->unwindEntry->getName() == section_names::compactUnwind)
+ prepareRelocations(d->unwindEntry);
+}
+
// Compact unwind relocations have different semantics, so we handle them in a
// separate code path from regular relocations. First, we do not wish to add
// rebase opcodes for __LD,__compact_unwind, because that section doesn't
// actually end up in the final binary. Second, personality pointers always
// reside in the GOT and must be treated specially.
-template <class Ptr>
-void UnwindInfoSectionImpl<Ptr>::prepareRelocations(ConcatInputSection *isec) {
+void UnwindInfoSectionImpl::prepareRelocations(ConcatInputSection *isec) {
assert(!isec->shouldOmitFromOutput() &&
"__compact_unwind section should not be omitted");
@@ -206,8 +250,7 @@ void UnwindInfoSectionImpl<Ptr>::prepareRelocations(ConcatInputSection *isec) {
// compact unwind entries that references them, and thus appear as section
// relocs. There is no need to prepare them. We only prepare relocs for
// personality functions.
- if (r.offset % sizeof(CompactUnwindEntry<Ptr>) !=
- offsetof(CompactUnwindEntry<Ptr>, personality))
+ if (r.offset != cuOffsets.personality)
continue;
if (auto *s = r.referent.dyn_cast<Symbol *>()) {
@@ -230,7 +273,7 @@ void UnwindInfoSectionImpl<Ptr>::prepareRelocations(ConcatInputSection *isec) {
r.referent = s = sym;
}
if (auto *undefined = dyn_cast<Undefined>(s)) {
- treatUndefinedSymbol(*undefined);
+ treatUndefinedSymbol(*undefined, isec, r.offset);
// treatUndefinedSymbol() can replace s with a DylibSymbol; re-check.
if (isa<Undefined>(s))
continue;
@@ -265,8 +308,10 @@ void UnwindInfoSectionImpl<Ptr>::prepareRelocations(ConcatInputSection *isec) {
s = make<Defined>("<internal>", /*file=*/nullptr, referentIsec,
r.addend, /*size=*/0, /*isWeakDef=*/false,
/*isExternal=*/false, /*isPrivateExtern=*/false,
+ /*includeInSymtab=*/true,
/*isThumb=*/false, /*isReferencedDynamically=*/false,
/*noDeadStrip=*/false);
+ s->used = true;
in.got->addEntry(s);
}
r.referent = s;
@@ -275,64 +320,55 @@ void UnwindInfoSectionImpl<Ptr>::prepareRelocations(ConcatInputSection *isec) {
}
}
-// Unwind info lives in __DATA, and finalization of __TEXT will occur before
-// finalization of __DATA. Moreover, the finalization of unwind info depends on
-// the exact addresses that it references. So it is safe for compact unwind to
-// reference addresses in __TEXT, but not addresses in any other segment.
-static ConcatInputSection *checkTextSegment(InputSection *isec) {
- if (isec->getSegName() != segment_names::text)
- error("compact unwind references address in " + toString(isec) +
- " which is not in segment __TEXT");
- // __text should always be a ConcatInputSection.
- return cast<ConcatInputSection>(isec);
-}
-
// We need to apply the relocations to the pre-link compact unwind section
// before converting it to post-link form. There should only be absolute
// relocations here: since we are not emitting the pre-link CU section, there
// is no source address to make a relative location meaningful.
-template <class Ptr>
-void UnwindInfoSectionImpl<Ptr>::relocateCompactUnwind(
- std::vector<CompactUnwindEntry<Ptr>> &cuEntries) {
- parallelForEachN(0, symbolsVec.size(), [&](size_t i) {
- uint8_t *buf = reinterpret_cast<uint8_t *>(cuEntries.data()) +
- i * sizeof(CompactUnwindEntry<Ptr>);
+void UnwindInfoSectionImpl::relocateCompactUnwind(
+ std::vector<CompactUnwindEntry> &cuEntries) {
+ parallelFor(0, symbolsVec.size(), [&](size_t i) {
+ CompactUnwindEntry &cu = cuEntries[i];
const Defined *d = symbolsVec[i].second;
- // Write the functionAddress.
- writeAddress(buf, d->getVA(), sizeof(Ptr) == 8 ? 3 : 2);
+ cu.functionAddress = d->getVA();
if (!d->unwindEntry)
return;
- // Write the rest of the CUE.
- memcpy(buf + sizeof(Ptr), d->unwindEntry->data.data(),
- d->unwindEntry->data.size());
+ // If we have DWARF unwind info, create a CU entry that points to it.
+ if (d->unwindEntry->getName() == section_names::ehFrame) {
+ cu.encoding = target->modeDwarfEncoding | d->unwindEntry->outSecOff;
+ const FDE &fde = cast<ObjFile>(d->getFile())->fdes[d->unwindEntry];
+ cu.functionLength = fde.funcLength;
+ cu.personality = fde.personality;
+ cu.lsda = fde.lsda;
+ return;
+ }
+
+ assert(d->unwindEntry->getName() == section_names::compactUnwind);
+
+ auto buf = reinterpret_cast<const uint8_t *>(d->unwindEntry->data.data()) -
+ target->wordSize;
+ cu.functionLength =
+ support::endian::read32le(buf + cuOffsets.functionLength);
+ cu.encoding = support::endian::read32le(buf + cuOffsets.encoding);
for (const Reloc &r : d->unwindEntry->relocs) {
- uint64_t referentVA = 0;
- if (auto *referentSym = r.referent.dyn_cast<Symbol *>()) {
- if (!isa<Undefined>(referentSym)) {
- if (auto *defined = dyn_cast<Defined>(referentSym))
- checkTextSegment(defined->isec);
- // At this point in the link, we may not yet know the final address of
- // the GOT, so we just encode the index. We make it a 1-based index so
- // that we can distinguish the null pointer case.
- referentVA = referentSym->gotIndex + 1;
- }
- } else {
- auto *referentIsec = r.referent.get<InputSection *>();
- checkTextSegment(referentIsec);
- referentVA = referentIsec->getVA(r.addend);
+ if (r.offset == cuOffsets.personality) {
+ cu.personality = r.referent.get<Symbol *>();
+ } else if (r.offset == cuOffsets.lsda) {
+ if (auto *referentSym = r.referent.dyn_cast<Symbol *>())
+ cu.lsda = cast<Defined>(referentSym)->isec;
+ else
+ cu.lsda = r.referent.get<InputSection *>();
}
- writeAddress(buf + r.offset, referentVA, r.length);
}
});
}
// There should only be a handful of unique personality pointers, so we can
// encode them as 2-bit indices into a small array.
-template <class Ptr> void UnwindInfoSectionImpl<Ptr>::encodePersonalities() {
+void UnwindInfoSectionImpl::encodePersonalities() {
for (size_t idx : cuIndices) {
- CompactUnwindEntry<Ptr> &cu = cuEntries[idx];
- if (cu.personality == 0)
+ CompactUnwindEntry &cu = cuEntries[idx];
+ if (cu.personality == nullptr)
continue;
// Linear search is fast enough for a small array.
auto it = find(personalities, cu.personality);
@@ -348,7 +384,7 @@ template <class Ptr> void UnwindInfoSectionImpl<Ptr>::encodePersonalities() {
static_cast<compact_unwind_encoding_t>(UNWIND_PERSONALITY_MASK));
}
if (personalities.size() > 3)
- error("too many personalities (" + std::to_string(personalities.size()) +
+ error("too many personalities (" + Twine(personalities.size()) +
") for compact unwind to encode");
}
@@ -377,23 +413,9 @@ static bool canFoldEncoding(compact_unwind_encoding_t encoding) {
return true;
}
-template <class Ptr>
-Reloc *
-UnwindInfoSectionImpl<Ptr>::findLsdaReloc(ConcatInputSection *isec) const {
- if (isec == nullptr)
- return nullptr;
- auto it = llvm::find_if(isec->relocs, [](const Reloc &r) {
- return r.offset % sizeof(CompactUnwindEntry<Ptr>) ==
- offsetof(CompactUnwindEntry<Ptr>, lsda);
- });
- if (it == isec->relocs.end())
- return nullptr;
- return &*it;
-}
-
// Scan the __LD,__compact_unwind entries and compute the space needs of
// __TEXT,__unwind_info and __TEXT,__eh_frame.
-template <class Ptr> void UnwindInfoSectionImpl<Ptr>::finalize() {
+void UnwindInfoSectionImpl::finalize() {
if (symbols.empty())
return;
@@ -433,25 +455,9 @@ template <class Ptr> void UnwindInfoSectionImpl<Ptr>::finalize() {
cuEntries[*foldBegin].encoding == cuEntries[*foldEnd].encoding &&
cuEntries[*foldBegin].personality ==
cuEntries[*foldEnd].personality &&
- canFoldEncoding(cuEntries[*foldEnd].encoding)) {
- // In most cases, we can just compare the values of cuEntries[*].lsda.
- // However, it is possible for -rename_section to cause the LSDA section
- // (__gcc_except_tab) to be finalized after the unwind info section. In
- // that case, we don't yet have unique addresses for the LSDA entries.
- // So we check their relocations instead.
- // FIXME: should we account for an LSDA at an absolute address? ld64 seems
- // to support it, but it seems unlikely to be used in practice.
- Reloc *lsda1 = findLsdaReloc(symbolsVec[*foldBegin].second->unwindEntry);
- Reloc *lsda2 = findLsdaReloc(symbolsVec[*foldEnd].second->unwindEntry);
- if (lsda1 == nullptr && lsda2 == nullptr)
- continue;
- if (lsda1 == nullptr || lsda2 == nullptr)
- break;
- if (lsda1->referent != lsda2->referent)
- break;
- if (lsda1->addend != lsda2->addend)
- break;
- }
+ cuEntries[*foldBegin].lsda == cuEntries[*foldEnd].lsda &&
+ canFoldEncoding(cuEntries[*foldEnd].encoding))
+ ;
*foldWrite++ = *foldBegin;
foldBegin = foldEnd;
}
@@ -509,7 +515,7 @@ template <class Ptr> void UnwindInfoSectionImpl<Ptr>::finalize() {
sizeof(uint32_t);
while (wordsRemaining >= 1 && i < cuIndices.size()) {
idx = cuIndices[i];
- const CompactUnwindEntry<Ptr> *cuPtr = &cuEntries[idx];
+ const CompactUnwindEntry *cuPtr = &cuEntries[idx];
if (cuPtr->functionAddress >= functionAddressMax) {
break;
} else if (commonEncodingIndexes.count(cuPtr->encoding) ||
@@ -544,8 +550,7 @@ template <class Ptr> void UnwindInfoSectionImpl<Ptr>::finalize() {
for (size_t idx : cuIndices) {
lsdaIndex[idx] = entriesWithLsda.size();
- const Defined *d = symbolsVec[idx].second;
- if (findLsdaReloc(d->unwindEntry))
+ if (cuEntries[idx].lsda)
entriesWithLsda.push_back(idx);
}
@@ -564,8 +569,7 @@ template <class Ptr> void UnwindInfoSectionImpl<Ptr>::finalize() {
// All inputs are relocated and output addresses are known, so write!
-template <class Ptr>
-void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
+void UnwindInfoSectionImpl::writeTo(uint8_t *buf) const {
assert(!cuIndices.empty() && "call only if there is unwind info");
// section header
@@ -587,9 +591,8 @@ void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
*i32p++ = encoding.first;
// Personalities
- for (Ptr personality : personalities)
- *i32p++ =
- in.got->addr + (personality - 1) * target->wordSize - in.header->addr;
+ for (const Symbol *personality : personalities)
+ *i32p++ = personality->getGotVA() - in.header->addr;
// Level-1 index
uint32_t lsdaOffset =
@@ -608,7 +611,7 @@ void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
l2PagesOffset += SECOND_LEVEL_PAGE_BYTES;
}
// Level-1 sentinel
- const CompactUnwindEntry<Ptr> &cuEnd = cuEntries[cuIndices.back()];
+ const CompactUnwindEntry &cuEnd = cuEntries[cuIndices.back()];
iep->functionOffset =
cuEnd.functionAddress - in.header->addr + cuEnd.functionLength;
iep->secondLevelPagesSectionOffset = 0;
@@ -621,18 +624,8 @@ void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
auto *lep =
reinterpret_cast<unwind_info_section_header_lsda_index_entry *>(iep);
for (size_t idx : entriesWithLsda) {
- const CompactUnwindEntry<Ptr> &cu = cuEntries[idx];
- const Defined *d = symbolsVec[idx].second;
- if (Reloc *r = findLsdaReloc(d->unwindEntry)) {
- uint64_t va;
- if (auto *isec = r->referent.dyn_cast<InputSection *>()) {
- va = isec->getVA(r->addend);
- } else {
- auto *sym = r->referent.get<Symbol *>();
- va = sym->getVA() + r->addend;
- }
- lep->lsdaOffset = va - in.header->addr;
- }
+ const CompactUnwindEntry &cu = cuEntries[idx];
+ lep->lsdaOffset = cu.lsda->getVA(/*off=*/0) - in.header->addr;
lep->functionOffset = cu.functionAddress - in.header->addr;
lep++;
}
@@ -655,7 +648,7 @@ void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
p2p->encodingsCount = page.localEncodings.size();
auto *ep = reinterpret_cast<uint32_t *>(&p2p[1]);
for (size_t i = 0; i < page.entryCount; i++) {
- const CompactUnwindEntry<Ptr> &cue =
+ const CompactUnwindEntry &cue =
cuEntries[cuIndices[page.entryIndex + i]];
auto it = commonEncodingIndexes.find(cue.encoding);
if (it == commonEncodingIndexes.end())
@@ -675,7 +668,7 @@ void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
p2p->entryCount = page.entryCount;
auto *ep = reinterpret_cast<uint32_t *>(&p2p[1]);
for (size_t i = 0; i < page.entryCount; i++) {
- const CompactUnwindEntry<Ptr> &cue =
+ const CompactUnwindEntry &cue =
cuEntries[cuIndices[page.entryIndex + i]];
*ep++ = cue.functionAddress;
*ep++ = cue.encoding;
@@ -686,8 +679,5 @@ void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
}
UnwindInfoSection *macho::makeUnwindInfoSection() {
- if (target->wordSize == 8)
- return make<UnwindInfoSectionImpl<uint64_t>>();
- else
- return make<UnwindInfoSectionImpl<uint32_t>>();
+ return make<UnwindInfoSectionImpl>();
}