From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id /8hQIxCYzmIo7xEAWB0awg (envelope-from ) for ; Wed, 13 Jul 2022 06:01:52 -0400 Received: by simark.ca (Postfix, from userid 112) id 808CA1E5EA; Wed, 13 Jul 2022 06:01:52 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,MAILING_LIST_MULTI, RDNS_DYNAMIC,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 Received: from sourceware.org (ip-8-43-85-97.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 8028C1E222 for ; Wed, 13 Jul 2022 06:01:51 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A03BD3851ABF for ; Wed, 13 Jul 2022 10:01:50 +0000 (GMT) Received: from mail-wr1-f49.google.com (mail-wr1-f49.google.com [209.85.221.49]) by sourceware.org (Postfix) with ESMTPS id 434663851A87 for ; Wed, 13 Jul 2022 10:01:38 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 434663851A87 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=palves.net Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-wr1-f49.google.com with SMTP id q9so14754159wrd.8 for ; Wed, 13 Jul 2022 03:01:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:mime-version :content-transfer-encoding; bh=Yf7qvKyECiUcXyFMWujyyoRPOMrNPwT85DMbf4LisBQ=; b=CcBSZDFVqodl+dj1EvkK7oA4+79MIlnmutLXcPM4WlamHfIIZaQGjP9TsMiv/+VMkH Kr0rO32Hlnl7AbgvQ0t/jjyyipTnXCsSTHZMIubADw2d99mOsKlV+/I+2yIM8C1VdHKs G8Q6JwMqhYJxwkvZLVD0MGkTKyhgy+r+lsE9d/ui4YAHTTqeedCy5COuFHymjCVjibBE hJ6i1JE4DkFw0fFbcDxugTBJ4oiIAa/KhP40VDYCnNjOf+h9leTWpNGnkf5nUdzYt0uV mHVmTE9R0Z3fUHq0ZdkrsrJgZXb7/cZbhZCHMczzZw/8QHs9aeOx5DjWai21GNEs34qL R7xA== X-Gm-Message-State: AJIora+c8aAAzI/DjNSA8XHTaghUR8Sx8yGJKRKc4GxC/GLqsSH1j5UZ jn66Eg75HMEjAd51ryf+kjFsrzkgD0Y= X-Google-Smtp-Source: AGRyM1t1VoYjLrkCFxzfA6hAAj39dOzu8CEjp9enULROLUtcihcv/YuEVwLDwjjkdOo65esq3CTV/w== X-Received: by 2002:a5d:4402:0:b0:21d:8093:138c with SMTP id z2-20020a5d4402000000b0021d8093138cmr2392159wrq.535.1657706496156; Wed, 13 Jul 2022 03:01:36 -0700 (PDT) Received: from localhost ([2001:8a0:f924:2600:209d:85e2:409e:8726]) by smtp.gmail.com with ESMTPSA id h6-20020a5d5486000000b0021b829d111csm10528164wrv.112.2022.07.13.03.01.34 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 13 Jul 2022 03:01:34 -0700 (PDT) From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH] Fix "until LINE" in main, when "until" runs into longjmp Date: Wed, 13 Jul 2022 11:01:33 +0100 Message-Id: <20220713100133.3879810-1-pedro@palves.net> X-Mailer: git-send-email 2.36.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces+public-inbox=simark.ca@sourceware.org Sender: "Gdb-patches" With a test like this: 1 #include 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=, 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 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 | 2 +- .../gdb.base/longjmp-until-in-main.c | 34 ++++++++++++++ .../gdb.base/longjmp-until-in-main.exp | 44 +++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 gdb/testsuite/gdb.base/longjmp-until-in-main.c create mode 100644 gdb/testsuite/gdb.base/longjmp-until-in-main.exp 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..3b9ef945a34 --- /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 . */ + +#include + +void +func () +{ + 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..33a907fb83e --- /dev/null +++ b/gdb/testsuite/gdb.base/longjmp-until-in-main.exp @@ -0,0 +1,44 @@ +# Copyright 2008-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 . + +# 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" base-commit: dd4c046506cd4da46b439a2b4f8b6d933ecbb961 -- 2.36.0