mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-30 13:33:53 +08:00
95ce627aeb
When a core file is created from within GDB add the target description into a note within the core file. When loading a core file, if the target description note is present then load the target description from the core file. The benefit of this is that we can be sure that, when analysing the core file within GDB, that we are using the exact same target description as was in use at the time the core file was created. GDB already supports a mechanism for figuring out the target description from a given corefile; gdbarch_core_read_description. This new mechanism (GDB adding the target description) is not going to replace the old mechanism. Core files generated outside of GDB will not include a target description, and so GDB still needs to be able to figure out a target description for these files. My primary motivation for adding this feature is that, in a future commit, I will be adding support for bare metal core dumps on some targets. For RISC-V specifically, I want to be able to dump all the available control status registers. As different targets will present different sets of register in their target description, including registers that are possibly not otherwise known to GDB I wanted a way to capture these registers in the core dump. I therefore need a mechanism to write out an arbitrary set of registers, and to then derive a target description from this arbitrary set when later loading the core file. The obvious approach (I think) is to just reuse the target description. Once I'd decided to add support for writing out the target description I could either choose to make this RISC-V only, or make it generic. I figure that having the target description in the core file doesn't hurt, and _might_ be helpful. So that's how I got here, general support for including the target description in GDB generated core files. In previous versions of this patch I added the target description from generic code (in gcore.c). However, doing this creates a dependency between GDB's common code and bfd ELF support. As ELF support in gdb is optional (for example the target x86_64-apple-darwin20.3.0 does not include ELF support) then having gcore.c require ELF support would break the GDB build in some cases. Instead, in this version of the patch, writing the target description note is done from each specific targets make notes function. Each of these now calls a common function in gcore-elf.c (which is only linked in when bfd has ELF support). And so only targets that are ELF based will call the new function and we can therefore avoid an unconditional dependency on ELF support. gdb/ChangeLog: * corelow.c: Add 'xml-tdesc.h' include. (core_target::read_description): Load the target description from the core file when possible. * fbsd-tdep.c (fbsd_make_corefile_notes): Add target description note. * gcore-elf.c: Add 'gdbsupport/tdesc.h' include. (gcore_elf_make_tdesc_note): New function. * gcore-elf.h (gcore_elf_make_tdesc_note): Declare. * linux-tdep.c (linux_make_corefile_notes): Add target description note.
167 lines
5.4 KiB
C
167 lines
5.4 KiB
C
/* Copyright (C) 2021 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/>. */
|
|
|
|
#include "defs.h"
|
|
#include "gcore-elf.h"
|
|
#include "elf-bfd.h"
|
|
#include "target.h"
|
|
#include "regcache.h"
|
|
#include "gdbarch.h"
|
|
#include "gdbthread.h"
|
|
#include "inferior.h"
|
|
#include "regset.h"
|
|
#include "gdbsupport/tdesc.h"
|
|
|
|
/* Structure for passing information from GCORE_COLLECT_THREAD_REGISTERS
|
|
via an iterator to GCORE_COLLECT_REGSET_SECTION_CB. */
|
|
|
|
struct gcore_elf_collect_regset_section_cb_data
|
|
{
|
|
gcore_elf_collect_regset_section_cb_data
|
|
(struct gdbarch *gdbarch, const struct regcache *regcache,
|
|
bfd *obfd, ptid_t ptid, gdb_signal stop_signal,
|
|
gdb::unique_xmalloc_ptr<char> *note_data, int *note_size)
|
|
: gdbarch (gdbarch), regcache (regcache), obfd (obfd),
|
|
note_data (note_data), note_size (note_size),
|
|
stop_signal (stop_signal)
|
|
{
|
|
/* The LWP is often not available for bare metal target, in which case
|
|
use the tid instead. */
|
|
if (ptid.lwp_p ())
|
|
lwp = ptid.lwp ();
|
|
else
|
|
lwp = ptid.tid ();
|
|
}
|
|
|
|
struct gdbarch *gdbarch;
|
|
const struct regcache *regcache;
|
|
bfd *obfd;
|
|
gdb::unique_xmalloc_ptr<char> *note_data;
|
|
int *note_size;
|
|
unsigned long lwp;
|
|
enum gdb_signal stop_signal;
|
|
bool abort_iteration = false;
|
|
};
|
|
|
|
/* Callback for ITERATE_OVER_REGSET_SECTIONS that records a single
|
|
regset in the core file note section. */
|
|
|
|
static void
|
|
gcore_elf_collect_regset_section_cb (const char *sect_name,
|
|
int supply_size, int collect_size,
|
|
const struct regset *regset,
|
|
const char *human_name, void *cb_data)
|
|
{
|
|
struct gcore_elf_collect_regset_section_cb_data *data
|
|
= (struct gcore_elf_collect_regset_section_cb_data *) cb_data;
|
|
bool variable_size_section = (regset != nullptr
|
|
&& regset->flags & REGSET_VARIABLE_SIZE);
|
|
|
|
gdb_assert (variable_size_section || supply_size == collect_size);
|
|
|
|
if (data->abort_iteration)
|
|
return;
|
|
|
|
gdb_assert (regset != nullptr && regset->collect_regset != nullptr);
|
|
|
|
/* This is intentionally zero-initialized by using std::vector, so
|
|
that any padding bytes in the core file will show as 0. */
|
|
std::vector<gdb_byte> buf (collect_size);
|
|
|
|
regset->collect_regset (regset, data->regcache, -1, buf.data (),
|
|
collect_size);
|
|
|
|
/* PRSTATUS still needs to be treated specially. */
|
|
if (strcmp (sect_name, ".reg") == 0)
|
|
data->note_data->reset (elfcore_write_prstatus
|
|
(data->obfd, data->note_data->release (),
|
|
data->note_size, data->lwp,
|
|
gdb_signal_to_host (data->stop_signal),
|
|
buf.data ()));
|
|
else
|
|
data->note_data->reset (elfcore_write_register_note
|
|
(data->obfd, data->note_data->release (),
|
|
data->note_size, sect_name, buf.data (),
|
|
collect_size));
|
|
|
|
if (*data->note_data == nullptr)
|
|
data->abort_iteration = true;
|
|
}
|
|
|
|
/* Records the register state of thread PTID out of REGCACHE into the note
|
|
buffer represented by *NOTE_DATA and NOTE_SIZE. OBFD is the bfd into
|
|
which the core file is being created, and STOP_SIGNAL is the signal that
|
|
cause thread PTID to stop. */
|
|
|
|
static void
|
|
gcore_elf_collect_thread_registers
|
|
(const struct regcache *regcache, ptid_t ptid, bfd *obfd,
|
|
gdb::unique_xmalloc_ptr<char> *note_data, int *note_size,
|
|
enum gdb_signal stop_signal)
|
|
{
|
|
struct gdbarch *gdbarch = regcache->arch ();
|
|
gcore_elf_collect_regset_section_cb_data data (gdbarch, regcache, obfd,
|
|
ptid, stop_signal,
|
|
note_data, note_size);
|
|
gdbarch_iterate_over_regset_sections
|
|
(gdbarch, gcore_elf_collect_regset_section_cb, &data, regcache);
|
|
}
|
|
|
|
/* See gcore-elf.h. */
|
|
|
|
void
|
|
gcore_elf_build_thread_register_notes
|
|
(struct gdbarch *gdbarch, struct thread_info *info, gdb_signal stop_signal,
|
|
bfd *obfd, gdb::unique_xmalloc_ptr<char> *note_data, int *note_size)
|
|
{
|
|
struct regcache *regcache
|
|
= get_thread_arch_regcache (info->inf->process_target (),
|
|
info->ptid, gdbarch);
|
|
target_fetch_registers (regcache, -1);
|
|
gcore_elf_collect_thread_registers (regcache, info->ptid, obfd,
|
|
note_data, note_size, stop_signal);
|
|
}
|
|
|
|
/* See gcore-elf.h. */
|
|
|
|
void
|
|
gcore_elf_make_tdesc_note (bfd *obfd,
|
|
gdb::unique_xmalloc_ptr<char> *note_data,
|
|
int *note_size)
|
|
{
|
|
/* Append the target description to the core file. */
|
|
const struct target_desc *tdesc = gdbarch_target_desc (target_gdbarch ());
|
|
const char *tdesc_xml
|
|
= tdesc == nullptr ? nullptr : tdesc_get_features_xml (tdesc);
|
|
if (tdesc_xml != nullptr && *tdesc_xml != '\0')
|
|
{
|
|
/* Skip the leading '@'. */
|
|
if (*tdesc_xml == '@')
|
|
++tdesc_xml;
|
|
|
|
/* Include the null terminator in the length. */
|
|
size_t tdesc_len = strlen (tdesc_xml) + 1;
|
|
|
|
/* Now add the target description into the core file. */
|
|
note_data->reset (elfcore_write_register_note (obfd,
|
|
note_data->release (),
|
|
note_size,
|
|
".gdb-tdesc", tdesc_xml,
|
|
tdesc_len));
|
|
}
|
|
}
|