From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id ScBsEgt8W2qsJhEAWB0awg (envelope-from ) for ; Sat, 18 Jul 2026 09:13:47 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=FfKDtOWW; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 477FA1E099; Sat, 18 Jul 2026 09:13:47 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-6.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [IPv6:2620:52:6:3111::32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 582AB1E099 for ; Sat, 18 Jul 2026 09:13:45 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id ABB274BA2E1F for ; Sat, 18 Jul 2026 13:13:44 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org ABB274BA2E1F Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=FfKDtOWW Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 71F114BA2E1F for ; Sat, 18 Jul 2026 13:11:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 71F114BA2E1F Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 71F114BA2E1F Authentication-Results: sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1784380307; cv=none; b=Jc5cGEU46GF3Dd0MOi+ak+Fo1Mhr/awTRh0afUBSJs2wNCVsU89xFX0skzKqLYsDC1dBTGMF8e/DKCywPHKYX/hn2YMRSdPH5eqhtt/QrsEKPHb6r9FoPCDRxwikKzz395RyhRzKgnjwAKtrwQAQCVCwer7bH6TYqHQWLoe8u2o= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1784380307; c=relaxed/simple; bh=ksUv7jx0TniBJHW8IwEqLplB8NRBu6U9pQ5sKewLO2Q=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=FiW12aqhIWdE8PELYQMUF5vBK7kID6mM//oaeZcTJ7UpuaMZF/x0dFVpfeWz4Jc6IRwWHG4L//RcEGvZSAnhDztfGC+3UVXMq/YK/As9O7SwokFbbmkVexhB3hv3V4uTBmd3+fELjlTZCvT9tI0OZgVqvGDpYWcju0MmuhJhlKc= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=FfKDtOWW DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 71F114BA2E1F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1784380307; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ktRgnE9d1j2j+3M31muwffy1PhZW+hJ7TJhcp7tqjBg=; b=FfKDtOWWBwn61BrA+Ww6YUrlStr0NA+w57PRXbVb03ZWctexL53X5NPaQwEWWlK9E7pFqJ XI3QX3F0KrkdwtMwVt3EvfZY2RSsj/3/2JXcAmHI3g3nUIgwlxRhZjommic5HpdLVDKwrY NaMwj11UYKkkuF3yWZv38E4b8u2mse4= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-179-hV_-Yoc0OEm0ecrKm4-ZKg-1; Sat, 18 Jul 2026 09:11:45 -0400 X-MC-Unique: hV_-Yoc0OEm0ecrKm4-ZKg-1 X-Mimecast-MFC-AGG-ID: hV_-Yoc0OEm0ecrKm4-ZKg_1784380304 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-4784d0fb9cfso3609697f8f.2 for ; Sat, 18 Jul 2026 06:11:45 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784380304; x=1784985104; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=ktRgnE9d1j2j+3M31muwffy1PhZW+hJ7TJhcp7tqjBg=; b=Gj3zvF5jzYDhUx2Baez/vZIP1UzmI5pholJR63i6ZW+qSKFXfmPA17iv+u8jk03aLN KvkULFUyfcH7G1mSC9TmpJjCAjLGAhrJULJNMdpOH9bUJb9osLNucQecRBRfu2B2RImb kI5kXMhzrBIrh0vmPgCuKeRq6LCvFvX+SeE95RR4cnv/Xgj+UU+mENOUp2ekVCuSS5zc 5kNSC2gLV6HoW0Hq6hnI3Avxb4E76qNHf5+43OPvgYl2pymcvNsEyxATHYYD2TQ7FDWk oGLb4PSX/NS+7UA2qo821LdRHjyhxbmUe/F63tLFtipIn71Z5tXrfD1gUoPmyuck4rWn I4pA== X-Gm-Message-State: AOJu0Yxec8IkI9MddIFvqqavFXe7WuF+32NVicdrZ04xsUttxyy1UNvw LoQo6zXMFfC+NwP5BIIZBg3gG4uXUphSHlS6cm4cLA+dVmB6nlsqR9HcvfxIsYy/y4MVk4N+ust 15XgjLPfw8ODv4TM08YGaPy1Q4et16v9ZPpX2weELm20ORKmRaEENI7J3Rb/R54x7Uts1hbvGC5 CnhrK4yMAIzx+zSKExe37SvMnbrkNuMnBOdTi5WeR8AIgPsPU= X-Gm-Gg: AfdE7cmx+/09DckhCXXudNYW0KbjYlXP6/14xZ5X170+orR3JrFUtUtGrgxzLocCCWd K0TTDX16fu/ZheZnFIlbuzXkwJ9waMjNWQXYICnsFxyvnxVhS4egiiBrjdXukRGLS5IX+3uecOt SSbOEHGcoM9e1CJoQlQVXxdHXzG0qO4W+UVeI6TqBGL/Uze4Ni6wzDhZfuXo0mvfQ7nYquKtq/L /WMOSOO240dByQccWK7HAva/KYK8j6y6QcCgve4RLN53xC+eUTw1kVUGmfIizWa/FCnnZ9aX5Q4 un3eQBpl3hEHzdEscNdLwgbEnaZUNe6uenJN6flU+goV/t+XRgkXaEfDCTq4M0L0FctxXD/pWQ= = X-Received: by 2002:a7b:ca50:0:b0:495:3fe1:4ce4 with SMTP id 5b1f17b1804b1-4954a40c110mr58464405e9.26.1784380303910; Sat, 18 Jul 2026 06:11:43 -0700 (PDT) X-Received: by 2002:a7b:ca50:0:b0:495:3fe1:4ce4 with SMTP id 5b1f17b1804b1-4954a40c110mr58464025e9.26.1784380303241; Sat, 18 Jul 2026 06:11:43 -0700 (PDT) Received: from localhost ([109.144.221.116]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49548e0fcdbsm122303235e9.2.2026.07.18.06.11.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Jul 2026 06:11:42 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCHv7 3/4] gdb: allow 'until' to work in outermost frame Date: Sat, 18 Jul 2026 14:11:26 +0100 Message-Id: <041b5b45868f5e24ea1c84db50f4d9854c146e32.1784380038.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: PhBMsQGXrUlaUf_QaZOX1qaSReqx61oEM4H_yeycpmQ_1784380304 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 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 The 'until' command with an argument, e.g. 'until *ADDRESS', is implemented by until_break_command in breakpoint.c. The most important thing this function does is convert the location argument, '*ADDRESS' in my example, into a vector of symtab_and_line objects. Each of these symtab_and_line objects is then used to create a temporary bp_until breakpoint. The other thing that until_break_command does is create a breakpoint in the caller frame. This breakpoint is there to ensure the inferior stops upon exiting the frame in which the 'until' command was issued, if the until breakpoint was not hit. When compiling an assembler file into a static test program, if I use 'starti' to stop the inferior within the outermost frame, and then try to use 'until *ADDRESS' I see the following error: (gdb) starti Starting program: /tmp/hello Program stopped. 0x0000000000401000 in _start () (gdb) bt #0 0x0000000000401000 in _start () (gdb) until *0x0000000000401015 Warning: Cannot insert breakpoint 0. Cannot access memory at address 0x1 Command aborted. (gdb) The problem here is the breakpoint that 'until' tries to create in the caller frame. Though the 'bt' in the above example indicates that there is only a single frame, the outermost frame, this is only because GDB has specific code in get_prev_frame to stop the backtrace at the outermost frame. If we turn this off and try 'bt' again: (gdb) set backtrace past-entry on (gdb) bt #0 0x0000000000401000 in _start () #1 0x0000000000000001 in ?? () #2 0x00007fffffffac73 in ?? () #3 0x0000000000000000 in ?? () (gdb) What we are seeing here is the garbage values that happen to be in the registers tricking GDB into thinking there are frames before _start. GDB's code to handle this is in get_prev_frame, where we call inside_entry_func. This checks if a frame is one of the two possible entry frames, the inferior entry frame or the executable entry frame. See the previous commit for more details. The important thing is that the inferior entry frame is the absolute outer frame, the very first frame that the inferior executed when starting, while the executable entry frame is just the first frame within the main executable. There are a number of user configurable filters in get_prev_frame, the backtrace past-main filter, the backtrace frame limit filter, and the backtrace past-entry filter that we are discussing here. When creating the caller frame breakpoint, the 'until' command doesn't use get_prev_frame, it uses frame_unwind_caller_frame, which calls get_prev_frame_always. This is so that the user configurable filters don't prevent the caller frame breakpoint from being created. But this means that when creating the breakpoint, we skip the entry frame check. As a result, the 'until' command will try to place a breakpoint in the bogus frame #1 shown above. As the frame is at address 0x1, which is non-writable, we see an error when trying to insert the breakpoint. In this commit I propose that we add an entry frame check into frame_unwind_caller_frame, similar to the one found in get_prev_frame. However, while get_prev_frame checks for both the inferior and executable entry frames (see previous commit for details on the differences), in frame_unwind_caller_frame I think we only need to check for the inferior entry frame. My reasoning here is that, in most cases, any frames before the inferior entry frame are likely to be invalid, and any attempt to write to them will trigger an error. In contrast, frames before the executable entry frame sit between standard libraries entry code and the executable's entry function, these frames, if GDB can find them, are likely to be valid. An earlier version of this patch tried placing this check into get_prev_frame_always, however, if get_prev_frame_always is unable to unwind a frame then it must set the stop reason on the last frame, this can be seen in `get_frame_unwind_stop_reason` where the assert `gdb_assert (frame->prev_p);` will trigger if the previous frame is not setup correctly. However, setting the stop reason means the decision about whether there's a previous frame or not is permanent (at least until the next frame cache flush), but we need commands like 'bt -past-entry' to work, which means the choice for whether there's a previous frame or not needs to remain dynamic. Placing the check in frame_unwind_caller_frame seemed like the best solution. This is the function that is called by other parts of GDB when they need the caller frame. The test for this fix ran into an issue where the frame-id for the outermost frame would change between the first instruction and later instructions in the frame. I created bug PR gdb/34245 for this issue. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34245 --- gdb/frame.c | 72 ++++++-- gdb/testsuite/gdb.base/until-in-entry-frame.c | 22 +++ .../gdb.base/until-in-entry-frame.exp | 173 ++++++++++++++++++ 3 files changed, 253 insertions(+), 14 deletions(-) create mode 100644 gdb/testsuite/gdb.base/until-in-entry-frame.c create mode 100644 gdb/testsuite/gdb.base/until-in-entry-frame.exp diff --git a/gdb/frame.c b/gdb/frame.c index e61bfd8d1f7..caa208c6d42 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -734,6 +734,52 @@ get_stack_frame_id (const frame_info_ptr &next_frame) return get_frame_id (skip_artificial_frames (next_frame)); } +/* When checking if a frame is an entry frame, there are two different + entry frames to consider. This enum is used to select which entry + frame(s) we wish to consider. See the inside_entry_func function. */ + +enum class entry_address_type_flag +{ + /* The executable's entry frame. This is the first frame within the main + executable. */ + executable = (1 << 0), + + /* The inferior's entry frame. This is the first frame within the + inferior, this can be outside the main executable, e.g. for a + dynamically linked executable, this will be the first frame in the + dynamic linker. */ + inferior = (1 << 1), +}; +DEF_ENUM_FLAGS_TYPE (enum entry_address_type_flag, entry_address_type_flags); + +/* Return true if THIS_FRAME is inside the either of the possible entry + frames based on ENTRY_ADDR_TYPES, otherwise return false. */ + +static bool +inside_entry_func (const frame_info_ptr &this_frame, + entry_address_type_flags entry_addr_types) +{ + /* It doesn't make sense to call this with no flag bits set. */ + gdb_assert (entry_addr_types != (entry_address_type_flags) 0); + + CORE_ADDR frame_func_addr; + if (!get_frame_func_if_available (this_frame, &frame_func_addr)) + return false; + + const entry_point_info &ep_info + = current_program_space->get_entry_point_info (); + + /* For each flag set in ENTRY_ADDR_TYPES if FRAME_FUNC_ADDR matches the + corresponding entry address from EP_INFO then THIS_FRAME is an entry + frame. */ + return (((entry_addr_types & entry_address_type_flag::executable) + == entry_address_type_flag::executable + && ep_info.exec_entry_address () == frame_func_addr) + || ((entry_addr_types & entry_address_type_flag::inferior) + == entry_address_type_flag::inferior + && ep_info.inferior_entry_address () == frame_func_addr)); +} + /* Helper for the various frame_unwind_caller_* functions. Unwind INITIAL_NEXT_FRAME at least one frame, but skip any artificial frames, that is inline or tailcall frames. @@ -744,6 +790,15 @@ get_stack_frame_id (const frame_info_ptr &next_frame) static frame_info_ptr frame_unwind_caller_frame (const frame_info_ptr &initial_next_frame) { + /* Avoid returning a frame which is possibly before the inferior's entry + frame, any such frame is likely invalid. However, if the user has + turned on 'backtrace past-entry' then we assume they know what they + are doing and allow these frames. */ + if (!user_set_backtrace_options.backtrace_past_entry + && inside_entry_func (initial_next_frame, + entry_address_type_flag::inferior)) + return nullptr; + frame_info_ptr this_frame = get_prev_frame_always (initial_next_frame); if (this_frame == nullptr) return nullptr; @@ -2698,19 +2753,6 @@ inside_main_func (const frame_info_ptr &this_frame) return sym_addr == get_frame_func (this_frame); } -/* Test whether THIS_FRAME is inside the process entry point function. */ - -static bool -inside_entry_func (const frame_info_ptr &this_frame) -{ - const entry_point_info &ep_info - = current_program_space->get_entry_point_info (); - - CORE_ADDR frame_func_addr = get_frame_func (this_frame); - return (ep_info.exec_entry_address () == frame_func_addr - || ep_info.inferior_entry_address () == frame_func_addr); -} - /* Return a structure containing various interesting information about the frame that called THIS_FRAME. Returns NULL if there is either no such frame or the frame fails any of a set of target-independent @@ -2793,7 +2835,9 @@ get_prev_frame (const frame_info_ptr &this_frame) && get_frame_type (this_frame) == NORMAL_FRAME && !user_set_backtrace_options.backtrace_past_entry && frame_pc.has_value () - && inside_entry_func (this_frame)) + && inside_entry_func (this_frame, + (entry_address_type_flag::inferior + | entry_address_type_flag::executable))) { frame_debug_got_null_frame (this_frame, "inside entry func"); return NULL; diff --git a/gdb/testsuite/gdb.base/until-in-entry-frame.c b/gdb/testsuite/gdb.base/until-in-entry-frame.c new file mode 100644 index 00000000000..6a0e311ef41 --- /dev/null +++ b/gdb/testsuite/gdb.base/until-in-entry-frame.c @@ -0,0 +1,22 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2026 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 . */ + +int +main (void) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.base/until-in-entry-frame.exp b/gdb/testsuite/gdb.base/until-in-entry-frame.exp new file mode 100644 index 00000000000..e863c070bd4 --- /dev/null +++ b/gdb/testsuite/gdb.base/until-in-entry-frame.exp @@ -0,0 +1,173 @@ +# Copyright 2026 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 . + +# Check that 'until' in the outermost frame works as expected. + +standard_testfile + +if { [build_executable "failed to build" $testfile $srcfile] } { + return +} + +set testfile_static ${testfile}-static +if { [build_executable "failed to build" $testfile_static $srcfile \ + { debug additional_flags=-static } ] } { + return +} + +# Use 'frame' to get the name of the current function. Returns the +# name of the current function, or the empty string if the current +# function name cannot be established. +proc current_function_name { testname } { + set func_name "" + gdb_test_multiple "frame" $testname { + -re -wrap "#0\\s+(?:$::hex in )?(\[^( \]+) \\(.*" { + set func_name $expect_out(1,string) + } + + -re -wrap "#0\\s+\[^\r\n\]*\\s+in (\[^( \]+) \\(.*" { + set func_name $expect_out(1,string) + } + } + + return $func_name +} + +# Start the inferior with 'starti', then use 'until' within the +# outermost frame. This is the real outermost frame, not 'main'. +# +# PAST_ENTRY should be 'on' or 'off' and is used in a 'set backtrace +# past-entry ...' command. +proc run_test { use_stepi past_entry testfile } { + clean_restart $testfile + + if {$::use_gdb_stub && [target_info exists gdb,do_reload_on_run]} { + # This is the path taken by the 'native-gdbserver' board. + # Start gdbserver and connect, this should leave the inferior + # at the first instruction. + if { [gdb_reload] != 0 } { + return -1 + } + pass "connected to gdbserver" + } else { + # Start inferior with 'starti' and then wait for a prompt. + if { [gdb_starti_cmd] < 0 } { + untested starti + return + } + gdb_test "" "Program stopped.*" "prompt after starti" + } + + set func_name [current_function_name \ + "function name for first function"] + + gdb_test_no_output "set backtrace past-entry $past_entry" + + # On x86-64 GDB does get a valid frame-id for the very first + # instruction, but from the second instruction onwards it gets + # outer_frame_id. As a result an 'until' setup at the first + # instruction doesn't match later on within the same function as + # the frame-ids no longer match. + # + # Try to work around this by issuing a 'stepi' to move to the + # second instruction. + # + # This is only a heuristic though. On different architectures GDB + # might have a valid frame-id for multiple instructions within the + # outer frame, or might not have outer_frame_id for all + # instructions. + # + # Also, we need to consider that the very first instruction might + # be a control flow instruction, so using stepi could send the + # inferior to another function. We try to spot this case and + # perform an early return if that happens. + if { $use_stepi } { + gdb_test "stepi" + set func_name_after [current_function_name \ + "function name after stepi"] + if { $func_name ne $func_name_after } { + unsupported "stepi moved to another function" + return + } + } + + # If the 'until' doesn't trigger then stop in 'main' as a backup. + gdb_breakpoint main + + # Find an address to use in the 'until' command. + set until_address "" + set capture_address false + gdb_test_multiple "disassemble" "find address for until" -lbl { + -re "\r\n=> $::hex \[^\r\n\]+(?=\r\n)" { + set capture_address true + exp_continue + } + + -re "\r\n\\s+($::hex) \[^\r\n\]+(?=\r\n)" { + if { $capture_address } { + set until_address $expect_out(1,string) + set capture_address false + } + + exp_continue + } + + -re "\r\n$::gdb_prompt $" { + set found_address [expr { $until_address ne "" }] + if { !$found_address } { + unsupported "$gdb_test_name (no instruction found)" + return + } + pass $gdb_test_name + } + } + + # Send the 'until' command and then wait for GDB to stop. See the + # notes above about the 'stepi' for why we sometimes expect to see + # GDB reach 'main' here. + send_gdb "until *${until_address}\n" + gdb_test_multiple "" "after until" { + -re -wrap "Breakpoint $::decimal, (?:\[^\r\n\]+ in )?main \\(\\).*" { + kfail "gdb/34245" "$gdb_test_name (skipped until breakpoint)" + } + + -re -wrap "\r\n(?:$::hex in )?$func_name \\(\\).*" { + pass $gdb_test_name + } + + -re -wrap "Warning:\r\nCannot insert breakpoint 0\\.\r\nCannot access memory at address $::hex.*Command aborted\\." { + # With PAST_ENTRY 'on', GDB discovers some invalid frames + # past the entry frame based on whatever happens to be in + # the registers when the entry function is called. These + # invalid frames are often non-writable, so GDB will fail + # to insert the breakpoint when the inferior resumes. + # + # We still count this as a pass though; the user should + # only turn on 'backtrace past-entry' when they know that + # is needed, so failure here is totally expected. + gdb_assert { $past_entry eq "on" } "$gdb_test_name (caller breakpoint failed)" + } + } +} + +foreach_with_prefix past_entry { off on } { + foreach_with_prefix use_stepi { true false } { + run_test $use_stepi $past_entry $testfile + + with_test_prefix "static" { + run_test $use_stepi $past_entry $testfile_static + } + } +} -- 2.25.4