Blame SOURCES/gdb-test-for-rhbz1976887.patch

a1b30c
From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
a1b30c
From: =?UTF-8?q?Alexandra=20H=C3=A1jkov=C3=A1?= <ahajkova@redhat.com>
a1b30c
Date: Wed, 29 Sep 2021 11:14:51 +0200
a1b30c
Subject: gdb-test-for-rhbz1976887.patch
a1b30c
a1b30c
;; Backport test for RHBZ 1976887 (Kevin Buettner).
a1b30c
a1b30c
Test case reproducing PR28030 bug
a1b30c
a1b30c
The original reproducer for PR28030 required use of a specific
a1b30c
compiler version - gcc-c++-11.1.1-3.fc34 is mentioned in the PR,
a1b30c
though it seems probable that other gcc versions might also be able to
a1b30c
reproduce the bug as well.  This commit introduces a test case which,
a1b30c
using the DWARF assembler, provides a reproducer which is independent
a1b30c
of the compiler version.  (Well, it'll work with whatever compilers
a1b30c
the DWARF assembler works with.)
a1b30c
a1b30c
To the best of my knowledge, it's also the first test case which uses
a1b30c
the DWARF assembler to provide debug info for a shared object.  That
a1b30c
being the case, I provided more than the usual commentary which should
a1b30c
allow this case to be used as a template when a combo shared
a1b30c
library / DWARF assembler test case is required in the future.
a1b30c
a1b30c
I provide some details regarding the bug in a comment near the
a1b30c
beginning of locexpr-dml.exp.
a1b30c
a1b30c
This problem was difficult to reproduce; I found myself constantly
a1b30c
referring to the backtrace while trying to figure out what (else) I
a1b30c
might be missing while trying to create a reproducer.  Below is a
a1b30c
partial backtrace which I include for posterity.
a1b30c
a1b30c
 #0  internal_error (
a1b30c
    file=0xc50110 "/ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/gdbtypes.c", line=5575,
a1b30c
    fmt=0xc520c0 "Unexpected type field location kind: %d")
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdbsupport/errors.cc:51
a1b30c
 #1  0x00000000006ef0c5 in copy_type_recursive (objfile=0x1635930,
a1b30c
    type=0x274c260, copied_types=0x30bb290)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/gdbtypes.c:5575
a1b30c
 #2  0x00000000006ef382 in copy_type_recursive (objfile=0x1635930,
a1b30c
    type=0x274ca10, copied_types=0x30bb290)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/gdbtypes.c:5602
a1b30c
 #3  0x0000000000a7409a in preserve_one_value (value=0x24269f0,
a1b30c
    objfile=0x1635930, copied_types=0x30bb290)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/value.c:2529
a1b30c
 #4  0x000000000072012a in gdbscm_preserve_values (
a1b30c
    extlang=0xc55720 <extension_language_guile>, objfile=0x1635930,
a1b30c
    copied_types=0x30bb290)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/guile/scm-value.c:94
a1b30c
 #5  0x00000000006a3f82 in preserve_ext_lang_values (objfile=0x1635930,
a1b30c
    copied_types=0x30bb290)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/extension.c:568
a1b30c
 #6  0x0000000000a7428d in preserve_values (objfile=0x1635930)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/value.c:2579
a1b30c
 #7  0x000000000082d514 in objfile::~objfile (this=0x1635930,
a1b30c
    __in_chrg=<optimized out>)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/objfiles.c:549
a1b30c
 #8  0x0000000000831cc8 in std::_Sp_counted_ptr<objfile*, (__gnu_cxx::_Lock_policy)2>::_M_dispose (this=0x1654580)
a1b30c
    at /usr/include/c++/11/bits/shared_ptr_base.h:348
a1b30c
 #9  0x00000000004e6617 in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x1654580) at /usr/include/c++/11/bits/shared_ptr_base.h:168
a1b30c
 #10 0x00000000004e1d2f in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count (this=0x190bb88, __in_chrg=<optimized out>)
a1b30c
    at /usr/include/c++/11/bits/shared_ptr_base.h:705
