diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -1013,6 +1013,11 @@ referentIsec = cast(r.referent.dyn_cast()); } + // 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. if (referentIsec->getSegName() != segment_names::text) error(isec->getLocation(r.offset) + " references section " + referentIsec->getName() + " which is not in segment __TEXT"); diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -95,7 +95,7 @@ // TODO(gkm): prune __eh_frame entries superseded by __unwind_info, PR50410 // TODO(gkm): how do we align the 2nd-level pages? -template struct CompactUnwindEntry { +template struct CompactUnwindLayout { Ptr functionAddress; uint32_t functionLength; compact_unwind_encoding_t encoding; @@ -103,6 +103,14 @@ Ptr lsda; }; +struct CompactUnwindEntry { + uint64_t functionAddress; + uint32_t functionLength; + compact_unwind_encoding_t encoding; + Symbol *personality; + InputSection *lsda; +}; + using EncodingMap = DenseMap; struct SecondLevelPage { @@ -118,8 +126,7 @@ class UnwindInfoSectionImpl final : public UnwindInfoSection { public: void prepareRelocations(ConcatInputSection *) override; - void relocateCompactUnwind(std::vector> &); - Reloc *findLsdaReloc(ConcatInputSection *) const; + void relocateCompactUnwind(std::vector &); void encodePersonalities(); void finalize() override; void writeTo(uint8_t *buf) const override; @@ -129,11 +136,10 @@ EncodingMap commonEncodingIndexes; // The entries here will be in the same order as their originating symbols // in symbolsVec. - std::vector> cuEntries; + std::vector cuEntries; // Indices into the cuEntries vector. std::vector cuIndices; - // Indices of personality functions within the GOT. - std::vector personalities; + std::vector personalities; SmallDenseMap, Symbol *> personalityTable; // Indices into cuEntries for CUEs with a non-null LSDA. @@ -206,8 +212,7 @@ // 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) != - offsetof(CompactUnwindEntry, personality)) + if (r.offset != offsetof(CompactUnwindLayout, personality)) continue; if (auto *s = r.referent.dyn_cast()) { @@ -275,55 +280,35 @@ } } -// 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, InputSection *cuIsec, uint64_t off) { - if (isec->getSegName() != segment_names::text) - error(cuIsec->getLocation(off) + " references section " + isec->getName() + - " which is not in segment __TEXT"); - // __TEXT should always contain ConcatInputSections. - return cast(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 void UnwindInfoSectionImpl::relocateCompactUnwind( - std::vector> &cuEntries) { + std::vector &cuEntries) { parallelForEachN(0, symbolsVec.size(), [&](size_t i) { - uint8_t *buf = reinterpret_cast(cuEntries.data()) + - i * sizeof(CompactUnwindEntry); + 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()); + auto buf = reinterpret_cast(d->unwindEntry->data.data()) - + sizeof(Ptr); + cu.functionLength = support::endian::read32le( + buf + offsetof(CompactUnwindLayout, functionLength)); + cu.encoding = support::endian::read32le( + buf + offsetof(CompactUnwindLayout, encoding)); for (const Reloc &r : d->unwindEntry->relocs) { - uint64_t referentVA = 0; - if (auto *referentSym = r.referent.dyn_cast()) { - if (!isa(referentSym)) { - if (auto *defined = dyn_cast(referentSym)) - checkTextSegment(defined->isec, d->unwindEntry, r.offset); - // 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(); - checkTextSegment(referentIsec, d->unwindEntry, r.offset); - referentVA = referentIsec->getVA(r.addend); + if (r.offset == offsetof(CompactUnwindLayout, personality)) { + cu.personality = r.referent.get(); + } else if (r.offset == offsetof(CompactUnwindLayout, lsda)) { + if (auto *referentSym = r.referent.dyn_cast()) + cu.lsda = cast(referentSym)->isec; + else + cu.lsda = r.referent.get(); } - writeAddress(buf + r.offset, referentVA, r.length); } }); } @@ -332,8 +317,8 @@ // encode them as 2-bit indices into a small array. template void UnwindInfoSectionImpl::encodePersonalities() { for (size_t idx : cuIndices) { - CompactUnwindEntry &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); @@ -349,7 +334,7 @@ static_cast(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"); } @@ -378,20 +363,6 @@ return true; } -template -Reloc * -UnwindInfoSectionImpl::findLsdaReloc(ConcatInputSection *isec) const { - if (isec == nullptr) - return nullptr; - auto it = llvm::find_if(isec->relocs, [](const Reloc &r) { - return r.offset % sizeof(CompactUnwindEntry) == - offsetof(CompactUnwindEntry, 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 void UnwindInfoSectionImpl::finalize() { @@ -434,25 +405,9 @@ 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; } @@ -510,7 +465,7 @@ sizeof(uint32_t); while (wordsRemaining >= 1 && i < cuIndices.size()) { idx = cuIndices[i]; - const CompactUnwindEntry *cuPtr = &cuEntries[idx]; + const CompactUnwindEntry *cuPtr = &cuEntries[idx]; if (cuPtr->functionAddress >= functionAddressMax) { break; } else if (commonEncodingIndexes.count(cuPtr->encoding) || @@ -545,8 +500,7 @@ 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); } @@ -588,9 +542,8 @@ *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 = @@ -609,7 +562,7 @@ l2PagesOffset += SECOND_LEVEL_PAGE_BYTES; } // Level-1 sentinel - const CompactUnwindEntry &cuEnd = cuEntries[cuIndices.back()]; + const CompactUnwindEntry &cuEnd = cuEntries[cuIndices.back()]; iep->functionOffset = cuEnd.functionAddress - in.header->addr + cuEnd.functionLength; iep->secondLevelPagesSectionOffset = 0; @@ -622,18 +575,8 @@ auto *lep = reinterpret_cast(iep); for (size_t idx : entriesWithLsda) { - const CompactUnwindEntry &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()) { - va = isec->getVA(r->addend); - } else { - auto *sym = r->referent.get(); - 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++; } @@ -656,7 +599,7 @@ p2p->encodingsCount = page.localEncodings.size(); auto *ep = reinterpret_cast(&p2p[1]); for (size_t i = 0; i < page.entryCount; i++) { - const CompactUnwindEntry &cue = + const CompactUnwindEntry &cue = cuEntries[cuIndices[page.entryIndex + i]]; auto it = commonEncodingIndexes.find(cue.encoding); if (it == commonEncodingIndexes.end()) @@ -676,7 +619,7 @@ p2p->entryCount = page.entryCount; auto *ep = reinterpret_cast(&p2p[1]); for (size_t i = 0; i < page.entryCount; i++) { - const CompactUnwindEntry &cue = + const CompactUnwindEntry &cue = cuEntries[cuIndices[page.entryIndex + i]]; *ep++ = cue.functionAddress; *ep++ = cue.encoding; diff --git a/lld/test/MachO/compact-unwind.s b/lld/test/MachO/compact-unwind.s --- a/lld/test/MachO/compact-unwind.s +++ b/lld/test/MachO/compact-unwind.s @@ -81,6 +81,7 @@ .globl _my_personality, _exception0 .text .p2align 2 +.no_dead_strip _foo _foo: .cfi_startproc ## This will generate a section relocation. @@ -91,6 +92,7 @@ .cfi_endproc .p2align 2 +.no_dead_strip _bar _bar: .cfi_startproc ## Check that we dedup references to the same statically-linked personality. @@ -100,7 +102,11 @@ ret .cfi_endproc +.data .p2align 2 +## We put this personality in `__data` to test if we correctly handle +## personality symbols whose output addresses occur after that of the +## `__unwind_info` section. _my_personality: ret @@ -108,6 +114,8 @@ _exception0: .space 1 +.subsections_via_symbols + #--- main.s .globl _main, _quux, _my_personality, _exception1 diff --git a/lld/test/MachO/invalid/compact-unwind-bad-reloc.s b/lld/test/MachO/invalid/compact-unwind-bad-reloc.s --- a/lld/test/MachO/invalid/compact-unwind-bad-reloc.s +++ b/lld/test/MachO/invalid/compact-unwind-bad-reloc.s @@ -1,10 +1,9 @@ # REQUIRES: x86 # RUN: rm -rf %t; split-file %s %t # RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin19.0.0 %t/bad-function.s -o %t/bad-function.o -# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin19.0.0 %t/bad-personality.s -o %t/bad-personality.o -# RUN: not %lld -lSystem -lc++ %t/bad-function.o -o %t 2>&1 | FileCheck %s -DFILE=%t/bad-function.o -D#OFF=0x0 -# RUN: not %lld -lSystem -lc++ %t/bad-personality.o -o %t 2>&1 | FileCheck %s -DFILE=%t/bad-personality.o -D#OFF=0x10 -# CHECK: error: [[FILE]]:(__compact_unwind+0x[[#%x,OFF]]) references section __data which is not in segment __TEXT +# RUN: not %lld -lSystem -dylib -lc++ %t/bad-function.o -o /dev/null 2>&1 | FileCheck %s +# CHECK: error: {{.*}}bad-function.o:(__compact_unwind+0x0) references section __data which is not in segment __TEXT +# CHECK: error: {{.*}}bad-function.o:(__compact_unwind+0x20) references section __data which is not in segment __TEXT #--- bad-function.s .data @@ -15,16 +14,11 @@ retq .cfi_endproc -#--- bad-personality.s -.globl _main, _not_a_function -.text -_main: +_not_a_function_2: .cfi_startproc - .cfi_personality 155, _my_personality + .cfi_personality 155, ___gxx_personality_v0 .cfi_def_cfa_offset 16 retq .cfi_endproc -.data -.globl _my_personality -_my_personality: +.subsections_via_symbols