From 0f24ffcdfa9bb2b6380cedbb24f16d4ab9920dd7 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 25 Jun 2021 22:50:46 -0400 Subject: [PATCH] [lld/mac] Don't fold UNWIND_X86_64_MODE_STACK_IND unwind entries libunwind uses unwind info to find the function address belonging to the current instruction pointer. libunwind/src/CompactUnwinder.hpp's step functions read functionStart for UNWIND_X86_64_MODE_STACK_IND (and for nothing else), so these encodings need a dedicated entry per function, so that the runtime can get the stacksize off the `subq` instrunction in the function's prologue. This matches ld64. (CompactUnwinder.hpp from https://opensource.apple.com/source/libunwind/ also reads functionStart in a few more cases if `SUPPORT_OLD_BINARIES` is set, but it defaults to 0, and ld64 seems to not worry about these additional cases.) Related upstream bug: https://crbug.com/1220175 Differential Revision: https://reviews.llvm.org/D104978 --- lld/MachO/UnwindInfoSection.cpp | 28 +++++++++++++++- lld/test/MachO/compact-unwind-stack-ind.s | 54 +++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 lld/test/MachO/compact-unwind-stack-ind.s diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp index 8ad71c0..6e9f4ff 100644 --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -316,6 +316,31 @@ static void addEntriesForFunctionsWithoutUnwindInfo( markNoUnwindInfo(d); } +static bool canFoldEncoding(compact_unwind_encoding_t encoding) { + // From compact_unwind_encoding.h: + // UNWIND_X86_64_MODE_STACK_IND: + // A "frameless" (RBP not used as frame pointer) function large constant + // stack size. This case is like the previous, except the stack size is too + // large to encode in the compact unwind encoding. Instead it requires that + // the function contains "subq $nnnnnnnn,RSP" in its prolog. The compact + // encoding contains the offset to the nnnnnnnn value in the function in + // UNWIND_X86_64_FRAMELESS_STACK_SIZE. + // Since this means the unwinder has to look at the `subq` in the function + // of the unwind info's unwind address, two functions that have identical + // unwind info can't be folded if it's using this encoding since both + // entries need unique addresses. + static_assert(UNWIND_X86_64_MODE_MASK == UNWIND_X86_MODE_MASK, ""); + static_assert(UNWIND_X86_64_MODE_STACK_IND == UNWIND_X86_MODE_STACK_IND, ""); + if ((target->cpuType == CPU_TYPE_X86_64 || target->cpuType == CPU_TYPE_X86) && + (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND) { + // FIXME: Consider passing in the two function addresses and getting + // their two stack sizes off the `subq` and only returning false if they're + // actually different. + return false; + } + return true; +} + // Scan the __LD,__compact_unwind entries and compute the space needs of // __TEXT,__unwind_info and __TEXT,__eh_frame template void UnwindInfoSectionImpl::finalize() { @@ -377,7 +402,8 @@ template void UnwindInfoSectionImpl::finalize() { while (++foldEnd < cuPtrVector.end() && (*foldBegin)->encoding == (*foldEnd)->encoding && (*foldBegin)->personality == (*foldEnd)->personality && - (*foldBegin)->lsda == (*foldEnd)->lsda) + (*foldBegin)->lsda == (*foldEnd)->lsda && + canFoldEncoding((*foldEnd)->encoding)) ; *foldWrite++ = *foldBegin; foldBegin = foldEnd; diff --git a/lld/test/MachO/compact-unwind-stack-ind.s b/lld/test/MachO/compact-unwind-stack-ind.s new file mode 100644 index 0000000..13d5e92 --- /dev/null +++ b/lld/test/MachO/compact-unwind-stack-ind.s @@ -0,0 +1,54 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin19.0.0 %s -o %t.o +# RUN: %lld -arch x86_64 -dylib %t.o -o %t.dylib +# RUN: llvm-objdump --macho --syms --unwind-info %t.dylib | FileCheck %s + +## Both _f and _g have the same compact unwind encoding, +## but different stack sizes. So their compact unwindings +## can't be merged. +# CHECK: SYMBOL TABLE: +# CHECK: [[#%x,F:]] g F __TEXT,__text _f +# CHECK: [[#%x,G:]] g F __TEXT,__text _g +# CHECK: Number of common encodings in array: 0x1 +# CHECK: Common encodings: (count = 1) +# CHECK: encoding[0]: 0x03032000 +# CHECK: Second level indices: +# CHECK: Second level index[0]: +# CHECK: [0]: function offset=0x[[#%.8x,F]], encoding[0]=0x03032000 +# CHECK: [1]: function offset=0x[[#%.8x,G]], encoding[0]=0x03032000 + +## Based on compiling +## int f() { +## char alloca[3260] = { 0 }; +## return alloca[0]; +## } +## +## int g() { +## char alloca[2560] = { 0 }; +## return alloca[0]; +## } +## with `-fomit-frame-pointer -fno-stack-protector -S`. +.section __TEXT,__text,regular,pure_instructions +.build_version macos, 10, 15 sdk_version 10, 15, 6 + +.globl _f +.p2align 4, 0x90 +_f: + .cfi_startproc + subq $3272, %rsp + .cfi_def_cfa_offset 3280 + addq $3272, %rsp + retq + .cfi_endproc + +.globl _g +.p2align 4, 0x90 +_g: + .cfi_startproc + subq $2568, %rsp + .cfi_def_cfa_offset 2576 + addq $2568, %rsp + retq + .cfi_endproc + +.subsections_via_symbols -- 2.7.4