a1b30c
 #11 0x000000000082feee in std::__shared_ptr<objfile, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr (this=0x190bb80, __in_chrg=<optimized out>)
a1b30c
    at /usr/include/c++/11/bits/shared_ptr_base.h:1154
a1b30c
 #12 0x000000000082ff0a in std::shared_ptr<objfile>::~shared_ptr (
a1b30c
    this=0x190bb80, __in_chrg=<optimized out>)
a1b30c
    at /usr/include/c++/11/bits/shared_ptr.h:122
a1b30c
 #13 0x000000000085ed7e in __gnu_cxx::new_allocator<std::_List_node<std::shared_ptr<objfile> > >::destroy<std::shared_ptr<objfile> > (this=0x114bc00,
a1b30c
    __p=0x190bb80) at /usr/include/c++/11/ext/new_allocator.h:168
a1b30c
 #14 0x000000000085e88d in std::allocator_traits<std::allocator<std::_List_node<std::shared_ptr<objfile> > > >::destroy<std::shared_ptr<objfile> > (__a=...,
a1b30c
    __p=0x190bb80) at /usr/include/c++/11/bits/alloc_traits.h:531
a1b30c
 #15 0x000000000085e50c in std::__cxx11::list<std::shared_ptr<objfile>, std::allocator<std::shared_ptr<objfile> > >::_M_erase (this=0x114bc00, __position=
a1b30c
  std::shared_ptr<objfile> (expired, weak count 1) = {get() = 0x1635930})
a1b30c
    at /usr/include/c++/11/bits/stl_list.h:1925
a1b30c
 #16 0x000000000085df0e in std::__cxx11::list<std::shared_ptr<objfile>, std::allocator<std::shared_ptr<objfile> > >::erase (this=0x114bc00, __position=
a1b30c
  std::shared_ptr<objfile> (expired, weak count 1) = {get() = 0x1635930})
a1b30c
    at /usr/include/c++/11/bits/list.tcc:158
a1b30c
 #17 0x000000000085c748 in program_space::remove_objfile (this=0x114bbc0,
a1b30c
    objfile=0x1635930)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/progspace.c:210
a1b30c
 #18 0x000000000082d3ae in objfile::unlink (this=0x1635930)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/objfiles.c:487
a1b30c
 #19 0x000000000082e68c in objfile_purge_solibs ()
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/objfiles.c:875
a1b30c
 #20 0x000000000092dd37 in no_shared_libraries (ignored=0x0, from_tty=1)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/solib.c:1236
a1b30c
 #21 0x00000000009a37fe in target_pre_inferior (from_tty=1)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/target.c:2496
a1b30c
 #22 0x00000000007454d6 in run_command_1 (args=0x0, from_tty=1,
a1b30c
    run_how=RUN_NORMAL)
a1b30c
    at /ironwood1/sourceware-git/f34-pr28030/bld/../../worktree-pr28030/gdb/infcmd.c:437
