[PATCH v2] gdb: add support for handling core dumps on arm-none-eabi

Paul Mathieu via Gdb-patches
2020-10-03T18:15:02Z

Thanks Simon for the super helpful feedback!
A few answers inline, and hopefully a properly formatted patch at the
bottom.

> Note that I am unable to apply this patch either because long lines were
> again wrapped by your email client. Please use git-send-email. You can
> still reply in the thread by using --in-reply-to.

Couldn't get git send-email to work with my SMTP, but I got some version
of mutt to comply. Hopefully you can apply the patch this time.

> Ok, so that answers the question in my other email, about which tool
> produces this format of core. The answer is this specific python
> script, and the format is "Paul's ARM core format" :).

It was some team work between what gdb understood "natively" and what
the patch does.
More specifically, this patch only seems to tell gdb to get the
registers from the core file the way it would normally do, and then
supply them almost verbatim to the regcache.
The struct for these registers is nothing special either, you can find
it in the kernel code as well:
https://elixir.bootlin.com/linux/latest/source/arch/arm/include/uapi/asm/ptrace.h#L135

> I don't know what Luis thinks about this, but I would be a bit hesitant
> to add support in GDB for a core format that's not standard nor the
> output of some "well-known" tool (which would be a de facto standard).
>
> Is there a format that already exists in the ARM ecosystem for core
> dumps of bare-metal systems, we could base our stuff on?

I'm not aware of anything that does that. There are tools that generate
memory dumps and CPU register dumps into their own proprietary format,
but nothing that I know that you could call a 'core file'.

Are there other tools besides gdb that deal with core dumps? Maybe that
could help me find other formats.

That being said, the format used here is "well-known" in the sense that
it's the exact same format the linux kernel would use to dump cores on
arm-linux-*

> Alternatively, do you think you could implement GDB's generate-core-file
> command for bare-metal ARM? First, it would make sense for GDB to be
> able to produce a core in some format and be able to ingest it again.
> And it would act as some kind of documentation / reference
> implementation of what GDB expects, and that could become some de facto
> standard. People who would like to have their core readable by GDB
> would produce it in this format.

I had never cloned the gdb source repo until 2 days ago. I have no idea
how much work that would be, given that I know next to nothing about the
gdb codebase.

I do agree that this seems like a better way to do it, since there are
already many integrations with a gdb server talking to a live bare-metal
target over a physical debugger.

As long as gdb already supports primitives to grab memory dumps and cpu
registers of a remote target, I'd imagine generating a core file
shouldn't be too much work. I'm quite sure most of that functionality
already exists for extremely similar targets.

>> +void _initialize_arm_none_tdep (void);
>> +void
>> +_initialize_arm_none_tdep (void)
>> +{
>> + gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_LINUX,
>> + arm_none_elf_init_abi);
>
> I don't think this is what you want to do. This "declares" that
> combination of architecture ARM and osabi Linux exists, and that
> arm_none_elf_init_abi should be called when such a combo is detected to
> initialize the gdbarch object.
>
> However, such a combo is already registered somewhere else, as you can
> guess:
>
> https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arm-linux-tdep.c;h=f60cb51763f1c5b14811494b646acf54b62d1f10;hb=HEAD#l2010
>
> So I presume that if you build a GDB configured with
> --enable-targets=all (which will include arm-linux-tdep.c), you will hit
> this assertion:
>
> https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/osabi.c;h=e8a813b62f268417edf5f0ad164c9c942bd43b1e;hb=HEAD#l179

Ah, I was afraid of that. Thanks for the pointers.

> What I think you want is to register an osabi sniffer, whose job is to
> try to guess an osabi given a bfd. Apparently that bfd can sometimes
> represent the ELF core file, so you can look up sections and see if that
> file looks like a core file you can handle. And if so, return
> GDB_OSABI_NONE.
>
> And then, you'd need to register (with gdbarch_register_osabi) osabi
> GDB_OSABI_NONE with the arm architecture (same as you did, but with
> GDB_OSABI_NONE instead of GDB_OSABI_LINUX) with the callback to call
> when this combo is detected:
>
> gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_NONE,
> arm_none_elf_init_abi);
>
> I'm not sure about that part, but I think it's the right direction.
>
> Now, the question is how to recognize one of these cores? As an
> example, to recognize Cygwin core dumps (which use ELF files as a
> container without a recognizable ABI tag, so it's kind of the same
> situation as you), we use the .reg section size to determine if the BFD
> is a Cygwin core:
>
> https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/amd64-windows-tdep.c;h=e427c20538961bda2a1a0f033207eebce64c4729;hb=HEAD#l1387
>
> So, you can do that, although I think we all agree that it's not ideal.
> If there was some way of identifying that the core is in the format we
> recognize (say, if there was some special section with a tag), it would
> make it much easier to identify it without having to guess.

Having a sniffer for GDB_OSABI_NONE seems like the right way to do this.

Since the format isn't specified (yet), I can still freely control it. I
imagine I could add some sort of OSABI marker in the core file to mark
it as an arm-none-eabi core.o

My first guess was to set the EI_OSABI e_ident elf header field to
ELFOSABI_NONE, but that happens to be the same enum value as
ELFOSABI_SYSV, which is afaik broadly used by the linux kernel for core
files. So it wouldn't be a good marker.

