/* Copyright (C) 2021-2023 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 . */ #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 *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 *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 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 *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 *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 *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)); } }