public inbox for gdb-cvs@sourceware.org
help / color / mirror / Atom feed
From: Pedro Alves <palves@sourceware.org>
To: gdb-cvs@sourceware.org
Subject: [binutils-gdb] Fix "until LINE" in main, when "until" runs into longjmp
Date: Wed, 13 Jul 2022 13:24:08 +0000 (GMT)	[thread overview]
Message-ID: <20220713132408.24CB0385021A@sourceware.org> (raw)

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=0f443d1b70ff8c338a536b5ce1cd963f8ee8d206

commit 0f443d1b70ff8c338a536b5ce1cd963f8ee8d206
Author: Pedro Alves <pedro@palves.net>
Date:   Tue Jul 12 22:14:37 2022 +0100

    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

Diff:
---
 gdb/breakpoint.c                                 |  2 +-
 gdb/testsuite/gdb.base/longjmp-until-in-main.c   | 34 ++++++++++++++++++
 gdb/testsuite/gdb.base/longjmp-until-in-main.exp | 44 ++++++++++++++++++++++++
 3 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index a3be12557f6..74f53368464 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -10489,7 +10489,7 @@ until_break_command (const char *arg, int from_tty, int anywhere)
 				    caller_frame_id, bp_until);
       breakpoints.emplace_back (std::move (caller_breakpoint));
 
-      set_longjmp_breakpoint (tp, caller_frame_id);
+      set_longjmp_breakpoint (tp, stack_frame_id);
       lj_deleter.emplace (thread);
     }
 
diff --git a/gdb/testsuite/gdb.base/longjmp-until-in-main.c b/gdb/testsuite/gdb.base/longjmp-until-in-main.c
new file mode 100644
index 00000000000..d4d860fe06d
--- /dev/null
+++ b/gdb/testsuite/gdb.base/longjmp-until-in-main.c
@@ -0,0 +1,34 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <setjmp.h>
+
+void
+func (void)
+{
+  jmp_buf buf;
+
+  if (setjmp (buf) == 0)
+    longjmp (buf, 1);
+}
+
+int
+main ()
+{
+  func ();
+  return 0; /* until to here */
+}
diff --git a/gdb/testsuite/gdb.base/longjmp-until-in-main.exp b/gdb/testsuite/gdb.base/longjmp-until-in-main.exp
new file mode 100644
index 00000000000..53bc3ed283b
--- /dev/null
+++ b/gdb/testsuite/gdb.base/longjmp-until-in-main.exp
@@ -0,0 +1,44 @@
+# Copyright 2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test "until LINE", started in the "main()" frame, where the until
+# command runs into a longjmp that lands in a frame that is inner than
+# main.  GDB internally intercepts the longjmp, sets a breakpoint at
+# the jump destination, and once there, decides whether to stop or
+# ignore the breakpoint hit depending on whether the initiating frame
+# is present on the frame chain.  GDB used to have a bug where it
+# recorded the frame of the caller of main instead of the frame of
+# main as the initiating frame, and then later on when deciding
+# whether the longjmp landed somewhere inner than main, since
+# unwinding normally stops at main, GDB would fail to find the
+# initiating frame.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} {
+    return
+}
+
+if {![runto_main]} {
+    return
+}
+
+delete_breakpoints
+
+set until_to_line [gdb_get_line_number "until to here"]
+
+gdb_test "until $until_to_line" \
+    " until to here .*" \
+    "until \$line, in main"


                 reply	other threads:[~2022-07-13 13:24 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220713132408.24CB0385021A@sourceware.org \
    --to=palves@sourceware.org \
    --cc=gdb-cvs@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).