[PATCH] gdb: skip objfiles with no BFD in DWARF unwinder

Pedro Alves pedro@palves.net
Tue Apr 5 19:08:30 GMT 2022


On 2022-04-05 11:04, Jan Vrany via Gdb-patches wrote:
> While playing with JIT reader I experienced GDB to crash on null-pointer
> dereference when stepping through non-jitted code.
> 
> The problem was that dwarf2_frame_find_fde () assumed that all objfiles
> have BFD but that's not always true. To address this problem, this
> commit skips such objfiles.
> 
> As for the test, I initially tried to use temporary breakpoint and then
> 'continue' to get out of the jitted code but this for some reason did
> not trigger the crash. Using 'finish' to get out of jitted code followed
> by 'next' triggered it.

I tried it here and here's what I saw.  So "next" is stepping over the
"function_add" function.  While "next"ing, GDB stops at the first instruction of
function_add, and looks at the frame's type, which runs all the frame sniffers
looking for one that claims the frame, including the DWARF unwinder, and here is
where we run into the crash:

$ gdb ...
(gdb) start
...
Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdc08) at /home/pedro/gdb/binutils-gdb/src/gdb/testsuite/gdb.base/jit-reader-host.c:58
58        struct jithost_abi *symfile = malloc (sizeof (struct jithost_abi));
(gdb) jit-reader-load /home/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.base/jit-reader/jit-reader.so
(gdb) b 95
Breakpoint 2 at 0x555555555322: file /home/pedro/gdb/binutils-gdb/src/gdb/testsuite/gdb.base/jit-reader-host.c, line 95.
(gdb) c
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff7ffb001 in jit_function_stack_mangle ()
(gdb) c
Continuing.

Breakpoint 2, main (argc=1, argv=0x7fffffffdc08) at /home/pedro/gdb/binutils-gdb/src/gdb/testsuite/gdb.base/jit-reader-host.c:95
95        function_add (5, 6);
(gdb) set debug infrun 1
(gdb) n

  ...
  [infrun] start_step_over: exit
  [infrun] context_switch: Switching context from 0.0.0 to 353387.353387.0
  [infrun] handle_signal_stop: stop_pc=0x7ffff7ffb00a
  ...


Thread 1 "gdb" received signal SIGSEGV, Segmentation fault.
0x00005555559886c8 in bfd_usrdata (abfd=0x0) at ../bfd/bfd.h:6919
6919      return abfd->usrdata;
(top-gdb) bt
#0  0x00005555559886c8 in bfd_usrdata (abfd=0x0) at ../bfd/bfd.h:6919
#1  0x000055555598a65f in gdb_bfd_requires_relocations (abfd=0x0) at /home/pedro/gdb/binutils-gdb/src/gdb/gdb_bfd.c:1030
#2  0x0000555555885dbb in find_comp_unit (objfile=0x5555565a6170) at /home/pedro/gdb/binutils-gdb/src/gdb/dwarf2/frame.c:1538
#3  0x0000555555885efa in dwarf2_frame_find_fde (pc=0x7fffffffd008, out_per_objfile=0x0) at /home/pedro/gdb/binutils-gdb/src/gdb/dwarf2/frame.c:1568
#4  0x0000555555885591 in dwarf2_frame_sniffer (self=0x555556426440 <dwarf2_frame_unwind>, this_frame=0x555556d6b7f0, this_cache=0x555556d6b808) at /home/pedro/gdb/binutils-gdb/src/gdb/dwarf2/frame.c:1254
#5  0x0000555555979639 in frame_unwind_try_unwinder (this_frame=0x555556d6b7f0, this_cache=0x555556d6b808, unwinder=0x555556426440 <dwarf2_frame_unwind>) at /home/pedro/gdb/binutils-gdb/src/gdb/frame-unwind.c:131
#6  0x000055555597991a in frame_unwind_find_by_frame (this_frame=0x555556d6b7f0, this_cache=0x555556d6b808) at /home/pedro/gdb/binutils-gdb/src/gdb/frame-unwind.c:203
#7  0x0000555555980948 in get_frame_type (frame=0x555556d6b7f0) at /home/pedro/gdb/binutils-gdb/src/gdb/frame.c:2817
#8  0x0000555555a2187b in process_event_stop_test (ecs=0x7fffffffd7c0) at /home/pedro/gdb/binutils-gdb/src/gdb/infrun.c:6966
#9  0x0000555555a20b3f in handle_signal_stop (ecs=0x7fffffffd7c0) at /home/pedro/gdb/binutils-gdb/src/gdb/infrun.c:6585
#10 0x0000555555a1ec37 in handle_inferior_event (ecs=0x7fffffffd7c0) at /home/pedro/gdb/binutils-gdb/src/gdb/infrun.c:5837
...

That get_frame_type call is from here:

6963      if (ecs->event_thread->control.step_range_end != 1
6964          && (ecs->event_thread->control.step_over_calls == STEP_OVER_UNDEBUGGABLE
6965              || ecs->event_thread->control.step_over_calls == STEP_OVER_ALL)
6966          && get_frame_type (frame) == SIGTRAMP_FRAME)
6967        {
6968          infrun_debug_printf ("stepped into signal trampoline");



OK, that makes sense.  We stopped at 0x7ffff7ffb00a, which is function_add, and GDB tried to get info
about the current frame.  

This means that we can write a more targeted testcase that does not rely on how "next" works.
What is important is that we trigger the unwinding machinery inside the "function_add" function.

So...

(gdb) jit-reader-load /home/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.base/jit-reader/jit-reader.so
(gdb) b 95
Breakpoint 1 at 0x1322: file /home/pedro/gdb/binutils-gdb/src/gdb/testsuite/gdb.base/jit-reader-host.c, line 95.
(gdb) r
Starting program: /home/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.base/jit-reader/jit-reader 

Program received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff7ffb001 in jit_function_stack_mangle ()
(gdb) c
Continuing.

Breakpoint 1, main (argc=1, argv=0x7fffffffdc08) at /home/pedro/gdb/binutils-gdb/src/gdb/testsuite/gdb.base/jit-reader-host.c:95
95        function_add (5, 6);
(gdb) b *function_add
Breakpoint 2 at 0x7ffff7ffb00a
(gdb) c
Continuing.

Thread 1 "gdb" received signal SIGSEGV, Segmentation fault.
0x00005555559886c8 in bfd_usrdata (During symbol reading: incomplete CFI data; unspecified registers (e.g., rax) at 0x5555559886d0
abfd=0x0) at ../bfd/bfd.h:6919
6919      return abfd->usrdata;
(top-gdb) 


So in a nutshell, it would be better for the test to do:

 fin
 b *function_add
 c       << this crashes as GDB prints the current frame
 bt      << add this for good measure.

Instead of:

 fin
 bt    << not clear what this is for
 next  << reaches the unwinder today, triggering the crash, but who knows the future


More information about the Gdb-patches mailing list