This is an archive of the discontinued LLVM Phabricator instance.

[lldb] do not propagate eTrapHandlerFrame repeatedly
AbandonedPublic

Authored by llunak on Aug 23 2020, 4:40 AM.

Details

Summary

This fixes handle-abrt test on my x86_64 openSUSE 15.2. The regression was introduced by 3fd917d8860e9bdcabc14c536da4377307906be0.

Diff Detail

Repository
rLLDB LLDB

Event Timeline

llunak created this revision.Aug 23 2020, 4:40 AM
llunak requested review of this revision.Aug 23 2020, 4:40 AM

And its parent 'raise' is set so as well because of 'GetNextFrame()->m_frame_type == eTrapHandlerFrame '.

Sorry, can you clarify what code path you're referring to here? I see two occurrences of GetNextFrame()->m_frame_type == eTrapHandlerFrame in the code, and three occurrences of m_frame_type = eTrapHandlerFrame, but it's not clear how one of the former leads to one of the latter, or if I'm misunderstanding what you're saying.

And its parent 'raise' is set so as well because of 'GetNextFrame()->m_frame_type == eTrapHandlerFrame '.

Sorry, can you clarify what code path you're referring to here? I see two occurrences of GetNextFrame()->m_frame_type == eTrapHandlerFrame in the code, and three occurrences of m_frame_type = eTrapHandlerFrame, but it's not clear how one of the former leads to one of the latter, or if I'm misunderstanding what you're saying.

The one in RegisterContextUnwind::GetFullUnwindPlanForFrame(). It sets behaves_like_zeroth_frame, which in turn leads to executing the if() block later down in the function.

After applying the following patch

diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp
index cf9336857ba..1de27ca764e 100644
--- a/lldb/source/Target/RegisterContextUnwind.cpp
+++ b/lldb/source/Target/RegisterContextUnwind.cpp
@@ -1,3 +1,4 @@
+#include <iostream>
 //===-- RegisterContextUnwind.cpp -----------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -263,6 +264,7 @@ void RegisterContextUnwind::InitializeZerothFrame() {
 // RegisterContextUnwind "below" it to provide things like its current pc value.
 
 void RegisterContextUnwind::InitializeNonZerothFrame() {
+    std::cerr << __PRETTY_FUNCTION__ << ":" << GetSymbolOrFunctionName(m_sym_ctx).AsCString("") << std::endl;
   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
   if (IsFrameZero()) {
     m_frame_type = eNotAValidFrame;
@@ -637,6 +639,7 @@ bool RegisterContextUnwind::IsFrameZero() const { return m_frame_number == 0; }
 //   the function, maybe backed up by 1, -1 if unknown
 
 UnwindPlanSP RegisterContextUnwind::GetFastUnwindPlanForFrame() {
+    std::cerr << __PRETTY_FUNCTION__ << ":" << GetSymbolOrFunctionName(m_sym_ctx).AsCString("") << std::endl;
   UnwindPlanSP unwind_plan_sp;
   ModuleSP pc_module_sp(m_current_pc.GetModule());
 
@@ -689,6 +692,7 @@ UnwindPlanSP RegisterContextUnwind::GetFastUnwindPlanForFrame() {
 //   the function, maybe backed up by 1, -1 if unknown
 
 UnwindPlanSP RegisterContextUnwind::GetFullUnwindPlanForFrame() {
+    std::cerr << __PRETTY_FUNCTION__ << ":" << GetSymbolOrFunctionName(m_sym_ctx).AsCString("") << std::endl;
   UnwindPlanSP unwind_plan_sp;
   UnwindPlanSP arch_default_unwind_plan_sp;
   ExecutionContext exe_ctx(m_thread.shared_from_this());
@@ -1763,6 +1767,7 @@ bool RegisterContextUnwind::ForceSwitchToFallbackUnwindPlan() {
 
 void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(
     lldb::UnwindPlanSP unwind_plan) {
+    std::cerr << __PRETTY_FUNCTION__ << ":" << GetSymbolOrFunctionName(m_sym_ctx).AsCString("") << std::endl;
   if (unwind_plan->GetUnwindPlanForSignalTrap() != eLazyBoolYes) {
     // Unwind plan does not indicate trap handler.  Do nothing.  We may
     // already be flagged as trap handler flag due to the symbol being
@@ -1775,6 +1780,7 @@ void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(
   }
 
   m_frame_type = eTrapHandlerFrame;
+    std::cerr << "SET:" << GetSymbolOrFunctionName(m_sym_ctx).AsCString("") << std::endl;
 
   if (m_current_offset_backed_up_one != m_current_offset) {
     // We backed up the pc by 1 to compute the symbol context, but

and then executing manually what TestHandleAbort.py does directly in lldb, then doing the 'bt' command when the breakpoint at "Set a breakpoint here" results in this output:

(lldb) bt
void lldb_private::RegisterContextUnwind::InitializeNonZerothFrame():
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFastUnwindPlanForFrame():__restore_rt
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFullUnwindPlanForFrame():__restore_rt
void lldb_private::RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(lldb::UnwindPlanSP):__restore_rt
void lldb_private::RegisterContextUnwind::InitializeNonZerothFrame():
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFastUnwindPlanForFrame():raise
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFullUnwindPlanForFrame():raise
void lldb_private::RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(lldb::UnwindPlanSP):raise
SET:raise
void lldb_private::RegisterContextUnwind::InitializeNonZerothFrame():
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFastUnwindPlanForFrame():abort
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFullUnwindPlanForFrame():abort
void lldb_private::RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(lldb::UnwindPlanSP):abort
SET:abort
void lldb_private::RegisterContextUnwind::InitializeNonZerothFrame():
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFastUnwindPlanForFrame():abort_caller
void lldb_private::RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(lldb::UnwindPlanSP):abort_caller
void lldb_private::RegisterContextUnwind::InitializeNonZerothFrame():
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFastUnwindPlanForFrame():main
void lldb_private::RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(lldb::UnwindPlanSP):main
void lldb_private::RegisterContextUnwind::InitializeNonZerothFrame():
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFastUnwindPlanForFrame():__libc_start_main
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFullUnwindPlanForFrame():__libc_start_main
void lldb_private::RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(lldb::UnwindPlanSP):__libc_start_main
void lldb_private::RegisterContextUnwind::InitializeNonZerothFrame():
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFastUnwindPlanForFrame():_start
lldb::UnwindPlanSP lldb_private::RegisterContextUnwind::GetFullUnwindPlanForFrame():_start
void lldb_private::RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(lldb::UnwindPlanSP):_start
void lldb_private::RegisterContextUnwind::InitializeNonZerothFrame():
void lldb_private::RegisterContextUnwind::InitializeNonZerothFrame():
* thread #1, name = 'a.out', stop reason = breakpoint 1.1
  * frame #0: 0x00000000004006bb a.out`handler(sig=6) at main.c:7:5
    frame #1: 0x00007ffff7a555a0 libc.so.6`__restore_rt
    frame #2: 0x00007ffff7a55520 libc.so.6`raise + 272
    frame #3: 0x00007ffff7a56b01 libc.so.6`abort + 337
    frame #4: 0x00000000004006e9 a.out at main.c:12:5
    frame #5: 0x0000000000400743 a.out`main at main.c:23:5
    frame #6: 0x00007ffff7a4034a libc.so.6`__libc_start_main + 234
    frame #7: 0x00000000004005fa a.out`_start at start.S:120

For raise and abort, GetFastUnwindPlanForFrame() returns nullptr, which leads to calling GetFullUnwindPlanForFrame(), which triggers the behaves_like_zeroth_frame path, which leads to returning an unwind plan with GetUnwindPlanForSignalTrap() set to yes, which leads to propagating eTrapHandlerFrame in PropagateTrapHandlerFlagFromUnwindPlan(). Which leads to getting the symbol for abort_handler() wrong. At least that's my understanding. I have no idea why it's broken for me and (apparently) it works for everybody else.

Hi, I'll review this problem / suggested patch in a bit but I think it might be helpful to outline what the intention of all this is. Let's say we have a stack of

frame #0 - handler_func()
frame #1 - magic_function_called_from_kernel()
frame #2 - doing_work()

doing_work() was executing along when an asynchronous signal came in - a SIGINT or something. The kernel stops it, saves its register context (all the registers) into a buffer on the stack, and invokes magic_function_called_from_kernel. m_f_c_f_k calls the handler_func that the program registered as the handler for SIGINT earlier.

In this situation, there are two *special* things about doing_work that the unwinder must do.

First, normally when you are off of the currently-executing stack frame (frame 0), only registers that are callee-spilled aka non-volatile are retrievable. For instance, on the x86 SysV ABI, rdi is an argument register. In frame 0, you can print rdi. On frame #1, rdi may have been modified since it called frame 0, so lldb won't let you retrieve it. However, when a stack frame has been interrupted asynchronously and we have a full saved register context, we can retrieve any register. Printing stack frame 2's $rdi is possible and valid. frame #2 behaves like a zeroth stack frame, because the next frame -- frame #1, magic_function_called_from_kernel, is a trap handler and has the full register context.

The second thing that is different about frame #2 is how we look up the source line and symbol context. When we set the source line & block scope & function scope searching for a frame up the stack, we subtract 1 from the return-pc value. Why do we subtract 1? Two important reasons. First, a function may end by calling a no-return function like abort() and the x86 ABI will push the return address on the stack with the next pc value. But if the callq abort is the last instruction, then the return address points to the next function! Also, there are cases (often with optimized code) where a variable is in a register up until the function call (and is still retrievable) but after the function call, it may be considered dead because it's a different code path through the function. So doing source line / block scope / function scope / location list lookups with $return-pc - 1 is very important. However, when we interrupt a function asynchronously, like doing_work here, we may be legitimately on the very first instruction of doing_work. Backing up the pc by 1 would have us claiming that we're in the *previous* function.

So this is why we have all this code for computing behaves_like_zeroth_frame in these areas. In my original design, I conflated the two important points here: That we have a full saved register context and that the function may have been interrupted asynchronously, so we need to treat this as if it's a currently-executing stack frame, even though it's in the middle of the stack. If either of these is not true: if the NextFrame (frame #1 in this case) does NOT have a full register context, or this frame (frame #2) was NOT interrupted asynchronously, then treating frame #2 as behaves_like_zeroth_frame is incorrect and will lead to bugs.

If I understand correctly, in

frame #0: 0x00000000004006bb a.out`handler(sig=6) at main.c:7:5
frame #1: 0x00007ffff7a555a0 libc.so.6`__restore_rt
frame #2: 0x00007ffff7a55520 libc.so.6`raise + 272
frame #3: 0x00007ffff7a56b01 libc.so.6`abort + 337

lldb thinks that both frames 1 & 2 are trap handler frames. They have full register context available for the frame above them on the stack (that is, frames 2 & 3) and frames 2 & 3 were interrupted asynchronously. This doesn't sound right? How do we decide what is a trap handler frame? One way is to look for the 'S' augmentation in the eh_frame / dwarf debug_frame CIE/FDE for the function -

unwind_plan.SetUnwindPlanForSignalTrap(
  strchr(cie->augmentation, 'S') ? eLazyBoolYes : eLazyBoolNo);

The other way is from the Platform CalculateTrapHandlerSymbolNames method. PlatformLinux sets these to

void PlatformLinux::CalculateTrapHandlerSymbolNames() {
  m_trap_handlers.push_back(ConstString("_sigtramp"));
  m_trap_handlers.push_back(ConstString("__kernel_rt_sigreturn"));
  m_trap_handlers.push_back(ConstString("__restore_rt"));

is one of these wrong?

Maybe start with a simpler question -- does abort call raise? Like, through a normal CALLQ? Does raise call __restore_rt? Through a normal CALLQ? Do any of these have the 'S' augmentation string in their eh_frame? UnwindPlan::Dump should print whether an unwindplan says it is a trap handler or not, but it doesn't currently (sigh). If it did, you could do image show-unwind -n raise and see what lldb thinks it is.

separate from the fact that UnwindPlan::Dump does not print m_plan_is_for_signal_trap for an UnwindPlan, what we need to see from a real trap handler -- in lldb's terminology -- is that we have restore rules for all of the registers.

If we're getting things marked as a trap handler which are not, maybe we should change the unwind rules to *explicitly* mark any registers that the UnwindPlan doesn't supply as "unavailable". If raise doesn't have a save rule for rdi, that means that the value of rdi in raise has been unmodified from the caller function, and it could be supplied (invalidly). In the middle of a stack, it isn't such a big deal because the next frame down the stack (e.g. frame 1) will not pass rdi up the stack, I suppose.

Minor followup on the 'image show-unwind' output -- I just landed a patch to print when a function or unwindplan are marked as being a trap handler.

lldb thinks that both frames 1 & 2 are trap handler frames. They have full register context available for the frame above them on the stack (that is, frames 2 & 3) and frames 2 & 3 were interrupted asynchronously. This doesn't sound right? How do we decide what is a trap handler frame? One way is to look for the 'S' augmentation in the eh_frame / dwarf debug_frame CIE/FDE for the function -

...

The other way is from the Platform CalculateTrapHandlerSymbolNames method. PlatformLinux sets these to

...

is one of these wrong?

I don't know. I do have some knowledge about how stack frames and traps work, but the reason I find it hard to explain the actual problem is because I'm not familiar with the LLDB unwind code and struggle to understand what and why it's really doing (e.g. for the "GetSymbolOrFunctionName(m_sym_ctx).AsCString("")" debug output I posted above, I'm still not sure if this printing "abort" means it's finding out information about "abort" or the next frame above it).

Maybe start with a simpler question -- does abort call raise? Like, through a normal CALLQ?

Yes.

libc.so.6`abort:
->  0x7ffff7a56afc <+332>: callq  0x7ffff7a55410            ; raise

Does raise call __restore_rt? Through a normal CALLQ?

No.

libc.so.6`raise:
->  0x7ffff7a5551e <+270>: syscall 
    0x7ffff7a55520 <+272>: movq   0x108(%rsp), %rcx
    0x7ffff7a55528 <+280>: xorq   %fs:0x28, %rcx
    0x7ffff7a55531 <+289>: movl   %r8d, %eax

Minor followup on the 'image show-unwind' output -- I just landed a patch to print when a function or unwindplan are marked as being a trap handler.

(lldb) image show-unwind -n __restore_rt
UNWIND PLANS for ld-2.26.so`__restore_rt (start addr 0x7ffff7df2270)
This function's name is listed by the platform as a trap handler.

Asynchronous (not restricted to call-sites) UnwindPlan is 'eh_frame CFI'
Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI'

Assembly language inspection UnwindPlan:
This UnwindPlan originally sourced from assembly insn profiling
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [ld-2.26.so.PT_LOAD[0]..text + 107856-0x000000000001a560)
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 

eh_frame UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: yes.
Address range of this UnwindPlan: [ld-2.26.so.PT_LOAD[0]..text + 107855-0x000000000001a559)
row[0]:    0: CFA=DW_OP_breg7 +160, DW_OP_deref => rax=[DW_OP_breg7 +144] rdx=[DW_OP_breg7 +136] rcx=[DW_OP_breg7 +152] rbx=[DW_OP_breg7 +128] rsi=[DW_OP_breg7 +112] rdi=[DW_OP_breg7 +104] rbp=[DW_OP_breg7 +120] rsp=[DW_OP_breg7 +160] r8=[DW_OP_breg7 +40] r9=[DW_OP_breg7 +48] r10=[DW_OP_breg7 +56] r11=[DW_OP_breg7 +64] r12=[DW_OP_breg7 +72] r13=[DW_OP_breg7 +80] r14=[DW_OP_breg7 +88] r15=[DW_OP_breg7 +96] rip=[DW_OP_breg7 +168] 

Arch default UnwindPlan:
This UnwindPlan originally sourced from x86_64 default unwind plan
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
row[0]:    0: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

Arch default at entry point UnwindPlan:
This UnwindPlan originally sourced from x86_64 at-func-entry default
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: not specified.
This UnwindPlan is for a trap handler function: not specified.
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 


UNWIND PLANS for libc.so.6`__restore_rt (start addr 0x7ffff7a555a0)
This function's name is listed by the platform as a trap handler.

Asynchronous (not restricted to call-sites) UnwindPlan is 'eh_frame CFI'
Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI'

Assembly language inspection UnwindPlan:
This UnwindPlan originally sourced from assembly insn profiling
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88624-0x0000000000015a40)
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 

eh_frame UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: yes.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88623-0x0000000000015a39)
row[0]:    0: CFA=DW_OP_breg7 +160, DW_OP_deref => rax=[DW_OP_breg7 +144] rdx=[DW_OP_breg7 +136] rcx=[DW_OP_breg7 +152] rbx=[DW_OP_breg7 +128] rsi=[DW_OP_breg7 +112] rdi=[DW_OP_breg7 +104] rbp=[DW_OP_breg7 +120] rsp=[DW_OP_breg7 +160] r8=[DW_OP_breg7 +40] r9=[DW_OP_breg7 +48] r10=[DW_OP_breg7 +56] r11=[DW_OP_breg7 +64] r12=[DW_OP_breg7 +72] r13=[DW_OP_breg7 +80] r14=[DW_OP_breg7 +88] r15=[DW_OP_breg7 +96] rip=[DW_OP_breg7 +168] 

Arch default UnwindPlan:
This UnwindPlan originally sourced from x86_64 default unwind plan
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
row[0]:    0: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

Arch default at entry point UnwindPlan:
This UnwindPlan originally sourced from x86_64 at-func-entry default
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: not specified.
This UnwindPlan is for a trap handler function: not specified.
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
(lldb) image show-unwind -n raise
UNWIND PLANS for libc.so.6`raise (start addr 0x7ffff7a55410)

Asynchronous (not restricted to call-sites) UnwindPlan is 'eh_frame CFI plus augmentation from assembly parsing'
Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI'

Assembly language inspection UnwindPlan:
This UnwindPlan originally sourced from assembly insn profiling
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88224-0x00000000000159e3)
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 
row[1]:    1: CFA=rsp+16 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 
row[2]:   25: CFA=rsp+288 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 
row[3]:  301: CFA=rsp+16 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 
row[4]:  302: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 
row[5]:  303: CFA=rsp+288 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

eh_frame UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88224-0x00000000000159e3)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    1: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[2]:   25: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 
row[3]:  301: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[4]:  302: CFA=rsp +8 => rbx=[CFA-16] rip=[CFA-8] 
row[5]:  304: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 

eh_frame augmented UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI plus augmentation from assembly parsing
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: yes.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88224-0x00000000000159e3)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    1: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[2]:   25: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 
row[3]:  301: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[4]:  302: CFA=rsp +8 => rbx=[CFA-16] rip=[CFA-8] 
row[5]:  304: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 

Arch default UnwindPlan:
This UnwindPlan originally sourced from x86_64 default unwind plan
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
row[0]:    0: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

Arch default at entry point UnwindPlan:
This UnwindPlan originally sourced from x86_64 at-func-entry default
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: not specified.
This UnwindPlan is for a trap handler function: not specified.
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
(lldb) image show-unwind -n abort
UNWIND PLANS for libc.so.6`abort (start addr 0x7ffff7a569b0)

Asynchronous (not restricted to call-sites) UnwindPlan is 'eh_frame CFI'
Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI'

Assembly language inspection UnwindPlan:
This UnwindPlan originally sourced from assembly insn profiling
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 93760-0x0000000000017090)
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 
row[1]:    7: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 
row[2]:   93: CFA=rsp+432 => rsp=CFA+0 rip=[CFA-8] 
row[3]:  105: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 
row[4]:  315: CFA=rsp+432 => rsp=CFA+0 rip=[CFA-8] 
row[5]:  327: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 
row[6]:  406: CFA=rsp+432 => rsp=CFA+0 rip=[CFA-8] 
row[7]:  418: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 

eh_frame UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 93760-0x0000000000017090)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    7: CFA=rsp+304 => rip=[CFA-8] 
row[2]:   93: CFA=rsp+432 => rip=[CFA-8] 
row[3]:  105: CFA=rsp+304 => rip=[CFA-8] 
row[4]:  315: CFA=rsp+432 => rip=[CFA-8] 
row[5]:  327: CFA=rsp+304 => rip=[CFA-8] 
row[6]:  406: CFA=rsp+432 => rip=[CFA-8] 
row[7]:  418: CFA=rsp+304 => rip=[CFA-8] 

eh_frame augmented UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: yes.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 93760-0x0000000000017090)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    7: CFA=rsp+304 => rip=[CFA-8] 
row[2]:   93: CFA=rsp+432 => rip=[CFA-8] 
row[3]:  105: CFA=rsp+304 => rip=[CFA-8] 
row[4]:  315: CFA=rsp+432 => rip=[CFA-8] 
row[5]:  327: CFA=rsp+304 => rip=[CFA-8] 
row[6]:  406: CFA=rsp+432 => rip=[CFA-8] 
row[7]:  418: CFA=rsp+304 => rip=[CFA-8] 

Arch default UnwindPlan:
This UnwindPlan originally sourced from x86_64 default unwind plan
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
row[0]:    0: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

Arch default at entry point UnwindPlan:
This UnwindPlan originally sourced from x86_64 at-func-entry default
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: not specified.
This UnwindPlan is for a trap handler function: not specified.
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]

eh_frame augmented UnwindPlan:
...
This UnwindPlan is for a trap handler function: yes.

From memory, I'd have expected that for __restore_rt, but not for raise or abort. Can you dump the augmentation fields from the CIEs (or instrument FDEToUnwindPlan) to verify we're getting that right?

FWIW, I tried running the test in an SUSE 15.2 docker container (on an Ubuntu 18.04 host), and here's what I'm seeing. raise and abort indeed do not indicate that they had the S set in the augmentation:

(lldb) bt
 * thread #1, name = 'a.out', stop reason = breakpoint 2.1
  * frame #0: 0x000000000040064b a.out`handler(sig=6) at main.c:7:5
    frame #1: 0x00007ffff71245a0 libc.so.6`__restore_rt
    frame #2: 0x00007ffff7124520 libc.so.6`raise + 272
    frame #3: 0x00007ffff7125b01 libc.so.6`abort + 337
    frame #4: 0x0000000000400679 a.out`abort_caller at main.c:12:5
    frame #5: 0x00000000004006d3 a.out`main at main.c:23:5
    frame #6: 0x00007ffff710f34a libc.so.6`__libc_start_main + 234
    frame #7: 0x000000000040058a a.out`_start at start.S:120
(lldb) image show-unwind -n __restore_rt
UNWIND PLANS for ld-2.26.so`__restore_rt (start addr 0x7ffff7df2270)
This function's name is listed by the platform as a trap handler.

Asynchronous (not restricted to call-sites) UnwindPlan is 'eh_frame CFI'
Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI'

Assembly language inspection UnwindPlan:
This UnwindPlan originally sourced from assembly insn profiling
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [ld-2.26.so.PT_LOAD[0]..text + 107856-0x000000000001a560)
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 

eh_frame UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: yes.
Address range of this UnwindPlan: [ld-2.26.so.PT_LOAD[0]..text + 107855-0x000000000001a559)
row[0]:    0: CFA=DW_OP_breg7 +160, DW_OP_deref => rax=[DW_OP_breg7 +144] rdx=[DW_OP_breg7 +136] rcx=[DW_OP_breg7 +152] rbx=[DW_OP_breg7 +128] rsi=[DW_OP_breg7 +112] rdi=[DW_OP_breg7 +104] rbp=[DW_OP_breg7 +120] rsp=[DW_OP_breg7 +160] r8=[DW_OP_breg7 +40] r9=[DW_OP_breg7 +48] r10=[DW_OP_breg7 +56] r11=[DW_OP_breg7 +64] r12=[DW_OP_breg7 +72] r13=[DW_OP_breg7 +80] r14=[DW_OP_breg7 +88] r15=[DW_OP_breg7 +96] rip=[DW_OP_breg7 +168] 

Arch default UnwindPlan:
This UnwindPlan originally sourced from x86_64 default unwind plan
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
row[0]:    0: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

Arch default at entry point UnwindPlan:
This UnwindPlan originally sourced from x86_64 at-func-entry default
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: not specified.
This UnwindPlan is for a trap handler function: not specified.
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 


UNWIND PLANS for libc.so.6`__restore_rt (start addr 0x7ffff71245a0)
This function's name is listed by the platform as a trap handler.

Asynchronous (not restricted to call-sites) UnwindPlan is 'eh_frame CFI'
Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI'

Assembly language inspection UnwindPlan:
This UnwindPlan originally sourced from assembly insn profiling
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88624-0x0000000000015a40)
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 

eh_frame UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: yes.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88623-0x0000000000015a39)
row[0]:    0: CFA=DW_OP_breg7 +160, DW_OP_deref => rax=[DW_OP_breg7 +144] rdx=[DW_OP_breg7 +136] rcx=[DW_OP_breg7 +152] rbx=[DW_OP_breg7 +128] rsi=[DW_OP_breg7 +112] rdi=[DW_OP_breg7 +104] rbp=[DW_OP_breg7 +120] rsp=[DW_OP_breg7 +160] r8=[DW_OP_breg7 +40] r9=[DW_OP_breg7 +48] r10=[DW_OP_breg7 +56] r11=[DW_OP_breg7 +64] r12=[DW_OP_breg7 +72] r13=[DW_OP_breg7 +80] r14=[DW_OP_breg7 +88] r15=[DW_OP_breg7 +96] rip=[DW_OP_breg7 +168] 

Arch default UnwindPlan:
This UnwindPlan originally sourced from x86_64 default unwind plan
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
row[0]:    0: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

Arch default at entry point UnwindPlan:
This UnwindPlan originally sourced from x86_64 at-func-entry default
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: not specified.
This UnwindPlan is for a trap handler function: not specified.
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
(lldb) image show-unwind -n raise
UNWIND PLANS for libc.so.6`raise (start addr 0x7ffff7124410)

Asynchronous (not restricted to call-sites) UnwindPlan is 'eh_frame CFI plus augmentation from assembly parsing'
Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI'

Assembly language inspection UnwindPlan:
This UnwindPlan originally sourced from assembly insn profiling
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88224-0x00000000000159e3)
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 
row[1]:    1: CFA=rsp+16 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 
row[2]:   25: CFA=rsp+288 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 
row[3]:  301: CFA=rsp+16 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 
row[4]:  302: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 
row[5]:  303: CFA=rsp+288 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

eh_frame UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88224-0x00000000000159e3)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    1: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[2]:   25: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 
row[3]:  301: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[4]:  302: CFA=rsp +8 => rbx=[CFA-16] rip=[CFA-8] 
row[5]:  304: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 

eh_frame augmented UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI plus augmentation from assembly parsing
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88224-0x00000000000159e3)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    1: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[2]:   25: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 
row[3]:  301: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[4]:  302: CFA=rsp +8 => rbx=[CFA-16] rip=[CFA-8] 
row[5]:  304: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 

Arch default UnwindPlan:
This UnwindPlan originally sourced from x86_64 default unwind plan
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
row[0]:    0: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

Arch default at entry point UnwindPlan:
This UnwindPlan originally sourced from x86_64 at-func-entry default
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: not specified.
This UnwindPlan is for a trap handler function: not specified.
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
(lldb) image show-unwind -n abort
UNWIND PLANS for libc.so.6`abort (start addr 0x7ffff71259b0)

Asynchronous (not restricted to call-sites) UnwindPlan is 'eh_frame CFI'
Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI'

Assembly language inspection UnwindPlan:
This UnwindPlan originally sourced from assembly insn profiling
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 93760-0x0000000000017090)
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 
row[1]:    7: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 
row[2]:   93: CFA=rsp+432 => rsp=CFA+0 rip=[CFA-8] 
row[3]:  105: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 
row[4]:  315: CFA=rsp+432 => rsp=CFA+0 rip=[CFA-8] 
row[5]:  327: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 
row[6]:  406: CFA=rsp+432 => rsp=CFA+0 rip=[CFA-8] 
row[7]:  418: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 

eh_frame UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 93760-0x0000000000017090)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    7: CFA=rsp+304 => rip=[CFA-8] 
row[2]:   93: CFA=rsp+432 => rip=[CFA-8] 
row[3]:  105: CFA=rsp+304 => rip=[CFA-8] 
row[4]:  315: CFA=rsp+432 => rip=[CFA-8] 
row[5]:  327: CFA=rsp+304 => rip=[CFA-8] 
row[6]:  406: CFA=rsp+432 => rip=[CFA-8] 
row[7]:  418: CFA=rsp+304 => rip=[CFA-8] 

eh_frame augmented UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 93760-0x0000000000017090)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    7: CFA=rsp+304 => rip=[CFA-8] 
row[2]:   93: CFA=rsp+432 => rip=[CFA-8] 
row[3]:  105: CFA=rsp+304 => rip=[CFA-8] 
row[4]:  315: CFA=rsp+432 => rip=[CFA-8] 
row[5]:  327: CFA=rsp+304 => rip=[CFA-8] 
row[6]:  406: CFA=rsp+432 => rip=[CFA-8] 
row[7]:  418: CFA=rsp+304 => rip=[CFA-8] 

