diff --git a/lld/MachO/ConcatOutputSection.cpp b/lld/MachO/ConcatOutputSection.cpp --- a/lld/MachO/ConcatOutputSection.cpp +++ b/lld/MachO/ConcatOutputSection.cpp @@ -330,6 +330,7 @@ /*size=*/thunkSize, /*isWeakDef=*/false, /*isPrivateExtern=*/true, /*isThumb=*/false, /*isReferencedDynamically=*/false, /*noDeadStrip=*/false); + thunkInfo.sym->used = true; target->populateThunk(thunkInfo.isec, funcSym); finalizeOne(thunkInfo.isec); thunks.push_back(thunkInfo.isec); diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -1006,7 +1006,6 @@ continue; if (isec->getSegName() == segment_names::ld) { assert(isec->getName() == section_names::compactUnwind); - in.unwindInfo->addInput(isec); continue; } isec->outSecOff = inputOrder++; diff --git a/lld/MachO/ICF.cpp b/lld/MachO/ICF.cpp --- a/lld/MachO/ICF.cpp +++ b/lld/MachO/ICF.cpp @@ -309,22 +309,6 @@ } } -template -DenseSet findFunctionsWithUnwindInfo() { - DenseSet result; - for (ConcatInputSection *isec : in.unwindInfo->getInputs()) { - for (size_t i = 0; i < isec->relocs.size(); ++i) { - Reloc &r = isec->relocs[i]; - assert(target->hasAttr(r.type, RelocAttrBits::UNSIGNED)); - if (r.offset % sizeof(CompactUnwindEntry) != - offsetof(CompactUnwindEntry, functionAddress)) - continue; - result.insert(r.referent.get()); - } - } - return result; -} - void macho::foldIdenticalSections() { TimeTraceScope timeScope("Fold Identical Code Sections"); // The ICF equivalence-class segregation algorithm relies on pre-computed @@ -334,11 +318,6 @@ // parallelization. Therefore, we hash every InputSection here where we have // them all accessible as simple vectors. - // ICF can't fold functions with unwind info - DenseSet functionsWithUnwindInfo = - target->wordSize == 8 ? findFunctionsWithUnwindInfo() - : findFunctionsWithUnwindInfo(); - // If an InputSection is ineligible for ICF, we give it a unique ID to force // it into an unfoldable singleton equivalence class. Begin the unique-ID // space at inputSections.size(), so that it will never intersect with @@ -351,9 +330,15 @@ for (ConcatInputSection *isec : inputSections) { // FIXME: consider non-code __text sections as hashable? bool isHashable = (isCodeSection(isec) || isCfStringSection(isec)) && - !isec->shouldOmitFromOutput() && - !functionsWithUnwindInfo.contains(isec) && - isec->isHashableForICF(); + !isec->shouldOmitFromOutput() && isec->isHashableForICF(); + // ICF can't fold functions with unwind info + if (isHashable) + for (Defined *d : isec->symbols) + if (d->compactUnwind) { + isHashable = false; + break; + } + if (isHashable) hashable.push_back(isec); else diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -119,6 +119,7 @@ SubsectionMap &); void parseDebugInfo(); void parseDataInCode(); + void registerCompactUnwind(); }; // command-line -sectcreate file diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -235,9 +235,13 @@ // Note that "record" is a term I came up with. In contrast, "literal" is a term // used by the Mach-O format. static Optional getRecordSize(StringRef segname, StringRef name) { - if (name == section_names::cfString) + if (name == section_names::cfString) { if (config->icfLevel != ICFLevel::none && segname == segment_names::data) return target->wordSize == 8 ? 32 : 16; + } else if (name == section_names::compactUnwind) { + if (segname == segment_names::ld) + return target->wordSize == 8 ? 32 : 20; + } return {}; } @@ -562,7 +566,6 @@ isPrivateExtern, sym.n_desc & N_ARM_THUMB_DEF, sym.n_desc & REFERENCED_DYNAMICALLY, sym.n_desc & N_NO_DEAD_STRIP); } - assert(!isWeakDefCanBeHidden && "weak_def_can_be_hidden on already-hidden symbol?"); return make( @@ -662,11 +665,11 @@ uint64_t sectionAddr = sectionHeaders[i].addr; uint32_t sectionAlign = 1u << sectionHeaders[i].align; - InputSection *isec = subsecMap.back().isec; - // __cfstring has already been split into subsections during + InputSection *lastIsec = subsecMap.back().isec; + // Record-based sections have already been split into subsections during // parseSections(), so we simply need to match Symbols to the corresponding // subsection here. - if (config->icfLevel != ICFLevel::none && isCfStringSection(isec)) { + if (getRecordSize(lastIsec->getSegName(), lastIsec->getName())) { for (size_t j = 0; j < symbolIndices.size(); ++j) { uint32_t symIndex = symbolIndices[j]; const NList &sym = nList[symIndex]; @@ -674,7 +677,7 @@ uint64_t symbolOffset = sym.n_value - sectionAddr; InputSection *isec = findContainingSubsection(subsecMap, &symbolOffset); if (symbolOffset != 0) { - error(toString(this) + ": __cfstring contains symbol " + name + + error(toString(lastIsec) + ": symbol " + name + " at misaligned offset"); continue; } @@ -719,7 +722,6 @@ auto *concatIsec = cast(isec); auto *nextIsec = make(*concatIsec); - nextIsec->numRefs = 0; nextIsec->wasCoalesced = false; if (isZeroFill(isec->getFlags())) { // Zero-fill sections have NULL data.data() non-zero data.size() @@ -830,6 +832,7 @@ parseDebugInfo(); if (config->emitDataInCodeInfo) parseDataInCode(); + registerCompactUnwind(); } void ObjFile::parseDebugInfo() { @@ -870,6 +873,52 @@ })); } +// Create pointers from symbols to their associated compact unwind entries. +void ObjFile::registerCompactUnwind() { + // First, locate the __compact_unwind section. + SubsectionMap *cuSubsecMap = nullptr; + for (SubsectionMap &map : subsections) { + if (map.empty()) + continue; + if (map[0].isec->getSegName() != segment_names::ld) + continue; + cuSubsecMap = ↦ + break; + } + if (!cuSubsecMap) + return; + + for (SubsectionEntry &entry : *cuSubsecMap) { + ConcatInputSection *isec = cast(entry.isec); + ConcatInputSection *referentIsec; + for (const Reloc &r : isec->relocs) { + if (r.offset != 0) + continue; + uint64_t add = r.addend; + if (auto *sym = cast_or_null(r.referent.dyn_cast())) { + add += sym->value; + referentIsec = cast(sym->isec); + } else { + referentIsec = + cast(r.referent.dyn_cast()); + } + // The functionAddress relocations are typically section relocations. + // However, unwind info operates on a per-symbol basis, so we search for + // the function symbol here. + auto it = llvm::lower_bound( + referentIsec->symbols, add, + [](Defined *d, uint64_t add) { return d->value < add; }); + // The relocation should point at the exact address of a symbol (with no + // addend). + if (it == referentIsec->symbols.end() || (*it)->value != add) { + assert(referentIsec->wasCoalesced); + continue; + } + (*it)->compactUnwind = isec; + } + } +} + // The path can point to either a dylib or a .tbd file. static DylibFile *loadDylib(StringRef path, DylibFile *umbrella) { Optional mbref = readFile(path); diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h --- a/lld/MachO/InputSection.h +++ b/lld/MachO/InputSection.h @@ -11,12 +11,14 @@ #include "Config.h" #include "Relocations.h" +#include "Symbols.h" #include "lld/Common/LLVM.h" #include "lld/Common/Memory.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/TinyPtrVector.h" #include "llvm/BinaryFormat/MachO.h" namespace lld { @@ -24,7 +26,6 @@ class InputFile; class OutputSection; -class Defined; class InputSection { public: @@ -61,6 +62,9 @@ ArrayRef data; std::vector relocs; + // The symbols that belong to this InputSection, sorted by value. With + // .subsections_via_symbols, there is typically only one element here. + llvm::TinyPtrVector symbols; protected: // The fields in this struct are immutable. Since we create a lot of @@ -84,6 +88,10 @@ : align(align), callSiteCount(0), isFinal(false), data(data), shared(make(file, name, segname, flags, kind)) {} + InputSection(const InputSection &rhs) + : align(rhs.align), callSiteCount(0), isFinal(false), data(rhs.data), + shared(rhs.shared) {} + const Shared *const shared; }; @@ -107,7 +115,7 @@ // ConcatInputSections are entirely live or dead, so the offset is irrelevant. bool isLive(uint64_t off) const override { return live; } void markLive(uint64_t off) override { live = true; } - bool isCoalescedWeak() const { return wasCoalesced && numRefs == 0; } + bool isCoalescedWeak() const { return wasCoalesced && symbols.size() == 0; } bool shouldOmitFromOutput() const { return !live || isCoalescedWeak(); } bool isHashableForICF() const; void hashForICF(); @@ -134,8 +142,6 @@ // first and not copied to the output. bool wasCoalesced = false; bool live = !config->deadStrip; - // How many symbols refer to this InputSection. - uint32_t numRefs = 0; // This variable has two usages. Initially, it represents the input order. // After assignAddresses is called, it represents the offset from the // beginning of the output section this section was assigned to. diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -93,9 +93,33 @@ align = std::max(align, copy->align); copy->live = false; copy->wasCoalesced = true; - numRefs += copy->numRefs; - copy->numRefs = 0; copy->replacement = this; + + // Merge the sorted vectors of symbols together. + auto it = symbols.begin(); + for (auto copyIt = copy->symbols.begin(); copyIt != copy->symbols.end();) { + if (it == symbols.end()) { + symbols.push_back(*copyIt++); + it = symbols.end(); + } else if ((*it)->value > (*copyIt)->value) { + std::swap(*it++, *copyIt); + } else { + ++it; + } + } + copy->symbols.clear(); + + // Remove duplicate compact unwind info for symbols at the same address. + if (symbols.size() == 0) + return; + it = symbols.begin(); + uint64_t v = (*it)->value; + for (++it; it != symbols.end(); ++it) { + if ((*it)->value == v) + (*it)->compactUnwind = nullptr; + else + v = (*it)->value; + } } void ConcatInputSection::writeTo(uint8_t *buf) { diff --git a/lld/MachO/MarkLive.cpp b/lld/MachO/MarkLive.cpp --- a/lld/MachO/MarkLive.cpp +++ b/lld/MachO/MarkLive.cpp @@ -45,10 +45,15 @@ }; auto addSym = [&](Symbol *s) { + if (s->used) + return; s->used = true; - if (auto *d = dyn_cast(s)) + if (auto *d = dyn_cast(s)) { if (d->isec) enqueue(d->isec, d->value); + if (d->compactUnwind) + enqueue(d->compactUnwind, 0); + } }; // Add GC roots. @@ -118,31 +123,6 @@ } } - // Dead strip runs before UnwindInfoSection handling so we need to keep - // __LD,__compact_unwind alive here. - // But that section contains absolute references to __TEXT,__text and - // keeps most code alive due to that. So we can't just enqueue() the - // section: We must skip the relocations for the functionAddress - // in each CompactUnwindEntry. - // See also scanEhFrameSection() in lld/ELF/MarkLive.cpp. - for (ConcatInputSection *isec : in.unwindInfo->getInputs()) { - isec->live = true; - const int compactUnwindEntrySize = - target->wordSize == 8 ? sizeof(CompactUnwindEntry) - : sizeof(CompactUnwindEntry); - for (const Reloc &r : isec->relocs) { - // This is the relocation for the address of the function itself. - // Ignore it, else these would keep everything alive. - if (r.offset % compactUnwindEntrySize == 0) - continue; - - if (auto *s = r.referent.dyn_cast()) - addSym(s); - else - enqueue(r.referent.get(), r.addend); - } - } - do { // Mark things reachable from GC roots as live. while (!worklist.empty()) { @@ -156,6 +136,8 @@ else enqueue(r.referent.get(), r.addend); } + for (Defined *d : s->symbols) + addSym(d); } // S_ATTR_LIVE_SUPPORT sections are live if they point _to_ a live section. diff --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp --- a/lld/MachO/SymbolTable.cpp +++ b/lld/MachO/SymbolTable.cpp @@ -10,6 +10,7 @@ #include "ConcatOutputSection.h" #include "Config.h" #include "InputFiles.h" +#include "InputSection.h" #include "Symbols.h" #include "SyntheticSections.h" #include "lld/Common/ErrorHandler.h" diff --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h --- a/lld/MachO/Symbols.h +++ b/lld/MachO/Symbols.h @@ -9,8 +9,8 @@ #ifndef LLD_MACHO_SYMBOLS_H #define LLD_MACHO_SYMBOLS_H +#include "Config.h" #include "InputFiles.h" -#include "InputSection.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Strings.h" @@ -20,7 +20,6 @@ namespace lld { namespace macho { -class InputSection; class MachHeaderSection; struct StringRefZ { @@ -51,7 +50,7 @@ return {nameData, nameSize}; } - bool isLive() const; + bool isLive() const { return used; } virtual uint64_t getVA() const { return 0; } @@ -114,23 +113,13 @@ public: Defined(StringRefZ name, InputFile *file, InputSection *isec, uint64_t value, uint64_t size, bool isWeakDef, bool isExternal, bool isPrivateExtern, - bool isThumb, bool isReferencedDynamically, bool noDeadStrip) - : Symbol(DefinedKind, name, file), isec(isec), value(value), size(size), - overridesWeakDef(false), privateExtern(isPrivateExtern), - includeInSymtab(true), thumb(isThumb), - referencedDynamically(isReferencedDynamically), - noDeadStrip(noDeadStrip), weakDef(isWeakDef), external(isExternal) { - if (auto concatIsec = dyn_cast_or_null(isec)) - concatIsec->numRefs++; - } + bool isThumb, bool isReferencedDynamically, bool noDeadStrip); bool isWeakDef() const override { return weakDef; } bool isExternalWeakDef() const { return isWeakDef() && isExternal() && !privateExtern; } - bool isTlv() const override { - return !isAbsolute() && isThreadLocalVariables(isec->getFlags()); - } + bool isTlv() const override; bool isExternal() const { return external; } bool isAbsolute() const { return isec == nullptr; } @@ -145,6 +134,7 @@ uint64_t value; // size is only calculated for regular (non-bitcode) symbols. uint64_t size; + ConcatInputSection *compactUnwind = nullptr; bool overridesWeakDef : 1; // Whether this symbol should appear in the output binary's export trie. diff --git a/lld/MachO/Symbols.cpp b/lld/MachO/Symbols.cpp --- a/lld/MachO/Symbols.cpp +++ b/lld/MachO/Symbols.cpp @@ -31,26 +31,33 @@ uint64_t Symbol::getGotVA() const { return in.got->getVA(gotIndex); } uint64_t Symbol::getTlvVA() const { return in.tlvPointers->getVA(gotIndex); } -bool Symbol::isLive() const { - if (isa(this) || isa(this)) - return used; - - if (auto *d = dyn_cast(this)) { - // Non-absolute symbols might be alive because their section is - // no_dead_strip or live_support. In that case, the section will know - // that it's live but `used` might be false. Non-absolute symbols always - // have to use the section's `live` bit as source of truth. - if (d->isAbsolute()) - return used; - return d->isec->canonical()->isLive(d->value); +Defined::Defined(StringRefZ name, InputFile *file, InputSection *isec, + uint64_t value, uint64_t size, bool isWeakDef, bool isExternal, + bool isPrivateExtern, bool isThumb, + bool isReferencedDynamically, bool noDeadStrip) + : Symbol(DefinedKind, name, file), isec(isec), value(value), size(size), + overridesWeakDef(false), privateExtern(isPrivateExtern), + includeInSymtab(true), thumb(isThumb), + referencedDynamically(isReferencedDynamically), noDeadStrip(noDeadStrip), + weakDef(isWeakDef), external(isExternal) { + if (isec) { + isec->symbols.push_back(this); + // Maintain sorted order. + for (auto it = isec->symbols.rbegin(), rend = isec->symbols.rend(); + it != rend; ++it) { + auto next = std::next(it); + if (next == rend) + break; + if ((*it)->value < (*next)->value) + std::swap(*next, *it); + else + break; + } } +} - assert(!isa(this) && - "replaceCommonSymbols() runs before dead code stripping, and isLive() " - "should only be called after dead code stripping"); - - // Assume any other kind of symbol is live. - return true; +bool Defined::isTlv() const { + return !isAbsolute() && isThreadLocalVariables(isec->getFlags()); } uint64_t Defined::getVA() const { diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -621,14 +621,14 @@ ConcatOutputSection::getOrCreateForInput(in.imageLoaderCache); inputSections.push_back(in.imageLoaderCache); // Since this isn't in the symbol table or in any input file, the noDeadStrip - // argument doesn't matter. It's kept alive by ImageLoaderCacheSection() - // setting `live` to true on the backing InputSection. + // argument doesn't matter. dyldPrivate = make("__dyld_private", nullptr, in.imageLoaderCache, 0, 0, /*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/false, /*isThumb=*/false, /*isReferencedDynamically=*/false, /*noDeadStrip=*/false); + dyldPrivate->used = true; } LazyPointerSection::LazyPointerSection() diff --git a/lld/MachO/UnwindInfoSection.h b/lld/MachO/UnwindInfoSection.h --- a/lld/MachO/UnwindInfoSection.h +++ b/lld/MachO/UnwindInfoSection.h @@ -31,7 +31,7 @@ return !compactUnwindSection->inputs.empty() && !allEntriesAreOmitted; } uint64_t getSize() const override { return unwindInfoSize; } - virtual void addInput(ConcatInputSection *) = 0; + virtual void addSymbol(const Defined *) = 0; std::vector getInputs() { return compactUnwindSection->inputs; } diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -107,7 +107,7 @@ class UnwindInfoSectionImpl final : public UnwindInfoSection { public: void prepareRelocations(ConcatInputSection *) override; - void addInput(ConcatInputSection *) override; + void addSymbol(const Defined *) override; void finalize() override; void writeTo(uint8_t *buf) const override; @@ -141,11 +141,11 @@ } template -void UnwindInfoSectionImpl::addInput(ConcatInputSection *isec) { - assert(isec->getSegName() == segment_names::ld && - isec->getName() == section_names::compactUnwind); - isec->parent = compactUnwindSection; - compactUnwindSection->addInput(isec); +void UnwindInfoSectionImpl::addSymbol(const Defined *d) { + if (d->compactUnwind) { + d->compactUnwind->parent = compactUnwindSection; + compactUnwindSection->addInput(d->compactUnwind); + } } // Compact unwind relocations have different semantics, so we handle them in a @@ -255,9 +255,6 @@ return cast(isec); } -template -constexpr Ptr TombstoneValue = std::numeric_limits::max(); - // 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 @@ -274,7 +271,7 @@ memcpy(buf, isec->data.data(), isec->data.size()); for (const Reloc &r : isec->relocs) { - uint64_t referentVA = TombstoneValue; + uint64_t referentVA = 0; if (auto *referentSym = r.referent.dyn_cast()) { if (!isa(referentSym)) { if (auto *defined = dyn_cast(referentSym)) @@ -333,8 +330,7 @@ std::vector> &cuVector) { DenseSet hasUnwindInfo; for (CompactUnwindEntry &cuEntry : cuVector) - if (cuEntry.functionAddress != TombstoneValue) - hasUnwindInfo.insert(cuEntry.functionAddress); + hasUnwindInfo.insert(cuEntry.functionAddress); // Add explicit "has no unwind info" entries for all global and local symbols // without unwind info. @@ -414,28 +410,6 @@ return a->functionAddress < b->functionAddress; }); - // Dead-stripped functions get a functionAddress of TombstoneValue in - // relocateCompactUnwind(). Filter them out here. - // FIXME: This doesn't yet collect associated data like LSDAs kept - // alive only by a now-removed CompactUnwindEntry or other comdat-like - // data (`kindNoneGroupSubordinate*` in ld64). - CompactUnwindEntry tombstone; - tombstone.functionAddress = TombstoneValue; - cuPtrVector.erase( - std::lower_bound(cuPtrVector.begin(), cuPtrVector.end(), &tombstone, - [](const CompactUnwindEntry *a, - const CompactUnwindEntry *b) { - return a->functionAddress < b->functionAddress; - }), - cuPtrVector.end()); - - // If there are no entries left after adding explicit "no unwind info" - // entries and removing entries for dead-stripped functions, don't write - // an __unwind_info section at all. - assert(allEntriesAreOmitted == cuPtrVector.empty()); - if (cuPtrVector.empty()) - return; - // Fold adjacent entries with matching encoding+personality+lsda // We use three iterators on the same cuPtrVector to fold in-situ: // (1) `foldBegin` is the first of a potential sequence of matching entries @@ -608,7 +582,6 @@ } // Level-1 sentinel const CompactUnwindEntry &cuEnd = *cuPtrVector.back(); - assert(cuEnd.functionAddress != TombstoneValue); iep->functionOffset = cuEnd.functionAddress - in.header->addr + cuEnd.functionLength; iep->secondLevelPagesSectionOffset = 0; diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -673,8 +673,11 @@ TimeTraceScope timeScope("Scan symbols"); for (const Symbol *sym : symtab->getSymbols()) { if (const auto *defined = dyn_cast(sym)) { - if (defined->overridesWeakDef && defined->isLive()) + if (!defined->isLive()) + continue; + if (defined->overridesWeakDef) in.weakBinding->addNonWeakDefinition(defined); + in.unwindInfo->addSymbol(defined); } else if (const auto *dysym = dyn_cast(sym)) { // This branch intentionally doesn't check isLive(). if (dysym->isDynamicLookup()) @@ -683,6 +686,15 @@ std::max(dysym->getFile()->refState, dysym->getRefState()); } } + + for (const InputFile *file : inputFiles) { + if (auto *objFile = dyn_cast(file)) + for (Symbol *sym : objFile->symbols) { + if (auto *defined = dyn_cast_or_null(sym)) + if (!defined->isExternal() && defined->isLive()) + in.unwindInfo->addSymbol(defined); + } + } } // TODO: ld64 enforces the old load commands in a few other cases. @@ -1101,6 +1113,7 @@ treatSpecialUndefineds(); if (config->entry && !isa(config->entry)) prepareBranchTarget(config->entry); + scanSymbols(); scanRelocations(); // Do not proceed if there was an undefined symbol. @@ -1109,7 +1122,6 @@ if (in.stubHelper->isNeeded()) in.stubHelper->setup(); - scanSymbols(); createOutputSections(); // After this point, we create no new segments; HOWEVER, we might diff --git a/lld/test/MachO/compact-unwind-both-local-and-dylib-personality.s b/lld/test/MachO/compact-unwind-both-local-and-dylib-personality.s --- a/lld/test/MachO/compact-unwind-both-local-and-dylib-personality.s +++ b/lld/test/MachO/compact-unwind-both-local-and-dylib-personality.s @@ -28,21 +28,21 @@ # RUN: llvm-objdump --macho --unwind-info --syms --indirect-symbols --bind %t/a.out | FileCheck %s --check-prefixes=A,CHECK -D#%x,OFF=0x100000000 # RUN: llvm-objdump --macho --unwind-info --syms --indirect-symbols --bind %t/b.out | FileCheck %s --check-prefixes=BC,CHECK -D#%x,OFF=0x100000000 -# RUN: llvm-objdump --macho --unwind-info --syms --indirect-symbols --bind %t/c.out | FileCheck %s --check-prefixes=BC,CHECK -D#%x,OFF=0 +# RUN: llvm-objdump --macho --unwind-info --syms --indirect-symbols --bind %t/c.out | FileCheck %s --check-prefixes=BC,C,CHECK -D#%x,OFF=0 # A: Indirect symbols for (__DATA_CONST,__got) # A-NEXT: address index name -# A-DAG: 0x[[#%x,GXX_PERSONALITY_LO:]] [[#]] ___gxx_personality_v0 -# A-DAG: 0x[[#%x,GXX_PERSONALITY_HI:]] [[#]] ___gxx_personality_v0 -# A-DAG: 0x[[#%x,PERSONALITY_1:]] LOCAL -# A-DAG: 0x[[#%x,PERSONALITY_2:]] LOCAL +# A: 0x[[#%x,GXX_PERSONALITY_LO:]] [[#]] ___gxx_personality_v0 +# A: 0x[[#%x,GXX_PERSONALITY_HI:]] [[#]] ___gxx_personality_v0 +# A: 0x[[#%x,PERSONALITY_1:]] LOCAL +# A: 0x[[#%x,PERSONALITY_2:]] LOCAL # BC: Indirect symbols for (__DATA_CONST,__got) # BC-NEXT: address index name -# BC-DAG: 0x[[#%x,GXX_PERSONALITY_HI:]] LOCAL -# BC-DAG: 0x[[#%x,GXX_PERSONALITY_LO:]] LOCAL -# BC-DAG: 0x[[#%x,PERSONALITY_1:]] LOCAL -# BC-DAG: 0x[[#%x,PERSONALITY_2:]] LOCAL +# C: 0x[[#%x,GXX_PERSONALITY_HI:]] LOCAL +# BC: 0x[[#%x,GXX_PERSONALITY_LO:]] LOCAL +# BC: 0x[[#%x,PERSONALITY_1:]] LOCAL +# BC: 0x[[#%x,PERSONALITY_2:]] LOCAL # CHECK: Personality functions: (count = 3) # CHECK-DAG: personality[{{[0-9]+}}]: 0x{{0*}}[[#GXX_PERSONALITY_LO-OFF]] diff --git a/lld/test/MachO/dead-strip.s b/lld/test/MachO/dead-strip.s --- a/lld/test/MachO/dead-strip.s +++ b/lld/test/MachO/dead-strip.s @@ -248,6 +248,7 @@ # UNWIND-NEXT: *UND* ___gxx_personality_v0 # UNWIND-NEXT: *UND* ___cxa_begin_catch # UNWIND-NEXT: *UND* dyld_stub_binder +# UNWIND-NOT: GCC_except_table0 ## If a dead stripped function has a strong ref to a dylib symbol but ## a live function only a weak ref, the dylib is still not a WEAK_DYLIB. @@ -636,7 +637,13 @@ #--- unwind.s ## This is the output of `clang -O2 -S throw.cc` where throw.cc ## looks like this: -## void unref() {} +## int unref() { +## try { +## throw 0; +## } catch (int i) { +## return i + 1; +## } +## } ## int main() { ## try { ## throw 0; @@ -645,92 +652,175 @@ ## } ## } .section __TEXT,__text,regular,pure_instructions - -.globl __Z5unrefv -.p2align 4, 0x90 -__Z5unrefv: -.cfi_startproc - pushq %rbp - .cfi_def_cfa_offset 16 - .cfi_offset %rbp, -16 - movq %rsp, %rbp - .cfi_def_cfa_register %rbp - popq %rbp - retq - .cfi_endproc - -.globl _main +.globl __Z5unrefv ## -- Begin function _Z5unrefv .p2align 4, 0x90 -_main: +__Z5unrefv: ## @_Z5unrefv Lfunc_begin0: .cfi_startproc .cfi_personality 155, ___gxx_personality_v0 .cfi_lsda 16, Lexception0 - pushq %rbp +## %bb.0: + pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 - movq %rsp, %rbp + movq %rsp, %rbp .cfi_def_cfa_register %rbp - pushq %rbx - pushq %rax - .cfi_offset %rbx, -24 - movl $4, %edi - callq ___cxa_allocate_exception - movl $0, (%rax) + subq $16, %rsp + movl $4, %edi + callq ___cxa_allocate_exception + movl $0, (%rax) Ltmp0: - movq __ZTIi@GOTPCREL(%rip), %rsi - movq %rax, %rdi - xorl %edx, %edx - callq ___cxa_throw + movq __ZTIi@GOTPCREL(%rip), %rsi + movq %rax, %rdi + xorl %edx, %edx + callq ___cxa_throw Ltmp1: +## %bb.1: ud2 -LBB1_2: +LBB0_2: Ltmp2: - movq %rax, %rdi - callq ___cxa_begin_catch - movl (%rax), %ebx - callq ___cxa_end_catch - movl %ebx, %eax - addq $8, %rsp - popq %rbx - popq %rbp + leaq -4(%rbp), %rcx + movq %rax, %rdi + movl %edx, %esi + movq %rcx, %rdx + callq __Z5unrefv.cold.1 + movl -4(%rbp), %eax + addq $16, %rsp + popq %rbp retq Lfunc_end0: -.cfi_endproc - -.section __TEXT,__gcc_except_tab -.p2align 2 -GCC_except_table1: + .cfi_endproc + .section __TEXT,__gcc_except_tab + .p2align 2 +GCC_except_table0: Lexception0: - .byte 255 ## @LPStart Encoding = omit - .byte 155 ## @TType Encoding = indirect pcrel sdata4 + .byte 255 ## @LPStart Encoding = omit + .byte 155 ## @TType Encoding = indirect pcrel sdata4 .uleb128 Lttbase0-Lttbaseref0 Lttbaseref0: - .byte 1 ## Call site Encoding = uleb128 + .byte 1 ## Call site Encoding = uleb128 .uleb128 Lcst_end0-Lcst_begin0 Lcst_begin0: - .uleb128 Lfunc_begin0-Lfunc_begin0 ## >> Call Site 1 << - .uleb128 Ltmp0-Lfunc_begin0 ## Call between Lfunc_begin0 and Ltmp0 - .byte 0 ## has no landing pad - .byte 0 ## On action: cleanup - .uleb128 Ltmp0-Lfunc_begin0 ## >> Call Site 2 << - .uleb128 Ltmp1-Ltmp0 ## Call between Ltmp0 and Ltmp1 - .uleb128 Ltmp2-Lfunc_begin0 ## jumps to Ltmp2 - .byte 1 ## On action: 1 - .uleb128 Ltmp1-Lfunc_begin0 ## >> Call Site 3 << - .uleb128 Lfunc_end0-Ltmp1 ## Call between Ltmp1 and Lfunc_end0 - .byte 0 ## has no landing pad - .byte 0 ## On action: cleanup + .uleb128 Lfunc_begin0-Lfunc_begin0 ## >> Call Site 1 << + .uleb128 Ltmp0-Lfunc_begin0 ## Call between Lfunc_begin0 and Ltmp0 + .byte 0 ## has no landing pad + .byte 0 ## On action: cleanup + .uleb128 Ltmp0-Lfunc_begin0 ## >> Call Site 2 << + .uleb128 Ltmp1-Ltmp0 ## Call between Ltmp0 and Ltmp1 + .uleb128 Ltmp2-Lfunc_begin0 ## jumps to Ltmp2 + .byte 1 ## On action: 1 + .uleb128 Ltmp1-Lfunc_begin0 ## >> Call Site 3 << + .uleb128 Lfunc_end0-Ltmp1 ## Call between Ltmp1 and Lfunc_end0 + .byte 0 ## has no landing pad + .byte 0 ## On action: cleanup Lcst_end0: - .byte 1 ## >> Action Record 1 << - ## Catch TypeInfo 1 - .byte 0 ## No further actions + .byte 1 ## >> Action Record 1 << + ## Catch TypeInfo 1 + .byte 0 ## No further actions .p2align 2 - ## >> Catch TypeInfos << - .long __ZTIi@GOTPCREL+4 ## TypeInfo 1 + ## >> Catch TypeInfos << + .long __ZTIi@GOTPCREL+4 ## TypeInfo 1 Lttbase0: .p2align 2 - ## -- End function + ## -- End function + .section __TEXT,__text,regular,pure_instructions + .globl _main ## -- Begin function main + .p2align 4, 0x90 +_main: ## @main +Lfunc_begin1: + .cfi_startproc + .cfi_personality 155, ___gxx_personality_v0 + .cfi_lsda 16, Lexception1 +## %bb.0: + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + pushq %rbx + pushq %rax + .cfi_offset %rbx, -24 + movl $4, %edi + callq ___cxa_allocate_exception + movl $0, (%rax) +Ltmp3: + movq __ZTIi@GOTPCREL(%rip), %rsi + movq %rax, %rdi + xorl %edx, %edx + callq ___cxa_throw +Ltmp4: +## %bb.1: + ud2 +LBB1_2: +Ltmp5: + movq %rax, %rdi + callq ___cxa_begin_catch + movl (%rax), %ebx + callq ___cxa_end_catch + movl %ebx, %eax + addq $8, %rsp + popq %rbx + popq %rbp + retq +Lfunc_end1: + .cfi_endproc + .section __TEXT,__gcc_except_tab + .p2align 2 +GCC_except_table1: +Lexception1: + .byte 255 ## @LPStart Encoding = omit + .byte 155 ## @TType Encoding = indirect pcrel sdata4 + .uleb128 Lttbase1-Lttbaseref1 +Lttbaseref1: + .byte 1 ## Call site Encoding = uleb128 + .uleb128 Lcst_end1-Lcst_begin1 +Lcst_begin1: + .uleb128 Lfunc_begin1-Lfunc_begin1 ## >> Call Site 1 << + .uleb128 Ltmp3-Lfunc_begin1 ## Call between Lfunc_begin1 and Ltmp3 + .byte 0 ## has no landing pad + .byte 0 ## On action: cleanup + .uleb128 Ltmp3-Lfunc_begin1 ## >> Call Site 2 << + .uleb128 Ltmp4-Ltmp3 ## Call between Ltmp3 and Ltmp4 + .uleb128 Ltmp5-Lfunc_begin1 ## jumps to Ltmp5 + .byte 1 ## On action: 1 + .uleb128 Ltmp4-Lfunc_begin1 ## >> Call Site 3 << + .uleb128 Lfunc_end1-Ltmp4 ## Call between Ltmp4 and Lfunc_end1 + .byte 0 ## has no landing pad + .byte 0 ## On action: cleanup +Lcst_end1: + .byte 1 ## >> Action Record 1 << + ## Catch TypeInfo 1 + .byte 0 ## No further actions + .p2align 2 + ## >> Catch TypeInfos << + .long __ZTIi@GOTPCREL+4 ## TypeInfo 1 +Lttbase1: + .p2align 2 + ## -- End function + .section __TEXT,__text,regular,pure_instructions + .p2align 4, 0x90 ## -- Begin function _Z5unrefv.cold.1 +__Z5unrefv.cold.1: ## @_Z5unrefv.cold.1 + .cfi_startproc +## %bb.0: + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + pushq %rbx + pushq %rax + .cfi_offset %rbx, -24 + movq %rdx, %rbx + callq ___cxa_begin_catch + movl (%rax), %eax + incl %eax + movl %eax, (%rbx) + addq $8, %rsp + popq %rbx + popq %rbp + jmp ___cxa_end_catch ## TAILCALL + .cfi_endproc + ## -- End function .subsections_via_symbols #--- weak-ref.s diff --git a/lld/test/MachO/invalid/cfstring.s b/lld/test/MachO/invalid/cfstring.s --- a/lld/test/MachO/invalid/cfstring.s +++ b/lld/test/MachO/invalid/cfstring.s @@ -2,7 +2,7 @@ # RUN: rm -rf %t; mkdir %t # RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/test.o # RUN: not %lld -dylib -framework CoreFoundation --icf=all %t/test.o 2>&1 | FileCheck %s -# CHECK: error: {{.*}}test.o: __cfstring contains symbol _uh_oh at misaligned offset +# CHECK: error: {{.*}}test.o:(__cfstring): symbol _uh_oh at misaligned offset .cstring L_.str: