arc: Add support for Linux coredump files
authorAnton Kolesov <Anton.Kolesov@synopsys.com>
Mon, 30 Jan 2017 15:32:10 +0000 (18:32 +0300)
committerShahab Vahedi <shahab@synopsys.com>
Wed, 7 Oct 2020 16:08:56 +0000 (18:08 +0200)
With the implemenations in this patch, ARC gdb can handle
coredump related matters.  The binutils counter part of
this patch has already been pushed [1].

v2 [2]:
- arc_linux_collect_gregset: Use "reg <= ARC_LAST_REGNUM" instead of
  "reg < ARC_LAST_REGNUM" for the condition check of the for-loop.
- arc-linux-tdep.c: Use "ARC_LAST_REGNUM < ARRAY_SIZE (...)" instead of
  "ARC_LAST_REGNUM <= ARRAY_SIZE (...)" for the "asserts".
- Use "buf + arc_linux_core_reg_offsets[ARC_ERET_REGNUM]" instead of
  "buf + REG_OFF (6)".
- Fix a few typos/indentation.

v3 [3]:
- Use gdb_assert_not_reached(text) instead of gdb_assert (!text).
- Remove unnecessary braces in the for loop.

[1] arc: Add support for ARC HS extra registers in core files
https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=2745674244d6aecddcf636475034bdb9c0a6b4a0

[2] First remarks
https://sourceware.org/pipermail/gdb-patches/2020-September/171912.html

[3] Second remarks
https://sourceware.org/pipermail/gdb-patches/2020-October/172302.html

gdb/ChangeLog:

* arc-linux-tdep.h: New file.
* arc-linux-tdep.c (arc_linux_core_reg_offsets,
arc_linux_supply_gregset, arc_linux_supply_v2_regset,
arc_linux_collect_gregset, arc_linux_collect_v2_regset,
arc_linux_gregset, arc_linux_v2_regset,
arc_linux_iterate_over_regset_sections,
arc_linux_core_read_description): Implement.
(arc_linux_init_osabi): Set iterate_over_regset_sections.
* arc-tdep.h (ARC_OFFSET_NO_REGISTER): Declare.
        (arc_gdbarch_features_create): Add.
* arc-tdep.c (arc_gdbarch_features_create): Not static anymore.

gdb/ChangeLog
gdb/arc-linux-tdep.c
gdb/arc-linux-tdep.h [new file with mode: 0644]
gdb/arc-tdep.c
gdb/arc-tdep.h

index 3caea814a67e62472c1ca58cf5d0f01a5ee09665..3e72d920ae4ec1e30bf51f1350439d3879d43da7 100644 (file)
@@ -1,3 +1,17 @@
+2020-10-07  Anton Kolesov  <anton.kolesov@synopsys.com>
+
+       * arc-linux-tdep.h: New file.
+       * arc-linux-tdep.c (arc_linux_core_reg_offsets,
+       arc_linux_supply_gregset, arc_linux_supply_v2_regset,
+       arc_linux_collect_gregset, arc_linux_collect_v2_regset,
+       arc_linux_gregset, arc_linux_v2_regset,
+       arc_linux_iterate_over_regset_sections,
+       arc_linux_core_read_description): Implement.
+       (arc_linux_init_osabi): Set iterate_over_regset_sections.
+       * arc-tdep.h (ARC_OFFSET_NO_REGISTER): Declare.
+       (arc_gdbarch_features_create): Add.
+       * arc-tdep.c (arc_gdbarch_features_create): Not static anymore.
+
 2020-10-07  Shahab Vahedi  <shahab@synopsys.com>
 
        * arch/arc.h: Rename "arc_gdbarch_features" to
index 36f32459bbeeab7fb269853d3b4411be45cbe4b5..9ff5f1214a196b1a0a74975ee68673a0f9a9f475 100644 (file)
 
 /* ARC header files.  */
 #include "opcodes/arc-dis.h"
+#include "arc-linux-tdep.h"
 #include "arc-tdep.h"
