[lldb-dev] Stack unwinding with hand-crafted CFI

Pavel Labath labath at google.com
Tue Apr 21 08:39:24 PDT 2015


(this is a followup to <http://reviews.llvm.org/D9150>)

Hello Jason,

I am trying to get stack unwinding to work from within deep bowels of
glibc. Glibc contains a lot of functions (and "functions"), which do
crazy stuff with the stack, frame and instruction pointers. Luckily,
most (all?) of these functions contain CFI which generally does "the
right thing". Unfortunately, these UnwindPlans are sometimes not
picked by LLDB because they are too strange.

For example take the "function" _L_lock_4746, which has the following
dissassembly:
$ disassemble -n _L_lock_4746
libc.so.6`_L_lock_4746:
    0x7ffff70519bc <+0>:  leaq   (%rdx), %rdi
    0x7ffff70519bf <+3>:  subq   $0x80, %rsp
    0x7ffff70519c6 <+10>: callq  0x7ffff70df120            ;
__lll_lock_wait_private
    0x7ffff70519cb <+15>: addq   $0x80, %rsp
    0x7ffff70519d2 <+22>: jmp    0x7ffff70515fa            ;
_IO_new_file_underflow + 138 at fileops.c:592


And the following unwind plans:
$ image show-unwind -n _L_lock_4746
UNWIND PLANS for libc.so.6`_L_lock_4746 (start addr 0x7ffff70519bc)

Asynchronous (not restricted to call-sites) UnwindPlan is 'assembly
insn profiling'
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.
Address range of this UnwindPlan: [libc.so.6..text + 374044-0x000000000005b537)
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]
row[1]:   10: CFA=rsp+136 => rsp=CFA+0 rip=[CFA-8]
row[2]:   22: 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.
Address range of this UnwindPlan: [libc.so.6..text + 374044-0x000000000005b537)
row[0]:    0: CFA=rsp-128 => rip=dwarf-expr
row[1]:    3: CFA=rsp-128 => rip=dwarf-expr
row[2]:   10: CFA=rsp +0 => rip=dwarf-expr
row[3]:   14: CFA=rsp+128 => rip=dwarf-expr
row[4]:   22: CFA=rsp-128 => rip=dwarf-expr

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.
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.
row[0]:    0: CFA=rsp +8 => rsp=CFA+0 rip=[CFA-8]


The "eh_frame CFI" plan is hand crafted to be valid at all locations
within the function and if I force its use it actually produces the
correct backtrace. However, the problem is that it is not selected by
default. This happens because when we try to augment it, we fail
because UnwindAssembly_x86::AugmentUnwindPlanFromCallSite expects the
CFI plan to be in a very specific form. Then we end up falling back to
"assembly insn profiling", which is completely bogus in this case.

So, I would like to teach LLDB to use the provided CFI for unwinding
in strange functions like these. However, I am unsure what is the
cleanest solution. I would probably need to short-circuit the
augementing logic to avoid trying to augment plans like these and just
use the CFI plan as-is. Do yo have suggestions on how to do this?
Perhaps set plan-valid-at-all-locations to true if we detect the
author has gone through extra trouble to produce the CFI (use "rip set
by dwarf-expr", or some more complex contidion as an indicator)? Then
we could use the presence of this flag as an indicator that we can
avoid augmentation?

What do you think?

cheers,
pavel



More information about the lldb-dev mailing list