Not sure what a better way would be to not abuse the ELF structure and
produce reasonable ELF core dumps (since they already work so well with
gdb).

Paul

gdb/Changelog
2018-09-29 Robin Haberkorn <robin.haberkorn@googlemail.com>
2020-10-02 Paul Mathieu <paulmathieu@google.com>
* arm-none-tdep.c: Source file added. Provide CPU registers from a core
file
* floating point registers not yet supported (FIXME)
---
gdb/Makefile.in | 2 +
gdb/arm-none-tdep.c | 106 ++++++++++++++++++++++++++++++++++++++++++++
gdb/configure.tgt | 2 +-
3 files changed, 109 insertions(+), 1 deletion(-)
create mode 100644 gdb/arm-none-tdep.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index dbede7a9cf..7f0e3ea0b0 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -720,6 +720,7 @@ ALL_TARGET_OBS = \
arm-obsd-tdep.o \
arm-pikeos-tdep.o \
arm-symbian-tdep.o \
+ arm-none-tdep.o \
arm-tdep.o \
arm-wince-tdep.o \
avr-tdep.o \
@@ -2150,6 +2151,7 @@ ALLDEPFILES = \
arm-nbsd-tdep.c \
arm-obsd-tdep.c \
arm-symbian-tdep.c \
+ arm-none-tdep.c \
arm-tdep.c \
avr-tdep.c \
bfin-linux-tdep.c \
diff --git a/gdb/arm-none-tdep.c b/gdb/arm-none-tdep.c
new file mode 100644
index 0000000000..21743c40a0
--- /dev/null
+++ b/gdb/arm-none-tdep.c
@@ -0,0 +1,106 @@
+/* Native-dependent code for GDB targetting bare-metal ARM.
+
+ 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 "osabi.h"
+
+#include "arch/arm.h"
+#include "arm-tdep.h"
+#include "gdbarch.h"
+#include "regcache.h"
+#include "regset.h"
+
+struct arm_none_reg
+{
+ uint32_t reg[13];
+ uint32_t sp;
+ uint32_t lr;
+ uint32_t pc;
+ uint32_t cpsr;
+ uint32_t orig_r0;
+};
+
+static void
+arm_none_supply_gregset (const struct regset *regset,
+ struct regcache *regcache, int regnum,
+ const void *gregs, size_t len)
+{
+ const arm_none_reg *gregset = static_cast<const arm_none_reg *> (gregs);
+ gdb_assert (len >= sizeof (arm_none_reg));
+
+ /* Integer registers. */
+ for (int i = ARM_A1_REGNUM; i < ARM_SP_REGNUM; i++)
+ if (regnum == -1 || regnum == i)
+ regcache->raw_supply (i, (char *)&gregset->reg[i]);
+
+ if (regnum == -1 || regnum == ARM_SP_REGNUM)
+ regcache->raw_supply (ARM_SP_REGNUM, (char *)&gregset->sp);
+
+ if (regnum == -1 || regnum == ARM_LR_REGNUM)
+ regcache->raw_supply (ARM_LR_REGNUM, (char *)&gregset->lr);
+
+ if (regnum == -1 || regnum == ARM_PC_REGNUM)
+ {
+ CORE_ADDR r_pc
+ = gdbarch_addr_bits_remove (regcache->arch (), gregset->pc);
+ regcache->raw_supply (ARM_PC_REGNUM, (char *)&r_pc);
+ }
+
+ if (regnum == -1 || regnum == ARM_PS_REGNUM)
+ {
+ if (arm_apcs_32)
+ regcache->raw_supply (ARM_PS_REGNUM, (char *)&gregset->cpsr);
+ else
+ regcache->raw_supply (ARM_PS_REGNUM, (char *)&gregset->pc);
+ }
+}
+
+static const struct regset arm_none_regset
+ = { nullptr, arm_none_supply_gregset,
+ /* We don't need a collect function because we only use this reading
+ registers (via iterate_over_regset_sections and
+ fetch_regs/fetch_register). */
+ nullptr, 0 };
+
+static void
+arm_none_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
+{
+ cb (".reg", sizeof (arm_none_reg), sizeof (arm_none_reg), &arm_none_regset,
+ NULL, cb_data);
+}
+
+static void arm_none_elf_init_abi (struct gdbarch_info info,
+ struct gdbarch *gdbarch);
+static void
+arm_none_elf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ set_gdbarch_iterate_over_regset_sections (
+ gdbarch, arm_none_iterate_over_regset_sections);
+}
+
+void _initialize_arm_none_tdep (void);
+void
+_initialize_arm_none_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_LINUX,
+ arm_none_elf_init_abi);
+}
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index a3e11c4b9b..0cf05efdd1 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -189,7 +189,7 @@ arm*-*-symbianelf*)
;;
arm*-*-*)
# Target: ARM embedded system
- gdb_target_obs="arm-pikeos-tdep.o"
+ gdb_target_obs="arm-pikeos-tdep.o arm-none-tdep.o"
gdb_sim=../sim/arm/libsim.a
;;

--
2.28.0.806.g8561365e88-goog