From d6565a2dbcbe0932cd5cbb95bf2fc06855dfe938 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Mon, 21 Jun 2021 22:29:11 -0400 Subject: [PATCH] [lld/mac] Add explicit "no unwind info" entries for functions without unwind info Fixes PR50529. With this, lld-linked Chromium base_unittests passes on arm macs. Surprisingly, no measurable impact on link time. Differential Revision: https://reviews.llvm.org/D104681 --- lld/MachO/UnwindInfoSection.cpp | 42 ++++++++++++++++++++++++++-- lld/test/MachO/compact-unwind.s | 17 ++++++++++- lld/test/MachO/tools/validate-unwind-info.py | 5 +++- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp index deeef18..13be856 100644 --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -256,9 +256,9 @@ relocateCompactUnwind(ConcatOutputSection *compactUnwindSection, // There should only be a handful of unique personality pointers, so we can // encode them as 2-bit indices into a small array. template -void encodePersonalities( - const std::vector *> &cuPtrVector, - std::vector &personalities) { +static void +encodePersonalities(const std::vector *> &cuPtrVector, + std::vector &personalities) { for (CompactUnwindEntry *cu : cuPtrVector) { if (cu->personality == 0) continue; @@ -280,6 +280,40 @@ void encodePersonalities( ") 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) + if (cuEntry.functionAddress != UINT64_MAX) + 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() && 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); +} + // Scan the __LD,__compact_unwind entries and compute the space needs of // __TEXT,__unwind_info and __TEXT,__eh_frame template void UnwindInfoSectionImpl::finalize() { @@ -301,6 +335,8 @@ template void UnwindInfoSectionImpl::finalize() { cuVector.resize(cuCount); relocateCompactUnwind(compactUnwindSection, cuVector); + addEntriesForFunctionsWithoutUnwindInfo(cuVector); + // Rather than sort & fold the 32-byte entries directly, we create a // vector of pointers to entries and sort & fold that instead. cuPtrVector.reserve(cuCount); diff --git a/lld/test/MachO/compact-unwind.s b/lld/test/MachO/compact-unwind.s index 1da965f2..72aeaf8 100644 --- a/lld/test/MachO/compact-unwind.s +++ b/lld/test/MachO/compact-unwind.s @@ -33,7 +33,9 @@ # CHECK: SYMBOL TABLE: # CHECK-DAG: [[#%x,MAIN:]] g F __TEXT,__text _main +# CHECK-DAG: [[#%x,QUUX:]] g F __TEXT,__text _quux # CHECK-DAG: [[#%x,FOO:]] l F __TEXT,__text _foo +# CHECK-DAG: [[#%x,BAZ:]] l F __TEXT,__text _baz # CHECK-DAG: [[#%x,EXCEPTION0:]] g O __TEXT,__gcc_except_tab _exception0 # CHECK-DAG: [[#%x,EXCEPTION1:]] g O __TEXT,__gcc_except_tab _exception1 @@ -44,6 +46,11 @@ # CHECK: LSDA descriptors: # CHECK-DAG: function offset=0x[[#%.8x,FOO-BASE]], LSDA offset=0x[[#%.8x,EXCEPTION0-BASE]] # CHECK-DAG: function offset=0x[[#%.8x,MAIN-BASE]], LSDA offset=0x[[#%.8x,EXCEPTION1-BASE]] +# CHECK: Second level indices: +# CHECK-DAG: function offset=0x[[#%.8x,MAIN-BASE]], encoding +# CHECK-DAG: function offset=0x[[#%.8x,FOO-BASE]], encoding +# CHECK-DAG: function offset=0x[[#%.8x,BAZ-BASE]], encoding +# CHECK-DAG: function offset=0x[[#%.8x,QUUX-BASE]], encoding{{.*}}=0x00000000 ## Check that we do not add rebase opcodes to the compact unwind section. # CHECK: Rebase table: @@ -83,7 +90,7 @@ _exception0: .space 1 #--- main.s -.globl _main, _my_personality, _exception1 +.globl _main, _quux, _my_personality, _exception1 .text .p2align 2 @@ -95,6 +102,14 @@ _main: ret .cfi_endproc +## _quux has no unwind information. +## (In real life, it'd be part of a separate TU that was built with +## -fno-exceptions, while the previous and next TU might be Objective-C++ +## which has unwind info for Objective-C). +.p2align 2 +_quux: + ret + .p2align 2 _baz: .cfi_startproc diff --git a/lld/test/MachO/tools/validate-unwind-info.py b/lld/test/MachO/tools/validate-unwind-info.py index 9f1b726..592751c 100755 --- a/lld/test/MachO/tools/validate-unwind-info.py +++ b/lld/test/MachO/tools/validate-unwind-info.py @@ -37,9 +37,12 @@ def main(): if not object_encodings_map: sys.exit("no object encodings found in input") + # generate-cfi-funcs.py doesn't generate unwind info for _main. + object_encodings_map['_main'] = '00000000' + program_symbols_map = {address:symbol for address, symbol in - re.findall(r"^%s(%s) g\s+F __TEXT,__text (x\1)$" % (hex8, hex8), + re.findall(r"^%s(%s) g\s+F __TEXT,__text (x\1|_main)$" % (hex8, hex8), objdump_string, re.MULTILINE)} if not program_symbols_map: sys.exit("no program symbols found in input") -- 2.7.4