public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Andrew Burgess <aburgess@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/gdb] gdb-17.2-rebase-f44: fix rhbz2467251 - multi piece computed locations and synthetic pointers
Date: Sun, 28 Jun 2026 00:02:23 GMT [thread overview]
Message-ID: <178260494312.1.13532735939363010268.rpms-gdb-0b14d14bbaae@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/gdb
Branch : gdb-17.2-rebase-f44
Commit : 0b14d14bbaae07b5cc3ebd1c8237c3e6e4f5cc9d
Author : Andrew Burgess <aburgess@redhat.com>
Date : 2026-05-16T14:57:54+01:00
Stats : +1244/-1 in 5 file(s)
URL : https://src.fedoraproject.org/rpms/gdb/c/0b14d14bbaae07b5cc3ebd1c8237c3e6e4f5cc9d?branch=gdb-17.2-rebase-f44
Log:
fix rhbz2467251 - multi piece computed locations and synthetic pointers
Backport the following upstream commits in order to address RHBZ
2467251: d980317c7f1, 958d06262a7, 7d1d7386561, 8915de0883c,
8f65ab7b71f. These commits will all drop out when we rebase to GDB
18.
---
diff --git a/_gdb.spec.Patch.include b/_gdb.spec.Patch.include
index 1f9e060..be9d97c 100644
--- a/_gdb.spec.Patch.include
+++ b/_gdb.spec.Patch.include
@@ -62,3 +62,9 @@ Patch012: gdb-rhbz2366461-bad-solib-entry-addr.patch
# (Tom de Vries)
Patch013: gdb-fix-testsuite-newer-tcl.patch
+# Backport the following upstream commits in order to address RHBZ
+# 2467251: d980317c7f1, 958d06262a7, 7d1d7386561, 8915de0883c,
+# 8f65ab7b71f. These commits will all drop out when we rebase to GDB
+# 18.
+Patch014: gdb-rhbz2467251-computed-location-synthetic-pointers.patch
+
diff --git a/_gdb.spec.patch.include b/_gdb.spec.patch.include
index cb13330..e7e96e2 100644
--- a/_gdb.spec.patch.include
+++ b/_gdb.spec.patch.include
@@ -11,3 +11,4 @@
%patch -p1 -P011
%patch -p1 -P012
%patch -p1 -P013
+%patch -p1 -P014
diff --git a/_patch_order b/_patch_order
index eb933e5..2c8892c 100644
--- a/_patch_order
+++ b/_patch_order
@@ -11,3 +11,4 @@ gdb-fileio-test-fixes.patch
gdb-rhbz2366461-missing-thread.patch
gdb-rhbz2366461-bad-solib-entry-addr.patch
gdb-fix-testsuite-newer-tcl.patch
+gdb-rhbz2467251-computed-location-synthetic-pointers.patch
diff --git a/gdb-rhbz2467251-computed-location-synthetic-pointers.patch b/gdb-rhbz2467251-computed-location-synthetic-pointers.patch
new file mode 100644
index 0000000..f2c1e0a
--- /dev/null
+++ b/gdb-rhbz2467251-computed-location-synthetic-pointers.patch
@@ -0,0 +1,1229 @@
+From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
+From: Andrew Burgess <aburgess@redhat.com>
+Date: Fri, 8 May 2026 21:44:12 +0100
+Subject: gdb-rhbz2467251-computed-location-synthetic-pointers.patch
+
+;; Backport the following upstream commits in order to address RHBZ
+;; 2467251: d980317c7f1, 958d06262a7, 7d1d7386561, 8915de0883c,
+;; 8f65ab7b71f. These commits will all drop out when we rebase to GDB
+;; 18.
+
+*** Commit d980317c7f1: gdb: int to bool conversion in valprint.{c,}h
+
+gdb: int to bool conversion in valprint.{c,h}
+
+Some int to bool conversion in valprint.c and valprint.h. I also
+moved the header comment on val_print_scalar_type_p into the header.
+
+There should be no user visible changes after this commit.
+
+Approved-By: Tom Tromey <tom@tromey.com>
+
+Commit 958d06262a7: gdb: fix coerce_pieced_ref for multi-piece values
+
+gdb: fix coerce_pieced_ref for multi-piece values
+
+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
+
+Approved-By: Tom Tromey <tom@tromey.com>
+
+Commit 7d1d7386561: gdb: rename argument in valprint_check_validity
+
+gdb: rename argument in valprint_check_validity
+
+To understand the motivation for this commit you'll need to look ahead
+two commits in this series for an upcoming bug fix.
+
+Look at coerce_pieced_ref in dwarf2/expr.c, and this call:
+
+ if (value->bits_synthetic_pointer (value->embedded_offset (),
+ TARGET_CHAR_BIT * type->length ()))
+ { ... }
+
+This passes the result from value::embedded_offset as the first argument.
+Though it's not clear from value.h, the number returned by
+value::embedded_offset is a byte offset.
+
+The value::bits_synthetic_pointer call can end up calling
+check_pieced_synthetic_pointer, with the first argument to
+value::bits_synthetic_pointer becoming the second argument to
+check_pieced_synthetic_pointer. The second argument to
+check_pieced_synthetic_pointer is called BIT_OFFSET, a value in bits.
+Looking at how this argument is used confirms that it is expected to be
+a value in bits, not bytes.
+
+The fix should be easy, multiply the embedded offset by
+TARGET_CHAR_BIT, but I think things are a little more complex than
+this.
+
+I started looking at how value::bits_synthetic_pointer is used, there
+are only 7 uses, and they can be grouped as follows:
+
+gdb/dwarf2/expr.c
+gdb/valops.c
+
+ These call value::embedded_offset directly within the call to
+ value::bits_synthetic_pointer.
+
+gdb/opencl-lang.c
+
+ This function is used as a wrapper that implements a
+ lval_funcs::check_synthetic_pointer callback, it adjusts the
+ arguments and then calls value::bits_synthetic_pointer. As such we'd
+ not expect to see embedded offset mentioned here as that would (we
+ assume) be handled by the caller.
+
+gdb/valprint.c (in generic_val_print_ref)
+
+ This call passes an argument called 'embedded_offset', but the
+ function generic_val_print_ref is only used in one place, and the
+ embedded_offset is always passed as zero.
+
+gdb/valprint.c (in valprint_check_validity)
+
+ This call also passes an argument called 'embedded_offset'. This
+ function is used in two places. In common_val_print the
+ embedded_offset is always passed as zero, but in
+ valprint_check_validity (in cp-valprint.c) the embedded_offset being
+ passed is the offset of a field within the value, not the value's
+ embedded_offset within some larger value.
+
+gdb/cp-valprint.c
+gdb/p-valprint.c
+
+ As with the call to valprint_check_validity in cp-valprint.c, these
+ two direct calls to value::bits_synthetic_pointer pass the offset of a
+ field within the value, rather than the value's embedded_offset.
+
+What I see in the above is some confusion. In some places we are
+passing the value::embedded_offset, while in other places we are
+passing the offset of a field within the value itself.
+
+If we consider the direct call to value::bits_synthetic_pointer in
+gdb/cp-valprint.c, where a field offset is passed, then it should be
+possible, that if we can create an object with a non-zero
+embedded_offset (which isn't accounted for in this code path), then we
+should see some bugs in GDB, and indeed, this is what I do see.
+
+My plan for fixing this is to have the offset passed to
+value::bits_synthetic_pointer always be a field offset within the value,
+the value::embedded_offset will then be handled directly within the
+various value::bits_synthetic_pointer implementations.
+
+This commit is a small refactor in preparation for this fix.
+
+I believe part of the confusion here is that we have functions that
+take arguments called embedded_offset, when the value they should
+accept is no longer the embedded offset.
+
+So in this commit I propose renaming the embedded_offset argument to
+field_byte_offset in valprint_check_validity. This is purely a
+mechanical rename, there should be no user visible changes after this
+commit.
+
+Approved-By: Tom Tromey <tom@tromey.com>
+
+Commit 8915de0883c: gdb: remove embedded_offset argument that is always 0
+
+gdb: remove embedded_offset argument that is always 0
+
+For the background and motivation for this patch you should read the
+previous commit.
+
+The goal of this commit started as similar to the previous one; rename
+the embedded_offset argument to generic_val_print_ref. To do this I
+started tracing back which values are passed into this function so I
+could make sure the new argument name matched the usage.
+
+But it turns out that the only value that is ever passed to this
+function is zero.
+
+I believe that, like the last commit, the embedded_offset is actually
+representing the offset of a field within a value. However, in all of
+the use cases, the "field" being accessed is the entire value, hence
+why we always pass 0, we are asking about the whole value starting
+from the very beginning.
+
+Given this, I couldn't bring myself to rename the argument. Let's just
+remove it. It turns out that there's a bunch of functions in
+valprint.c that take an argument called embedded_offset, which are
+always zero.
+
+This commit removes the argument, and updates the code to assume zero.
+
+There should be no user visible changes after this commit.
+
+Approved-By: Tom Tromey <tom@tromey.com>
+
+Commit 8f65ab7b71f: gdb: use value::embedded_offset in check_pieced_synthetic_pointer
+
+gdb: use value::embedded_offset in check_pieced_synthetic_pointer
+
+This commit builds on the previous two commits. You should go and
+read them both for context.
+
+Looking at the users of value::bits_synthetic_pointer, all callers but
+two are now passing either the offset of a field within the value, or
+hard-coded zero as they want to ask about the entire value.
+
+The two exceptions are in coerce_pieced_ref (in dwarf2/expr.c) and in
+value_addr (in valops.c) where we still pass value::embedded_offset.
+
+The problem is that some of the other callers, where we currently pass
+just a field offset, might also have a non-zero embedded offset,
+specifically, the call in cp_print_value_fields is in this category.
+In cp_print_value_fields we are printing fields from a value which is
+potentially a base class contained within an instance of a derived
+class, this will be represented by a non-zero embedded_offset, which
+is currently not taken into account.
+
+Additionally, the two callers that currently pass the
+value::embedded_offset don't scale the embedded offset from bytes to
+bits.
+
+The failure to scale from bytes to bits is interesting, I originally
+tried to create some tests that exposed this, but constantly failed.
+It turns out, that in both these locations, in all code paths that I
+could find, the value::embedded_offset will always be zero. For
+example in coerce_pieced_ref, a reference cannot be a base class, and
+so we never expect to see a non-zero embedded_offset in this case.
+
+Similarly, in value_addr, the use of value::bits_synthetic_pointer is
+within a block that only applies to reference types, which means the
+embedded_offset will be zero.
+
+Now within check_pieced_synthetic_pointer we already add the
+value::offset into the bit_offset which is passed in. Remember, after
+the previous two commits, the incoming bit_offset is (almost) always
+the offset of a field within the value. The almost here is the two
+cases mentioned above.
+
+I propose that within check_pieced_synthetic_pointer we should take
+into account both value::offset and value::embedded_offset. Then the
+two cases that currently pass value::embedded_offset can be changed to
+just pass zero.
+
+With this done, and the previous two commits, we now have a consistent
+model. value::bits_synthetic_pointer expects the (bit) offset of a
+field within the value to check. In many cases this will be zero
+meaning we want to check from the start of the value, but in some
+cases it can be non-zero.
+
+Then within value::bits_synthetic_pointer implementations, like
+check_pieced_synthetic_pointer, we will take the value::offset and
+value::embedded_offset into account, remembering to scale them from
+bytes to bits.
+
+I have also changed indirect_pieced_value to also take the
+embedded_offset into account. I have done this for consistency rather
+than necessity. I believe that the embedded_offset will always be
+zero within indirect_pieced_value. The indirect_pieced_value function
+is only called from value_ind (in valops.c), and only operates on
+TYPE_CODE_PTR types (checked for in indirect_pieced_value). As a
+TYPE_CODE_PTR cannot be the base class for a derived type, then we
+don't expect to ever see a TYPE_CODE_PTR value with a non-zero
+embedded_offset. But, having indirect_pieced_value take the embedded
+offset into account is simple enough, and future proofs the code.
+
+In both check_pieced_synthetic_pointer and indirect_pieced_value I
+have changed uses of '8' to 'TARGET_CHAR_BIT', I was already touching
+some of these lines, and I think TARGET_CHAR_BIT is clearer, but one
+line in indirect_pieced_value was just updated to use TARGET_CHAR_BIT,
+this is done for consistency.
+
+The gdb.dwarf2/multi-piece-inherited-bitfield.exp test fails without
+this patch, this exposes the case where the embedded_offset is
+non-zero and we were previously failing to take this into account.
+
+The gdb.dwarf2/multi-piece-primitive-field.exp test was something I
+wrote while trying to exercise the coerce_pieced_ref code path some
+more. It is an inheritance based version of the existing test, I was
+wondering if this would result in a value with a non-zero
+embedded_offset, but due to how the fields are extracted from the
+aggregate prior to calling coerce_pieced_ref the embedded_offset is
+always zero in this function.
+
+Approved-By: Tom Tromey <tom@tromey.com>
+
+diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
+--- a/gdb/dwarf2/expr.c
++++ b/gdb/dwarf2/expr.c
+@@ -492,7 +492,8 @@ check_pieced_synthetic_pointer (const value *value, LONGEST bit_offset,
+ piece_closure *c = (piece_closure *) value->computed_closure ();
+ int i;
+
+- bit_offset += 8 * value->offset ();
++ bit_offset += (TARGET_CHAR_BIT
++ * (value->offset () + value->embedded_offset ()));
+ if (value->bitsize ())
+ bit_offset += value->bitpos ();
+
+@@ -537,8 +538,9 @@ indirect_pieced_value (value *value)
+ if (type->code () != TYPE_CODE_PTR)
+ return NULL;
+
+- int bit_length = 8 * type->length ();
+- LONGEST bit_offset = 8 * value->offset ();
++ int bit_length = TARGET_CHAR_BIT * type->length ();
++ LONGEST bit_offset
++ = (TARGET_CHAR_BIT * (value->offset () + value->embedded_offset ()));
+ if (value->bitsize ())
+ bit_offset += value->bitpos ();
+
+@@ -602,22 +604,62 @@ coerce_pieced_ref (const value *value)
+ {
+ struct type *type = check_typedef (value->type ());
+
+- if (value->bits_synthetic_pointer (value->embedded_offset (),
+- TARGET_CHAR_BIT * type->length ()))
++ if (value->bits_synthetic_pointer (0, TARGET_CHAR_BIT * type->length ()))
+ {
+ const piece_closure *closure
+ = (piece_closure *) value->computed_closure ();
+ 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-inherited-bitfield.c b/gdb/testsuite/gdb.dwarf2/multi-piece-inherited-bitfield.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.dwarf2/multi-piece-inherited-bitfield.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 <http://www.gnu.org/licenses/>. */
++
++int target_var = 42;
++
++int
++main (void)
++{
++ asm ("main_label: .globl main_label");
++ return 0;
++}
+diff --git a/gdb/testsuite/gdb.dwarf2/multi-piece-inherited-bitfield.exp b/gdb/testsuite/gdb.dwarf2/multi-piece-inherited-bitfield.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.dwarf2/multi-piece-inherited-bitfield.exp
+@@ -0,0 +1,151 @@
++# 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 <http://www.gnu.org/licenses/>.
++
++# Setup a derived struct that uses multiple inheritance and is described
++# by a multi-piece DWARF location. The second base class contains a
++# bitfield member. At one time, printing such a struct would incorrectly
++# display the bitfield as <synthetic pointer>.
++#
++# The cause was that check_pieced_synthetic_pointer (dwarf2/expr.c)
++# computes the bit offset as 8 * value->offset() but does not add
++# 8 * value->embedded_offset(). When a base subobject is extracted via
++# primitive_field, it gets a non-zero embedded_offset but offset remains
++# zero. check_pieced_synthetic_pointer then checks piece 0 instead of
++# the correct piece, and if piece 0 happens to be an implicit pointer,
++# it incorrectly reports the bitfield as a synthetic pointer.
++
++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 char_label int_label first_label second_label \
++ derived_label target_label
++ set int_size [get_sizeof "int" -1]
++
++ char_label: DW_TAG_base_type {
++ {DW_AT_byte_size 1 DW_FORM_udata}
++ {DW_AT_encoding @DW_ATE_unsigned_char}
++ {DW_AT_name "char"}
++ }
++
++ 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"}
++ }
++
++ first_label: DW_TAG_structure_type {
++ {DW_AT_name "First"}
++ {DW_AT_byte_size 1 DW_FORM_udata}
++ } {
++ DW_TAG_member {
++ {DW_AT_name "a"}
++ {DW_AT_type :${char_label}}
++ {DW_AT_data_member_location 0 DW_FORM_udata}
++ }
++ }
++
++ second_label: DW_TAG_structure_type {
++ {DW_AT_name "Second"}
++ {DW_AT_byte_size 1 DW_FORM_udata}
++ } {
++ DW_TAG_member {
++ {DW_AT_name "bf"}
++ {DW_AT_type :${int_label}}
++ {DW_AT_data_member_location 0 DW_FORM_udata}
++ {DW_AT_bit_size 8 DW_FORM_udata}
++ }
++ }
++
++ derived_label: DW_TAG_structure_type {
++ {DW_AT_name "Derived"}
++ {DW_AT_byte_size 2 DW_FORM_udata}
++ } {
++ DW_TAG_inheritance {
++ {DW_AT_type :${first_label}}
++ {DW_AT_data_member_location 0 DW_FORM_udata}
++ }
++
++ DW_TAG_inheritance {
++ {DW_AT_type :${second_label}}
++ {DW_AT_data_member_location 1 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 "d"}
++ {DW_AT_type :${derived_label}}
++ {DW_AT_location {
++ DW_OP_GNU_implicit_pointer $target_label 0
++ DW_OP_piece 1
++ DW_OP_const1u 42
++ DW_OP_stack_value
++ DW_OP_piece 1
++ } 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 'd' is in scope.
++if {![runto_main]} {
++ return
++}
++
++# With the bug in check_pieced_synthetic_pointer, the bitfield bf in the
++# Second base subobject is incorrectly reported as <synthetic pointer>.
++# This happens because check_pieced_synthetic_pointer does not account for
++# embedded_offset, so it checks piece 0 (the implicit pointer covering
++# First) instead of piece 1 (the stack value covering Second).
++gdb_test "print d" " = {<First> = {a = <synthetic pointer>}, <Second> = {bf = 42}, <No data fields>}" \
++ "bitfield in base subobject is not synthetic pointer"
+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
+--- /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 <http://www.gnu.org/licenses/>. */
++
++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
+--- /dev/null
++++ b/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.exp
+@@ -0,0 +1,190 @@
++# 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 <http://www.gnu.org/licenses/>.
++
++# 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 \
++ first_label second_label derived_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}
++ }
++ }
++
++ first_label: DW_TAG_structure_type {
++ {DW_AT_name "first"}
++ {DW_AT_byte_size ${int_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}
++ }
++ }
++
++ second_label: DW_TAG_structure_type {
++ {DW_AT_name "second"}
++ {DW_AT_byte_size ${addr_size} DW_FORM_udata}
++ } {
++ DW_TAG_member {
++ {DW_AT_name "ref"}
++ {DW_AT_type :${ref_type_label}}
++ {DW_AT_data_member_location 0 DW_FORM_udata}
++ }
++ }
++
++ derived_label: DW_TAG_structure_type {
++ {DW_AT_name "derived"}
++ {DW_AT_byte_size ${struct_size} DW_FORM_udata}
++ } {
++ DW_TAG_inheritance {
++ {DW_AT_type :${first_label}}
++ {DW_AT_data_member_location 0 DW_FORM_udata}
++ }
++
++ DW_TAG_inheritance {
++ {DW_AT_type :${second_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}
++ }
++
++ DW_TAG_variable {
++ {DW_AT_name "d"}
++ {DW_AT_type :${derived_label}}
++ {DW_AT_location {
++ DW_OP_const4u 456
++ 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"
++
++# Same test but with inheritance. The reference field is in the
++# second base class, so it reaches coerce_pieced_ref through a
++# different path to the above case.
++gdb_test "print d" \
++ " = {<first> = {x = 456}, <second> = {ref = @$hex}, <No data fields>}" \
++ "print inherited multi-piece struct with implicit pointer ref"
++
++gdb_test "print d.ref" " = \\(int &\\) @$hex: 42" \
++ "print ref member of inherited multi-piece struct"
+diff --git a/gdb/valops.c b/gdb/valops.c
+--- a/gdb/valops.c
++++ b/gdb/valops.c
+@@ -1545,8 +1545,7 @@ value_addr (struct value *arg1)
+
+ if (TYPE_IS_REFERENCE (type))
+ {
+- if (arg1->bits_synthetic_pointer (arg1->embedded_offset (),
+- TARGET_CHAR_BIT * type->length ()))
++ if (arg1->bits_synthetic_pointer (0, TARGET_CHAR_BIT * type->length ()))
+ arg1 = coerce_ref (arg1);
+ else
+ {
+diff --git a/gdb/valprint.c b/gdb/valprint.c
+--- a/gdb/valprint.c
++++ b/gdb/valprint.c
+@@ -92,7 +92,6 @@ static void set_output_radix_1 (int, unsigned);
+
+ static void val_print_type_code_flags (struct type *type,
+ struct value *original_value,
+- int embedded_offset,
+ struct ui_file *stream);
+
+ /* Start print_max at this value. */
+@@ -310,11 +309,9 @@ show_symbol_print (struct ui_file *file, int from_tty,
+
+ \f
+
+-/* A helper function for val_print. When printing in "summary" mode,
+- we want to print scalar arguments, but not aggregate arguments.
+- This function distinguishes between the two. */
++/* See valprint.h. */
+
+-int
++bool
+ val_print_scalar_type_p (struct type *type)
+ {
+ type = check_typedef (type);
+@@ -330,9 +327,9 @@ val_print_scalar_type_p (struct type *type)
+ case TYPE_CODE_UNION:
+ case TYPE_CODE_SET:
+ case TYPE_CODE_STRING:
+- return 0;
++ return false;
+ default:
+- return 1;
++ return true;
+ }
+ }
+
+@@ -350,10 +347,10 @@ val_print_scalar_or_string_type_p (struct type *type,
+
+ /* See valprint.h. */
+
+-int
++bool
+ valprint_check_validity (struct ui_file *stream,
+ struct type *type,
+- LONGEST embedded_offset,
++ LONGEST field_byte_offset,
+ const struct value *val)
+ {
+ type = check_typedef (type);
+@@ -361,31 +358,31 @@ valprint_check_validity (struct ui_file *stream,
+ if (type_not_associated (type))
+ {
+ val_print_not_associated (stream);
+- return 0;
++ return false;
+ }
+
+ if (type_not_allocated (type))
+ {
+ val_print_not_allocated (stream);
+- return 0;
++ return false;
+ }
+
+ if (type->code () != TYPE_CODE_UNION
+ && type->code () != TYPE_CODE_STRUCT
+ && type->code () != TYPE_CODE_ARRAY)
+ {
+- if (val->bits_any_optimized_out (TARGET_CHAR_BIT * embedded_offset,
++ if (val->bits_any_optimized_out (TARGET_CHAR_BIT * field_byte_offset,
+ TARGET_CHAR_BIT * type->length ()))
+ {
+ val_print_optimized_out (val, stream);
+- return 0;
++ return false;
+ }
+
+- if (val->bits_synthetic_pointer (TARGET_CHAR_BIT * embedded_offset,
++ if (val->bits_synthetic_pointer (TARGET_CHAR_BIT * field_byte_offset,
+ TARGET_CHAR_BIT * type->length ()))
+ {
+- const int is_ref = type->code () == TYPE_CODE_REF;
+- int ref_is_addressable = 0;
++ const bool is_ref = type->code () == TYPE_CODE_REF;
++ bool ref_is_addressable = false;
+
+ if (is_ref)
+ {
+@@ -403,14 +400,14 @@ valprint_check_validity (struct ui_file *stream,
+ return is_ref;
+ }
+
+- if (!val->bytes_available (embedded_offset, type->length ()))
++ if (!val->bytes_available (field_byte_offset, type->length ()))
+ {
+ val_print_unavailable (stream);
+- return 0;
++ return false;
+ }
+ }
+
+- return 1;
++ return true;
+ }
+
+ void
+@@ -595,14 +592,13 @@ generic_value_print_ptr (struct value *val, struct ui_file *stream,
+
+ static void
+ print_ref_address (struct type *type, const gdb_byte *address_buffer,
+- int embedded_offset, struct ui_file *stream)
++ struct ui_file *stream)
+ {
+ struct gdbarch *gdbarch = type->arch ();
+
+ if (address_buffer != NULL)
+ {
+- CORE_ADDR address
+- = extract_typed_address (address_buffer + embedded_offset, type);
++ CORE_ADDR address = extract_typed_address (address_buffer, type);
+
+ gdb_printf (stream, "@");
+ gdb_puts (paddress (gdbarch, address), stream);
+@@ -631,32 +627,27 @@ get_value_addr_contents (struct value *deref_val)
+
+ static void
+ generic_val_print_ref (struct type *type,
+- int embedded_offset, struct ui_file *stream, int recurse,
++ struct ui_file *stream, int recurse,
+ struct value *original_value,
+ const struct value_print_options *options)
+ {
+ struct type *elttype = check_typedef (type->target_type ());
+ struct value *deref_val = NULL;
+ const bool value_is_synthetic
+- = original_value->bits_synthetic_pointer (TARGET_CHAR_BIT * embedded_offset,
+- TARGET_CHAR_BIT * type->length ());
+- const int must_coerce_ref = ((options->addressprint && value_is_synthetic)
+- || options->deref_ref);
+- const int type_is_defined = elttype->code () != TYPE_CODE_UNDEF;
++ = original_value->bits_synthetic_pointer (0, (TARGET_CHAR_BIT
++ * type->length ()));
++ const bool must_coerce_ref = ((options->addressprint && value_is_synthetic)
++ || options->deref_ref);
++ const bool type_is_defined = elttype->code () != TYPE_CODE_UNDEF;
+ const gdb_byte *valaddr = original_value->contents_for_printing ().data ();
+
+ if (must_coerce_ref && type_is_defined)
+ {
+ deref_val = coerce_ref_if_computed (original_value);
+
+- if (deref_val != NULL)
+- {
+- /* More complicated computed references are not supported. */
+- gdb_assert (embedded_offset == 0);
+- }
+- else
++ if (deref_val == nullptr)
+ deref_val = value_at (type->target_type (),
+- unpack_pointer (type, valaddr + embedded_offset));
++ unpack_pointer (type, valaddr));
+ }
+ /* Else, original_value isn't a synthetic reference or we don't have to print
+ the reference's contents.
+@@ -676,7 +667,7 @@ generic_val_print_ref (struct type *type,
+ ? get_value_addr_contents (deref_val)
+ : valaddr);
+
+- print_ref_address (type, address, embedded_offset, stream);
++ print_ref_address (type, address, stream);
+
+ if (options->deref_ref)
+ gdb_puts (": ", stream);
+@@ -718,7 +709,7 @@ generic_val_print_enum_1 (struct type *type, LONGEST val,
+ }
+ else if (type->is_flag_enum ())
+ {
+- int first = 1;
++ bool first = true;
+
+ /* We have a "flag" enum, so we try to decompose it into pieces as
+ appropriate. The enum may have multiple enumerators representing
+@@ -738,7 +729,7 @@ generic_val_print_enum_1 (struct type *type, LONGEST val,
+ if (first)
+ {
+ gdb_puts ("(", stream);
+- first = 0;
++ first = false;
+ }
+ else
+ gdb_puts (" | ", stream);
+@@ -780,20 +771,15 @@ generic_val_print_enum_1 (struct type *type, LONGEST val,
+ /* generic_val_print helper for TYPE_CODE_ENUM. */
+
+ static void
+-generic_val_print_enum (struct type *type,
+- int embedded_offset, struct ui_file *stream,
++generic_val_print_enum (struct type *type, struct ui_file *stream,
+ struct value *original_value,
+ const struct value_print_options *options)
+ {
+- LONGEST val;
+- struct gdbarch *gdbarch = type->arch ();
+- int unit_size = gdbarch_addressable_memory_unit_size (gdbarch);
+-
+ gdb_assert (!options->format);
+
+ const gdb_byte *valaddr = original_value->contents_for_printing ().data ();
+
+- val = unpack_long (type, valaddr + embedded_offset * unit_size);
++ LONGEST val = unpack_long (type, valaddr);
+
+ generic_val_print_enum_1 (type, val, stream);
+ }
+@@ -801,8 +787,7 @@ generic_val_print_enum (struct type *type,
+ /* generic_val_print helper for TYPE_CODE_FUNC and TYPE_CODE_METHOD. */
+
+ static void
+-generic_val_print_func (struct type *type,
+- int embedded_offset, CORE_ADDR address,
++generic_val_print_func (struct type *type, CORE_ADDR address,
+ struct ui_file *stream,
+ struct value *original_value,
+ const struct value_print_options *options)
+@@ -1015,7 +1000,7 @@ generic_value_print (struct value *val, struct ui_file *stream, int recurse,
+
+ case TYPE_CODE_REF:
+ case TYPE_CODE_RVALUE_REF:
+- generic_val_print_ref (type, 0, stream, recurse,
++ generic_val_print_ref (type, stream, recurse,
+ val, options);
+ break;
+
+@@ -1023,14 +1008,14 @@ generic_value_print (struct value *val, struct ui_file *stream, int recurse,
+ if (options->format)
+ value_print_scalar_formatted (val, options, 0, stream);
+ else
+- generic_val_print_enum (type, 0, stream, val, options);
++ generic_val_print_enum (type, stream, val, options);
+ break;
+
+ case TYPE_CODE_FLAGS:
+ if (options->format)
+ value_print_scalar_formatted (val, options, 0, stream);
+ else
+- val_print_type_code_flags (type, val, 0, stream);
++ val_print_type_code_flags (type, val, stream);
+ break;
+
+ case TYPE_CODE_FUNC:
+@@ -1038,7 +1023,7 @@ generic_value_print (struct value *val, struct ui_file *stream, int recurse,
+ if (options->format)
+ value_print_scalar_formatted (val, options, 0, stream);
+ else
+- generic_val_print_func (type, 0, val->address (), stream,
++ generic_val_print_func (type, val->address (), stream,
+ val, options);
+ break;
+
+@@ -1190,11 +1175,11 @@ val_print_check_max_depth (struct ui_file *stream, int recurse,
+ return false;
+ }
+
+-/* Check whether the value VAL is printable. Return 1 if it is;
+- return 0 and print an appropriate error message to STREAM according to
+- OPTIONS if it is not. */
++/* Check whether the value VAL is printable. Return true if it is;
++ return false and print an appropriate error message to STREAM
++ according to OPTIONS if it is not. */
+
+-static int
++static bool
+ value_check_printable (struct value *val, struct ui_file *stream,
+ const struct value_print_options *options)
+ {
+@@ -1202,7 +1187,7 @@ value_check_printable (struct value *val, struct ui_file *stream,
+ {
+ fprintf_styled (stream, metadata_style.style (),
+ _("<address of value unknown>"));
+- return 0;
++ return false;
+ }
+
+ if (val->entirely_optimized_out ())
+@@ -1211,7 +1196,7 @@ value_check_printable (struct value *val, struct ui_file *stream,
+ gdb_printf (stream, "...");
+ else
+ val_print_optimized_out (val, stream);
+- return 0;
++ return false;
+ }
+
+ if (val->entirely_unavailable ())
+@@ -1220,7 +1205,7 @@ value_check_printable (struct value *val, struct ui_file *stream,
+ gdb_printf (stream, "...");
+ else
+ val_print_unavailable (stream);
+- return 0;
++ return false;
+ }
+
+ if (val->type ()->code () == TYPE_CODE_INTERNAL_FUNCTION)
+@@ -1228,16 +1213,16 @@ value_check_printable (struct value *val, struct ui_file *stream,
+ fprintf_styled (stream, metadata_style.style (),
+ _("<internal function %s>"),
+ value_internal_function_name (val));
+- return 0;
++ return false;
+ }
+
+ if (type_not_allocated (val->type ()))
+ {
+ val_print_not_allocated (stream);
+- return 0;
++ return false;
+ }
+
+- return 1;
++ return true;
+ }
+
+ /* See valprint.h. */
+@@ -1292,10 +1277,9 @@ debug_val (struct value *val)
+
+ static void
+ val_print_type_code_flags (struct type *type, struct value *original_value,
+- int embedded_offset, struct ui_file *stream)
++ struct ui_file *stream)
+ {
+- const gdb_byte *valaddr = (original_value->contents_for_printing ().data ()
+- + embedded_offset);
++ const gdb_byte *valaddr = (original_value->contents_for_printing ().data ());
+ ULONGEST val = unpack_long (type, valaddr);
+ int field, nfields = type->num_fields ();
+ struct gdbarch *gdbarch = type->arch ();
+@@ -3188,7 +3172,7 @@ test_print_flags (gdbarch *arch)
+ store_unsigned_integer (contents, 4, gdbarch_byte_order (arch), 0xaa);
+
+ string_file out;
+- val_print_type_code_flags (flags_type, val, 0, &out);
++ val_print_type_code_flags (flags_type, val, &out);
+ SELF_CHECK (out.string () == "[ A=2 B=1 C=5 ]");
+ }
+
+diff --git a/gdb/valprint.h b/gdb/valprint.h
+--- a/gdb/valprint.h
++++ b/gdb/valprint.h
+@@ -197,17 +197,15 @@ extern void print_function_pointer_address (const struct value_print_options *op
+
+ /* Helper function to check the validity of some bits of a value.
+
+- If TYPE represents some aggregate type (e.g., a structure), return 1.
++ If TYPE represents some aggregate type (e.g., a structure), return true.
+
+- Otherwise, any of the bytes starting at OFFSET and extending for
+- TYPE->length () bytes are invalid, print a message to STREAM and
+- return 0. The checking is done using FUNCS.
++ For non-aggregate TYPEs, if any of the bytes starting at FIELD_BYTE_OFFSET
++ and extending for TYPE->length () bytes are invalid, print a message to
++ STREAM and return false. Otherwise, return true. */
+
+- Otherwise, return 1. */
+-
+-extern int valprint_check_validity (struct ui_file *stream, struct type *type,
+- LONGEST embedded_offset,
+- const struct value *val);
++extern bool valprint_check_validity (struct ui_file *stream, struct type *type,
++ LONGEST field_byte_offset,
++ const struct value *val);
+
+ extern void val_print_optimized_out (const struct value *val,
+ struct ui_file *stream);
+@@ -272,7 +270,11 @@ extern void generic_printstr (struct ui_file *stream, struct type *type,
+
+ extern void output_command (const char *args, int from_tty);
+
+-extern int val_print_scalar_type_p (struct type *type);
++/* When printing in "summary" mode we want to print scalar arguments
++ but not aggregate arguments. Return true if TYPE is a scalar type,
++ false if it is an aggregate. */
++
++extern bool val_print_scalar_type_p (struct type *type);
+
+ struct format_data
+ {
diff --git a/gdb.spec b/gdb.spec
index fcfa711..3c28c00 100644
--- a/gdb.spec
+++ b/gdb.spec
@@ -45,7 +45,7 @@ Version: 17.1
# The release always contains a leading reserved number, start it at 1.
# `upstream' is not a part of `name' to stay fully rpm dependencies compatible for the testing.
-Release: 5%{?dist}
+Release: 6%{?dist}
License: GPL-3.0-or-later AND BSD-3-Clause AND FSFAP AND LGPL-2.1-or-later AND GPL-2.0-or-later AND LGPL-2.0-or-later AND LicenseRef-Fedora-Public-Domain AND GFDL-1.3-or-later AND LGPL-2.0-or-later WITH GCC-exception-2.0 AND GPL-3.0-or-later WITH GCC-exception-3.1 AND GPL-2.0-or-later WITH GNU-compiler-exception AND MIT
# Do not provide URL for snapshots as the file lasts there only for 2 days.
@@ -932,6 +932,12 @@ fi
# endif scl
%changelog
+* Sat May 16 2026 Andrew Burgess <aburgess@redhat.com>
+- Backport the following upstream commits in order to address RHBZ
+ 2467251: d980317c7f1, 958d06262a7, 7d1d7386561, 8915de0883c,
+ 8f65ab7b71f. These commits will all drop out when we rebase to GDB
+ 18.
+
* Tue May 5 2026 Keith Seitz <keiths@redhat.com>
- Backport upstream commit ef7727ae from Tom de Vries to
fix the test suite for newer Tcl releases.
reply other threads:[~2026-06-28 0:02 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178260494312.1.13532735939363010268.rpms-gdb-0b14d14bbaae@fedoraproject.org \
--to=aburgess@redhat.com \
--cc=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox