From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id gVTvHUb8Amr1WjMAWB0awg (envelope-from ) for ; Tue, 12 May 2026 06:09:10 -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=VQEbCrZo; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 7361C1E0C3; Tue, 12 May 2026 06:09:10 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.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,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.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 19F311E093 for ; Tue, 12 May 2026 06:09:08 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B07644BAE7EE for ; Tue, 12 May 2026 10:09:07 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B07644BAE7EE 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=VQEbCrZo Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id BA6CB4BA2E32 for ; Tue, 12 May 2026 10:07:42 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org BA6CB4BA2E32 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 BA6CB4BA2E32 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1778580463; cv=none; b=M3CytozOaPgZXAX5gqmcQ9UjExo4GuUir9nhCCgLHddtHVKhSHY6NkhVQITKzzONKun+LiEEVQmV4MdSS+zvYCZdXzzt1P7WCy76InVlNTmj7kLYYvw5uC/1AX1uS5iPo/8stvYBcZHm07EQxJqmiUt4AAKq2JnszBq3vUtqGLQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1778580463; c=relaxed/simple; bh=jAT7wFlEM8oVkekf8idFa5f/VY4pI2S6vym5Mkndq4A=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=k1A402ekH0bTI7IadMzyQIp0hPeGppNs+aodtDJ91nWIAw024zVzoBQH/E8/RhQyew4T7Z4PLEEuJHqYczYSBR+aWpmh9lMkiEaKqqeRg8MDqALZaLHEdk0f/WUVnqi7EeupJ6fAnrzIzqElMasXVqOHAgs7wI+xSOAWJJSFapI= 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=VQEbCrZo DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BA6CB4BA2E32 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1778580462; 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=VhjeoWK0d6dJ3G/bktfZN/pRLtYg7QW/cqdTjULgI6Q=; b=VQEbCrZo2VTmMjFwmZXzxZOCHG0hyyYzmXb3op7yQyWquDvbgVX9R+eEvfdPL0YGdwZLt1 oveo7WYiSdPLhCGjgP0Le5e7bkPVysKe17MT6V9Ccfz6H/X0bgZ7TmCUIl+oKqtgUQZ53T 9oQM1O5fkKLV+berLnyGNOJ9eJUP3dE= 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-318-W1uJqwyzNOeZsyi3CpZ-5A-1; Tue, 12 May 2026 06:07:39 -0400 X-MC-Unique: W1uJqwyzNOeZsyi3CpZ-5A-1 X-Mimecast-MFC-AGG-ID: W1uJqwyzNOeZsyi3CpZ-5A_1778580459 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-488c0120047so31467825e9.0 for ; Tue, 12 May 2026 03:07:39 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778580458; x=1779185258; 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; bh=VhjeoWK0d6dJ3G/bktfZN/pRLtYg7QW/cqdTjULgI6Q=; b=hP8Bi5JgyDwpdHZ3+uBndjvq4zn+pyGhKw12GBvJlBYOpfcvDNev2tUPVJFnmStXUj ZKg0Zopbvgfc1EJlN9d1ym3gCXM3BuzX7cblQJyJY+23I52R37PNMPhSVE2I3crVD2O6 hS1TfrAKPNEa1ege5LtYwHiyqP09WdsPVoff/ge7H9fnMyJiDAQE2hVZP+bQAYAgwyyX oUPxybDRT92Vc9i+07rCZi2PtOHW/YDK+mEWDvawCCd6RUvWCcgYR9cNlVj+B8/ajdTW GGcX/XprKgh+/jV8KpYkeGgjvmyutEKf1BeotGFufycXEd+mIEZwRkmojQp76H6/1ATt E87g== X-Gm-Message-State: AOJu0YxX01k4xiUZD7vF3ni8TVf0KFdWgRtB/8OT0J42suFVu2CO6ALV cxPEwfZp9ugMVlt2Kl/hcA+fiz5VhbrwgAHH/ErbJv/htAkUymRw2msaYFpvOBDHWV+IRlQW/US 0TSVqG0f7xRkkz0ibUfSZ9XBEDQ9z5gnjgpYvBmVhBkhLxEypiK+JclF44fKQuwuU/uEgNDQ/CL OBMJnHCBxZ09RF9UhjxxQwyILVuoNi0GNCvP5VTYWWJ8KlLPc= X-Gm-Gg: Acq92OEwfhauSKCz+CBnSeNPpm8e2vb2BY5MNqKsDzy4DtBmtGqwVHc2SOk6XENG6n/ b37UC9roLe4s73VgqO+SVf1g91TMGnLiN19oT8nWCqEkj0qAIdDZmELw3q3ab/bv1nJLHUXsnFY vyy+JxlKTa3oeidLDPyw1N1YOqkgjUiYCV6FH1wRy/f7iMhOgyhN3hLUaAnTzA35BUbVTgIFn9S fmdjHH03ezg4u3d+onRFrfOBllEoq25aIXeoW8fCimaf7YAdCqswKiigi5Aty7at4Mc4o6jmZfP yVekOOPehUmieZxhp70XciUo6pccd0o+0zWjH3t3JjW3AVEFHSTI1gAhKxA195uD0OOGNXF2NDA J3TU1WYUxlbDcC/vn X-Received: by 2002:a05:600c:4689:b0:48d:366:b962 with SMTP id 5b1f17b1804b1-48e8fe4b570mr35483245e9.6.1778580457987; Tue, 12 May 2026 03:07:37 -0700 (PDT) X-Received: by 2002:a05:600c:4689:b0:48d:366:b962 with SMTP id 5b1f17b1804b1-48e8fe4b570mr35482235e9.6.1778580457204; Tue, 12 May 2026 03:07:37 -0700 (PDT) Received: from localhost ([31.111.84.232]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48e9053f690sm37144685e9.7.2026.05.12.03.07.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 May 2026 03:07:36 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 3/6] gdb: fix coerce_pieced_ref for multi-piece values Date: Tue, 12 May 2026 11:07:24 +0100 Message-Id: <9d1e7f6587074e0b6effbda010cb643d897bb13b.1778579473.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: NSs19Aoge5Lv59MQAo78LJGlrnxckB3NTGZvwCH8TkQ_1778580459 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 Bug PR gdb/30693 describes a case where the following assertion can be triggered: ../../gdb/dwarf2/loc.c:2213: internal-error: value* coerce_pieced_ref(const value*): Assertion `closure->pieces.size () == 1' failed. The problem is that coerce_pieced_ref makes the following claim: /* gdb represents synthetic pointers as pieced values with a single piece. */ gdb_assert (closure != NULL); gdb_assert (closure->pieces.size () == 1); But this is not really true. If an aggregate type contains a synthetic pointer, then it is possible that the aggregate type will have a computed location consisting of multiple pieces. When GDB prints the fields of that aggregate type these fields are extracted by calling value::primitive_field. Within value::primitive_field the location of the field is set by calling value::set_component_location. When the parent value that holds the field has a computed location, the field value gains a reference to the parent value's closure, this can be seen in copy_pieced_value_closure in dwarf2/expr.c. What this means is that, if the aggregate value has a multi-piece computed location, then the synthetic pointer field will also have a reference to that same multi-piece computed location, even if there is really only a single piece that describes the synthetic pointer itself. Some parts of GDB are already aware of this. If we look at check_pieced_synthetic_pointer which implements the value::bits_synthetic_pointer function, you'll see that this function searches through all of the pieces to find the piece that covers the value we are looking for, it then checks if that piece is an implicit pointer location. But back in coerce_pieced_ref, after calling value::bits_synthetic_pointer, we still make the assertion that there will be just a single piece. Fix this by copying the search through all pieces logic into coerce_pieced_ref (see note on efficiency below). We now search through all the pieces looking for a piece that describes the location of the synthetic pointer, and we then use that piece to form the pointer's value. There are some assertions in the new code, these align with how check_pieced_synthetic_pointer operates. In addition, there is an error for the case where multiple pieces are used to describe the location of a synthetic pointer. This case is technically allowed by check_pieced_synthetic_pointer, but supporting this would require changes to indirect_synthetic_pointer, so I propose leaving that until we see such a case in the wild. On efficiency, you'll notice that check_pieced_synthetic_pointer performs a search through all the location pieces, and coerce_pieced_ref also has to search through the pieces. It would be nice if this could be avoided in order to avoid multiple searches. Currently though coerce_pieced_ref calls value->bits_synthetic_pointer, which is an API that should be agnostic to the underlying implementation, i.e. shouldn't need to know that the implementation is computed, so passing pieces back would be harder. Maybe coerce_pieced_ref could avoid the value::bits_synthetic_pointer call, and instead call check_pieced_synthetic_pointer directly, or some related helper function, and could get the pieces back that way. But this breaks the cleanly structured API that we currently have. For now I'm leaving things as they are. My assumption is that the number of pieces used to represent a value is pretty low, so the search is actually pretty cheap. There's a new test that uses the DWARF assembler to create a representative example of a multi-piece aggregate that contains a synthetic pointer member variable. This test triggers the assertion before this commit. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30693 Bug: https://bugzilla.redhat.com/show_bug.cgi?id=2467251 --- gdb/dwarf2/expr.c | 53 ++++++- .../gdb.dwarf2/multi-piece-primitive-field.c | 25 ++++ .../multi-piece-primitive-field.exp | 130 ++++++++++++++++++ 3 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.c create mode 100644 gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.exp diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c index af39ec08385..a554dd2c880 100644 --- a/gdb/dwarf2/expr.c +++ b/gdb/dwarf2/expr.c @@ -620,14 +620,55 @@ coerce_pieced_ref (const value *value) frame_info_ptr frame = get_selected_frame (_("No frame selected.")); - /* gdb represents synthetic pointers as pieced values with a single - piece. */ - gdb_assert (closure != NULL); - gdb_assert (closure->pieces.size () == 1); + gdb_assert (closure != nullptr); + + /* The value::bits_synthetic_pointer will return true if multiple + pieces are used to cover VALUE, so long as each piece is + DWARF_VALUE_IMPLICIT_POINTER. I guess maybe this is possible, but + we've not seen such a case in the wild yet. For now then we look + in CLOSURE for a single DWARF_VALUE_IMPLICIT_POINTER piece that + covers VALUE. */ + LONGEST bit_offset = TARGET_CHAR_BIT * (value->embedded_offset () + + value->offset ()); + if (value->bitsize ()) + bit_offset += value->bitpos (); + int bit_length = TARGET_CHAR_BIT * type->length (); + + const dwarf_expr_piece *piece = nullptr; + for (size_t i = 0; i < closure->pieces.size (); i++) + { + const dwarf_expr_piece *p = &closure->pieces[i]; + size_t this_size_bits = p->size; + + if (bit_offset >= this_size_bits) + { + bit_offset -= this_size_bits; + continue; + } + + /* value::bits_synthetic_pointer does allow for multiple + pieces to describe the location of a single synthetic + pointer, or for a synthetic pointer to not start at the + exact start of a piece, however, we don't currently + support this case. */ + if (bit_offset != 0 || bit_length != this_size_bits) + error (_("unsupported value-piece configuration " + "bit_offset = %s, bit_length = %d, this_size_bits = %s"), + plongest (bit_offset), bit_length, + pulongest (this_size_bits)); + + piece = p; + break; + } + + /* If value::bits_synthetic_pointer returned true then we should have + found a suitable piece. */ + gdb_assert (piece != nullptr); + gdb_assert (piece->location == DWARF_VALUE_IMPLICIT_POINTER); return indirect_synthetic_pointer - (closure->pieces[0].v.ptr.die_sect_off, - closure->pieces[0].v.ptr.offset, + (piece->v.ptr.die_sect_off, + piece->v.ptr.offset, closure->per_cu, closure->per_objfile, frame, type); } else diff --git a/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.c b/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.c new file mode 100644 index 00000000000..7ec6b26af49 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.c @@ -0,0 +1,25 @@ +/* Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 target_var = 42; + +int +main (void) +{ + asm ("main_label: .globl main_label"); + return 0; +} diff --git a/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.exp b/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.exp new file mode 100644 index 00000000000..6349d43d67e --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.exp @@ -0,0 +1,130 @@ +# 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 . + +# Setup a struct variable that is described by a multi-piece DWARF location. +# One of the struct's fields is a C++ reference type implemented via +# DW_OP_GNU_implicit_pointer. At one time, attempting to print such a field +# would trigger an assertion in GDB. +# +# The cause was that primitive_field extracts the reference field and +# set_component_location copies the parent's entire piece_closure (just +# incrementing its refcount). Then coerce_pieced_ref would assert +# closure->pieces.size() == 1, which failed because the closure still had +# all the parent's pieces. +# +# This test confirms that this issue has now been fixed. + +require allow_cplus_tests + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +standard_testfile .c .S + +set asm_file [standard_output_file ${srcfile2}] + +# First compile the C file only, so we can query some type sizes. +if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} { + return +} + +Dwarf::assemble ${asm_file} { + cu {} { + DW_TAG_compile_unit { + DW_AT_language @DW_LANG_C_plus_plus + } { + declare_labels int_label struct_label ref_type_label target_label + set int_size [get_sizeof "int" -1] + set addr_size [get_sizeof "void *" -1] + set struct_size [expr {$int_size + $addr_size}] + + int_label: DW_TAG_base_type { + DW_AT_byte_size ${int_size} DW_FORM_udata + DW_AT_encoding @DW_ATE_signed + DW_AT_name "int" + } + + ref_type_label: DW_TAG_reference_type { + DW_AT_byte_size ${addr_size} DW_FORM_udata + DW_AT_type :${int_label} + } + + struct_label: DW_TAG_structure_type { + DW_AT_name "S" + DW_AT_byte_size ${struct_size} DW_FORM_udata + } { + DW_TAG_member { + DW_AT_name "x" + DW_AT_type :${int_label} + DW_AT_data_member_location 0 DW_FORM_udata + } + + DW_TAG_member { + DW_AT_name "ref" + DW_AT_type :${ref_type_label} + DW_AT_data_member_location ${int_size} DW_FORM_udata + } + } + + target_label: DW_TAG_variable { + DW_AT_name "target_var" + DW_AT_type :${int_label} + DW_AT_external 1 DW_FORM_flag + DW_AT_location { + DW_OP_addr [gdb_target_symbol "target_var"] + } SPECIAL_expr + } + + DW_TAG_subprogram { + MACRO_AT_func { "main" } + DW_AT_type :${int_label} + DW_AT_external 1 DW_FORM_flag + } { + DW_TAG_variable { + DW_AT_name "s" + DW_AT_type :${struct_label} + DW_AT_location { + DW_OP_const4u 123 + DW_OP_stack_value + DW_OP_piece $int_size + DW_OP_GNU_implicit_pointer $target_label 0 + DW_OP_piece $addr_size + } SPECIAL_expr + } + } + } + } +} + +# Now compile both C source and generated DWARF assembly. +if {[prepare_for_testing "failed to prepare" ${testfile} \ + [list ${asm_file} ${srcfile}] {}]} { + return +} + +# Start the inferior so that 's' is in scope. +if {![runto_main]} { + return +} + +# Printing `s` used to crash with an assertion failure in coerce_pieced_ref +# because the ref field inherited the parent struct's multi-piece closure. +gdb_test "print s" " = {x = 123, ref = @$hex}" \ + "print multi-piece struct with implicit pointer reference field" + +gdb_test "print s.ref" " = \\(int &\\) @$hex: 42" \ + "print ref member of multi-piece struct" -- 2.25.4