a1b30c
a1b30c
I'll note a few points regarding this backtrace:
a1b30c
a1b30c
Frame #1 is where the internal error occurs.  It's caused by an
a1b30c
unhandled case for FIELD_LOC_KIND_DWARF_BLOCK.  The fix for this bug
a1b30c
adds support for this case.
a1b30c
a1b30c
Frame #22 - it's a partial backtrace - shows that GDB is attempting to
a1b30c
(re)run the program.  You can see the exact command sequence that was
a1b30c
used for reproducing this problem in the PR (at
a1b30c
https://sourceware.org/bugzilla/show_bug.cgi?id=28030), but in a
a1b30c
nutshell, after starting the program and advancing to the appropriate
a1b30c
source line, GDB was asked to step into libstdc++; a "finish" command
a1b30c
was issued, returning a value.  The fact that a value was returned is
a1b30c
very important.  GDB was then used to step back into libstdc++.  A
a1b30c
breakpoint was set on a source line in the library after which a "run"
a1b30c
command was issued.
a1b30c
a1b30c
Frame #19 shows a call to objfile_purge_solibs.  It's aptly named.
a1b30c
a1b30c
Frame #7 is a call to the destructor for one of the objfile solibs; it
a1b30c
turned out to be the one for libstdc++.
a1b30c
a1b30c
Frames #6 thru #3 show various value preservation frames.  If you look
a1b30c
at preserve_values() in gdb/value.c, the value history is preserved
a1b30c
first, followed by internal variables, followed by values for the
a1b30c
extension languages (python and guile).
a1b30c
a1b30c
diff --git a/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location-lib.c b/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location-lib.c
a1b30c
new file mode 100644
a1b30c
--- /dev/null
a1b30c
+++ b/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location-lib.c
a1b30c
@@ -0,0 +1,48 @@
a1b30c
+/* Copyright (C) 2021 Free Software Foundation, Inc.
a1b30c
+
a1b30c
+   This file is part of GDB.
a1b30c
+
a1b30c
+   This program is free software; you can redistribute it and/or modify
a1b30c
+   it under the terms of the GNU General Public License as published by
a1b30c
+   the Free Software Foundation; either version 3 of the License, or
a1b30c
+   (at your option) any later version.
a1b30c
+
a1b30c
+   This program is distributed in the hope that it will be useful,
a1b30c
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a1b30c
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a1b30c
+   GNU General Public License for more details.
a1b30c
+
a1b30c
+   You should have received a copy of the GNU General Public License
a1b30c
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
a1b30c
+
a1b30c
+#include "locexpr-data-member-location.h"
a1b30c
+
a1b30c
+struct A g_A = {3, 4};
a1b30c
+struct B g_B = { {8, 9}, 10, 11 };
a1b30c
+
a1b30c
+B *
a1b30c
+foo ()
a1b30c
+{						/* foo prologue */
a1b30c
+  asm ("foo_label: .globl foo_label");
a1b30c
+  return &g_;;					/* foo return */
a1b30c
+}						/* foo end */
a1b30c
+
a1b30c
+B *
a1b30c
+bar (B *v)
a1b30c
+{						/* bar prologue */
a1b30c
+  asm ("bar_label: .globl bar_label");
a1b30c
+  return v;					/* bar return */
a1b30c
+}						/* bar end */
a1b30c
+
a1b30c
+/* Some of the DWARF assembler procs (e.g. function_range) compile
a1b30c
+   this file, expecting it to be a complete program with a main()
a1b30c
+   function.  When IS_SHAREDLIB is NOT defined, we have main() as
a1b30c
+   defined below.  */
a1b30c
+
a1b30c
+#ifndef IS_SHAREDLIB
a1b30c
+int
a1b30c
+main ()
a1b30c
+{
a1b30c
+  B *b = foo ();
a1b30c
+}
a1b30c
+#endif
a1b30c
diff --git a/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location-main.c b/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location-main.c
a1b30c
new file mode 100644
a1b30c
--- /dev/null
a1b30c
+++ b/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location-main.c
a1b30c
@@ -0,0 +1,27 @@
a1b30c
+/* Copyright (C) 2021 Free Software Foundation, Inc.
a1b30c
+
a1b30c
+   This file is part of GDB.
a1b30c
+
a1b30c
+   This program is free software; you can redistribute it and/or modify
a1b30c
+   it under the terms of the GNU General Public License as published by
a1b30c
+   the Free Software Foundation; either version 3 of the License, or
a1b30c
+   (at your option) any later version.
a1b30c
+
a1b30c
+   This program is distributed in the hope that it will be useful,
a1b30c
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a1b30c
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a1b30c
+   GNU General Public License for more details.
a1b30c
+
a1b30c
+   You should have received a copy of the GNU General Public License
a1b30c
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
a1b30c
+
a1b30c
+#include "locexpr-data-member-location.h"
a1b30c
+
a1b30c
+int
a1b30c
+main (void)
a1b30c
+{
a1b30c
+  B *v1;
a1b30c
+  v1 = bar (foo ());
a1b30c
+
a1b30c
+  return 0;
a1b30c
+}
a1b30c
diff --git a/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.exp b/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.exp
a1b30c
new file mode 100644
a1b30c
--- /dev/null
a1b30c
+++ b/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.exp
a1b30c
@@ -0,0 +1,349 @@
a1b30c
+# Copyright 2021 Free Software Foundation, Inc.
a1b30c
+
a1b30c
+# This program is free software; you can redistribute it and/or modify
a1b30c
+# it under the terms of the GNU General Public License as published by
a1b30c
+# the Free Software Foundation; either version 3 of the License, or
a1b30c
+# (at your option) any later version.
a1b30c
+#
a1b30c
+# This program is distributed in the hope that it will be useful,
a1b30c
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
a1b30c
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a1b30c
+# GNU General Public License for more details.
a1b30c
+#
a1b30c
+# You should have received a copy of the GNU General Public License
a1b30c
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
a1b30c
+
a1b30c
+# This test case uses the DWARF assembler to reproduce the problem
a1b30c
+# described by PR28030.  The bug turned out to be that
a1b30c
+# FIELD_LOC_KIND_DWARF_BLOCK was not handled when recursively copying
a1b30c
+# a value's type when preserving the value history during the freeing
a1b30c
+# up of objfiles associated with a shared object.  (Yes, figuring out
a1b30c
+# how to make this happen in a concise test case turned out to be
a1b30c
+# challenging.)
a1b30c
+#
a1b30c
+# The following elements proved to be necessary for reproducing the
a1b30c
+# problem:
a1b30c
+#
a1b30c
+# 1) A location expression needed to be used with
a1b30c
+#    DW_AT_data_member_location rather than a simple offset.
a1b30c
+#    Moreover, this location expression needed to use opcodes
a1b30c
+#    which GDB's DWARF reader could not convert to a simple
a1b30c
+#    offset.  (Note, however, that GDB could probably be improved
a1b30c
+#    to handle the opcodes chosen for this test; if decode_locdesc()
a1b30c
+#    in dwarf2/read.c is ever updated to handle both DW_OP_pick and
a1b30c
+#    DW_OP_drop, then this test could end up passing even if
a1b30c
+#    the bug it's intended to test has not been fixed.)
a1b30c
+#
a1b30c
+# 2) The debug info containing the above DWARF info needed
a1b30c
+#    to be associated with a shared object since the problem
a1b30c
+#    occurred while GDB was preserving values during the
a1b30c
+#    purging of shared objects.
a1b30c
+#
a1b30c
+# 3) After performing some simple gdb commands, the program is
a1b30c
+#    run again.  In the course of running the objfile destructor
a1b30c
+#    associated with the shared object, values are preserved
a1b30c
+#    along with their types.  As noted earlier, it was during
a1b30c
+#    the recursive type copy that the bug was observed.
a1b30c
+#
a1b30c
+# Therefore, due to #2 above, this test case creates debug info
a1b30c
+# which is then used by a shared object.
a1b30c
+
a1b30c
+# This test can't be run on targets lacking shared library support.
a1b30c
+if [skip_shlib_tests] {
a1b30c
+    return 0
a1b30c
+}
a1b30c
+
a1b30c
+load_lib dwarf.exp
a1b30c
+
a1b30c
+# This test can only be run on targets which support DWARF-2 and use gas.
a1b30c
+if ![dwarf2_support] {
a1b30c
+    return 0
a1b30c
+}
a1b30c
+
a1b30c
+# gdb_test_file_name is the name of this file without the .exp
a1b30c
+# extension.  Use it to form basenames for the main program
a1b30c
+# and shared object.
a1b30c
+set main_basename ${::gdb_test_file_name}-main
a1b30c
+set lib_basename ${::gdb_test_file_name}-lib
a1b30c
+
a1b30c
+# We're generating DWARF assembly for the shared object; therefore,
a1b30c
+# the source file for the library / shared object must be listed first
a1b30c
+# (in the standard_testfile invocation) since ${srcfile} is used by
a1b30c
+# get_func_info (for determining the start, end, and length of a
a1b30c
+# function).
a1b30c
+#
a1b30c
+# The output of Dwarf::assemble will be placed in $lib_basename.S
a1b30c
+# which will be ${srcfile3} after the execution of standard_testfile.
a1b30c
+
a1b30c
+standard_testfile $lib_basename.c $main_basename.c $lib_basename.S
a1b30c
+
a1b30c
+set libsrc "${::srcdir}/${::subdir}/${::srcfile}"
a1b30c
+set lib_so [standard_output_file ${lib_basename}.so]
a1b30c
+set asm_file [standard_output_file ${::srcfile3}]
a1b30c
+
a1b30c
+# We need to know the size of some types in order to write some of the
a1b30c
+# debugging info that we're about to generate.  For that, we ask GDB
a1b30c
+# by debugging the shared object associated with this test case.
a1b30c
+
a1b30c
+# Compile the shared library: -DIS_SHAREDLIB prevents main() from
a1b30c
+# being defined.  Note that debugging symbols will be present for
a1b30c
+# this compilation.
a1b30c
+if {[gdb_compile_shlib $libsrc $lib_so \
a1b30c
+                       {additional_flags=-DIS_SHAREDLIB debug}] != ""} {
a1b30c
+    untested "failed to compile shared library"
a1b30c
+    return
a1b30c
+}
a1b30c
+
a1b30c
+# Start a fresh GDB and load the shared library.
a1b30c
+clean_restart $lib_so
a1b30c
+
a1b30c
+# Using our running GDB session, determine sizes of several types.
a1b30c
+set long_size [get_sizeof "long" -1]
a1b30c
+set addr_size [get_sizeof "void *" -1]
a1b30c
+set struct_A_size [get_sizeof "g_A" -1]
a1b30c
+set struct_B_size [get_sizeof "g_B" -1]
a1b30c
+
a1b30c
+if { $long_size == -1 || $addr_size == -1 \
a1b30c
+     || $struct_A_size == -1 || $struct_B_size == -1} {
a1b30c
+    perror "Can't determine type sizes"
a1b30c
+    return
a1b30c
+}
a1b30c
+
a1b30c
+# Retrieve struct offset of MBR in struct TP
a1b30c
+proc get_offsetof { tp mbr } {
a1b30c
+    return [get_integer_valueof "&((${tp} *) 0)->${mbr}" -1]
a1b30c
+}
a1b30c
+
a1b30c
+# Use running GDB session to get struct offsets
a1b30c
+set A_a [get_offsetof A a]
a1b30c
+set A_x [get_offsetof A x]
a1b30c
+set B_a [get_offsetof B a]
a1b30c
+set B_b [get_offsetof B b]
a1b30c
+set B_x2 [get_offsetof B x2]
a1b30c
+
a1b30c
+# Create the DWARF.
a1b30c
+Dwarf::assemble ${asm_file} {
a1b30c
+    declare_labels L
a1b30c
+
a1b30c
+    # Find start, end, and length of functions foo and bar.
a1b30c
+    # These calls to get_func_info will create and set variables
a1b30c
+    # foo_start, bar_start, foo_end, bar_end, foo_len, and
a1b30c
+    # bar_len.
a1b30c
+    #
a1b30c
+    # In order to get the right answers, get_func_info (and,
a1b30c
+    # underneath, function_range) should use the same compiler flags
a1b30c
+    # as those used to make a shared object.  For any targets that get
a1b30c
+    # this far, -fpic is probably correct.
a1b30c
+    #
a1b30c
+    # Also, it should be noted that IS_SHAREDLIB is NOT defined as one
a1b30c
+    # of the additional flags.  Not defining IS_SHAREDLIB will cause a
a1b30c
+    # main() to be defined for the compilation of the shared library
a1b30c
+    # source file which happens as a result of using get_func_info;
a1b30c
+    # this is currently required in order to this facility.
a1b30c
+    set flags {additional_flags=-fpic debug}
a1b30c
+    get_func_info foo $flags
a1b30c
+    get_func_info bar $flags
a1b30c
+
a1b30c
+    cu {} {
a1b30c
+	DW_TAG_compile_unit {
a1b30c
+	    {DW_AT_language @DW_LANG_C_plus_plus}
a1b30c
+	    {name ${::srcfile}}
a1b30c
+	    {stmt_list $L DW_FORM_sec_offset}
a1b30c
+        } {
a1b30c
+	    declare_labels int_label class_A_label class_B_label \
a1b30c
+	                   B_ptr_label
a1b30c
+
a1b30c
+	    int_label: DW_TAG_base_type {
a1b30c
+		{DW_AT_byte_size ${::long_size} DW_FORM_udata}
a1b30c
+		{DW_AT_encoding @DW_ATE_signed}
a1b30c
+		{DW_AT_name "int"}
a1b30c
+	    }
a1b30c
+
a1b30c
+	    class_A_label: DW_TAG_class_type {
a1b30c
+		{DW_AT_name "A"}
a1b30c
+		{DW_AT_byte_size ${::struct_A_size} DW_FORM_sdata}
a1b30c
+	    } {
a1b30c
+		DW_TAG_member {
a1b30c
+		    {DW_AT_name "a"}
a1b30c
+		    {DW_AT_type :$int_label}
a1b30c
+		    {DW_AT_data_member_location ${::A_a} DW_FORM_udata}
a1b30c
+		}
a1b30c
+		DW_TAG_member {
a1b30c
+		    {DW_AT_name "x"}
a1b30c
+		    {DW_AT_type :$int_label}
a1b30c
+		    {DW_AT_data_member_location ${::A_x} DW_FORM_udata}
a1b30c
+		}
a1b30c
+	    }
a1b30c
+
a1b30c
+	    class_B_label: DW_TAG_class_type {
a1b30c
+		{DW_AT_name "B"}
a1b30c
+		{DW_AT_byte_size ${::struct_B_size} DW_FORM_sdata}
a1b30c
+	    } {
a1b30c
+		# While there are easier / better ways to specify an
a1b30c
+		# offset used by DW_AT_data_member_location than that
a1b30c
+		# used below, we need a location expression here in
a1b30c
+		# order to reproduce the bug.  Moreover, this location
a1b30c
+		# expression needs to use opcodes that aren't handled
a1b30c
+		# by decode_locdesc() in dwarf2/read.c; if we use
a1b30c
+		# opcodes that _are_ handled by that function, the
a1b30c
+		# location expression will be converted into a simple
a1b30c
+		# offset - which will then (again) not reproduce the
a1b30c
+		# bug.  At the time that this test was written,
a1b30c
+		# neither DW_OP_pick nor DW_OP_drop were being handled
a1b30c
+		# by decode_locdesc(); this is why those opcodes were
a1b30c
+		# chosen.
a1b30c
+		DW_TAG_inheritance {
a1b30c
+		    {DW_AT_type :$class_A_label}
a1b30c
+		    {DW_AT_data_member_location {
a1b30c
+			DW_OP_constu ${::B_a}
a1b30c
+			DW_OP_plus
a1b30c
+			DW_OP_pick 0
a1b30c
+			DW_OP_drop} SPECIAL_expr}
a1b30c
+		    {DW_AT_accessibility 1 DW_FORM_data1}
a1b30c
+		}
a1b30c
+		DW_TAG_member {
a1b30c
+		    {DW_AT_name "b"}
a1b30c
+		    {DW_AT_type :$int_label}
a1b30c
+		    {DW_AT_data_member_location ${::B_b} DW_FORM_udata}
a1b30c
+		}
a1b30c
+		DW_TAG_member {
a1b30c
+		    {DW_AT_name "x2"}
a1b30c
+		    {DW_AT_type :$int_label}
a1b30c
+		    {DW_AT_data_member_location ${::B_x2} DW_FORM_udata}
a1b30c
+		}
a1b30c
+	    }
a1b30c
+
a1b30c
+	    B_ptr_label: DW_TAG_pointer_type {
a1b30c
+		{DW_AT_type :$class_B_label}
a1b30c
+		{DW_AT_byte_size ${::addr_size} DW_FORM_sdata}
a1b30c
+	    }
a1b30c
+
a1b30c
+	    DW_TAG_variable {
a1b30c
+		{DW_AT_name "g_A"}
a1b30c
+		{DW_AT_type :$class_A_label}
a1b30c
+		{DW_AT_external 1 flag}
a1b30c
+		{DW_AT_location {DW_OP_addr [gdb_target_symbol "g_A"]} \
a1b30c
+		                 SPECIAL_expr}
a1b30c
+	    }
a1b30c
+
a1b30c
+	    DW_TAG_variable {
a1b30c
+		{DW_AT_name "g_B"}
a1b30c
+		{DW_AT_type :$class_B_label}
a1b30c
+		{DW_AT_external 1 flag}
a1b30c
+		{DW_AT_location {DW_OP_addr [gdb_target_symbol "g_B"]} \
a1b30c
+		                 SPECIAL_expr}
a1b30c
+	    }
a1b30c
+
a1b30c
+	    # We can't use MACRO_AT for the definitions of foo and bar
a1b30c
+	    # because it doesn't provide a way to pass the appropriate
a1b30c
+	    # flags.  Therefore, we list the name, low_pc, and high_pc
a1b30c
+	    # explicitly.
a1b30c
+	    DW_TAG_subprogram {
a1b30c
+		{DW_AT_name foo}
a1b30c
+		{DW_AT_low_pc $foo_start DW_FORM_addr}
a1b30c
+		{DW_AT_high_pc $foo_end DW_FORM_addr}
a1b30c
+		{DW_AT_type :${B_ptr_label}}
a1b30c
+		{DW_AT_external 1 flag}
a1b30c
+	    }
a1b30c
+
a1b30c
+	    DW_TAG_subprogram {
a1b30c
+		{DW_AT_name bar}
a1b30c
+		{DW_AT_low_pc $bar_start DW_FORM_addr}
a1b30c
+		{DW_AT_high_pc $bar_end DW_FORM_addr}
a1b30c
+		{DW_AT_type :${B_ptr_label}}
a1b30c
+		{DW_AT_external 1 flag}
a1b30c
+	    } {
a1b30c
+		DW_TAG_formal_parameter {
a1b30c
+		    {DW_AT_name v}
a1b30c
+		    {DW_AT_type :${B_ptr_label}}
a1b30c
+		}
a1b30c
+	    }
a1b30c
+	}
a1b30c
+    }
a1b30c
+
a1b30c
+    lines {version 2} L {
a1b30c
+	include_dir "${::srcdir}/${::subdir}"
a1b30c
+	file_name "${::srcfile}" 1
a1b30c
+
a1b30c
+	# Generate a line table program.
a1b30c
+	program {
a1b30c
+	    {DW_LNE_set_address $foo_start}
a1b30c
+	    {line [gdb_get_line_number "foo prologue"]}
a1b30c
+	    {DW_LNS_copy}
a1b30c
+	    {DW_LNE_set_address foo_label}
a1b30c
+	    {line [gdb_get_line_number "foo return"]}
a1b30c
+	    {DW_LNS_copy}
a1b30c
+	    {line [gdb_get_line_number "foo end"]}
a1b30c
+	    {DW_LNS_copy}
a1b30c
+	    {DW_LNE_set_address $foo_end}
a1b30c
+	    {DW_LNS_advance_line 1}
a1b30c
+	    {DW_LNS_copy}
a1b30c
+	    {DW_LNE_end_sequence}
a1b30c
+
a1b30c
+	    {DW_LNE_set_address $bar_start}
a1b30c
+	    {line [gdb_get_line_number "bar prologue"]}
a1b30c
+	    {DW_LNS_copy}
a1b30c
+	    {DW_LNE_set_address bar_label}
a1b30c
+	    {line [gdb_get_line_number "bar return"]}
a1b30c
+	    {DW_LNS_copy}
a1b30c
+	    {line [gdb_get_line_number "bar end"]}
a1b30c
+	    {DW_LNS_copy}
a1b30c
+	    {DW_LNE_set_address $bar_end}
a1b30c
+	    {DW_LNS_advance_line 1}
a1b30c
+	    {DW_LNS_copy}
a1b30c
+	    {DW_LNE_end_sequence}
a1b30c
+	}
a1b30c
+    }
a1b30c
+}
a1b30c
+
a1b30c
+# Compile the shared object again, but this time include / use the
a1b30c
+# DWARF info that we've created above.  Note that (again)
a1b30c
+# -DIS_SHAREDLIB is used to prevent inclusion of main() in the shared
a1b30c
+# object.  Also note the use of the "nodebug" option.  Any debugging
a1b30c
+# information that we need will be provided by the DWARF info created
a1b30c
+# above.
a1b30c
+if {[gdb_compile_shlib [list $libsrc $asm_file] $lib_so \
a1b30c
+                       {additional_flags=-DIS_SHAREDLIB nodebug}] != ""} {
a1b30c
+    untested "failed to compile shared library"
a1b30c
+    return
a1b30c
+}
a1b30c
+
a1b30c
+# Compile the main program for use with the shared object.
a1b30c
+if [prepare_for_testing "failed to prepare" ${testfile} \
a1b30c
+                        ${::srcfile2} [list debug shlib=$lib_so]] {
a1b30c
+    return -1
a1b30c
+}
a1b30c
+
a1b30c
+# Do whatever is necessary to make sure that the shared library is
a1b30c
+# loaded for remote targets.
a1b30c
+gdb_load_shlib ${lib_so}
a1b30c
+
a1b30c
+if ![runto_main] then {
a1b30c
+    fail "can't run to main"
a1b30c
+    return
a1b30c
+}
a1b30c
+
a1b30c
+# Step into foo so that we can finish out of it.
a1b30c
+gdb_test "step" "foo .. at .* foo end.*" "step into foo"
a1b30c
+
a1b30c
+# Finishing out of foo will create a value that will later need to
a1b30c
+# be preserved when restarting the program.
a1b30c
+gdb_test "finish" "= \\(class B \\*\\) ${::hex} .*" "finish out of foo"
a1b30c
+
a1b30c
+# Dereferencing and printing the return value isn't necessary
a1b30c
+# for reproducing the bug, but we should make sure that the
a1b30c
+# return value is what we expect it to be.
a1b30c
+gdb_test "p *$" { = { = {a = 8, x = 9}, b = 10, x2 = 11}} \
a1b30c
+         "dereference return value"
a1b30c
+
a1b30c
+# The original PR28030 reproducer stepped back into the shared object,
a1b30c
+# so we'll do the same here:
a1b30c
+gdb_test "step" "bar \\(.*" "step into bar"
a1b30c
+
a1b30c
+# We don't want a clean restart here since that will be too clean.
a1b30c
+# The original reproducer for PR28030 set a breakpoint in the shared
a1b30c
+# library and then restarted via "run".  The command below does roughly
a1b30c
+# the same thing.  It's at this step that an internal error would
a1b30c
+# occur for PR28030.  The "message" argument tells runto to turn on
a1b30c
+# the printing of PASSes while runto is doing its job.
a1b30c
+runto "bar" message
a1b30c
diff --git a/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.h b/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.h
a1b30c
new file mode 100644
a1b30c
--- /dev/null
a1b30c
+++ b/gdb/testsuite/gdb.dwarf2/locexpr-data-member-location.h
a1b30c
@@ -0,0 +1,30 @@
a1b30c
+/* Copyright (C) 2021 Free Software Foundation, Inc.
a1b30c
+
a1b30c
+   This file is part of GDB.
a1b30c
+
a1b30c
+   This program is free software; you can redistribute it and/or modify
a1b30c
+   it under the terms of the GNU General Public License as published by
a1b30c
+   the Free Software Foundation; either version 3 of the License, or
a1b30c
+   (at your option) any later version.
a1b30c
+
a1b30c
+   This program is distributed in the hope that it will be useful,
a1b30c
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
a1b30c
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a1b30c
+   GNU General Public License for more details.
a1b30c
+
a1b30c
+   You should have received a copy of the GNU General Public License
a1b30c
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
a1b30c
+
a1b30c
+typedef struct A {
a1b30c
+    long a;
a1b30c
+    long x;
a1b30c
+} A;
a1b30c
+
a1b30c
+typedef struct B {
a1b30c
+    A a;
a1b30c
+    long b;
a1b30c
+    long x2;
a1b30c
+} B;
a1b30c
+
a1b30c
+extern B *foo ();
a1b30c
+extern B *bar (B *v);