+#include "arch/arc.h"
+
+#define REGOFF(offset) (offset * ARC_REGISTER_SIZE)
+
+/* arc_linux_core_reg_offsets[i] is the offset in the .reg section of GDB
+   regnum i.  Array index is an internal GDB register number, as defined in
+   arc-tdep.h:arc_regnum.
+
+   From include/uapi/asm/ptrace.h in the ARC Linux sources.  */
+
+/* The layout of this struct is tightly bound to "arc_regnum" enum
+   in arc-tdep.h.  Any change of order in there, must be reflected
+   here as well.  */
+static const int arc_linux_core_reg_offsets[] = {
+  /* R0 - R12.  */
+  REGOFF (22), REGOFF (21), REGOFF (20), REGOFF (19),
+  REGOFF (18), REGOFF (17), REGOFF (16), REGOFF (15),
+  REGOFF (14), REGOFF (13), REGOFF (12), REGOFF (11),
+  REGOFF (10),
+
+  /* R13 - R25.  */
+  REGOFF (37), REGOFF (36), REGOFF (35), REGOFF (34),
+  REGOFF (33), REGOFF (32), REGOFF (31), REGOFF (30),
+  REGOFF (29), REGOFF (28), REGOFF (27), REGOFF (26),
+  REGOFF (25),
+
+  REGOFF (9),                  /* R26 (GP) */
+  REGOFF (8),                  /* FP */
+  REGOFF (23),                 /* SP */
+  ARC_OFFSET_NO_REGISTER,      /* ILINK */
+  ARC_OFFSET_NO_REGISTER,      /* R30 */
+  REGOFF (7),                  /* BLINK */
+
+  /* R32 - R59.  */
+  ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER,
+  ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER,
+  ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER,
+  ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER,
+  ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER,
+  ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER,
+  ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER,
+  ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER,
+  ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER,
+  ARC_OFFSET_NO_REGISTER,
+
+  REGOFF (4),                  /* LP_COUNT */
+  ARC_OFFSET_NO_REGISTER,      /* RESERVED */
+  ARC_OFFSET_NO_REGISTER,      /* LIMM */
+  ARC_OFFSET_NO_REGISTER,      /* PCL */
+
+  REGOFF (39),                 /* PC  */
+  REGOFF (5),                  /* STATUS32 */
+  REGOFF (2),                  /* LP_START */
+  REGOFF (3),                  /* LP_END */
+  REGOFF (1),                  /* BTA */
+  REGOFF (6)                   /* ERET */
+};
 
 /* Implement the "cannot_fetch_register" gdbarch method.  */
 
@@ -227,6 +285,136 @@ arc_linux_skip_solib_resolver (struct gdbarch *gdbarch, CORE_ADDR pc)
     }
 }
 