Arch default UnwindPlan:
This UnwindPlan originally sourced from x86_64 default unwind plan
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
row[0]:    0: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

Arch default at entry point UnwindPlan:
This UnwindPlan originally sourced from x86_64 at-func-entry default
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: not specified.
This UnwindPlan is for a trap handler function: not specified.
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]

To clarify, the failure did not reproduce for me in the SUSE 15.02 container. I don't know what I'm doing differently. To create the environment, I just did docker run ... opensuse/leap:15.2, and then zypper in gcc-c++ python3-devel inside the container.

Hi, thanks so much for showing all the unwind plans, sorry for dropping off the thread for a day.

I'm seeing something I very much don't expect, and it may be important. The unwind information for __restore_rt is marked as a trap handler in both the eh_frame unwind information and from the PlatformLinux, and the unwind rules look like a trap handler.

However, I see that both raise and abort have eh_frame UnwindPlans saying that they are not trap handlers and eh_frame augmented UnwindPlan saying they *are*. I think we have a little buggo where the augmented UnwindPlan is created and we have an uninitialized ivar or something. I'll look at this code tomorrow, I bet it's an obvious bug.

I need to re-read the original bug description, but this will have some minor misbehaviors especially after a no-return function like abort() where the next instruction may be an entirely wrong symbol context if we treat abort as a trap handler.

