From patchwork Mon Aug 24 16:22:16 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 142103 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2B3D34B9DB4C for ; Mon, 24 Aug 2026 16:23:11 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2B3D34B9DB4C 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=XOzIhEzw X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org 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 5C77A4BA7985 for ; Mon, 24 Aug 2026 16:22:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5C77A4BA7985 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 5C77A4BA7985 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=1787588548; cv=none; b=DKEsfYJ76tvcWbNZ8XwVtQTZx58fNX9w3f6wqYthld3WRDYbiPIbzG6MYH9K1IUsPNLbNe4pTr3QGzRg5lwicw/X+fwFXTsXwr1nabPb/ITrcMVnXUn7Ba/TULERDCeHZvksH2VfVTyRoORrc6/vvKSZicKZvIIlIhHqdDCFvfo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1787588548; c=relaxed/simple; bh=5l9Ud+KXaug8pQ02hTGdA61JAmcqK/EYXmgqsPnJerY=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=opts0F9hU8siGYHFE57sTuZaEjokl71oiZAoJy7oMgTWloOAMJ+gSj47NIOQRPTEe9eDVbWchxxg4It9joNKbE0NXQVH5qSPtAR7P3oPmn+2KeE13S0xxh8SUBKN0uOwz7yqZTYjODw5xikbNZDeaJMibJC4OhoNS1LsbmgcMsU= 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=XOzIhEzw DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5C77A4BA7985 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1787588548; 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=87CmVA4bM/D1sIDXNlLpl5F8yywX8iXHlK5Aqi785SQ=; b=XOzIhEzwpdpiEkvMWmd8SBjPRTeUnr1nQnu0+xjGoSuvABhMxPT06q426k4eadhs2JprU4 JZvp31hXgEfNs8yUsZi3Kb4/aqbPVILzcKMWH3DNlRJCnCLxDNzmGQYpZhtvtgcixv3Hen Ff32sUwN1uF7cVM+Cvvy4dyLkj7ar+A= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-581-QwCJNFGQMnyAEM21skXhow-1; Mon, 24 Aug 2026 12:22:26 -0400 X-MC-Unique: QwCJNFGQMnyAEM21skXhow-1 X-Mimecast-MFC-AGG-ID: QwCJNFGQMnyAEM21skXhow_1787588545 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-499913d1a79so20347465e9.0 for ; Mon, 24 Aug 2026 09:22:26 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787588545; x=1788193345; 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=87CmVA4bM/D1sIDXNlLpl5F8yywX8iXHlK5Aqi785SQ=; b=Lm422/YIVUnL9QiKy5qlYIc9TFr64LvajDi5Vk/jmnsbaFEI7qvtWYZpv2307WZ5bO IOs9mmLp2bpWNvnIwdUlvISZnTy/BB+Co2zAeGMM5+Dz+aB7xDvKYE4VOKxfvCiW8f63 7BrZR7lQln0KCUbqZb6WFDl6QKC2VHiSeJfgWk9meL0JuB4MEBta9EeZHEePi+eM/p8b ZjUOLaL7Xx6hGcuNzjjfIZVcEygDLi+AynC5z6eltQV+FgbQxCa4wqvt/6ahJDWRN3FF dqXPSNqHHWg4x4vbKk0+ZjvLClac95rtYym4IYMGC5+X7UAppSXbL/eqYH8qZ8Uf4tf+ xeag== X-Gm-Message-State: AFuF++k5A0doOQuhoF7Wb3QhNHgMAM7G2ZNe8j5T1dyhcFowtcKJrJo/ kWLqv1EZ2AcEpeNy7r2B+dq8XdO8PPjNdtlxGu6LRplB/vet0HDlBSSt0WRwWXRb59yhSnsHok/ WzeCNw5Zb+KYHGtJokbzNpibc9JJq5Fx46tx0Rb3tc4XCLuvuFUPasiJlhWM4HHvEzcoOtKoLab mgQ5YNL9RrbcHSJscssFx5mTwLz8SyAg61LwowNfLNK2GwDbc= X-Gm-Gg: AR+sD10+578eM+nnqJkzbxOh9b/2OPBCJQYUEfEK2hUiI/gfPaj6GJkPowrBbT+wULK 82Rs4wDr7ggEXcDcGDqGPOmTo7rnOyGfjiMLIbQon5vQypGWtHLei4lpRxnkN8SbFC4AoVcErOZ qw9EADVjpAwlLwGcjzM9+fC1MGnzJtPAtaNS/kdPkY6EhsxK7fGe566vQVZ7AoeDjgm88MIP+Ha FWq5OewNC8u7cIIH4gR59VxzKHPr+vNIFnSTmUniS++iiYL+7r6KC27YQl3111Ii2p3yb8/WxqM u98jPdiy5dGpgOQWyVIC2lPWYkZHeeRiHsSvIcIZkzR39qR7+E1glRAHCZ0uoDdAqsPxsiWCD9W pd/2Snz79HuINy7euIwOZBDtsmZE= X-Received: by 2002:a05:600c:5294:b0:499:7f38:d77 with SMTP id 5b1f17b1804b1-499b830f690mr371507375e9.6.1787588544879; Mon, 24 Aug 2026 09:22:24 -0700 (PDT) X-Received: by 2002:a05:600c:5294:b0:499:7f38:d77 with SMTP id 5b1f17b1804b1-499b830f690mr371505955e9.6.1787588544350; Mon, 24 Aug 2026 09:22:24 -0700 (PDT) Received: from localhost (128.223.159.143.dyn.plus.net. [143.159.223.128]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-499d6350331sm1306465e9.6.2026.08.24.09.22.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 24 Aug 2026 09:22:23 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Simon Marchi , Andrew Burgess , Pedro Alves Subject: [PATCHv9 1/4] gdb: rename program_space::entry_point_address* functions Date: Mon, 24 Aug 2026 17:22:16 +0100 Message-Id: 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: ZChJ0hGNm8wQWg0hHj5U2EUcPo0F-wXPSIgmsQ6ctUg_1787588545 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, POISEN_SPAM_PILL, POISEN_SPAM_PILL_1, POISEN_SPAM_PILL_3, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, RCVD_IN_PBL, SPF_HELO_PASS, SPF_NONE, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@sourceware.org Rename program_space::entry_point_address to program_space::exec_entry_point_address and program_space::entry_point_address_query to program_space::exec_entry_point_address_if_available. There are two aspects to this renaming. First I replace 'query' with 'if_available' in one of the functions. I feel this better describes the function, and also is inline with how other, similar, functions are named in GDB. The second part of the renaming is to add the 'exec_' prefix to the front of both function names. When a dynamically linked inferior is started the first instruction executed is actually within the run-time linker, not within the main executable, so it could be argued that the actual entry address for the inferior is not the entry address of the main executable. However, there is an equally valid argument that the entry address of the executable is also something worth finding. The existing entry address within the inferior is used for a number of tasks in GDB, for example displaced stepping (arch-utils.c), inferior function calls (arc-tdep.c and infcall.c), and for detecting the "entry" frame. This last one, the entry frame detection is interesting. In a dynamically linked executable it could be argued that there are two entry frames. The very first frame that is executed in the inferior, this is where 'starti' stops the inferior. And then the very first frame within the main executable. I think that both of these are valid. Currently, as we can only find the entry address within the main executable, we can only identify the first frame of the main executable. And this can cause some problems, consider: (gdb) starti Starting program: /tmp/hello Program stopped. 0x00007ffff7fd3110 in _start () from /lib64/ld-linux-x86-64.so.2 (gdb) bt #0 0x00007ffff7fd3110 in _start () from /lib64/ld-linux-x86-64.so.2 #1 0x0000000000000001 in ?? () #2 0x00007fffffffac13 in ?? () #3 0x0000000000000000 in ?? () (gdb) Here frames #1 to #3 are all bogus, created by GDB based on whatever values happen to be in the registers when the inferior starts. In a later commit I'd like to fix this problem, however, I would prefer that the other users of program_space::entry_point_address continue to use the address within the main executable. So, in order to keep the distinction between the two different types of entry point, I'm renaming the existing functions with the 'exec_' prefix. There should be no user visible changes after this commit. Approved-By: Pedro Alves --- gdb/arc-tdep.c | 2 +- gdb/arch-utils.c | 2 +- gdb/frame.c | 4 ++-- gdb/infcall.c | 2 +- gdb/linux-tdep.c | 2 +- gdb/progspace.c | 6 +++--- gdb/progspace.h | 12 ++++++------ gdb/solib-frv.c | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c index 4936a5c8fbb..7ed83c4cae6 100644 --- a/gdb/arc-tdep.c +++ b/gdb/arc-tdep.c @@ -860,7 +860,7 @@ arc_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp, CORE_ADDR funaddr, struct regcache *regcache) { *real_pc = funaddr; - *bp_addr = current_program_space->entry_point_address (); + *bp_addr = current_program_space->exec_entry_point_address (); return sp; } diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index e959788bd3b..88047731ce6 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -57,7 +57,7 @@ displaced_step_at_entry_point (struct gdbarch *gdbarch) CORE_ADDR addr; int bp_len; - addr = current_program_space->entry_point_address (); + addr = current_program_space->exec_entry_point_address (); /* Inferior calls also use the entry point as a breakpoint location. We don't want displaced stepping to interfere with those diff --git a/gdb/frame.c b/gdb/frame.c index b91e18fad99..a83f0b20686 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -2704,7 +2704,7 @@ static bool inside_entry_func (const frame_info_ptr &this_frame) { std::optional entry_point - = current_program_space->entry_point_address_query (); + = current_program_space->exec_entry_point_address_if_available (); if (!entry_point.has_value ()) return false; @@ -2780,7 +2780,7 @@ get_prev_frame (const frame_info_ptr &this_frame) added to work around that (now fixed) case. */ /* NOTE: cagney/2003-07-15: danielj (if I'm reading it right) suggested having the inside_entry_func test use the - inside_main_func() msymbol trick (along with entry_point_address() + inside_main_func() msymbol trick (along with exec_entry_point_address() I guess) to determine the address range of the start function. That should provide a far better stopper than the current heuristics. */ diff --git a/gdb/infcall.c b/gdb/infcall.c index e6b24ff5310..6d26841ede3 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -1300,7 +1300,7 @@ call_function_by_hand_dummy (struct value *function, CORE_ADDR dummy_addr; real_pc = funaddr; - dummy_addr = current_program_space->entry_point_address (); + dummy_addr = current_program_space->exec_entry_point_address (); /* A call dummy always consists of just a single breakpoint, so its address is the same as the address of the dummy. diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index bbc4009d43c..82d177d4dd8 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -2993,7 +2993,7 @@ linux_displaced_step_location (struct gdbarch *gdbarch) /* Determine entry point from target auxiliary vector. This avoids the need for symbols. Also, when debugging a stand-alone SPU - executable, entry_point_address () will point to an SPU + executable, exec_entry_point_address () will point to an SPU local-store address and is thus not usable as displaced stepping location. The auxiliary vector gets us the PowerPC-side entry point address instead. */ diff --git a/gdb/progspace.c b/gdb/progspace.c index 988bb237030..3003a137c17 100644 --- a/gdb/progspace.c +++ b/gdb/progspace.c @@ -263,7 +263,7 @@ program_space::empty () /* See progspace.h. */ std::optional -program_space::entry_point_address_query () const +program_space::exec_entry_point_address_if_available () const { objfile *objf = symfile_object_file; if (objf == NULL || !objf->per_bfd->ei.entry_point_p) @@ -276,9 +276,9 @@ program_space::entry_point_address_query () const /* See progspace.h. */ CORE_ADDR -program_space::entry_point_address () const +program_space::exec_entry_point_address () const { - std::optional retval = entry_point_address_query (); + std::optional retval = exec_entry_point_address_if_available (); if (!retval.has_value ()) error (_("Entry point address is not known.")); diff --git a/gdb/progspace.h b/gdb/progspace.h index e9261ff8590..1ae1e42f3bb 100644 --- a/gdb/progspace.h +++ b/gdb/progspace.h @@ -323,13 +323,13 @@ struct program_space return m_target_sections; } - /* If there is a valid and known entry point in this program space, - return it. Otherwise return an empty optional. */ - std::optional entry_point_address_query () const; + /* If there is a valid and known entry point in the main executable of + this program space, return it. Otherwise return an empty optional. */ + std::optional exec_entry_point_address_if_available () const; - /* Get the entry point address in this program space. Call error if - it is not known. */ - CORE_ADDR entry_point_address () const; + /* Get the entry point address for the main executable in this program + space. Call error if it is not known. */ + CORE_ADDR exec_entry_point_address () const; /* Return true if any objfile of this program space has partial symbols. */ diff --git a/gdb/solib-frv.c b/gdb/solib-frv.c index 08ee0a9578f..4284a711860 100644 --- a/gdb/solib-frv.c +++ b/gdb/solib-frv.c @@ -689,7 +689,7 @@ enable_break (void) } std::optional entry_point - = current_program_space->entry_point_address_query (); + = current_program_space->exec_entry_point_address_if_available (); if (!entry_point.has_value ()) { solib_debug_printf ("Symbol file has no entry point."); From patchwork Mon Aug 24 16:22:17 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 142101 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 378C64B99F60 for ; Mon, 24 Aug 2026 16:23:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 378C64B99F60 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=PdIHQC60 X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org 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 78CC94BA9008 for ; Mon, 24 Aug 2026 16:22:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 78CC94BA9008 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 78CC94BA9008 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=1787588550; cv=none; b=YmjL/29CEfo7htja9Dh4CG7IXVgq2AmiotRXNZEjrNvJaM5/Yk6WAPW9QDLPRLGOdafTrbuE2NJIKGmS/fzguljwEqGp471dgJIgBAT66oJDzSZUXP3SXGsXwzcbtVVmucO6b3W7X6Nl5BKTGEQLvzeLz45+VJ4RzYH47cYjlo0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1787588550; c=relaxed/simple; bh=EeRakoi3RhkQdzulFHStWyOBBUU00eAq9h66X/RnXkY=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=hHL/dQ/H4itSu8+UcFsYH6e8Bu/H04qcIQC0TENqBhfD/vvoBDLXpINNmKRGh0e0N8DQ+lHti2PtwR0624IHme1D3QgvecE864AzYxtrSFBj/p79DWEBiOlM4jnUfcre4XKx1XgGVz2OTEPsBOenz2S43eSrydsLwjc3I+gdyUI= 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=PdIHQC60 DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 78CC94BA9008 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1787588550; 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=CAGR4d/QpTZRf4NfRc+T2hs1CX/lW55bO2UgEye5c50=; b=PdIHQC60QPadjJ1jyDyaQLx4r2gYkspeIIoQujMPnuJF7iO+UyTna1PtMhT3UqS/sl/ctl X6zkabTwNZx5lLSZkaRkR+PF0xZoNBZ1Hn+76mwTQ5M6kxSKwHkVHxt0as9qwnOvqkScbK D2jhxLvBTArCg2IghChLh6hSmt4qTag= Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-345-sfwg-pp7OZewT7HU3sRofA-1; Mon, 24 Aug 2026 12:22:28 -0400 X-MC-Unique: sfwg-pp7OZewT7HU3sRofA-1 X-Mimecast-MFC-AGG-ID: sfwg-pp7OZewT7HU3sRofA_1787588547 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-4954dcd6131so32965155e9.3 for ; Mon, 24 Aug 2026 09:22:28 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787588547; x=1788193347; 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=CAGR4d/QpTZRf4NfRc+T2hs1CX/lW55bO2UgEye5c50=; b=VCuay02RbmT/sRNDGgYESvFI1RQsuv90ypCbxtGnyIY711yMCgP/FxwkKvV2mHCNEJ wikL9ruzMSsdFiOQWvt0IJQntsD+NDriBnsoX9er+7gsrcEGKZmUEvQYdfe3iIjpaz0i ilteSrTbLIPN+jUSKl4MhtNkT5ijb92yVWeJTAkC6X0meXYQ1NODNvF6+AAzk73tC0yo ENBuxy+jQ9HdmqZcGgfGInGuLdC1pMDruayfiKYC2I1Z2iaiO3GC49BZGw5zpUzLpyFR N5hsDtmr696XV2k1cpDl5WeqO1VOzQjrUNSLTFbqVz77XOGcVKp3QCxZ5FNl8Zd4kqEB lTxQ== X-Gm-Message-State: AFuF++mUZeLfVP9QcQpTFRVJN3ryOeWiQfgMnurkocHH3rIwYHNbZjUU jXg5R2olYNn16pslriPjDkAEKpR2OAsXPxt09xvcvdXYGkmnvMWcMpViby0URxNjMAkt4OWM5h4 LwKrVx4lnEFd1EJuRk0bR6Y+81P3XKiFUM2Tslv8aL84FaAgTumxFvsSbMqygIY5t/m8qEkyPIf jd+qk1hUiWfgliuFNnmvInmNIMCBrMnNuxkgOdC1F/ESvOw5w= X-Gm-Gg: AR+sD11H95sv/nh4WZ6t+pid0ma1L+Xcv62k4X8sIRmHtM631//3MdvmvtUiwnylClF /Ga12znDUNaL5eIzZPFM4DrCPFtnsUCdVWfMbIrjsr8yXD9mBGjZt0sxz17cSV653wUw5Qi0JQJ ATC/bUgTKmRIcY+LrkIfZv5JSZol2wFprKULI1g4ct1J+TDFAIHX4Hp6Y1owdcbevlyYzqKFJTg 14iqWNFAL5+wXvISEIB7lowWe8NIv8UrzJEeiplEs2oNfwQ8nPaDOBUl0vCGje9LMUJSJQYnETs DagqLNPQReeSAlKgAFIUSJf4PENa6oY6Y+3KFJi5LvYPV2h7zDhUTM9t/1/LyOoU+36fpEfJgXn w0ks+Y/coAxst3SyuXGj+2WHgUD0= X-Received: by 2002:a05:600c:8b77:b0:499:48bb:417e with SMTP id 5b1f17b1804b1-499b82f4163mr382454975e9.2.1787588546786; Mon, 24 Aug 2026 09:22:26 -0700 (PDT) X-Received: by 2002:a05:600c:8b77:b0:499:48bb:417e with SMTP id 5b1f17b1804b1-499b82f4163mr382452885e9.2.1787588546036; Mon, 24 Aug 2026 09:22:26 -0700 (PDT) Received: from localhost (128.223.159.143.dyn.plus.net. [143.159.223.128]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-499d5d7cccbsm1256945e9.3.2026.08.24.09.22.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 24 Aug 2026 09:22:25 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Simon Marchi , Andrew Burgess , Eli Zaretskii Subject: [PATCHv9 2/4] gdb: introduce program_space::get_entry_point_info function Date: Mon, 24 Aug 2026 17:22:17 +0100 Message-Id: <039c261c82414999de034cd1bd6d01f326c8a6d3.1787588386.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: 6XfvJItuBpJUGQ-4KLY91Dv0JkGE0K5oJe1nm9rK2N0_1787588547 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, POISEN_SPAM_PILL, POISEN_SPAM_PILL_1, POISEN_SPAM_PILL_3, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, RCVD_IN_PBL, SPF_HELO_PASS, SPF_NONE, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@sourceware.org I noticed that when debugging a dynamically linked executable, if I started the inferior with 'starti' then used 'bt' I would see some bogus frames: (gdb) starti Starting program: /tmp/hello Program stopped. 0x00007ffff7fd3110 in _start () from /lib64/ld-linux-x86-64.so.2 (gdb) bt #0 0x00007ffff7fd3110 in _start () from /lib64/ld-linux-x86-64.so.2 #1 0x0000000000000001 in ?? () #2 0x00007fffffffac13 in ?? () #3 0x0000000000000000 in ?? () (gdb) This surprised me as 'backtrace past-entry' was off: (gdb) show backtrace past-entry Whether backtraces should continue past the entry point of a program is off. I was expecting GDB to stop the backtrace at the inferior's entry address. Frame unwinding starts in get_prev_frame, and in here we find this block: if (this_frame->level >= 0 && get_frame_type (this_frame) == NORMAL_FRAME && !user_set_backtrace_options.backtrace_past_entry && frame_pc.has_value () && inside_entry_func (this_frame)) { frame_debug_got_null_frame (this_frame, "inside entry func"); return NULL; } Which uses inside_entry_func to terminate the backtrace when we reach the entry frame. The inside_entry_func function calls current_program_space->exec_entry_point_address_if_available and uses the result to figure out if we are in the entry frame. And here the problem becomes obvious, we are only checking if we are in the "entry frame" for the main executable, not for the inferior as a whole. And indeed, if I compile the same test program as a static binary, where there will be no run-time linker, and the entry point of the executable is the entry point for the inferior, then the 'bt' problem I saw above goes away. This suggests, I think, that we need to track two different entry addresses, the entry address for the executable file, and the entry address for the entire inferior. Then, for dynamically linked executables, these two addresses can be different, the former will still be the same address within the main executable file, while the latter will be the address of the entry point within the run-time linker. If we had this information then we could extend inside_entry_func to check both addresses, and the 'bt' problem seen above will be resolved. To make this information available I added a new solib_ops method, solib_ops::inferior_entry_point_address, for svr4 targets this figures out if the main executable is dynamically linked, and if it is, uses the AT_BASE auxv entry and the entry address pulled from the ELF header to compute the inferior entry address. I then added program_space::get_entry_point_info, which returns a struct containing the two entry point addresses, one comes from the new solib_ops method, and one comes from the existing method program_space::exec_entry_point_address_if_available. With the infrastructure in place I can then update inside_entry_func to check against both entry addresses. To aid in debugging GDB, I added a new maintenance command: maintenance info entry-address which just calls program_space::get_entry_point_info and then prints the two addresses. I left this as a maintenance command as I don't see much user utility in this right now, but it made it easier for me to see what GDB was doing, so I left the command in this commit. Reviewed-By: Eli Zaretskii Approved-By: Simon Marchi --- gdb/NEWS | 7 + gdb/doc/gdb.texinfo | 22 ++ gdb/frame.c | 10 +- gdb/progspace.c | 60 +++++ gdb/progspace.h | 49 ++++ gdb/solib-svr4.c | 85 +++++++ gdb/solib-svr4.h | 1 + gdb/solib.h | 14 ++ gdb/testsuite/gdb.base/bt-after-starti.exp | 259 +++++++++++++++++++++ gdb/testsuite/lib/gdb.exp | 39 ++++ 10 files changed, 541 insertions(+), 5 deletions(-) create mode 100644 gdb/testsuite/gdb.base/bt-after-starti.exp diff --git a/gdb/NEWS b/gdb/NEWS index 10c182067f9..93f052f0cc8 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -181,6 +181,13 @@ disable skip These are new aliases for 'skip delete', 'skip enable', and 'skip disable' respectively. +maint info entry-address + Display the inferior and main executable entry addresses for the + current inferior. The inferior entry address is the address of the + first instruction in the inferior that was executed. The main + executable entry address is the address of the first instruction in + the main executable that will be executed. + * MI changes ** The "-trace-save" command no longer supports the "-ctf" flag. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 0030698dcee..35fb9f78b08 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -43260,6 +43260,28 @@ Maintenance Commands Ignoring SystemTap probe libc longjmp in /lib64/libc.so.6.^M Ignoring SystemTap probe libc longjmp in /lib64/libc.so.6.^M @end smallexample + +@kindex maint info entry-address +@item maint info entry-address +Display the entry addresses for the currently selected inferior. Two +addresses are displayed, the inferior entry address and the executable +entry address. + +Neither address is the address of @code{main}. When a program is +started, low-level startup code (typically a function called +@code{_start}) runs before @code{main} is called. + +The executable entry address is the address of this startup code +within the main executable (@pxref{Files, ,Commands to Specify +Files}). + +The inferior entry address is the address of the very first +instruction executed when the inferior is started. For statically +linked programs this is the same as the executable entry address. For +dynamically linked programs the run-time linker must execute first in +order to load shared libraries, so the inferior entry address will be +an address within the run-time linker rather than within the main +executable. @end table The following command is useful for non-interactive invocations of diff --git a/gdb/frame.c b/gdb/frame.c index a83f0b20686..e61bfd8d1f7 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -2703,12 +2703,12 @@ inside_main_func (const frame_info_ptr &this_frame) static bool inside_entry_func (const frame_info_ptr &this_frame) { - std::optional entry_point - = current_program_space->exec_entry_point_address_if_available (); - if (!entry_point.has_value ()) - return false; + const entry_point_info &ep_info + = current_program_space->get_entry_point_info (); - return get_frame_func (this_frame) == *entry_point; + 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 diff --git a/gdb/progspace.c b/gdb/progspace.c index 3003a137c17..2e0b6e4b825 100644 --- a/gdb/progspace.c +++ b/gdb/progspace.c @@ -26,6 +26,7 @@ #include #include "cli/cli-style.h" #include "observable.h" +#include "arch-utils.h" /* The last program space number assigned. */ static int last_program_space_num = 0; @@ -288,6 +289,55 @@ program_space::exec_entry_point_address () const /* See progspace.h. */ +entry_point_info +program_space::get_entry_point_info () const +{ + std::optional exec_entry_address + = this->exec_entry_point_address_if_available (); + + std::optional inferior_entry_address; + if (m_solib_ops != nullptr) + inferior_entry_address = m_solib_ops->inferior_entry_point_address (); + + return entry_point_info (std::move (inferior_entry_address), + std::move (exec_entry_address)); +} + +/* Implement the 'maint info entry-address' command. */ + +static void +maintenance_info_entry_address (const char *args, int from_tty) +{ + if (args != nullptr && *args != '\0') + error (_("unknown argument: %s"), args); + + struct gdbarch *gdbarch = get_current_arch (); + + const entry_point_info &ep_info + = current_program_space->get_entry_point_info (); + + /* Display a single entry address ADDR with TITLE as a description. */ + auto display_entry_address + = [&gdbarch] (const char *title, + const std::optional &addr) -> void + { + gdb_puts (title); + if (addr.has_value ()) + fputs_styled (paddress (gdbarch, addr.value ()), + address_style.style (), gdb_stdout); + else + fputs_styled ("", metadata_style.style (), gdb_stdout); + gdb_puts ("\n"); + }; + + display_entry_address (_("Inferior entry address: "), + ep_info.inferior_entry_address ()); + display_entry_address (_("Executable entry address: "), + ep_info.exec_entry_address ()); +} + +/* See progspace.h. */ + bool program_space::has_partial_symbols () { @@ -498,6 +548,16 @@ initialize_progspace () _("Info about currently known program spaces."), &maintenanceinfolist); + add_cmd ("entry-address", class_maintenance, + maintenance_info_entry_address, + _("Information about the current inferior's entry addresses.\n\ +Display the address of the first instruction executed within the\n\ +inferior, and the first instruction executed within the main executable.\n\ +The entry address of the whole inferior will be prior to the\n\ +inferior starting, and either or both addresses can be if\n\ +GDB is unable to find the required information."), + &maintenanceinfolist); + /* There's always one program space. Note that this function isn't an automatic _initialize_foo function, since other _initialize_foo routines may need to install their per-pspace diff --git a/gdb/progspace.h b/gdb/progspace.h index 1ae1e42f3bb..69e70a1de6a 100644 --- a/gdb/progspace.h +++ b/gdb/progspace.h @@ -67,6 +67,50 @@ new_address_space () return address_space_ref_ptr::new_reference (new address_space); } +/* Class for tracking two possible entry points that an inferior might + have, the entry point for the entire inferior, and the entry point + within the main executable. */ + +struct entry_point_info +{ + entry_point_info (std::optional inferior_entry_address, + std::optional exec_entry_address) + : m_inferior_entry_address (inferior_entry_address), + m_exec_entry_address (exec_entry_address) + { /* Nothing. */ } + + DISABLE_COPY_AND_ASSIGN (entry_point_info); + + /* The entry address within the inferior as a whole. See the member + variable definition below for more details. */ + std::optional inferior_entry_address () const + { + return m_inferior_entry_address; + } + + /* The entry address within the main executable. See the member + variable definition below for more details. */ + std::optional exec_entry_address () const + { + return m_exec_entry_address; + } + +private: + /* The entry address within the inferior. This can be outside of the + main executable, e.g. for dynamically linked executables this could + be the entry address for the run-time linker. For statically linked + executables this will be the entry address of the main executable. + This can be empty if GDB doesn't know how to figure out the correct + entry address for any reason. */ + std::optional m_inferior_entry_address; + + /* The entry address within the main executable. This can be empty if + the main executable is not set yet, or GDB doesn't have an objfile + associated with the main executable, e.g. in some attach, or remote + debug cases. */ + std::optional m_exec_entry_address; +}; + /* A program space represents a symbolic view of an address space. Roughly speaking, it holds all the data associated with a non-running-yet program (main executable, main symbols), and when @@ -323,6 +367,11 @@ struct program_space return m_target_sections; } + /* Return information about the entry point in the main executable, and + the entry point for the inferior, which might be different from the + main executable. */ + entry_point_info get_entry_point_info () const; + /* If there is a valid and known entry point in the main executable of this program space, return it. Otherwise return an empty optional. */ std::optional exec_entry_point_address_if_available () const; diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index 8e3de4d3ea1..12f3e783744 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -3777,6 +3777,91 @@ svr4_solib_ops::get_solibs_in_ns (int nsid) const return ns_solibs; } +/* See solib.h. */ + +std::optional +svr4_solib_ops::inferior_entry_point_address () const +{ + std::optional interp_name_holder + = svr4_find_program_interpreter (); + + /* No interpreter means this is a static executable. Ask the + program_space for the entry address within the main executable. */ + if (!interp_name_holder.has_value ()) + return m_pspace->exec_entry_point_address_if_available (); + + /* For a dynamically linked executable the inferior's true entry point + is the entry point of the dynamic linker. We find this using the + AT_BASE auxiliary vector entry, which gives the dynamic linker's + load address, combined with e_entry address pulled from the + inferior. We assume that the ELF header can be read from AT_BASE. */ + CORE_ADDR at_base_addr; + if (target_auxv_search (AT_BASE, &at_base_addr) <= 0) + return {}; + + /* Determine ELF architecture type. Use the size of a program header + entry to determine which ELF header we can expect to find. */ + size_t e_entry_offset = 0; + size_t e_entry_size = 0; + CORE_ADDR at_phent; + if (target_auxv_search (AT_PHENT, &at_phent) <= 0) + return {}; + if (at_phent == sizeof (Elf32_External_Phdr)) + { + e_entry_offset = offsetof (Elf32_External_Ehdr, e_entry); + e_entry_size = 4; + } + else if (at_phent == sizeof (Elf64_External_Phdr)) + { + e_entry_offset = offsetof (Elf64_External_Ehdr, e_entry); + e_entry_size = 8; + } + else + return {}; + + /* Architecture of the current inferior. */ + gdbarch *gdbarch = current_inferior ()->arch (); + + /* Read the entry address from the ELF header at AT_BASE_ADDR. */ + CORE_ADDR e_entry; + gdb_byte buffer[sizeof (CORE_ADDR)]; + if (target_read_memory (at_base_addr + e_entry_offset, buffer, e_entry_size)) + return {}; + bfd_endian byte_order = gdbarch_byte_order (gdbarch); + e_entry = extract_unsigned_integer (buffer, e_entry_size, byte_order); + + /* Ensure AT_BASE_ADDR has proper sign in its possible upper bits so + that `+ at_base_addr' will overflow CORE_ADDR width not creating + invalid addresses like 0x101234567 for 32bit inferiors on 64bit + GDB. */ + int addr_bit = gdbarch_addr_bit (gdbarch); + if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT)) + { + CORE_ADDR space_size = (CORE_ADDR) 1 << addr_bit; + + gdb_assert (at_base_addr < space_size); + + /* E_ENTRY exceeding SPACE_SIZE would be for prelinked + 64bit ld.so with 32bit executable, it should not happen. */ + if (e_entry < space_size + && e_entry + at_base_addr >= space_size) + at_base_addr -= space_size; + } + + /* Compute the entry address. */ + e_entry = at_base_addr + e_entry; + + /* Handle the case where E_ENTRY is a function descriptor. Also remove + any non-address (e.g. tag) bits from E_ENTRY. */ + e_entry + = gdbarch_convert_from_func_ptr_addr (gdbarch, e_entry, + current_inferior ()->top_target ()); + e_entry + = gdbarch_addr_bits_remove (gdbarch, e_entry); + + return e_entry; +} + INIT_GDB_FILE (svr4_solib) { gdb::observers::free_objfile.attach (svr4_free_objfile_observer, diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h index 3078a092778..f76f350d951 100644 --- a/gdb/solib-svr4.h +++ b/gdb/solib-svr4.h @@ -115,6 +115,7 @@ struct svr4_solib_ops : public solib_ops void iterate_over_objfiles_in_search_order (iterate_over_objfiles_in_search_order_cb_ftype cb, objfile *current_objfile) const override; + std::optional inferior_entry_point_address () const override; /* Return the appropriate link map offsets table for the architecture. */ virtual link_map_offsets *fetch_link_map_offsets () const = 0; diff --git a/gdb/solib.h b/gdb/solib.h index 662d467b0cc..2db836dc005 100644 --- a/gdb/solib.h +++ b/gdb/solib.h @@ -283,6 +283,20 @@ struct solib_ops (iterate_over_objfiles_in_search_order_cb_ftype cb, objfile *current_objfile) const; + /* Return the inferior entry point address. This isn't always the entry + point of the main executable, but will be the actual entry point where + the inferior starts (or started) executing. + + For example on SVR4 targets, for dynamically linked executables, this + will be the entry address of the dynamic linker. But for statically + linked executables, this will be the entry point of the main + executable. + + If the entry address cannot be found then an empty optional is + returned. */ + virtual std::optional inferior_entry_point_address () const + { return {}; } + protected: /* The program space for which this solib_ops was created. */ program_space *m_pspace; diff --git a/gdb/testsuite/gdb.base/bt-after-starti.exp b/gdb/testsuite/gdb.base/bt-after-starti.exp new file mode 100644 index 00000000000..2b452365f89 --- /dev/null +++ b/gdb/testsuite/gdb.base/bt-after-starti.exp @@ -0,0 +1,259 @@ +# 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 'bt' from the very first instruction (where 'starti' +# stops GDB) doesn't display any unexpected frames. + +standard_testfile main.c + +# Build default executable, hopefully dynamically linked, but we check +# this below. +foreach pie_mode { pie nopie } { + if { [build_executable "failed to build" $testfile-$pie_mode $srcfile \ + [list debug $pie_mode]] } { + return + } +} + +# Build a statically linked executable. +set testfile_static ${testfile}-static +if { [build_executable "failed to build" $testfile_static $srcfile \ + {debug additional_flags=-static}] } { + return +} + +# Run the 'maint info entry-address' command. Expect INF_RE to match +# the inferior entry address and EXE_RE to match the executable entry +# address. TESTNAME is used as the test name. +proc check_maint_info_entry_addr { inf_re exe_re testname } { + gdb_test "maint info entry-address" \ + [multi_line \ + "Inferior entry address: ${inf_re}" \ + "Executable entry address: ${exe_re}"] \ + $testname +} + +# Use 'info proc mappings' to find the address of the first mapping of +# TESTFILE, return this address. If the address cannot be found then +# the empty string is returned. +# +# This emits a PASS or FAIL depending on whether the address is found +# or not. +proc get_mapped_file_base_addr { testfile } { + set base_addr "" + gdb_test_multiple "info proc mappings" "" -lbl { + -re "\r\n($::hex) \[^\r\n\]+/${testfile}\\s*(?=\r\n)" { + if { $base_addr eq "" } { + set base_addr $expect_out(1,string) + } + exp_continue + } + -re "\r\n$::gdb_prompt $" { + gdb_assert { $base_addr ne "" } \ + "find base address of $testfile" + } + } + return $base_addr +} + +# Use 'objdump' to extract the entry point address from TESTFILE. +proc get_exec_entry_address { testfile } { + set objdump_program [gdb_find_objdump] + set command "exec $objdump_program -f [standard_output_file $testfile]" + verbose -log "command is $command" + set result [catch {{*}$command} output] + verbose -log "result is $result" + verbose -log "output is $output" + + set testname "get_exec_entry_address" + + if {$result != 0} { + fail $testname + return "UNKNOWN" + } + + if {![regexp "\nstart address ($::hex)\n" $output trash addr]} { + fail "$testname (no entry point address header)" + return "UNKNOWN" + } + + # For ARM the least significant bit will be set to 1 for thumb + # code, clear that now. + if { [istarget "arm*-*-*"] } { + set addr [expr {$addr & ~0x1}] + } + + return 0x[format %lx $addr] +} + +# Start TESTFILE using 'starti'. Check if the 'backtrace' command +# prints any additional frames; we don't expect any. Create a core +# file from the running inferior. Restart GDB and load the core file, +# check that GDB can still figure out the entry addresses. +# +# When IS_DYNAMIC is true then TESTFILE is a dynamically linked +# executable, otherwise TESTFILE is statically linked. +proc run_test { testfile is_dynamic } { + clean_restart $testfile + + set exe_entry_addr [get_exec_entry_address $testfile] + + # The inferior entry address is always unknown before starting the + # inferior because GDB has no solib_ops yet, and it is only + # through that that we figure out the inferior entry address. + check_maint_info_entry_addr "" $exe_entry_addr \ + "check 'maint info entry-address' before inferior starts" + + 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 } { + fail "could not issue starti command" + return -1 + } + gdb_test "" "Program stopped.*" "prompt after starti" + } + + # Not every target supports finding the process entry point. + # Targets that don't support this report '', we check for + # this too on those targets. + if {[supports_process_entry_point]} { + set pc [get_hexadecimal_valueof "\$pc" "UNKNOWN" \ + "get current program counter"] + + if { !$is_dynamic } { + # In static binaries, the first address should be the first + # address in the executable. + gdb_assert { $pc == $exe_entry_addr } \ + "stopped at executable entry address" + } + + set inf_entry_addr $pc + } else { + set inf_entry_addr "" + } + + # If using PIE then the executable entry address we looked up from + # the objfile will have been relocated. Attempt to compute the + # relocated address and updated EXE_ENTRY_ADDR, otherwise set + # EXE_ENTRY_ADDR to a generic pattern that matches any address. + if {[exec_is_pie [standard_output_file $testfile]]} { + set base_addr [get_mapped_file_base_addr $testfile] + if { $base_addr ne "" } { + set exe_entry_addr \ + 0x[format %lx [expr {$base_addr + $exe_entry_addr}]] + } else { + # We failed to find the base address. The + # get_mapped_file_base_addr will have already emitted a + # FAIL. Set the expected pattern to something generic and + # push on, we can still test a bunch of things, we just + # don't know the address is correct. + set exe_entry_addr $::hex + } + } + + check_maint_info_entry_addr $inf_entry_addr $exe_entry_addr \ + "check 'maint info entry-address' after starti" + + # Allow backtrace past the entry frame, count how many frames GDB + # finds. If on this target there are no additional frames then + # the following test isn't going to tell us much, so skip it. + gdb_test_no_output "set backtrace past-entry on" + set frame_count 0 + set first_frame_is_unnamed false + gdb_test_multiple "bt" "count possible frames" -lbl { + -re "\r\n#0\\s+$::hex in \\?\\? \\(\\) \[^\r\n\]+(?=\r\n)" { + incr frame_count + set first_frame_is_unnamed true + exp_continue + } + -re "\r\n#($::decimal)\\s+\[^\r\n\]+(?=\r\n)" { + incr frame_count + exp_continue + } + -re "\r\n$::gdb_prompt $" { + gdb_assert { $frame_count > 0 } $gdb_test_name + } + } + + if { $frame_count <= 1 } { + unsupported "no additional frames that GDB can hide" + } elseif { $first_frame_is_unnamed } { + # GDB's entry frame detection relies on figuring out the $pc + # for the start of the frame. We know that frame #0 should be + # the entry frame, but if it is nameless then GDB will have + # failed to find the start of the frame, and so will not be + # able to perform entry frame detection. + unsupported "first frame is unnamed" + } else { + # Turn off backtrace past the entry frame. Use the 'bt' command, + # and check we see only a single frame. + gdb_test_no_output "set backtrace past-entry off" + gdb_test "bt" "^#0 \[^\r\n\]+" \ + "single frame when backtrace past-entry is off" + + # Check 'bt -past-entry'. + set frame_count_with_past_entry_flag 0 + gdb_test_multiple "bt -past-entry" "count frames again" -lbl { + -re "\r\n#($::decimal)\\s+\[^\r\n\]+(?=\r\n)" { + incr frame_count_with_past_entry_flag + exp_continue + } + -re "\r\n$::gdb_prompt $" { + gdb_assert \ + { $frame_count == $frame_count_with_past_entry_flag } \ + $gdb_test_name + } + } + } + + # Create a core file. Restart GDB. Load the core file. Check + # that 'maint info entry-address' gives the correct output. + set corefile [host_standard_output_file $testfile].core + if {![gdb_gcore_cmd $corefile "generate corefile"]} { + return + } + + clean_restart $testfile + + if {[gdb_core_cmd $corefile "load corefile"] != 1} { + return + } + + check_maint_info_entry_addr $inf_entry_addr $exe_entry_addr \ + "check 'maint info entry-address' after loading core file" +} + +with_test_prefix "dynamic" { + foreach_with_prefix pie_mode { pie nopie } { + set dyln_name [section_get $binfile-$pie_mode .interp] + if { $dyln_name eq "" } { + unsupported "couldn't find dynamic linker name" + } else { + run_test $testfile-$pie_mode true + } + } +} + +with_test_prefix "static" { + run_test $testfile_static false +} diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 6fb04869605..fd14f9fbc27 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -4223,6 +4223,45 @@ proc is_windows_based_target {} { return [expr {[istarget *-*-cygwin*] || [istarget *-*-mingw*]}] } +# Return true for svr4 targets, otherwise, return false. + +proc is_svr4_target {} { + return [expr {[istarget *-linux*] || [istarget *-freebsd*] + || [istarget *-netbsd*] || [istarget *-openbsd*] + || [istarget *-solaris*] || [istarget *-gnu]}] +} + +# Return true for targets that support finding the whole process entry +# address, otherwise, return false. + +proc supports_process_entry_point {} { + # SVR4 targets support finding the entry point. This is done + # within GDB so will work even for remote targets. + if {[is_svr4_target]} { + return true + } + + # Windows and Darwin don't currently support this. + if {[istarget *-*-mingw*] + || [istarget *-*-cygwin*] + || [istarget *-*-pe*] + || [istarget *-*-darwin*]} { + return false + } + + # For remote targets there is no RSP packet to retrieve the entry + # point, so this won't work unless the solib code can handle this + # within GDB, see the svr4 check above. + if {[gdb_protocol_is_remote]} { + return false + } + + # Assume everything else supports this by default. If a test + # fails because we get here then either fix GDB to support this + # feature, or add a new deny list entry above. + return true +} + # Return 1 if displaced stepping is supported on target, otherwise, return 0. proc support_displaced_stepping {} { From patchwork Mon Aug 24 16:22:18 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 142104 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 606A14BB1C04 for ; Mon, 24 Aug 2026 16:23:53 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 606A14BB1C04 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=EwMPq/sF X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org 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 8BF4C4BAE7CF for ; Mon, 24 Aug 2026 16:22:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8BF4C4BAE7CF 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 8BF4C4BAE7CF 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=1787588552; cv=none; b=Itr1Cvy2pVWmV39c2cTrpF3iaIcJ+cU1Xa8/cMrG6AI/sYGcKUwiFNiDGxhOkw6On+j7qvAOAQma3LDIFcqEGuNRxOKDiptZVlICykYg6TCH5gnYxcVPg3GySNyb5q3/X3wrFHuDRiwJeBugX+Fdlf7qgFQyOZK9aEyLH5ZxUFI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1787588552; c=relaxed/simple; bh=jwcjY2ALsphaDfOOlRCKz5BsT710Bav8zHCl/c0K/pQ=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=JG0XJURUkAjyS0+smU6DJAmZB5wmM5lhd9NUi/rAJIXjCta9UgAIA6AvGS3v8MgpaQnmi28ECjJ5bdA+OqJr0RMlIceUuUnvwXga9wWnru0tg3zpc/nxkEcT2VznSy1W1VIqit4CjAQ7U+XJ4ImKLHJP6VKUunXiDG6ApEg+Uy8= 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=EwMPq/sF DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8BF4C4BAE7CF DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1787588552; 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=bW4TCrjTSuWr9ot6d6QUHk43ECxZByIP8euSTMHtPUo=; b=EwMPq/sFuv/GmEpRMOGof3B6W1BawU4Aootu/YVrIHGTvCedhrMZQZRSViXt+9PGbO9nHJ zxIiJMkVp4Cr7okI9c9Z6nHDPt9ctU6zbuzCW06w3zZFhvSZvEcUUyAAiINAcHmyP76XWE UY7bvmERDSCVZ2Gq7d8apmQEV+bHAmw= Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-447-V_f6F0NUMKKYkFG43dXYwA-1; Mon, 24 Aug 2026 12:22:30 -0400 X-MC-Unique: V_f6F0NUMKKYkFG43dXYwA-1 X-Mimecast-MFC-AGG-ID: V_f6F0NUMKKYkFG43dXYwA_1787588549 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-496b6f97676so13716575e9.2 for ; Mon, 24 Aug 2026 09:22:30 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787588549; x=1788193349; 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=bW4TCrjTSuWr9ot6d6QUHk43ECxZByIP8euSTMHtPUo=; b=WNeu7K+YCifmkm4s5UUcicZV68eb0+YnGXyfuewfCSp7D/Xoq3sfPrQDDIuzedqidO Uj5KZZrWsi2hlYjHJ/vo6xlON7Tjk6JTxXhh0fIsw83AXC/eSS7dQZEq/uCk+xpdB1iK l2xcAFAs9y5rAwIaI8fIDjMoaeTkNYzYe9PIfutiYguwsUypFmjvGTqcaRz01Qvdac2w PFcvMQP3QqQ5bC4a+rj4l/GWtoAHBbvm2Rqw/OLyLAX9FuZnmjsvt1hEVxHHmvuVx7W3 /T8fKRR+ikaIEnhuvk1BA5WzkVQr/8RxRuLyJGzoDBBd1F80o2mO5yV31SW/s4MVtMv7 03Ew== X-Gm-Message-State: AFuF++nIv1MvBQn42BGNshgtG4kRXUP6RhYctUYkxvxTYBWx3cHitDjB 6B6wCLQYtBtiWG7V2FalPVu2rDgo05HDEpt1M9pDOcT+4/FPxXc5Tl1U7e3EhcpOXLy2dPlCuK/ ct/1tBmp702Xe6/1AqfsBYhEvWEYvUAtj5F21UYilqpGXotpXAapTFF4XEtWIRYD2IcTx/DrzUQ 85Q2sOgra1+0n6UgRd0lHNRu0JF4YFu/v0sxUHTz3ZiITFIHA= X-Gm-Gg: AR+sD1222BxUCXTIrGZMyidNbNJxRDpTn3fz4V7AXqzQhGRFVEbnxZO3zCmlGn4jw8M y6t1LvT3otFsSomyBa3T9KQryJAp/SgdGWoruAR5SAB5OcPnbA6jdq/WIrrgFo74P+uZk8jEhpz K0NeF8I45G8XJb4rGT7cM6XKcvaBVur0Wj6ftF/RzBB9NaCksa4HjH94nwCBX+mmjI6YjG4PZlh 48RRWsan76gSz6vm9OmeBQ3w7t0c4A/b7tz4+h4K7d2ovMPQ7PF3RliOzOzP/fT7RT46MvL8G8Z uiJbCjHQSEdjAHdfL4D4/9kJqDVWZCsd3nf4PCsNLsrjxInRsRHB0ydWFxT8Ui3sU8XqtkPVgz4 WmvPPAAMPng16VBpamzH4OO0gdP0= X-Received: by 2002:a05:600c:8b57:b0:499:d639:8475 with SMTP id 5b1f17b1804b1-499d63985bdmr6005125e9.6.1787588549092; Mon, 24 Aug 2026 09:22:29 -0700 (PDT) X-Received: by 2002:a05:600c:8b57:b0:499:d639:8475 with SMTP id 5b1f17b1804b1-499d63985bdmr6003015e9.6.1787588548332; Mon, 24 Aug 2026 09:22:28 -0700 (PDT) Received: from localhost (128.223.159.143.dyn.plus.net. [143.159.223.128]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-499d5d72d36sm1589905e9.3.2026.08.24.09.22.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 24 Aug 2026 09:22:27 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Simon Marchi , Andrew Burgess Subject: [PATCHv9 3/4] gdb: allow 'until' to work in outermost frame Date: Mon, 24 Aug 2026 17:22:18 +0100 Message-Id: <41fc9ac7b20577ba647a4be79b50b8f19811de9a.1787588386.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: CtkW2P38XellK95EwKG37dsHN7NJ0-a9b5oVOeWvucg_1787588549 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, PROLO_LEO1, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, RCVD_IN_PBL, SPF_HELO_PASS, SPF_NONE, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@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 path, similar to the one found in get_prev_frame. To achieve this I create get_prev_frame_with_entry_frame_check, a new function that checks if a frame is an entry frame, and returns NULL if it is. Otherwise, get_prev_frame_with_entry_frame_check just calls get_prev_frame_always. Then in get_prev_frame and frame_unwind_caller_frame, instead of calling get_prev_frame_always directly, we can call the new get_prev_frame_with_entry_frame_check. Finally, the existing entry frame check can be removed from get_prev_frame. Notice that in moving the entry frame check from get_prev_frame I have removed the 'frame_pc.has_value ()' check. Previously inside_entry_func called get_frame_func which would throw if the frame's $pc was not available. I have updated inside_entry_func to call get_frame_func_if_available, which doesn't throw. As a result the `frame_pc.has_value ()` check is no longer needed. An earlier version of this patch tried moving the inside_entry_frame 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. 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. 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. This was true for both the executable entry frame and the inferior entry frame. I created bug PR gdb/34245 for this issue. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34245 --- gdb/frame.c | 96 ++++---- gdb/testsuite/gdb.base/until-in-entry-frame.c | 22 ++ .../gdb.base/until-in-entry-frame.exp | 215 ++++++++++++++++++ 3 files changed, 281 insertions(+), 52 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..db09171b08d 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -734,6 +734,45 @@ get_stack_frame_id (const frame_info_ptr &next_frame) return get_frame_id (skip_artificial_frames (next_frame)); } +/* Return true if THIS_FRAME is either of the two possible entry frames; + the entry frame for the whole inferior, or the entry frame of just the + executable. */ + +static bool +inside_entry_func (const frame_info_ptr &this_frame) +{ + 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 (); + + /* Check both entry frames. */ + return (ep_info.exec_entry_address () == frame_func_addr + || ep_info.inferior_entry_address () == frame_func_addr); +} + +/* Check if THIS_FRAME is an entry frame. If it is, return NULL. + Otherwise, call get_prev_frame_always and return its result. */ + +static frame_info_ptr +get_prev_frame_with_entry_frame_check (const frame_info_ptr &this_frame) +{ + FRAME_SCOPED_DEBUG_START_END ("fi=%d", this_frame->level); + + if (this_frame->level >= 0 + && get_frame_type (this_frame) == NORMAL_FRAME + && !user_set_backtrace_options.backtrace_past_entry + && inside_entry_func (this_frame)) + { + frame_debug_printf ("is inside an entry frame"); + return nullptr; + } + + return get_prev_frame_always (this_frame); +} + /* 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,7 +783,8 @@ 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) { - frame_info_ptr this_frame = get_prev_frame_always (initial_next_frame); + frame_info_ptr this_frame + = get_prev_frame_with_entry_frame_check (initial_next_frame); if (this_frame == nullptr) return nullptr; @@ -754,11 +794,8 @@ frame_unwind_caller_frame (const frame_info_ptr &initial_next_frame) struct frame_id frame_unwind_caller_id (const frame_info_ptr &initial_next_frame) { - /* Use get_prev_frame_always, and not get_prev_frame. The latter - will truncate the frame chain, leading to this function - unintentionally returning a null_frame_id (e.g., when a caller - requests the frame ID of "main()"s caller. */ - + /* The frame_unwind_caller_frame call can return NULL if we are + initially in an entry frame. */ frame_info_ptr this_frame = frame_unwind_caller_frame (initial_next_frame); if (this_frame == nullptr) return null_frame_id; @@ -2698,19 +2735,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 @@ -2767,38 +2791,6 @@ get_prev_frame (const frame_info_ptr &this_frame) return NULL; } - /* If we're already inside the entry function for the main objfile, - then it isn't valid. Don't apply this test to a dummy frame - - dummy frame PCs typically land in the entry func. Don't apply - this test to the sentinel frame. Sentinel frames should always - be allowed to unwind. */ - /* NOTE: cagney/2003-07-07: Fixed a bug in inside_main_func() - - wasn't checking for "main" in the minimal symbols. With that - fixed asm-source tests now stop in "main" instead of halting the - backtrace in weird and wonderful ways somewhere inside the entry - file. Suspect that tests for inside the entry file/func were - added to work around that (now fixed) case. */ - /* NOTE: cagney/2003-07-15: danielj (if I'm reading it right) - suggested having the inside_entry_func test use the - inside_main_func() msymbol trick (along with exec_entry_point_address() - I guess) to determine the address range of the start function. - That should provide a far better stopper than the current - heuristics. */ - /* NOTE: tausq/2004-10-09: this is needed if, for example, the compiler - applied tail-call optimizations to main so that a function called - from main returns directly to the caller of main. Since we don't - stop at main, we should at least stop at the entry point of the - application. */ - if (this_frame->level >= 0 - && get_frame_type (this_frame) == NORMAL_FRAME - && !user_set_backtrace_options.backtrace_past_entry - && frame_pc.has_value () - && inside_entry_func (this_frame)) - { - frame_debug_got_null_frame (this_frame, "inside entry func"); - return NULL; - } - /* Assume that the only way to get a zero PC is through something like a SIGSEGV or a dummy frame, and hence that NORMAL frames will never unwind a zero PC. */ @@ -2812,7 +2804,7 @@ get_prev_frame (const frame_info_ptr &this_frame) return NULL; } - return get_prev_frame_always (this_frame); + return get_prev_frame_with_entry_frame_check (this_frame); } CORE_ADDR 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..b380107cc42 --- /dev/null +++ b/gdb/testsuite/gdb.base/until-in-entry-frame.exp @@ -0,0 +1,215 @@ +# 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. +# +# When AT_EXEC_ENTRY is true, continue to the executable entry frame +# rather than running the test in the inferior entry frame. If the +# inferior entry frame is the same as the executable entry frame then +# this proc will return early. +proc run_test { use_stepi past_entry at_exec_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 + } + 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"] + + set inferior_entry_address "" + set executable_entry_address "" + gdb_test_multiple "maintenance info entry-address" "" -lbl { + -re "\r\nInferior entry address: ($::hex)(?=\r\n)" { + set inferior_entry_address $expect_out(1,string) + exp_continue + } + -re "\r\nExecutable entry address: ($::hex)(?=\r\n)" { + set executable_entry_address $expect_out(1,string) + exp_continue + } + -re "\r\n$::gdb_prompt $" { + gdb_assert { $inferior_entry_address ne "" \ + && $executable_entry_address ne "" } \ + $gdb_test_name + } + } + + if { $at_exec_entry } { + # If we are being asked to run the test at the executable + # entry frame, but the inferior entry frame is the same as the + # executable entry frame, which will be the current frame, + # then there is nothing more to test here. + # + # This proc will be called again with AT_EXEC_ENTRY false, in + # which case the tests in this proc will be run. + if { $inferior_entry_address == $executable_entry_address } { + return + } + + # Move forward to the executable entry frame. + gdb_breakpoint "*$executable_entry_address" + gdb_continue_to_breakpoint "executable entry frame" + } + + 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 } { + foreach_with_prefix at_exec_entry { false true } { + run_test $use_stepi $past_entry $at_exec_entry $testfile + + with_test_prefix "static" { + run_test $use_stepi $past_entry $at_exec_entry $testfile_static + } + } + } +} From patchwork Mon Aug 24 16:22:19 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 142102 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 940314BAE7F9 for ; Mon, 24 Aug 2026 16:23:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 940314BAE7F9 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=A3/aMBzo X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org 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 C535A4BAE7E3 for ; Mon, 24 Aug 2026 16:22:33 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C535A4BAE7E3 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 C535A4BAE7E3 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=1787588553; cv=none; b=mojJz30eVEPXDr3GxKnwn/R/cuQKbT2TTVGeuTqms0vEDSEP9Ngn1eDHJnTW7yt4lFMrcFkmJZbz2qaDbWl9kVkf2ws3vxWWZ8IBKReyOWSPyJz2dXWUNqFx3kMN04WMBbJBYd7VPh6d0xL+crCdcfk6tLgY4cYSYlCqmLpM9zw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1787588553; c=relaxed/simple; bh=8sSeWQKo8RmETcUILaKT+yD5NJbUXd1s7/pcUjMs+FY=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=i/io+HGwFRaUWYMnSLhU84JBv0YrDGBdnWq2kLr0EgGxM+4v7F3dX5Y0wNQ21KT8ew+lgQv7ajL43Fnjed8SboAWFEGdbU5tiyTbyNLhpmMs/pCq9aMKa+gmMwonVRGCW8kwjxrkRag6XsT+xxfS9aGK6cvbCOE13KoopqKF13A= 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=A3/aMBzo DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C535A4BAE7E3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1787588553; 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=039hyNkpQeohu1q4h77el7Fz3otd+5WRaUwulj4Lo1U=; b=A3/aMBzoH23Gd7lNyui44xi8Ok+OGbAj/0pZ8jUno/BEeKJxJYhfx0WNnJPGohI/Xu1sBR 15lvcYsnGfM5AqEd8YGBROouIhZUpRsByfSC/JVB5ljU+kKfuhjMxxn0qwFxHwBfKeH0p+ xFzmV6Zo1bFWtB7IUlPOSKAJULl/oNY= 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-325-zrEXBgvKNQ2lAPBuN5-yWA-1; Mon, 24 Aug 2026 12:22:32 -0400 X-MC-Unique: zrEXBgvKNQ2lAPBuN5-yWA-1 X-Mimecast-MFC-AGG-ID: zrEXBgvKNQ2lAPBuN5-yWA_1787588551 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-482a5e9400eso2532988f8f.2 for ; Mon, 24 Aug 2026 09:22:32 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787588551; x=1788193351; 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=039hyNkpQeohu1q4h77el7Fz3otd+5WRaUwulj4Lo1U=; b=c+dpQjyPPjcCfQd8RB4rt8aEXovoB+UdwnFQ7OWOoY3mvVu6uNzLP7801hQ+3xDT4d UIV0iWoFnTflUs0gotrJaUrNzg/vdLDR9aMKeL3nfDveOpRqnM7jKdwVnCRUscwqQTBw HPmePEJC/SxNCd/AUDxTBp2Y/l1jhE5mReGaZC5XWaVK+VBqL7v5sNmko59CYIhmu4wC ss2pOlzoRRtRNBulRu5OMmxztU60nzagke8ArNO9BDNq3M8VXWH2YfwPredLAQM1Qz30 7jahwVGtBOBcuBfRmsjLLEXBmX7PJD8KQUBT4m2JjOGGJ/weGV5TKRNz/fd5/LVbRgX+ B2QA== X-Gm-Message-State: AFuF++lynEQ6eH1zuO1U87sdrct0sRNxLw03u4OL3YnjvsOKJhITEqJ5 8e09yigtDqyoG2RO7p3sBMnsSP3VRrr2MUVE6asFdZCUeBwatB990Why3E38PxjKpibdJ1wzCP8 e5jJmCIla8BCvxn2h2IhHBn3qY4Ndc+ntvalfdiIaQiNd8GmaiaS7xaJzsAvK51hug/WJgFd1mX LZZ7395yVxJVEM1jnZiPYi6XeDgeWjkmoY6Rq13B8CO0uOyNs= X-Gm-Gg: AR+sD10ib0pD936nRhODRtWkVjgod6eln5Na+LIRjFUlbV/uWSQfQnPZ8ZNlRsmcXCn HapKAkpE8W32VaCe5HMlAvxiOu9ahc/ADAukdq8op6XQUI19WDwGNRsBl3Cy6EhRpsIaKvDMaxl z/q0mFSUa2ov7uM0Q8JumtpAw2o8ecfN/3/rHLBMF8c8D1lQ/EKhuYsm4CbAtcnO9BdIiqzpN8i bOnwEqyUw5aWj5KOY7wcWss6BCbdoTKJc2zKLoW3KfiSuKi8u5ml+HwSA++cm5m19spxx4Ji8mf 8fhvE55DtbP445OoE7TjBUHY9/IaaqiBcOWvG1HEBXOHOWuo9k3crO7EKYJ7AsrqFoVqNZTnDQX nSmHTnXjoJgVogv0ZP+i61QJDNZ8= X-Received: by 2002:a05:6000:25fe:b0:47f:86af:8fe7 with SMTP id ffacd0b85a97d-482c0ba220fmr40350379f8f.22.1787588551089; Mon, 24 Aug 2026 09:22:31 -0700 (PDT) X-Received: by 2002:a05:6000:25fe:b0:47f:86af:8fe7 with SMTP id ffacd0b85a97d-482c0ba220fmr40350217f8f.22.1787588550537; Mon, 24 Aug 2026 09:22:30 -0700 (PDT) Received: from localhost (128.223.159.143.dyn.plus.net. [143.159.223.128]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-482c9b78ebfsm8748469f8f.12.2026.08.24.09.22.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 24 Aug 2026 09:22:28 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Simon Marchi , Andrew Burgess Subject: [PATCHv9 4/4] gdb: cache program space entry point information Date: Mon, 24 Aug 2026 17:22:19 +0100 Message-Id: <72b4024c8e485f7786dcb7c29aa2e6c6397d12aa.1787588386.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: l0pLASTVy0bBz__3BJZm6dEw-j-c1WEHioup0B_qsJw_1787588551 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, RCVD_IN_PBL, SPF_HELO_PASS, SPF_NONE, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@sourceware.org After the previous two patches, there are now two places where we check if a frame is an entry point frame, these are in get_prev_frame and frame_unwind_caller_frame. Both of these locations call inside_entry_func, which then calls program_space::get_entry_point_info. The calls to program_space::get_entry_point_info are not crazy expensive, but they are not free either, there are reads from target memory to read the auxv vector and the entry address offset, so on remote targets this could introduce a small delay. However, the result from program_space::get_entry_point_info is not expected to change from one call to the next. This information should be a property of the executable and libraries, so we really only need to figure it out once. This commit caches the entry point information within program_space, clearing it whenever an inferior starts or exits, whenever the executable is updated, or whenever the inferior execs. After clearing GDB will compute, and cache the updated information the next time it is needed, which will be whenever GDB needs to unwind a frame. It will be possible to observe this change by, for example, monitoring the remote target packets, but as far as the normal GDB output is concerned, there should be no user visible changes after this commit. --- gdb/progspace.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- gdb/progspace.h | 11 ++++++++++- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/gdb/progspace.c b/gdb/progspace.c index 2e0b6e4b825..b219acdf380 100644 --- a/gdb/progspace.c +++ b/gdb/progspace.c @@ -289,9 +289,12 @@ program_space::exec_entry_point_address () const /* See progspace.h. */ -entry_point_info +const entry_point_info & program_space::get_entry_point_info () const { + if (m_entry_point_info.has_value ()) + return m_entry_point_info.value (); + std::optional exec_entry_address = this->exec_entry_point_address_if_available (); @@ -299,8 +302,9 @@ program_space::get_entry_point_info () const if (m_solib_ops != nullptr) inferior_entry_address = m_solib_ops->inferior_entry_point_address (); - return entry_point_info (std::move (inferior_entry_address), - std::move (exec_entry_address)); + m_entry_point_info.emplace (std::move (inferior_entry_address), + std::move (exec_entry_address)); + return m_entry_point_info.value (); } /* Implement the 'maint info entry-address' command. */ @@ -538,11 +542,46 @@ program_space::clear_solib_cache () deleted_solibs.clear (); } +/* Clear cached entry point information in the program space of INF. */ + +static void +clear_cached_entry_point_info_for_inferior (inferior *inf) +{ + inf->pspace->clear_cached_entry_point_info (); +} + +/* Clear cached entry point information in the program space PSPACE. */ + +static void +clear_cached_entry_point_info_for_pspace (program_space *pspace, + bool /* reload */) +{ + pspace->clear_cached_entry_point_info (); +} + +/* Clear cached entry point information in the program space of EXEC_INF. */ + +static void +clear_cached_entry_point_info_after_exec (inferior *exec_inf, + inferior */* follow_inf */) +{ + exec_inf->pspace->clear_cached_entry_point_info (); +} + /* See progspace.h. */ void initialize_progspace () { + gdb::observers::inferior_created.attach + (clear_cached_entry_point_info_for_inferior, "program-space"); + gdb::observers::inferior_exit.attach + (clear_cached_entry_point_info_for_inferior, "program-space"); + gdb::observers::executable_changed.attach + (clear_cached_entry_point_info_for_pspace, "program-space"); + gdb::observers::inferior_execd.attach + (clear_cached_entry_point_info_after_exec, "program-space"); + add_cmd ("program-spaces", class_maintenance, maintenance_info_program_spaces_command, _("Info about currently known program spaces."), diff --git a/gdb/progspace.h b/gdb/progspace.h index 69e70a1de6a..12bf116eb99 100644 --- a/gdb/progspace.h +++ b/gdb/progspace.h @@ -370,7 +370,13 @@ struct program_space /* Return information about the entry point in the main executable, and the entry point for the inferior, which might be different from the main executable. */ - entry_point_info get_entry_point_info () const; + const entry_point_info &get_entry_point_info () const; + + /* Clear any cached entry point information. */ + void clear_cached_entry_point_info () + { + m_entry_point_info.reset (); + } /* If there is a valid and known entry point in the main executable of this program space, return it. Otherwise return an empty optional. */ @@ -463,6 +469,9 @@ struct program_space /* See `exec_filename`. */ gdb::unique_xmalloc_ptr m_exec_filename; + + /* Cached entry point information. See get_entry_point_info. */ + mutable std::optional m_entry_point_info; }; /* The list of all program spaces. There's always at least one. */