+void
+arc_linux_supply_gregset (const struct regset *regset,
+                         struct regcache *regcache,
+                         int regnum, const void *gregs, size_t size)
+{
+  gdb_static_assert (ARC_LAST_REGNUM
+                    < ARRAY_SIZE (arc_linux_core_reg_offsets));
+
+  const bfd_byte *buf = (const bfd_byte *) gregs;
+
+  for (int reg = 0; reg <= ARC_LAST_REGNUM; reg++)
+      if (arc_linux_core_reg_offsets[reg] != ARC_OFFSET_NO_REGISTER)
+       regcache->raw_supply (reg, buf + arc_linux_core_reg_offsets[reg]);
+}
+
+void
+arc_linux_supply_v2_regset (const struct regset *regset,
+                           struct regcache *regcache, int regnum,
+                           const void *v2_regs, size_t size)
+{
+  const bfd_byte *buf = (const bfd_byte *) v2_regs;
+
+  /* user_regs_arcv2 is defined in linux arch/arc/include/uapi/asm/ptrace.h.  */
+  regcache->raw_supply (ARC_R30_REGNUM, buf);
+  regcache->raw_supply (ARC_R58_REGNUM, buf + REGOFF (1));
+  regcache->raw_supply (ARC_R59_REGNUM, buf + REGOFF (2));
+}
+
+/* Populate BUF with register REGNUM from the REGCACHE.  */
+
+static void
+collect_register (const struct regcache *regcache, struct gdbarch *gdbarch,
+                 int regnum, gdb_byte *buf)
+{
+  /* Skip non-existing registers.  */
+  if ((arc_linux_core_reg_offsets[regnum] == ARC_OFFSET_NO_REGISTER))
+    return;
+
+  /* The address where the execution has stopped is in pseudo-register
+     STOP_PC.  However, when kernel code is returning from the exception,
+     it uses the value from ERET register.  Since, TRAP_S (the breakpoint
+     instruction) commits, the ERET points to the next instruction.  In
+     other words: ERET != STOP_PC.  To jump back from the kernel code to
+     the correct address, ERET must be overwritten by GDB's STOP_PC.  Else,
+     the program will continue at the address after the current instruction.
+     */
+  if (regnum == gdbarch_pc_regnum (gdbarch))
+    regnum = ARC_ERET_REGNUM;
+  regcache->raw_collect (regnum, buf + arc_linux_core_reg_offsets[regnum]);
+}
+
+void
+arc_linux_collect_gregset (const struct regset *regset,
+                          const struct regcache *regcache,
+                          int regnum, void *gregs, size_t size)
+{
+  gdb_static_assert (ARC_LAST_REGNUM
+                    < ARRAY_SIZE (arc_linux_core_reg_offsets));
+
+  gdb_byte *buf = (gdb_byte *) gregs;
+  struct gdbarch *gdbarch = regcache->arch ();
+
+  /* regnum == -1 means writing all the registers.  */
+  if (regnum == -1)
+    for (int reg = 0; reg <= ARC_LAST_REGNUM; reg++)
+      collect_register (regcache, gdbarch, reg, buf);
+  else if (regnum <= ARC_LAST_REGNUM)
+    collect_register (regcache, gdbarch, regnum, buf);
+  else
+    gdb_assert_not_reached ("Invalid regnum in arc_linux_collect_gregset.");
+}
+
+void
+arc_linux_collect_v2_regset (const struct regset *regset,
+                            const struct regcache *regcache, int regnum,
+                            void *v2_regs, size_t size)
+{
+  bfd_byte *buf = (bfd_byte *) v2_regs;
+
+  regcache->raw_collect (ARC_R30_REGNUM, buf);
+  regcache->raw_collect (ARC_R58_REGNUM, buf + REGOFF (1));
+  regcache->raw_collect (ARC_R59_REGNUM, buf + REGOFF (2));
+}
+
+/* Linux regset definitions.  */
+
+static const struct regset arc_linux_gregset = {
+  arc_linux_core_reg_offsets,
+  arc_linux_supply_gregset,
+  arc_linux_collect_gregset,
+};
+
+static const struct regset arc_linux_v2_regset = {
+  NULL,
+  arc_linux_supply_v2_regset,
+  arc_linux_collect_v2_regset,
+};
+
+/* Implement the `iterate_over_regset_sections` gdbarch method.  */
+
+static void
+arc_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
+                                       iterate_over_regset_sections_cb *cb,
+                                       void *cb_data,
+                                       const struct regcache *regcache)
+{
+  /* There are 40 registers in Linux user_regs_struct, although some of
+     them are now just a mere paddings, kept to maintain binary
+     compatibility with older tools.  */
+  const int sizeof_gregset = 40 * ARC_REGISTER_SIZE;
+
+  cb (".reg", sizeof_gregset, sizeof_gregset, &arc_linux_gregset, NULL,
+      cb_data);
+  cb (".reg-arc-v2", ARC_LINUX_SIZEOF_V2_REGSET, ARC_LINUX_SIZEOF_V2_REGSET,
+      &arc_linux_v2_regset, NULL, cb_data);
+}
+
+/* Implement the `core_read_description` gdbarch method.  */
+
+static const struct target_desc *
+arc_linux_core_read_description (struct gdbarch *gdbarch,
+                                struct target_ops *target,
+                                bfd *abfd)
+{
+  arc_arch_features features
+    = arc_arch_features_create (abfd,
+                               gdbarch_bfd_arch_info (gdbarch)->mach);
+  return arc_lookup_target_description (features);
+}
+
 /* Initialization specific to Linux environment.  */
 
 static void
@@ -260,6 +448,9 @@ arc_linux_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_gdbarch_software_single_step (gdbarch, arc_linux_software_single_step);
   set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
   set_gdbarch_skip_solib_resolver (gdbarch, arc_linux_skip_solib_resolver);
