arm-none-eabi core dump support

Message ID 59ceb22b-2215-fb27-10c0-7a127096e37b@metratec.com
State New, archived
Headers

Commit Message

Robin Haberkorn July 12, 2018, 6:36 p.m. UTC
  Hello!

I had the idea of debugging my embedded ARM project (built with the
arm-none-eabi toolchain) using core dumps. Core dumps would be useful even on
embedded targets for several reasons as you might guess: They can be
automatically created during crashes, even if you don't have a debug probe
attached, stored and later retrieved. They can be created while your firmware is
running, without interrupting it. And most importantly, they allow debugging
when you cannot even use a debug probe, maybe because your JTAG port is already
locked.

While core dump creation on an embedded target turned out to be relatively
simple, I found that arm-none-eabi-gdb, at least as of
embedded-gdb-7.12-branch-2017q2, does not have a core dump handler and throws
the "no core file handler recognizes format" error.
What other people instead do is apparently to make memory and register dumps in
a custom format instead of real core dumps, transferring this file to the PC and
using a custom gdb remote debug stub to feed gdb.
This is at least what these guys are doing apparently:
https://github.com/adamgreen/CrashDebug
Searching the net, including the gdb mailing list, shows that reading core dumps
on arm-none-eabi-gdb is an often asked for feature.

Since I wasn't consent with inventing my own core dump format or integrating
CrashDebug, I simply hacked GDB to support core dump handling via core-regset.c.
See attachment. It applies on top of users/ARM/embedded-gdb-7.12-branch-2017q2,
but not on top of master.
Also, as I said, it's a hack. It modifies several architecture-independent
files. Floating point registers are currently not supported.

While this wont work for all applications, it should for most typical embedded
ARM projects and IMHO it's better than nothing.
I would like to improve this patch and get this upstreamed.
Can you give me some pointers on where the additional patched-in definitions
should be placed instead?
What about core-regset.c - it seems to have been removed in the master branch?

Best regards,
Robin
  

Patch

From 13b7c2d2a602c1830ad4a3f2a50a66e3c4f2f1a3 Mon Sep 17 00:00:00 2001
From: Robin Haberkorn <robin.haberkorn@googlemail.com>
Date: Thu, 12 Jul 2018 17:29:04 +0600
Subject: [PATCH] added support for handling core dumps on arm-none-eabi-gdb
 (Embedded ARM)

 * this is merely a hack and not yet upstreamable, as it modifies core-regset.c,
   corelow.c and gregset.h with definitions taken from here and there.
 * core-regset.c is missing the following definitions:
   supply_gregset(), GDB_GREGSET_T, GDB_FPREGSET_T
 * probably, they should be added as a new module, but I don't know where
   it should be put. This should be discussed on the GDB mailing list.
 * also floatin point register retrieval is still missing and has simply
   been commented out.
---
 gdb/configure.tgt |  2 +-
 gdb/core-regset.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/corelow.c     |  2 +-
 gdb/gregset.h     |  3 +++
 4 files changed, 58 insertions(+), 2 deletions(-)

diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 7f1aac3..438f35b 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -112,7 +112,7 @@  arm*-*-symbianelf*)
 	;;
 arm*-*-*)
 	# Target: ARM embedded system
-	gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o"
+	gdb_target_obs="arm.o arm-get-next-pcs.o arm-tdep.o core-regset.o"
 	gdb_sim=../sim/arm/libsim.a
 	;;
 
diff --git a/gdb/core-regset.c b/gdb/core-regset.c
index 278887b..2227f5a 100644
--- a/gdb/core-regset.c
+++ b/gdb/core-regset.c
@@ -39,9 +39,60 @@ 
 #include <sys/procfs.h>
 #endif
 
+#include "arch/arm.h"
+
 /* Prototypes for supply_gregset etc.  */
 #include "gregset.h"
 
+#define ARM_CPSR_GREGNUM 16
+extern int arm_apcs_32;
+
+#define ARM_LINUX_SIZEOF_GREGSET (18 * INT_REGISTER_SIZE)
+
+void
+arm_linux_supply_gregset (const struct regset *regset,
+			  struct regcache *regcache,
+			  int regnum, const void *gregs_buf, size_t len)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  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[INT_REGISTER_SIZE];
+
+  for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++)
+    if (regnum == -1 || regnum == regno)
+      regcache_raw_supply (regcache, regno,
+			   gregs + INT_REGISTER_SIZE * regno);
+
+  if (regnum == ARM_PS_REGNUM || regnum == -1)
+    {
+      if (arm_apcs_32)
+	regcache_raw_supply (regcache, ARM_PS_REGNUM,
+			     gregs + INT_REGISTER_SIZE * ARM_CPSR_GREGNUM);
+      else
+	regcache_raw_supply (regcache, ARM_PS_REGNUM,
+			     gregs + INT_REGISTER_SIZE * ARM_PC_REGNUM);
+    }
+
+  if (regnum == ARM_PC_REGNUM || regnum == -1)
+    {
+      reg_pc = extract_unsigned_integer (gregs
+					 + INT_REGISTER_SIZE * ARM_PC_REGNUM,
+					 INT_REGISTER_SIZE, byte_order);
+      reg_pc = gdbarch_addr_bits_remove (gdbarch, reg_pc);
+      store_unsigned_integer (pc_buf, INT_REGISTER_SIZE, byte_order, reg_pc);
+      regcache_raw_supply (regcache, ARM_PC_REGNUM, pc_buf);
+    }
+}
+
+void
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp)
+{
+  arm_linux_supply_gregset (NULL, regcache, -1, gregsetp, 0);
+}
+
 /* Provide registers to GDB from a core file.
 
    CORE_REG_SECT points to an array of bytes, which are the contents
@@ -84,9 +135,11 @@  fetch_core_registers (struct regcache *regcache,
       else
 	{
 	  memcpy (&fpregset, core_reg_sect, sizeof (fpregset));
+#if 0
 	  if (gdbarch_fp0_regnum (get_regcache_arch (regcache)) >= 0)
 	    supply_fpregset (regcache,
 			     (const gdb_fpregset_t *) fpregset_p);
+#endif
 	}
       break;
 
diff --git a/gdb/corelow.c b/gdb/corelow.c
index c46af0a..504fd26 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -541,7 +541,7 @@  get_core_register_section (struct regcache *regcache,
       warning (_("Section `%s' in core file too small."), section_name);
       return;
     }
-  if (size != min_size && !(regset->flags & REGSET_VARIABLE_SIZE))
+  if (size != min_size && regset && !(regset->flags & REGSET_VARIABLE_SIZE))
     {
       warning (_("Unexpected size of section `%s' in core file."),
 	       section_name);
diff --git a/gdb/gregset.h b/gdb/gregset.h
index 3101edc..ff85d46 100644
--- a/gdb/gregset.h
+++ b/gdb/gregset.h
@@ -23,6 +23,9 @@ 
 #include <sys/procfs.h>
 #endif
 
+#define GDB_GREGSET_T struct { uint32_t reg[18]; }
+#define GDB_FPREGSET_T struct { uint32_t reg[18]; }
+
 #ifndef GDB_GREGSET_T
 #define GDB_GREGSET_T gregset_t
 #endif
-- 
2.7.4