Hi, could you try

diff --git a/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp b/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp
index 36e7b90cad2..66eb86fe1c0 100644

  • a/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp

+++ b/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp
@@ -1573,6 +1573,7 @@ bool x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite(

unwind_plan.SetSourceName(unwind_plan_source.c_str());
unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);

+ unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo);

}
return true;

}

and let me know how that goes? I don't have a linux system here to try it on, but I have a feeling this may fix the problem you're seeing.

and let me know how that goes? I don't have a linux system here to try it on, but I have a feeling this may fix the problem you're seeing.

Yes, it fixes my problem. However (if I'm getting it right), it fixes it by fixing only 'raise', but not 'abort'. If I do 'image show-unwind -n abort', I still get:

eh_frame UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
...
eh_frame augmented UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: yes.

The augmented unwind plan for abort is still for a trap handler (your patch affects "plus augmentation from assembly parsing", which is not this unwind plan). I don't know if this is a problem, but it looks suspicious to me, especially given that the eh_frame unwind plan is not for a trap handler.

From memory, I'd have expected that for __restore_rt, but not for raise or abort. Can you dump the augmentation fields from the CIEs (or instrument FDEToUnwindPlan) to verify we're getting that right?

I made FDEToUnwindPlan() to print the augmentation string and it contains 'S' just once, when "GetSymbolOrFunctionName(m_sym_ctx)" in RegisterContextUnwind::InitializeNonZerothFrame() says "__restore_rt". So I assume that means the info in the binary is correct.

llunak updated this revision to Diff 288780.Aug 29 2020, 9:05 AM
llunak edited the summary of this revision. (Show Details)

And it indeed was suspicious. 3fd917d8860e9bdcabc14c536da4377307906be0 didn't update the UnwindPlan copy ctor to copy the field.

This patch fixes the problem even without your change.

Ah, so we've still got a bug in x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite somehow for abort(). This method takes an UnwindPlan from eh_frame instructions (which says it is not a trap handler - correct), and adds some epilogue unwind rows at the end if needed and sets a couple of flags. I'm not sure how the trap handler lazybool is getting set to True in the process of this conversion. :/ I'll read through this method more closely Monday and try to spot how that is happening.

No, everything's fine :). See the updated patch. The UnwindPlan object already enters x86AssemblyInspectionEngine::AugmentUnwindPlanFromCallSite() with the lazybool uninitialized and fixing that fixes everything:

(lldb) bt
* thread #1, name = 'a.out', stop reason = breakpoint 1.1
  * frame #0: 0x00000000004006bb a.out`handler(sig=6) at main.c:7:5
    frame #1: 0x00007ffff7a555a0 libc.so.6`__restore_rt
    frame #2: 0x00007ffff7a55520 libc.so.6`raise + 272
    frame #3: 0x00007ffff7a56b01 libc.so.6`abort + 337
    frame #4: 0x00000000004006e9 a.out`abort_caller at main.c:12:5
    frame #5: 0x0000000000400743 a.out`main at main.c:23:5
    frame #6: 0x00007ffff7a4034a libc.so.6`__libc_start_main + 234
    frame #7: 0x00000000004005fa a.out`_start at start.S:120
(lldb) image show-unwind -n raise
UNWIND PLANS for libc.so.6`raise (start addr 0x7ffff7a55410)

Asynchronous (not restricted to call-sites) UnwindPlan is 'eh_frame CFI plus augmentation from assembly parsing'
Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI'

Assembly language inspection UnwindPlan:
This UnwindPlan originally sourced from assembly insn profiling
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88224-0x00000000000159e3)
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 
row[1]:    1: CFA=rsp+16 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 
row[2]:   25: CFA=rsp+288 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 
row[3]:  301: CFA=rsp+16 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 
row[4]:  302: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 
row[5]:  303: CFA=rsp+288 => rbx=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

eh_frame UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88224-0x00000000000159e3)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    1: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[2]:   25: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 
row[3]:  301: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[4]:  302: CFA=rsp +8 => rbx=[CFA-16] rip=[CFA-8] 
row[5]:  304: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 

eh_frame augmented UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI plus augmentation from assembly parsing
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 88224-0x00000000000159e3)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    1: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[2]:   25: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 
row[3]:  301: CFA=rsp+16 => rbx=[CFA-16] rip=[CFA-8] 
row[4]:  302: CFA=rsp +8 => rbx=[CFA-16] rip=[CFA-8] 
row[5]:  304: CFA=rsp+288 => rbx=[CFA-16] rip=[CFA-8] 

Arch default UnwindPlan:
This UnwindPlan originally sourced from x86_64 default unwind plan
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
row[0]:    0: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

Arch default at entry point UnwindPlan:
This UnwindPlan originally sourced from x86_64 at-func-entry default
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: not specified.
This UnwindPlan is for a trap handler function: not specified.
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 


(lldb) image show-unwind -n abort
UNWIND PLANS for libc.so.6`abort (start addr 0x7ffff7a569b0)

Asynchronous (not restricted to call-sites) UnwindPlan is 'eh_frame CFI'
Synchronous (restricted to call-sites) UnwindPlan is 'eh_frame CFI'

Assembly language inspection UnwindPlan:
This UnwindPlan originally sourced from assembly insn profiling
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: yes.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 93760-0x0000000000017090)
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8] 
row[1]:    7: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 
row[2]:   93: CFA=rsp+432 => rsp=CFA+0 rip=[CFA-8] 
row[3]:  105: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 
row[4]:  315: CFA=rsp+432 => rsp=CFA+0 rip=[CFA-8] 
row[5]:  327: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 
row[6]:  406: CFA=rsp+432 => rsp=CFA+0 rip=[CFA-8] 
row[7]:  418: CFA=rsp+304 => rsp=CFA+0 rip=[CFA-8] 

