diff --git a/lld/MachO/UnwindInfoSection.h b/lld/MachO/UnwindInfoSection.h --- a/lld/MachO/UnwindInfoSection.h +++ b/lld/MachO/UnwindInfoSection.h @@ -11,37 +11,29 @@ #include "ConcatOutputSection.h" #include "SyntheticSections.h" +#include "llvm/ADT/MapVector.h" #include "mach-o/compact_unwind_encoding.h" namespace lld { namespace macho { -template struct CompactUnwindEntry { - Ptr functionAddress; - uint32_t functionLength; - compact_unwind_encoding_t encoding; - Ptr personality; - Ptr lsda; -}; - class UnwindInfoSection : public SyntheticSection { public: - bool isNeeded() const override { - return !compactUnwindSection->inputs.empty() && !allEntriesAreOmitted; - } + // If all functions are free of unwind info, we can omit the unwind info + // section entirely. + bool isNeeded() const override { return !allEntriesAreOmitted; } uint64_t getSize() const override { return unwindInfoSize; } - virtual void addSymbol(const Defined *) = 0; - std::vector getInputs() { - return compactUnwindSection->inputs; - } + void addSymbol(const Defined *); void prepareRelocations(); protected: UnwindInfoSection(); virtual void prepareRelocations(ConcatInputSection *) = 0; - ConcatOutputSection *compactUnwindSection; + llvm::MapVector, + const Defined *> + symbols; uint64_t unwindInfoSize = 0; bool allEntriesAreOmitted = true; }; diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/BinaryFormat/MachO.h" +#include "llvm/Support/Parallel.h" using namespace llvm; using namespace llvm::MachO; @@ -92,6 +93,14 @@ // TODO(gkm): prune __eh_frame entries superseded by __unwind_info, PR50410 // TODO(gkm): how do we align the 2nd-level pages? +template struct CompactUnwindEntry { + Ptr functionAddress; + uint32_t functionLength; + compact_unwind_encoding_t encoding; + Ptr personality; + Ptr lsda; +}; + using EncodingMap = DenseMap; struct SecondLevelPage { @@ -107,7 +116,7 @@ class UnwindInfoSectionImpl final : public UnwindInfoSection { public: void prepareRelocations(ConcatInputSection *) override; - void addSymbol(const Defined *) override; + void relocateCompactUnwind(std::vector> &); void finalize() override; void writeTo(uint8_t *buf) const override; @@ -131,20 +140,38 @@ UnwindInfoSection::UnwindInfoSection() : SyntheticSection(segment_names::text, section_names::unwindInfo) { align = 4; - compactUnwindSection = - make(section_names::compactUnwind); } void UnwindInfoSection::prepareRelocations() { - for (ConcatInputSection *isec : compactUnwindSection->inputs) - prepareRelocations(isec); + // This iteration needs to be deterministic -- hence the use of a MapVector + // for UnwindInfoSection::symbols. + for (const Defined *d : make_second_range(symbols)) + if (d->compactUnwind) + prepareRelocations(d->compactUnwind); } -template -void UnwindInfoSectionImpl::addSymbol(const Defined *d) { - if (d->compactUnwind) { - d->compactUnwind->parent = compactUnwindSection; - compactUnwindSection->addInput(d->compactUnwind); +// Record function symbols that may need entries emitted in __unwind_info, which +// stores unwind data for address ranges. +// +// Note that if several adjacent functions have the same unwind encoding, LSDA, +// and personality function, they share one unwind entry. For this to work, +// functions without unwind info need explicit "no unwind info" unwind entries +// -- else the unwinder would think they have the unwind info of the closest +// function with unwind info right before in the image. Thus, we add function +// symbols for each unique address regardless of whether they have associated +// unwind info. +void UnwindInfoSection::addSymbol(const Defined *d) { + if (d->compactUnwind) + allEntriesAreOmitted = false; + // We don't yet know the final output address of this symbol, but we know that + // they are uniquely determined by a combination of the isec and value, so + // we use that as the key here. + auto p = symbols.insert({{d->isec, d->value}, d}); + // If we have multiple symbols at the same address, only one of them can have + // an associated CUE. + if (!p.second && d->compactUnwind) { + assert(!p.first->second->compactUnwind); + p.first->second = d; } } @@ -167,18 +194,6 @@ Reloc &r = isec->relocs[i]; assert(target->hasAttr(r.type, RelocAttrBits::UNSIGNED)); - if (r.offset % sizeof(CompactUnwindEntry) == 0) { - InputSection *referentIsec; - if (auto *isec = r.referent.dyn_cast()) - referentIsec = isec; - else - referentIsec = cast(r.referent.dyn_cast())->isec; - - if (!cast(referentIsec)->shouldOmitFromOutput()) - allEntriesAreOmitted = false; - continue; - } - if (r.offset % sizeof(CompactUnwindEntry) != offsetof(CompactUnwindEntry, personality)) continue; @@ -246,17 +261,21 @@ // relocations here: since we are not emitting the pre-link CU section, there // is no source address to make a relative location meaningful. template -static void -relocateCompactUnwind(ConcatOutputSection *compactUnwindSection, - std::vector> &cuVector) { - for (const ConcatInputSection *isec : compactUnwindSection->inputs) { - assert(isec->parent == compactUnwindSection); - - uint8_t *buf = - reinterpret_cast(cuVector.data()) + isec->outSecOff; - memcpy(buf, isec->data.data(), isec->data.size()); - - for (const Reloc &r : isec->relocs) { +void UnwindInfoSectionImpl::relocateCompactUnwind( + std::vector> &cuVector) { + auto symbolsVec = symbols.takeVector(); + parallelForEachN(0, symbolsVec.size(), [&](size_t i) { + uint8_t *buf = reinterpret_cast(cuVector.data()) + + i * sizeof(CompactUnwindEntry); + const Defined *d = symbolsVec[i].second; + // Write the functionAddress. + writeAddress(buf, d->getVA(), sizeof(Ptr) == 8 ? 3 : 2); + if (!d->compactUnwind) + return; + + // Write the rest of the CUE. + memcpy(buf, d->compactUnwind->data.data(), d->compactUnwind->data.size()); + for (const Reloc &r : d->compactUnwind->relocs) { uint64_t referentVA = 0; if (auto *referentSym = r.referent.dyn_cast()) { if (!isa(referentSym)) { @@ -275,7 +294,7 @@ } writeAddress(buf + r.offset, referentVA, r.length); } - } + }); } // There should only be a handful of unique personality pointers, so we can @@ -305,39 +324,6 @@ ") for compact unwind to encode"); } -// __unwind_info stores unwind data for address ranges. If several -// adjacent functions have the same unwind encoding, LSDA, and personality -// function, they share one unwind entry. For this to work, functions without -// unwind info need explicit "no unwind info" unwind entries -- else the -// unwinder would think they have the unwind info of the closest function -// with unwind info right before in the image. -template -static void addEntriesForFunctionsWithoutUnwindInfo( - std::vector> &cuVector) { - DenseSet hasUnwindInfo; - for (CompactUnwindEntry &cuEntry : cuVector) - hasUnwindInfo.insert(cuEntry.functionAddress); - - // Add explicit "has no unwind info" entries for all global and local symbols - // without unwind info. - auto markNoUnwindInfo = [&cuVector, &hasUnwindInfo](const Defined *d) { - if (d->isLive() && d->isec && isCodeSection(d->isec)) { - Ptr ptr = d->getVA(); - if (!hasUnwindInfo.count(ptr)) - cuVector.push_back({ptr, 0, 0, 0, 0}); - } - }; - for (Symbol *sym : symtab->getSymbols()) - if (auto *d = dyn_cast(sym)) - markNoUnwindInfo(d); - for (const InputFile *file : inputFiles) - if (auto *objFile = dyn_cast(file)) - for (Symbol *sym : objFile->symbols) - if (auto *d = dyn_cast_or_null(sym)) - if (!d->isExternal()) - markNoUnwindInfo(d); -} - static bool canFoldEncoding(compact_unwind_encoding_t encoding) { // From compact_unwind_encoding.h: // UNWIND_X86_64_MODE_STACK_IND: @@ -366,7 +352,7 @@ // Scan the __LD,__compact_unwind entries and compute the space needs of // __TEXT,__unwind_info and __TEXT,__eh_frame template void UnwindInfoSectionImpl::finalize() { - if (compactUnwindSection == nullptr) + if (symbols.empty()) return; // At this point, the address space for __TEXT,__text has been @@ -376,15 +362,8 @@ // we can fold adjacent CU entries with identical // encoding+personality+lsda. Folding is necessary because it reduces // the number of CU entries by as much as 3 orders of magnitude! - compactUnwindSection->finalize(); - assert(compactUnwindSection->getSize() % sizeof(CompactUnwindEntry) == - 0); - size_t cuCount = - compactUnwindSection->getSize() / sizeof(CompactUnwindEntry); - cuVector.resize(cuCount); - relocateCompactUnwind(compactUnwindSection, cuVector); - - addEntriesForFunctionsWithoutUnwindInfo(cuVector); + cuVector.resize(symbols.size()); + relocateCompactUnwind(cuVector); // Rather than sort & fold the 32-byte entries directly, we create a // vector of pointers to entries and sort & fold that instead. diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -677,7 +677,8 @@ continue; if (defined->overridesWeakDef) in.weakBinding->addNonWeakDefinition(defined); - in.unwindInfo->addSymbol(defined); + if (!defined->isAbsolute() && isCodeSection(defined->isec)) + in.unwindInfo->addSymbol(defined); } else if (const auto *dysym = dyn_cast(sym)) { // This branch intentionally doesn't check isLive(). if (dysym->isDynamicLookup()) @@ -691,7 +692,8 @@ if (auto *objFile = dyn_cast(file)) for (Symbol *sym : objFile->symbols) if (auto *defined = dyn_cast_or_null(sym)) - if (!defined->isExternal() && defined->isLive()) + if (!defined->isExternal() && defined->isLive() && + !defined->isAbsolute() && isCodeSection(defined->isec)) in.unwindInfo->addSymbol(defined); }