[PATCH] Support for corefiles for arm-none-eabi target.

Fredrik Hederstierna
2020-10-25T20:47:25Z

From: fredrikhederstierna <fredrik@hederstierna.com>

---
gdb/Makefile.in | 6 +
gdb/arm-none-tdep.c | 161 +++++++++++++++++++++++++++
gdb/arm-none-tdep.h | 31 ++++++
gdb/arm-tdep.c | 11 ++
gdb/configure.tgt | 3 +-
gdb/none-tdep.c | 264 ++++++++++++++++++++++++++++++++++++++++++++
gdb/none-tdep.h | 27 +++++
sim/arm/wrapper.c | 4 +
8 files changed, 506 insertions(+), 1 deletion(-)
create mode 100644 gdb/arm-none-tdep.c
create mode 100644 gdb/arm-none-tdep.h
create mode 100644 gdb/none-tdep.c
create mode 100644 gdb/none-tdep.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 8a160896e2..61a13312ce 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -721,6 +721,7 @@ ALL_TARGET_OBS = \
arm-fbsd-tdep.o \
arm-linux-tdep.o \
arm-netbsd-tdep.o \
+ arm-none-tdep.o \
arm-obsd-tdep.o \
arm-pikeos-tdep.o \
arm-symbian-tdep.o \
@@ -788,6 +789,7 @@ ALL_TARGET_OBS = \
nds32-tdep.o \
nios2-linux-tdep.o \
nios2-tdep.o \
+ none-tdep.o \
nto-tdep.o \
obsd-tdep.o \
or1k-linux-tdep.o \
@@ -1225,6 +1227,7 @@ HFILES_NO_SRCDIR = \
arch-utils.h \
arm-linux-tdep.h \
arm-netbsd-tdep.h \
+ arm-none-tdep.h \
arm-tdep.h \
async-event.h \
auto-load.h \
@@ -1350,6 +1353,7 @@ HFILES_NO_SRCDIR = \
netbsd-tdep.h \
nds32-tdep.h \
nios2-tdep.h \
+ none-tdep.h \
nto-tdep.h \
objc-lang.h \
objfiles.h \
@@ -2152,6 +2156,7 @@ ALLDEPFILES = \
arm-linux-tdep.c \
arm-netbsd-nat.c \
arm-netbsd-tdep.c \
+ arm-none-tdep.c \
arm-obsd-tdep.c \
arm-symbian-tdep.c \
arm-tdep.c \
@@ -2238,6 +2243,7 @@ ALLDEPFILES = \
nds32-tdep.c \
nios2-linux-tdep.c \
nios2-tdep.c \
+ none-tdep.c \
obsd-nat.c \
obsd-tdep.c \
posix-hdep.c \
diff --git a/gdb/arm-none-tdep.c b/gdb/arm-none-tdep.c
new file mode 100644
index 0000000000..0288e13175
--- /dev/null
+++ b/gdb/arm-none-tdep.c
@@ -0,0 +1,161 @@
+/* none on ARM target support.
+
+ Copyright (C) 2020 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 "arm-tdep.h"
+#include "arm-none-tdep.h"
+
+/* Core file and register set support. */
+#define ARM_NONE_SIZEOF_GREGSET (18 * ARM_INT_REGISTER_SIZE)
+
+/* Support VFP register format. */
+#define ARM_NONE_SIZEOF_VFP (32 * 8 + 4)
+
+/* The index to access CSPR in user_regs as defined in GLIBC. */
+#define ARM_NONE_CPSR_GREGNUM 16
+
+/* The ARM none register setup is based on the ARM GNU/Linux design. */
+
+static void
+arm_none_supply_gregset (const struct regset *regset,
+ struct regcache *regcache,
+ int regnum, const void *gregs_buf, size_t len)
+{
+ struct gdbarch *gdbarch = regcache->arch ();
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ const gdb_byte *gregs = (const gdb_byte *) gregs_buf;
+ int regno;
+ CORE_ADDR reg_pc;
+ gdb_byte pc_buf[ARM_INT_REGISTER_SIZE];
+
+ for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++)
+ if (regnum == -1 || regnum == regno)
+ regcache->raw_supply (regno, gregs + ARM_INT_REGISTER_SIZE * regno);
+
+ if (regnum == ARM_PS_REGNUM || regnum == -1)
+ {
+ if (arm_apcs_32)
+ regcache->raw_supply (ARM_PS_REGNUM,
+ gregs + ARM_INT_REGISTER_SIZE * ARM_NONE_CPSR_GREGNUM);
+ else
+ regcache->raw_supply (ARM_PS_REGNUM,
+ gregs + ARM_INT_REGISTER_SIZE * ARM_PC_REGNUM);
+ }
+
+ if (regnum == ARM_PC_REGNUM || regnum == -1)
+ {
+ reg_pc = extract_unsigned_integer (
+ gregs + ARM_INT_REGISTER_SIZE * ARM_PC_REGNUM,
+ ARM_INT_REGISTER_SIZE, byte_order);
+ reg_pc = gdbarch_addr_bits_remove (gdbarch, reg_pc);
+ store_unsigned_integer (pc_buf, ARM_INT_REGISTER_SIZE, byte_order,
+ reg_pc);
+ regcache->raw_supply (ARM_PC_REGNUM, pc_buf);
+ }
+}
+
+static void
+arm_none_collect_gregset (const struct regset *regset,
+ const struct regcache *regcache,
+ int regnum, void *gregs_buf, size_t len)
+{
+ gdb_byte *gregs = (gdb_byte *) gregs_buf;
+ int regno;
+
+ for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++)
+ if (regnum == -1 || regnum == regno)
+ regcache->raw_collect (regno,
+ gregs + ARM_INT_REGISTER_SIZE * regno);
+
+ if (regnum == ARM_PS_REGNUM || regnum == -1)
+ {
+ if (arm_apcs_32)
+ regcache->raw_collect (ARM_PS_REGNUM,
+ gregs + ARM_INT_REGISTER_SIZE * ARM_NONE_CPSR_GREGNUM);
+ else
+ regcache->raw_collect (ARM_PS_REGNUM,
+ gregs + ARM_INT_REGISTER_SIZE * ARM_PC_REGNUM);
+ }
+
+ if (regnum == ARM_PC_REGNUM || regnum == -1)
+ regcache->raw_collect (ARM_PC_REGNUM,
+ gregs + ARM_INT_REGISTER_SIZE * ARM_PC_REGNUM);
+}
+
+/* Support VFP register format. */
+
+static void
+arm_none_supply_vfp (const struct regset *regset,
+ struct regcache *regcache,
+ int regnum, const void *regs_buf, size_t len)
+{
+ const gdb_byte *regs = (const gdb_byte *) regs_buf;
+ int regno;
+
+ if (regnum == ARM_FPSCR_REGNUM || regnum == -1)
+ regcache->raw_supply (ARM_FPSCR_REGNUM, regs + 32 * 8);
+
+ for (regno = ARM_D0_REGNUM; regno <= ARM_D31_REGNUM; regno++)
+ if (regnum == -1 || regnum == regno)
+ regcache->raw_supply (regno, regs + (regno - ARM_D0_REGNUM) * 8);
+}
+
+static void
+arm_none_collect_vfp (const struct regset *regset,
+ const struct regcache *regcache,
+ int regnum, void *regs_buf, size_t len)
+{
+ gdb_byte *regs = (gdb_byte *) regs_buf;
+ int regno;
+
+ if (regnum == ARM_FPSCR_REGNUM || regnum == -1)
+ regcache->raw_collect (ARM_FPSCR_REGNUM, regs + 32 * 8);
+
+ for (regno = ARM_D0_REGNUM; regno <= ARM_D31_REGNUM; regno++)
+ if (regnum == -1 || regnum == regno)
+ regcache->raw_collect (regno, regs + (regno - ARM_D0_REGNUM) * 8);
+}
+
+static const struct regset arm_none_gregset =
+ {
+ nullptr, arm_none_supply_gregset, arm_none_collect_gregset
+ };
+
+static const struct regset arm_none_vfpregset =
+ {
+ nullptr, arm_none_supply_vfp, arm_none_collect_vfp
+ };
+
+/* Iterate over core file register note sections. */
+
+void
+arm_none_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ cb (".reg", ARM_NONE_SIZEOF_GREGSET, ARM_NONE_SIZEOF_GREGSET,
+ &arm_none_gregset, nullptr, cb_data);
+
+ if (tdep->vfp_register_count > 0)
+ cb (".reg-arm-vfp", ARM_NONE_SIZEOF_VFP, ARM_NONE_SIZEOF_VFP,
+ &arm_none_vfpregset, "VFP floating-point", cb_data);
+}
diff --git a/gdb/arm-none-tdep.h b/gdb/arm-none-tdep.h
new file mode 100644
index 0000000000..7e2784ff93
--- /dev/null
+++ b/gdb/arm-none-tdep.h
@@ -0,0 +1,31 @@
+/* none on ARM target support, prototypes.
+
+ Copyright (C) 2020 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/>. */
+
+#ifndef ARM_NONE_TDEP_H
+#define ARM_NONE_TDEP_H
+
+#include "bfd.h"
+#include "regset.h"
+
+void
+ arm_none_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache);
+#endif /* ARM_NONE_TDEP_H */
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index a214f22d7a..c361818367 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -50,6 +50,8 @@
#include "arch/arm.h"
#include "arch/arm-get-next-pcs.h"
#include "arm-tdep.h"
+#include "none-tdep.h"
+#include "arm-none-tdep.h"
#include "gdb/sim-arm.h"

#include "elf-bfd.h"
@@ -4205,6 +4207,10 @@ arm_register_sim_regno (struct gdbarch *gdbarch, int regnum)
if (regnum >= ARM_WCGR0_REGNUM && regnum <= ARM_WCGR7_REGNUM)
return regnum - ARM_WCGR0_REGNUM + SIM_ARM_IWMMXT_COP1R8_REGNUM;

+ /* The ARM simulator does not support D0-D31 nor FPSCR. */
+ if (regnum >= ARM_D0_REGNUM && regnum <= ARM_FPSCR_REGNUM)
+ return -1;
+
if (reg < NUM_GREGS)
return SIM_ARM_R0_REGNUM + reg;
reg -= NUM_GREGS;
@@ -9453,6 +9459,11 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
/* Virtual tables. */
set_gdbarch_vbit_in_delta (gdbarch, 1);

+ /* Default none core file support, can be overridden by osabi. */
+ none_init_corefile (gdbarch);
+ set_gdbarch_iterate_over_regset_sections (gdbarch,
+ arm_none_iterate_over_regset_sections);
+
/* Hook in the ABI-specific overrides, if they have been registered. */
gdbarch_init_osabi (info, gdbarch);

diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index d865ecdcb6..789541d49f 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -64,7 +64,8 @@ arc*-*-*)

