Using dwfl to enumerate frames of current thread

Mark Wielaard mjw at redhat.com
Sun Aug 23 21:57:31 UTC 2015


On Sat, Aug 22, 2015 at 12:18:46PM +0200, Ben Gamari wrote:
> Mark Wielaard <mjw at redhat.com> writes:
> 
> > On Fri, Aug 21, 2015 at 06:25:38PM +0200, Ben Gamari wrote:
> >> Does elfutils need .debug_ranges as well?
> >
> > Only if the DWARF producer created DIEs with DW_AT_ranges attributes.
> > You seem to never do that. If your range is just one simple one you can
> > just use a DW_AT_low_pc/high_pc pair and .debug_ranges will never be
> > used.
> >
> I actually ask because while we don't produce or use the .debug_ranges
> section, they sneak into our executables from the C runtime system
> objects. However, whatever is emitting it seems to be doing a
> questionable job,
> 
>     $ objdump --dwarf-check -e inplace/lib/bin/ghc-stage2 -x > /dev/null
>     objdump: Warning: There is an overlap [0x780 - 0x750] in .debug_ranges section.

I wonder why binutils objdump is really warning about that.
In this case it looks like the pc ranges might indeed overlap, but that is
somewhat expected. I don't think it is actually wrong. e.g. this one seems
to be a two nested lexical blocks, the outer block [750] obviously will
overlap with the pc ranges of the inner block [780]:

 [c1bdf9]            lexical_block
                     ranges               (sec_offset) range list [   750]
 [c1bdfe]              variable
                       abstract_origin      (ref4) [c1bad1]
                       location             (sec_offset) location list [  7269]
                       [...]
 [c1be46]              lexical_block
                       ranges               (sec_offset) range list [   780]
 [c1be4b]                variable
                         abstract_origin      (ref4) [c1bb2e]
                         location             (exprloc) 
                          [   0] fbreg -68

> You'll be pleased to know that libdw has worked out quite well. For
> instance, with my patch [1] GHC can produce a backtrace like,
> 
>           0x6ff81f    set_initial_registers (rts/Libdw.c:323.0)
>     0x7fd852eaba68    dwfl_thread_getframes ((null):0.0)
>     0x7fd852eab4bf    (null) ((null):0.0)
>     0x7fd852eab7f7    dwfl_getthreads ((null):0.0)
>     0x7fd852eabde3    dwfl_getthread_frames ((null):0.0)
>           0x6ffbfc    libdw_get_backtrace (rts/Libdw.c:295.0)
>           0x6f187e    backtrace_handler (rts/posix/Signals.c:540.0)
>     0x7fd852b0617f    (null) ((null):0.0)
>           0x407b58    s91t_info (nofib/shootout/n-body/Main.hs:66.27)
>           0x407cf8    r8YB_info (nofib/shootout/n-body/Main.hs:83.5)
>           0x408408    s99O_info (nofib/shootout/n-body/Main.hs:28.19)
>           0x409e80    Main_main1_info (nofib/shootout/n-body/Main.hs:27.26)
>           0x6f41c0    stg_catch_frame_info (rts/Exception.cmm:370.1)
>           0x6f29a8    stg_stop_thread_info (rts/StgStartup.cmm:42.1)
> 
> There's still a fair amount of work left to integrate this fully, but at
> least the tricky DWARF work is done.

Very nice!
Any idea why the libdw.so/dwfl_ functions don't have any info?
Where they simply build without debuginfo?

> [1] https://phabricator.haskell.org/D1156

Some quick answers to some of the questions there (I didn't read the full
bug report or the patch, so please let me know if you have any specific
questions):

- portability of elfutils.
  It is ported across a lot of arches on GNU/Linux.
  i386, x86_64, ppc, ppc64, ppc64le, s390x, arm and aarch64 are at least
  regularly tested (should be zero fail at release time) and there are
  other ports in the backends both in tree [alpha, ia64, sparc, tilegx]
  and some not yet merged out of tree [mips, m68k, hppa].
  In theory it should also work on other ELF/DWARF bases systems like
  *BSD, Debian has some limited success with kfreebsd, and Solaris. But
  there are some tricky dependencies of some of the dwfl functions on the
  /proc file system and ptrace, not all of them have clean backend/ebl
  functions. Darwin/MacOS is a bit harder since it doesn't use ELF and
  libdw currently depends on the DWARF container being ELF (and I have
  no idea what the ptrace/proc story is on Darwin). Windows is probably
  pretty hard given that it doesn't natively support ELF, DWARF or
  ptrace/proc.

- As I said before between libunwind/elfutils and libbacktrace I actually
  would have expected libbacktrace to be the easiest for you to use
  since it is actually designed for in-process unwinding. libunwind
  tries to do both in- and out-of-process unwinding, which I think is a
  little confusing, and has much less other functionality than elfutils
  with respect to model process memory, libraries, ELF, DWARF and symbol
  inspection. And elfutils really only tries to support out-of-process
  unwinding (but you happily managed to make it do in-process anyway, so
  maybe our design isn't so bad). Now that you got your DWARF/CFI correct
  I would give libbacktrace another go.

- To mark an end of stack you should set the CFI rule for the return
  register to undefined. See 6.4.4 Call Frame Calling Address.
  On x86_64 the return register is often just equal to rip and so
  using .cfi_undefined rip (in gas assembler) would do the trick.
  In general you can find fun and wonderful CFI describing interesting
  register unwinding tricks in glibc internals (try start.S, clone.S and
  __longjmp.S).

- Yes, perf can use elfutils to do unwinding. It does this "after the fact"
  It has a initial registers handler and memory read handler like you
  probably made for the in-process Dwfl_Thread_Callbacks. But they use
  the dumped register and partial stack dump they made during runtime
  to do the actual unwinding. So this only works if the CFI in your
  binary is complete and it is (mostly) expressed through the contents
  of the initial register dump and the stack values (which is almost always
  the case).

- We could in theory try to cleanup my hack to not need .debug_aranges
  if we really want to. But I hope we don't now that you have it :)

- Why both have pc ranges in the CU and in .debug_aranges (pointing to
  the CUs)? Because they technically describe different things. The
  ranges given in the CU are the covered program scope entries (code).
  While .debug_aranges give the ranges of code and data object addresses
  described by the CU (although I believe in practice it really is the
  same and even .debug_aranges only has the code ranges). Secondly it
  is really mildly more efficient since .debug_aranges is small and
  compact and doesn't refer to other data sections (the CUs are all
  spread out in the .debug_info section and can potentially point
  into the .debug_ranges section when the CU uses DW_AT_low_pc plus
  DW_AT_ranges to describe more complex pc ranges).

Cheers,

Mark


More information about the elfutils-devel mailing list