eh_frame UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 93760-0x0000000000017090)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    7: CFA=rsp+304 => rip=[CFA-8] 
row[2]:   93: CFA=rsp+432 => rip=[CFA-8] 
row[3]:  105: CFA=rsp+304 => rip=[CFA-8] 
row[4]:  315: CFA=rsp+432 => rip=[CFA-8] 
row[5]:  327: CFA=rsp+304 => rip=[CFA-8] 
row[6]:  406: CFA=rsp+432 => rip=[CFA-8] 
row[7]:  418: CFA=rsp+304 => rip=[CFA-8] 

eh_frame augmented UnwindPlan:
This UnwindPlan originally sourced from eh_frame CFI
This UnwindPlan is sourced from the compiler: yes.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
Address range of this UnwindPlan: [libc.so.6.PT_LOAD[0]..text + 93760-0x0000000000017090)
row[0]:    0: CFA=rsp +8 => rip=[CFA-8] 
row[1]:    7: CFA=rsp+304 => rip=[CFA-8] 
row[2]:   93: CFA=rsp+432 => rip=[CFA-8] 
row[3]:  105: CFA=rsp+304 => rip=[CFA-8] 
row[4]:  315: CFA=rsp+432 => rip=[CFA-8] 
row[5]:  327: CFA=rsp+304 => rip=[CFA-8] 
row[6]:  406: CFA=rsp+432 => rip=[CFA-8] 
row[7]:  418: CFA=rsp+304 => rip=[CFA-8] 

