You are correct. For the first frame you don't adjust the PC when looking up 
the unwind row. For the second frame on up you can decrement the PC value by 1 
before when doing the row lookup and that is usually enough to get you to the 
correct unwind row. The issue is that the return address points to the next 
instruction after the instruction that called the function. This also fixes 
issues with tail calls. Now you might ask why decrementing by 1 works when 
instructions can often be larger that 1 byte. We just need to get to the 
previous row in the unwind table, and decrementing by 1 is usually enough to 
get us there because unwind rows start at valid instruction opcode addresses.

The other tricky thing to watch out for is that the unwind information isn't 
always valid for the first frame for all values of the PC in a function. Why? 
Most unwind information is only valid at places that can throw exceptions. This 
means that when unwinding the first frame, you really can't trust the unwind 
info unless you know you are at a location that can throw an exception which is 
hard to detect by just looking at disassembly. Compilers have the ability to 
enable asynchronous unwinding with a compiler option, but even if we do enable 
this, there is no way to look at the unwind information at run time and tell 
the difference between synchronous unwind info (not valid everywhere in the 
function, only at places that can throw exceptions) and asynchronous unwind 
info (valid for any PC value in the function). To make things worse, the 
information that is put into .debug_frame often is just the same unwind info 
that is put into .eh_frame (with a few syntactic differences in encoding), so 
just know that .debug_frame is often only valid at exception call sites but 
there is not way to tell unless your compiler emits it compiler invocation 
flags in the DWARF in the DW_TAG_compile_unit as an attribute.

The LLDB debugger will use unwind info from the binary for all frames except 
the first frame. For the first frame, we actually decode assembly and create 
our own unwind information that is valid everywhere in the function. 

Now, the good news is, if you are making a backtrace for a thread that has 
crashed, it should be at a valid address and allow you to use the unwind 
information. If you also have other threads that were stopped when one thread 
crashes, they won't be at valid locations for unwind for the first frames.


> On Jul 31, 2020, at 5:37 AM, Jayvee Neumann via Dwarf-Discuss 
> <dwarf-discuss@lists.dwarfstd.org> wrote:
> 
> Hello together!
> 
> I am running into a problem while performing a stack trace of x86 code. The 
> assembly code I am running has been generated by mingw from C++ code and 
> looks like this:
> 
> 6c9c1210 <__ZN7my_class9my_methodEs>:
> 6c9c1210: sub    $0x1c,%esp
> 6c9c1213: mov    0x20(%esp),%edx
> 6c9c1217: mov    %edx,%eax
> 6c9c1219: test   %dx,%dx
> 6c9c121c: jle    6c9c1221 <__ZN7my_class9my_methodEs+0x11>
> 6c9c121e: lea    0x1(%edx),%eax
> 6c9c1221: cwtl   
> 6c9c1222: mov    %eax,(%esp)
> 6c9c1225: call   6c9c1190 <__Z12my_dummy_functions>
> 6c9c122a: add    $0x1c,%esp
> 6c9c122d: ret    $0x4
> 
> 6c9c1230 <__ZN8my_struct9my_methodEv>:
> 6c9c1230: sub    $0x1c,%esp
> 6c9c1233: movzwl 0x4(%ecx),%eax
> 6c9c1237: test   %ax,%ax
> 6c9c123a: jle    6c9c1241 <__ZN8my_struct9my_methodEv+0x11>
> 6c9c123c: add    $0x1,%eax
> 6c9c123f: jmp    6c9c1246 <__ZN8my_struct9my_methodEv+0x16>
> 6c9c1241: mov    $0x0,%eax
> 6c9c1246: cwtl   
> 6c9c1247: add    $0x8,%ecx
> 6c9c124a: mov    %eax,(%esp)
> 6c9c124d: call   6c9c1210 <__ZN7my_class9my_methodEs>
> 6c9c1252: sub    $0x4,%esp
> 6c9c1255: add    $0x1c,%esp
> 6c9c1258: ret    
> 6c9c1259: nop
> 6c9c125a: lea    0x0(%esi),%esi
> 
> The problem manifests itself, when the instruction pointer is inside " 
> __ZN7my_class9my_methodEs" (called at 0x6c9c124d).
> 
> In order to perform the stack trace, I use the DWARF frame information for 
> calculating the previous instruction pointer. This is done by assuming the 
> return address is the instruction pointer of the previous frame. This is 
> obviously not entirely correct, since the return address points to a location 
> AFTER the previous call. Nevertheless, this assumption seems to be standard 
> for other stack tracers.
> 
> I am having a problem with this though:
> The address where I start is 0x6c9c121e. Frame information tells me the 
> following:
> 
> 00000144 0000001c 00000000 FDE cie=00000000 pc=6c9c1210...6c9c1230
>   DW_CFA_advance_loc4: 3
>   DW_CFA_def_cfa_offset: +32
>   DW_CFA_advance_loc4: 26
>   DW_CFA_def_cfa_offset: +4
>   DW_CFA_nop:
>   DW_CFA_nop:
> 
> So the CFA offset is 32. There I find the next return address 0x6c9c124d. 
> Frame information tells me the following:
> 
> 00000164 00000028 00000000 FDE cie=00000000 pc=6c9c1230...6c9c1259
>   DW_CFA_advance_loc4: 3
>   DW_CFA_def_cfa_offset: +32
>   DW_CFA_advance_loc4: 31
>   DW_CFA_def_cfa_offset: +28
>   DW_CFA_advance_loc4: 3
>   DW_CFA_def_cfa_offset: +32
>   DW_CFA_advance_loc4: 3
>   DW_CFA_def_cfa_offset: +4
> 
> And here the problem arises. Due to 0x6c9c124d being the return address, the 
> CFA offset I read is invalid. By assuming an instruction pointer of 
> 0x6c9c124d I also assume that the "ret $0x4" instruction from 0x6c9c122d has 
> been executed and the stack is 4 bytes shorter. This is however not the case. 
> The return has not been executed yet.
> 
> So my question here is, how shall the stack tracer solve this issue? My first 
> Idea is to decrement the instruction pointer when looking through the frame 
> information (except for the deepest frame, where the instruction pointer is 
> correct). Would that be an approach that works always? How do other consumers 
> solve this issue?
> 
> Best regards
> Jayvee
> _______________________________________________
> Dwarf-Discuss mailing list
> Dwarf-Discuss@lists.dwarfstd.org
> http://lists.dwarfstd.org/listinfo.cgi/dwarf-discuss-dwarfstd.org

_______________________________________________
Dwarf-Discuss mailing list
Dwarf-Discuss@lists.dwarfstd.org
http://lists.dwarfstd.org/listinfo.cgi/dwarf-discuss-dwarfstd.org

Reply via email to