diff --git a/gdb/dwarf2/cu.c b/gdb/dwarf2/cu.c
index 019165f6e6c5..b1fd688188a7 100644
--- a/gdb/dwarf2/cu.c
+++ b/gdb/dwarf2/cu.c
@@ -32,6 +32,7 @@ dwarf2_cu::dwarf2_cu (dwarf2_per_cu_data *per_cu,
checked_producer (false),
producer_is_gxx_lt_4_6 (false),
producer_is_gcc_lt_4_3 (false),
+ producer_is_gcc_11 (false),
producer_is_icc (false),
producer_is_icc_lt_14 (false),
producer_is_codewarrior (false),
diff --git a/gdb/dwarf2/cu.h b/gdb/dwarf2/cu.h
index 078479a56988..bce0a3de63a4 100644
--- a/gdb/dwarf2/cu.h
+++ b/gdb/dwarf2/cu.h
@@ -253,6 +253,7 @@ struct dwarf2_cu
bool checked_producer : 1;
bool producer_is_gxx_lt_4_6 : 1;
bool producer_is_gcc_lt_4_3 : 1;
+ bool producer_is_gcc_11 : 1;
bool producer_is_icc : 1;
bool producer_is_icc_lt_14 : 1;
bool producer_is_codewarrior : 1;
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index f7cb95b40cba..1a749eac334f 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -14320,6 +14320,7 @@ check_producer (struct dwarf2_cu *cu)
{
cu->producer_is_gxx_lt_4_6 = major < 4 || (major == 4 && minor < 6);
cu->producer_is_gcc_lt_4_3 = major < 4 || (major == 4 && minor < 3);
+ cu->producer_is_gcc_11 = major == 11;
}
else if (producer_is_icc (cu->producer, &major, &minor))
{
@@ -14464,6 +14465,19 @@ handle_member_location (struct die_info *die, struct dwarf2_cu *cu,
if (attr->form_is_constant ())
{
LONGEST offset = attr->constant_value (0);
+
+ /* Work around this GCC 11 bug, where it would erroneously use -1
+ data member locations, instead of 0:
+
+ Negative DW_AT_data_member_location
+ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101378
+ */
+ if (offset == -1 && cu->producer_is_gcc_11)
+ {
+ complaint (_("DW_AT_data_member_location value of -1, assuming 0"));
+ offset = 0;
+ }
+
field->set_loc_bitpos (offset * bits_per_byte);
}
else if (attr->form_is_section_offset ())
diff --git a/gdb/testsuite/gdb.dwarf2/negative-data-member-location.c b/gdb/testsuite/gdb.dwarf2/negative-data-member-location.c
new file mode 100644
index 000000000000..6d8e8df01e9d
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/negative-data-member-location.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 2021-2022 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 . */
+
+struct the_struct
+{
+ /* Define the field as an array of char instead of an int to support targets
+ that have sizeof(int) != 4. */
+ char x[4];
+};
+
+struct the_struct s = { { 0x11, 0x22, 0x22, 0x11 } };
+
+int
+main (void)
+{
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/negative-data-member-location.exp b/gdb/testsuite/gdb.dwarf2/negative-data-member-location.exp
new file mode 100644
index 000000000000..57030d77134b
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/negative-data-member-location.exp
@@ -0,0 +1,77 @@
+# Copyright 2021-2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+# Test our workaround for a GCC 11 bug, where it sometimes puts a -1 value for
+# DW_AT_data_member_location:
+#
+# Negative DW_AT_data_member_location
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101378
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if { ![dwarf2_support] } {
+ return 0
+}
+
+standard_testfile .c -dw.S
+
+set asm_file [standard_output_file ${srcfile2}]
+
+Dwarf::assemble ${asm_file} {
+ cu {} {
+ DW_TAG_compile_unit {
+ {DW_AT_language @DW_LANG_C99}
+ {DW_AT_producer "GNU C++17 11.1.0 -mtune=generic -march=x86-64 -g3 -O0"}
+ {name ${::srcfile}}
+ } {
+ declare_labels int_label struct_label
+
+ int_label: DW_TAG_base_type {
+ {DW_AT_byte_size 4 DW_FORM_udata}
+ {DW_AT_encoding @DW_ATE_signed}
+ {DW_AT_name "int"}
+ }
+
+ struct_label: DW_TAG_structure_type {
+ {DW_AT_name "the_struct"}
+ {DW_AT_byte_size 4 DW_FORM_udata}
+ } {
+ DW_TAG_member {
+ {DW_AT_name "field"}
+ {DW_AT_type :$int_label}
+ {DW_AT_data_member_location -1 DW_FORM_sdata}
+ }
+ }
+
+ DW_TAG_variable {
+ {DW_AT_name "s"}
+ {DW_AT_type :$struct_label}
+ {DW_AT_location {DW_OP_addr [gdb_target_symbol "s"]} SPECIAL_expr}
+ }
+ }
+ }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} [list $srcfile $asm_file] {nodebug}] } {
+ return
+}
+
+if { ![runto_main] } {
+ return
+}
+
+gdb_test "print /x s" " = {field = 0x11222211}"
+gdb_test "print /x s.field" " = 0x11222211"