Arch default UnwindPlan:
This UnwindPlan originally sourced from x86_64 default unwind plan
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: no.
This UnwindPlan is for a trap handler function: no.
row[0]:    0: CFA=rbp+16 => rbp=[CFA-16] rsp=CFA+0 rip=[CFA-8] 

Arch default at entry point UnwindPlan:
This UnwindPlan originally sourced from x86_64 at-func-entry default
This UnwindPlan is sourced from the compiler: no.
This UnwindPlan is valid at all instruction locations: not specified.
This UnwindPlan is for a trap handler function: not specified.
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]

Hahaha I remember looking at this constructor and expecting to find an uninitialized ivar, but it was initialized correctly and I went to look for another place where the bug might be. I think I see why there's some confusion -- this change landed Thrusday:

commit 642cb7865f35ad7dbac78d716fcddaff561e8639
Author: Benjamin Kramer <benny.kra@googlemail.com>
Date: Wed Aug 26 13:25:41 2020 +0200

Copy m_plan_is_for_signal_trap member.

Otherwise it would stay uninitialized. Found by msan.

doing exactly that. When I was looking at the sources, that fix was already present. I think we can close this phab? I'm glad we would have found & fixed it even if Benjamin hadn't found it with msan. :)

llunak abandoned this revision.Aug 29 2020, 1:41 PM

Funny coincidence, the ctor has been broken for 5 years and that commit introducing my problem has been there for a year. Ok, closing, thank you for your help.