Fix "until LINE" in main, when "until" runs into longjmp
authorPedro Alves <pedro@palves.net>
Tue, 12 Jul 2022 21:14:37 +0000 (22:14 +0100)
committerPedro Alves <pedro@palves.net>
Wed, 13 Jul 2022 13:20:49 +0000 (14:20 +0100)
commit0f443d1b70ff8c338a536b5ce1cd963f8ee8d206
tree83d74ac979e286ed54fec5dc4703731ba96e20dc
parentcf6c1e710ee162a5adb0ae47acb731f2bfecc956
Fix "until LINE" in main, when "until" runs into longjmp

With a test like this:

1       #include <dlfcn.h>
2       int
3       main ()
4       {
5          dlsym (RTLD_DEFAULT, "FOO");
6          return 0;
7       }

and then "start" followed by "until 6", GDB currently incorrectly
stops inside the runtime loader, instead of line 6.  Vis:

  ...
  Temporary breakpoint 1, main () at until.c:5
  4       {
  (gdb) until 6
  0x00007ffff7f0a90d in __GI__dl_catch_exception (exception=exception@entry=0x7fffffffdb00, operate=<optimized out>, args=0x7ffff7f0a90d <__GI__dl_catch_exception+109>) at dl-error-skeleton.c:206
  206     dl-error-skeleton.c: No such file or directory.
  (gdb)

The problem is related to longjmp handling -- dlsym internally
longjmps on error.  The testcase can be reduced to this:

1       #include <setjmp.h>
2       void func () {
3         jmp_buf buf;
4         if (setjmp (buf) == 0)
5           longjmp (buf, 1);
6       }
7
8       int main () {
9         func ();
10        return 0; /* until to here */
11      }

and then with "start" followed by "until 10", GDB currently
incorrectly stops at line 4 (returning from setjmp), instead of line
10.

The problem is that the BPSTAT_WHAT_CLEAR_LONGJMP_RESUME code in
infrun.c fails to find the initiating frame, and so infrun thinks that
the longjmp jumped somewhere outer to "until"'s originating frame.

Here:

    case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME:
      {
struct frame_info *init_frame;

/* There are several cases to consider.

   1. The initiating frame no longer exists.  In this case we
   must stop, because the exception or longjmp has gone too
   far.

        ...

init_frame = frame_find_by_id (ecs->event_thread->initiating_frame);

if (init_frame)   // this is NULL!
  {
     ...
  }

/* For Cases 1 and 2, remove the step-resume breakpoint, if it
   exists.  */
delete_step_resume_breakpoint (ecs->event_thread);

end_stepping_range (ecs);   // case 1., so we stop.
      }

The initiating frame is set by until_break_command ->
set_longjmp_breakpoint.  The initiating frame is supposed to be the
frame that is selected when the command was issued, but
until_break_command instead passes the frame id of the _caller_ frame
by mistake.  When the "until LINE" command is issued from main, the
caller frame is the caller of main.  When later infrun tries to find
that frame by id, it fails to find it, because frame_find_by_id
doesn't unwind past main.

The bug is that we passed the caller frame's id to
set_longjmp_breakpoint.  We should have passed the selected frame's id
instead.

Change-Id: Iaae1af7cdddf296b7c5af82c3b5b7d9b66755b1c
gdb/breakpoint.c
gdb/testsuite/gdb.base/longjmp-until-in-main.c [new file with mode: 0644]
gdb/testsuite/gdb.base/longjmp-until-in-main.exp [new file with mode: 0644]