arm*-*-*)
cpu_obs="aarch32-tdep.o arch/aarch32.o arch/arm.o \
- arch/arm-get-next-pcs.o arm-tdep.o";;
+ arch/arm-get-next-pcs.o arm-tdep.o \
+ none-tdep.o arm-none-tdep.o";;

hppa*-*-*)
# Target: HP PA-RISC
diff --git a/gdb/none-tdep.c b/gdb/none-tdep.c
new file mode 100644
index 0000000000..b7a49924c6
--- /dev/null
+++ b/gdb/none-tdep.c
@@ -0,0 +1,264 @@
+/* Target-dependent code for none, architecture independent.
+
+ Copyright (C) 2020 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 "none-tdep.h"
+#include "regset.h"
+#include "elf-bfd.h" /* for elfcore_write_* */
+#include "inferior.h"
+
+using none_collect_thread_registers_ftype
+ = void (const struct regcache *, ptid_t, bfd *,
+ gdb::unique_xmalloc_ptr<char> &, int *, enum gdb_signal);
+
+/* The ARM none corefile setup is based on the ARM GNU/Linux design. */
+
+/* Structure for passing information from
+ none_collect_thread_registers via an iterator to
+ none_collect_regset_section_cb. */
+
+struct none_collect_regset_section_cb_data
+{
+ none_collect_regset_section_cb_data (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)
+ : gdbarch (gdbarch), regcache (regcache), obfd (obfd),
+ note_data (note_data), note_size (note_size),
+ lwp (lwp), stop_signal (stop_signal)
+ {}
+
+ 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 corefile note section. */
+
+static void
+none_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 none_collect_regset_section_cb_data *data
+ = (struct none_collect_regset_section_cb_data *) cb_data;
+ bool variable_size_section = (regset != nullptr
+ && regset->flags & REGSET_VARIABLE_SIZE);
+
+ if (!variable_size_section)
+ gdb_assert (supply_size == collect_size);
+
+ if (data->abort_iteration)
+ return;
+
+ gdb_assert (regset && regset->collect_regset);
+
+ /* 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 = 1;
+}
+
+/* Records the thread's register state for the corefile note
+ section. */
+
+static void
+none_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 ();
+ unsigned long lwp;
+
+ /* For remote targets the LWP may not be available, so use the TID. */
+ lwp = ptid.lwp ();
+ if (lwp == 0)
+ lwp = ptid.tid ();
+
+ none_collect_regset_section_cb_data data (gdbarch, regcache, obfd, note_data,
+ note_size, lwp, stop_signal);
+
+ gdbarch_iterate_over_regset_sections (gdbarch,
+ none_collect_regset_section_cb,
+ &data, regcache);
+}
+
+struct none_corefile_thread_data
+{
+ none_corefile_thread_data (struct gdbarch *gdbarch,
+ bfd *obfd,
+ gdb::unique_xmalloc_ptr<char> &note_data,
+ int *note_size,
+ enum gdb_signal stop_signal)
+ : gdbarch (gdbarch), obfd (obfd), note_data (note_data),
+ note_size (note_size), stop_signal (stop_signal)
+ {}
+
+ struct gdbarch *gdbarch;
+ bfd *obfd;
+ gdb::unique_xmalloc_ptr<char> &note_data;
+ int *note_size;
+ enum gdb_signal stop_signal;
+};
+
+/* Records the thread's register state for the corefile note
+ section. */
+
+static void
+none_corefile_thread (struct thread_info *info,
+ struct none_corefile_thread_data *args)
+{
+ struct regcache *regcache;
+
+ regcache = get_thread_arch_regcache (info->inf->process_target (),
+ info->ptid, args->gdbarch);
+
+ target_fetch_registers (regcache, -1);
+ none_collect_thread_registers
+ (regcache, info->ptid, args->obfd, args->note_data,
+ args->note_size, args->stop_signal);
+}
+
+/* Find the signalled thread. In case there's more than one signalled
+ thread, prefer the current thread, if it is signalled. If no
+ thread was signalled, default to the current thread, unless it has
+ exited, in which case return nullptr. */
+
+static thread_info *
+find_signalled_thread ()
+{
+ thread_info *curr_thr = inferior_thread ();
+ if (curr_thr->state != THREAD_EXITED
+ && curr_thr->suspend.stop_signal != GDB_SIGNAL_0)
+ return curr_thr;
+
+ for (thread_info *thr : current_inferior ()->non_exited_threads ())
+ if (thr->suspend.stop_signal != GDB_SIGNAL_0)
+ return thr;
+
+ /* Default to the current thread, unless it has exited. */
+ if (curr_thr->state != THREAD_EXITED)
+ return curr_thr;
+
+ return nullptr;
+}
+
+/* Fills the "to_make_corefile_note" target vector. Builds the note
+ section for a corefile, and returns it in a malloc buffer. */
+
+static gdb::unique_xmalloc_ptr<char>
+none_make_corefile_notes_1 (struct gdbarch *gdbarch, bfd *obfd, int *note_size,
+ none_collect_thread_registers_ftype collect)
+{
+ gdb::unique_xmalloc_ptr<char> note_data;
+
+ /* Process information. */
+ if (get_exec_file (0))
+ {
+ const char *fname = lbasename (get_exec_file (0));
+ std::string psargs = fname;
+ note_data.reset (elfcore_write_prpsinfo (obfd, note_data.release (),
+ note_size, fname,
+ psargs.c_str ()));
+ }
+
+ if (!note_data)
+ return nullptr;
+
+ /* Thread register information. */
+ try
+ {
+ update_thread_list ();
+ }
+ catch (const gdb_exception_error &e)
+ {
+ exception_print (gdb_stderr, e);
+ }
+
+ /* Like the kernel, prefer dumping the signalled thread first.
+ "First thread" is what tools use to infer the signalled
+ thread. */
+ thread_info *signalled_thr = find_signalled_thread ();
+ gdb_signal stop_signal;
+
+ if (signalled_thr != nullptr)
+ stop_signal = signalled_thr->suspend.stop_signal;
+ else
+ stop_signal = GDB_SIGNAL_0;
+
+ none_corefile_thread_data thread_args (gdbarch, obfd, note_data, note_size,
+ stop_signal);
+
+ if (signalled_thr != nullptr)
+ none_corefile_thread (signalled_thr, &thread_args);
+ for (thread_info *thr : current_inferior ()->non_exited_threads ())
+ {
+ if (thr == signalled_thr)
+ continue;
+
+ none_corefile_thread (thr, &thread_args);
+ }
+
+ return note_data;
+}
+
+static gdb::unique_xmalloc_ptr<char>
+none_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
+{
+ return none_make_corefile_notes_1 (gdbarch, obfd, note_size,
+ none_collect_thread_registers);
+}
+
+/* Setup default core file support for none targets.
+ Can be overridden later by OSABI. */
+
+void
+none_init_corefile (struct gdbarch *gdbarch)
+{
+ /* Default core file support. */
+ set_gdbarch_make_corefile_notes (gdbarch, none_make_corefile_notes);
+}
diff --git a/gdb/none-tdep.h b/gdb/none-tdep.h
new file mode 100644
index 0000000000..f25b124019
--- /dev/null
+++ b/gdb/none-tdep.h
@@ -0,0 +1,27 @@
+/* Target-dependent code for none, architecture independent.
+
+ Copyright (C) 2020 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/>. */
+
+#ifndef NONE_TDEP_H
+#define NONE_TDEP_H
+
+#include "gdbarch.h"
+
+void none_init_corefile (struct gdbarch *gdbarch);
+
+#endif /* none-tdep.h */
diff --git a/sim/arm/wrapper.c b/sim/arm/wrapper.c
index 68f8242288..759dac0331 100644
--- a/sim/arm/wrapper.c
+++ b/sim/arm/wrapper.c
@@ -515,6 +515,10 @@ arm_reg_fetch (SIM_CPU *cpu, int rn, unsigned char *memory, int length)

init ();

+ /* Check that memory and length are valid before trying read reg. */
+ if ((memory == NULL) || (length == 0))
+ return 0;
+
switch ((enum sim_arm_regs) rn)
{
case SIM_ARM_R0_REGNUM:
--
2.17.1