This commit fixes a bug mentioned by Florian Weimer during the
libpthread/ld.so load order discussion from 2021. Florian provided
instructions for reproducing the bug here:
https://sourceware.org/pipermail/gdb-patches/2021-April/177923.html
That particular test does some interesting things involving forks,
threads, and thread local storage. Fortunately, none of that is
needed to reproduce the problem.
I've made a new test case (which is now found in a separate commit)
contained in the files gdb.base/add-symbol-file-attach.{c,exp}. The
.c file is fairly simple as is the recipe for reproducing the problem.
After separately starting the test case and noting the process id,
start gdb (w/ no arguments), and do the following to reproduce the
assertion failure - for this run, the process id of the separately
started add-symbol-file-attach process is
4103218:
(gdb) add-symbol-file add-symbol-file-attach
add symbol table from file "add-symbol-file-attach"
(y or n) y
Reading symbols from add-symbol-file-attach...
(gdb) attach
4103218
Attaching to process
4103218
Load new symbol table from "/tmp/add-symbol-file-attach"? (y or n) y
Reading symbols from /tmp/add-symbol-file-attach...
Reading symbols from /lib64/libc.so.6...
(No debugging symbols found in /lib64/libc.so.6)
Reading symbols from /lib64/ld-linux-x86-64.so.2...
(No debugging symbols found in /lib64/ld-linux-x86-64.so.2)
0x00007f502130bf27 in pause () from /lib64/libc.so.6
(gdb) p foo
symtab.c:6417: internal-error: CORE_ADDR get_msymbol_address(objfile*,
const minimal_symbol*): Assertion `(objf->flags & OBJF_MAINLINE) == 0'
failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
The add-symbol-file command causes the symbols to be loaded without
the SYMFILE_MAINLINE (and hence the OBJFILE_MAINLINE) flags being
set. This, in turn, causes the "maybe_copied" flag to be set for
the global symbol (named "foo" in the provided test case).
The attach command will cause another objfile to be created, but
it will reuse the symtabs from the objfile created by add-symbol-file,
leading to a situation in which the OBJFILE_MAINLINE flag will be set
for the new (attach-created) objfile, however the "maybe_copied"
flag will still be set for the global symbol. Had it been loaded
anew, this flag would not be set due to OBJFILE_MAINLINE being set
for the objfile.
At present, minimal_symbol::value_address looks like this:
CORE_ADDR
minimal_symbol::value_address (objfile *objfile) const
{
if (this->maybe_copied (objfile))
return get_msymbol_address (objfile, this);
else
return (CORE_ADDR (this->unrelocated_address ())
+ objfile->section_offsets[this->section_index ()]);
}
So, we can now see the problem: When the "maybe_copied" flag is set,
get_msymbol_address() will be called. However, get_msymbol_address()
assumes that it won't be called with the OBF_MAINLINE flag set for
the objfile in question. It, in fact, contains an assert() which
makes sure that this is the case:
gdb_assert ((objf->flags & OBJF_MAINLINE) == 0);
(If this assert is removed, then get_msymbol_address() recurses
infinitely for the case under consideration.)
So, the problem here is that the maybe_copied flag is set for the
symbol AND the OBJF_MAINLINE flag is set for the objfile. As noted
earlier, this happens due to add-symbol-file being used; this causes
the maybe_copied flag to be set. Later, when the attach is performed,
OBJF_MAINLINE will be set for that objfile, leading to this
unfortunate situation.
My first cut at a solution involved adjusting the
MSYMBOL_VALUE_ADDRESS macro (which has since been changed to be the
method noted above) to include a test of the OBJFILE_MAINLINE flag.
However, Simon Marchi, in his review of my patch, suggested a better
solution. Simon observed that the 'maybe_copied' flag is (was, after
this commit) being set/initialized in record_minimal_symbol() using
using the objfile in the context in which the symbol was created.
Simon further observed:
Today, a single copy is created, as symtabs are shared between
objfiles. This means that everything that we store into a symbol
must be independent of any objfile. However, the value of the
maybe_copied field is dependent on the objfile in the context of
which the symbol was created. Meaning that when the symbol is
re-used in the context of another objfile, the maybe_copied value is
not right in the context of that objfile.
So I think it means there isn't a single "is this symbol maybe
copied" value, but instead "is this symbol maybe copied, in the
context of this given objfile". And the answer is yes or no,
depending on whether the objfile is mainline. So maybe_copied
should become a method that takes an objfile and returns an answer
based on that.
Simon's full review can be found here:
https://sourceware.org/pipermail/gdb-patches/2021-May/178855.html
Simon also provided a patch which implements this suggestion. The
current patch is mostly his work, though I did make some adjustments
during a rebase in addition to making some changes to account for a
concern from Tom Tromey.
During his review of the v3 series, Tom noted, "The old approach was
specific to ELF, while the new approach will be used by any object
format." Tom further observed, "...it seems like it could result in an
incorrect evaluation in some scenario." This seemed plausible to me,
so I introduced the flag 'object_format_has_copy_relocs' to struct
objfile. It is set at the end of elf_symfile_read() in elfread.c.
The minimal_symbol::maybe_copied method tests this new flag, forcing
this method to return false when the flag is not set. If we find that
other object file formats use the same copy reloc mechanism as ELF,
then 'object_format_has_copy_relocs' should be set for objfiles using
those formats.
Lastly, I'll note that this is a strange use case. It's far more
common to either let gdb figure out which file to load by itself when
attaching, i.e.
(gdb) attach
4104360
Attaching to process
4104360
Reading symbols from /tmp/add-symbol-file-attach...
Reading symbols from /lib64/libc.so.6...
(No debugging symbols found in /lib64/libc.so.6)
Reading symbols from /lib64/ld-linux-x86-64.so.2...
(No debugging symbols found in /lib64/ld-linux-x86-64.so.2)
0x00007fdb1fc33f27 in pause () from /lib64/libc.so.6
(gdb) p foo
$1 = 42
...or to use the "file" command prior to the attach, like this:
(gdb) file add-symbol-file-attach
Reading symbols from add-symbol-file-attach...
(gdb) attach
4104360
Attaching to program: /tmp/add-symbol-file-attach, process
4104360
Reading symbols from /lib64/libc.so.6...
(No debugging symbols found in /lib64/libc.so.6)
Reading symbols from /lib64/ld-linux-x86-64.so.2...
(No debugging symbols found in /lib64/ld-linux-x86-64.so.2)
0x00007fdb1fc33f27 in pause () from /lib64/libc.so.6
Both of these more common scenarios work perfectly fine; using
"add-symbol-file" to load the program to which you will attach
isn't recommended as a normal use case. That said, it's bad for
gdb to assert, hence this fix.
Reviewed-by: Simon Marchi <simon.marchi@polymtl.ca>
Co-Authored-by: Simon Marchi <simon.marchi@polymtl.ca>
Approved-by: Tom Tromey <tom@tromey.com>
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=27831
|| bfd_section == bfd_abs_section_ptr)
section_index = gdb_bfd_section_index (objfile->obfd.get (), bfd_section);
- struct minimal_symbol *result
- = reader.record_full (name, copy_name, address, ms_type, section_index);
- if ((objfile->flags & OBJF_MAINLINE) == 0
- && (ms_type == mst_data || ms_type == mst_bss))
- result->maybe_copied = 1;
-
- return result;
+ return reader.record_full (name, copy_name, address, ms_type, section_index);
}
/* Read the symbol table of an ELF file.
{
elfctf_build_psymtabs (objfile);
}
+
+ /* Copy relocations are used by some ABIs using the ELF format, so
+ set the objfile flag indicating this fact. */
+ objfile->object_format_has_copy_relocs = true;
}
/* Initialize anything that needs initializing when a completely new symbol
next time. If an objfile does not have the symbols, it will
never have them. */
bool skip_jit_symbol_lookup = false;
+
+ /* Flag which indicates, when true, that the object format
+ potentially supports copy relocations. ABIs for some
+ architectures that use ELF have a copy relocation in which the
+ initialization for a global variable defined in a shared object
+ will be copied to memory allocated to the main program during
+ dynamic linking. Therefore this flag will be set for ELF
+ objfiles. Other object formats that use the same copy relocation
+ mechanism as ELF should set this flag too. This flag is used in
+ conjunction with the minimal_symbol::maybe_copied method. */
+ bool object_format_has_copy_relocs = false;
};
/* A deleter for objfile. */
CORE_ADDR
minimal_symbol::value_address (objfile *objfile) const
{
- if (this->maybe_copied)
+ if (this->maybe_copied (objfile))
return get_msymbol_address (objfile, this);
else
return (CORE_ADDR (this->unrelocated_address ())
|| m_type == mst_file_text;
}
+/* See symtab.h. */
+
+bool
+minimal_symbol::maybe_copied (objfile *objfile) const
+{
+ return (objfile->object_format_has_copy_relocs
+ && (objfile->flags & OBJF_MAINLINE) == 0
+ && (m_type == mst_data || m_type == mst_bss));
+}
+
/* See whether FILENAME matches SEARCH_NAME using the rule that we
advertise to the user. (The manual's description of linespecs
describes what we advertise). Returns true if they match, false
CORE_ADDR
get_msymbol_address (struct objfile *objf, const struct minimal_symbol *minsym)
{
- gdb_assert (minsym->maybe_copied);
+ gdb_assert (minsym->maybe_copied (objf));
gdb_assert ((objf->flags & OBJF_MAINLINE) == 0);
const char *linkage_name = minsym->linkage_name ();
the object file format may not carry that piece of information. */
unsigned int m_has_size : 1;
- /* For data symbols only, if this is set, then the symbol might be
- subject to copy relocation. In this case, a minimal symbol
- matching the symbol's linkage name is first looked for in the
- main objfile. If found, then that address is used; otherwise the
- address in this symbol is used. */
-
- unsigned maybe_copied : 1;
-
/* Non-zero if this symbol ever had its demangled name set (even if
it was set to NULL). */
unsigned int name_set : 1;
/* True if MSYMBOL is of some text type. */
bool text_p () const;
+
+ /* For data symbols only, given an objfile, if 'maybe_copied'
+ evaluates to 'true' for that objfile, then the symbol might be
+ subject to copy relocation. In this case, a minimal symbol
+ matching the symbol's linkage name is first looked for in the
+ main objfile. If found, then that address is used; otherwise the
+ address in this symbol is used. */
+
+ bool maybe_copied (objfile *objfile) const;
};
#include "minsyms.h"