+  set_gdbarch_iterate_over_regset_sections
+    (gdbarch, arc_linux_iterate_over_regset_sections);
+  set_gdbarch_core_read_description (gdbarch, arc_linux_core_read_description);
 
   /* GNU/Linux uses SVR4-style shared libraries, with 32-bit ints, longs
      and pointers (ILP32).  */
diff --git a/gdb/arc-linux-tdep.h b/gdb/arc-linux-tdep.h
new file mode 100644 (file)
index 0000000..c26cacd
--- /dev/null
@@ -0,0 +1,52 @@
+/* Target dependent code for GNU/Linux ARC.
+
+   Copyright 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 ARC_LINUX_TDEP_H
+#define ARC_LINUX_TDEP_H
+
+#include "gdbarch.h"
+#include "regset.h"
+
+#define ARC_LINUX_SIZEOF_V2_REGSET (3 * ARC_REGISTER_SIZE)
+
+/* Reads registers from the NT_PRSTATUS data array into the regcache.  */
+
+void arc_linux_supply_gregset (const struct regset *regset,
+                              struct regcache *regcache, int regnum,
+                              const void *gregs, size_t size);
+
+/* Reads regsiters from the NT_ARC_V2 data array into the regcache.  */
+
+void arc_linux_supply_v2_regset (const struct regset *regset,
+                                struct regcache *regcache, int regnum,
+                                const void *v2_regs, size_t size);
+
+/* Writes registers from the regcache into the NT_PRSTATUS data array.  */
+
+void arc_linux_collect_gregset (const struct regset *regset,
+                               const struct regcache *regcache,
+                               int regnum, void *gregs, size_t size);
+
+/* Writes registers from the regcache into the NT_ARC_V2 data array.  */
+
+void arc_linux_collect_v2_regset (const struct regset *regset,
+                                 const struct regcache *regcache,
+                                 int regnum, void *v2_regs, size_t size);
+
+#endif /* ARC_LINUX_TDEP_H */
index ec624bcbb9b462cd3ec2d98670eebddf03a15c1a..3b7a419dfc18cac4aaebda4b4999ee26c00adce1 100644 (file)
@@ -1883,10 +1883,9 @@ mach_type_to_arc_isa (const unsigned long mach)
     }
 }
 
-/* Common construction code for ARC_ARCH_FEATURES struct.  If there
-   is no ABFD, then a FEATURE with default values is returned.  */
+/* See arc-tdep.h.  */
 
-static arc_arch_features
+arc_arch_features
 arc_arch_features_create (const bfd *abfd, const unsigned long mach)
 {
   /* Use 4 as a fallback value.  */
index 6331d29f402482830ac48ffb905b46b0966c47c8..09fcbeab9c785ceb2a3232f3d7ceba5413c63232 100644 (file)
@@ -85,7 +85,9 @@ enum arc_regnum
     ARC_LP_END_REGNUM,
     /* Branch target address.  */
     ARC_BTA_REGNUM,
-    ARC_LAST_AUX_REGNUM = ARC_BTA_REGNUM,
+    /* Exception return address.  */
+    ARC_ERET_REGNUM,
+    ARC_LAST_AUX_REGNUM = ARC_ERET_REGNUM,
     ARC_LAST_REGNUM = ARC_LAST_AUX_REGNUM,
 
     /* Additional ABI constants.  */
@@ -105,6 +107,9 @@ enum arc_regnum
 /* STATUS32 register: current instruction is a delay slot.  */
 #define ARC_STATUS32_DE_MASK (1 << 6)
 
+/* Special value for register offset arrays.  */
+#define ARC_OFFSET_NO_REGISTER (-1)
+
 #define arc_print(fmt, args...) fprintf_unfiltered (gdb_stdlog, fmt, ##args)
 
 extern int arc_debug;
@@ -182,4 +187,9 @@ CORE_ADDR arc_insn_get_branch_target (const struct arc_instruction &insn);
 
 CORE_ADDR arc_insn_get_linear_next_pc (const struct arc_instruction &insn);
 
+/* Create an arc_arch_features instance from the provided data.  */
+
+arc_arch_features arc_arch_features_create (const bfd *abfd,
+                                           const unsigned long mach);
+
 #endif /* ARC_TDEP_H */