Native debug arm program by aarch64 GDB
authorYao Qi <yao.qi@linaro.org>
Tue, 7 Jul 2015 15:58:19 +0000 (16:58 +0100)
committerYao Qi <yao.qi@linaro.org>
Tue, 7 Jul 2015 15:58:19 +0000 (16:58 +0100)
This patch is to let aarch64 GDB debug 32-bit arm program natively.  In
each function for fetching and storing registers, GDB will check
gdbarch_bfd_arch_info (gdbarch)->bits_per_word, if it is 32, call
the corresponding aarch32 functions in aarch32-linux-nat.c, otherwise
fall back to aarch64 code to fetch and store registers.

aarch64_linux_read_description has to return the right target description,
but we don't have gdbarch available there, so GDB fetches auxv and gets
AT_PHENT, in order to determine whether the target is 32-bit or 64-bit.
I learned this trick from solib-svr4.c.

gdb:

2015-07-07  Yao Qi  <yao.qi@linaro.org>

* aarch32-linux-nat.h (VFP_REGS_SIZE): New macro, moved from
arm-linux-nat.c.
* aarch64-linux-nat.c: Include aarch32-linux-nat.h and
elf/external.h.
(fetch_gregs_from_thread): Call aarch32_gp_regcache_supply
if target is 32-bit.
(store_gregs_to_thread): Call aarch32_gp_regcache_collect
if target is 32-bit.
(fetch_fpregs_from_thread): Call aarch32_vfp_regcache_supply
if target is 32-bit.
(store_fpregs_to_thread): Call aarch32_vfp_regcache_collect
if target is 32-bit.
(tdesc_arm_with_vfpv3, tdesc_arm_with_neon): Declare.
(aarch64_linux_read_description): Return the right target
description.
* arm-linux-nat.c (VFP_REGS_SIZE): Moved to aarch32-linux-nat.h.
* config/aarch64/linux.mh (NATDEPFILES): Add aarch32-linux-nat.o.
* configure.tgt (aarch64*-*-linux*): Add arm-tdep.o and
arm-linux-tdep.o

gdb/ChangeLog
gdb/aarch32-linux-nat.h
gdb/aarch64-linux-nat.c
gdb/arm-linux-nat.c
gdb/config/aarch64/linux.mh
gdb/configure.tgt

index 2ac936626bbbfa4bfbec6148aa015c3397f7ad01..c1f3994664e2e62cf5eb79d7f6a9d34502914d35 100644 (file)
@@ -1,3 +1,25 @@
+2015-07-07  Yao Qi  <yao.qi@linaro.org>
+
+       * aarch32-linux-nat.h (VFP_REGS_SIZE): New macro, moved from
+       arm-linux-nat.c.
+       * aarch64-linux-nat.c: Include aarch32-linux-nat.h and
+       elf/external.h.
+       (fetch_gregs_from_thread): Call aarch32_gp_regcache_supply
+       if target is 32-bit.
+       (store_gregs_to_thread): Call aarch32_gp_regcache_collect
+       if target is 32-bit.
+       (fetch_fpregs_from_thread): Call aarch32_vfp_regcache_supply
+       if target is 32-bit.
+       (store_fpregs_to_thread): Call aarch32_vfp_regcache_collect
+       if target is 32-bit.
+       (tdesc_arm_with_vfpv3, tdesc_arm_with_neon): Declare.
+       (aarch64_linux_read_description): Return the right target
+       description.
+       * arm-linux-nat.c (VFP_REGS_SIZE): Moved to aarch32-linux-nat.h.
+       * config/aarch64/linux.mh (NATDEPFILES): Add aarch32-linux-nat.o.
+       * configure.tgt (aarch64*-*-linux*): Add arm-tdep.o and
+       arm-linux-tdep.o.
+
 2015-07-07  Yao Qi  <yao.qi@linaro.org>
 
        * aarch32-linux-nat.c: New file.
index 1b7ff83e937c647df3da14be56936176fb1382b1..d7b5e166c941e019ab98bd294b7322b8e9b52473 100644 (file)
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
+/* Fetch and store VFP Registers.  The kernel object has space for 32
+   64-bit registers, and the FPSCR.  This is even when on a VFPv2 or
+   VFPv3D16 target.  */
+#define VFP_REGS_SIZE (32 * 8 + 4)
+
 void aarch32_gp_regcache_supply (struct regcache *regcache, uint32_t *regs,
                                 int arm_apcs_32);
 
index 9959b81a2d4d18ee71fde322c455b7d95bba51e5..d48624fc723b0c383da2e34e28c1862e4100ec92 100644 (file)
@@ -29,6 +29,9 @@
 #include "gdbcmd.h"
 #include "aarch64-tdep.h"
 #include "aarch64-linux-tdep.h"
+#include "aarch32-linux-nat.h"
+
+#include "elf/external.h"
 #include "elf/common.h"
 
 #include <sys/ptrace.h>
@@ -458,22 +461,36 @@ aarch64_show_debug_reg_state (struct aarch64_debug_reg_state *state,
 static void
 fetch_gregs_from_thread (struct regcache *regcache)
 {
-  int ret, regno, tid;
+  int ret, tid;
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
   elf_gregset_t regs;
   struct iovec iovec;
 
+  /* Make sure REGS can hold all registers contents on both aarch64
+     and arm.  */
+  gdb_static_assert (sizeof (regs) >= 18 * 4);
+
   tid = get_thread_id (inferior_ptid);
 
   iovec.iov_base = &regs;
-  iovec.iov_len = sizeof (regs);
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+    iovec.iov_len = 18 * 4;
+  else
+    iovec.iov_len = sizeof (regs);
 
   ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec);
   if (ret < 0)
     perror_with_name (_("Unable to fetch general registers."));
 
-  for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
-    regcache_raw_supply (regcache, regno,
-                        (char *) &regs[regno - AARCH64_X0_REGNUM]);
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+    aarch32_gp_regcache_supply (regcache, (uint32_t *) regs, 1);
+  else
+    {
+      int regno;
+
+      for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
+       regcache_raw_supply (regcache, regno, &regs[regno - AARCH64_X0_REGNUM]);
+    }
 }
 
 /* Store to the current thread the valid general-purpose register
@@ -482,23 +499,37 @@ fetch_gregs_from_thread (struct regcache *regcache)
 static void
 store_gregs_to_thread (const struct regcache *regcache)
 {
-  int ret, regno, tid;
+  int ret, tid;
   elf_gregset_t regs;
   struct iovec iovec;
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
 
+  /* Make sure REGS can hold all registers contents on both aarch64
+     and arm.  */
+  gdb_static_assert (sizeof (regs) >= 18 * 4);
   tid = get_thread_id (inferior_ptid);
 
   iovec.iov_base = &regs;
-  iovec.iov_len = sizeof (regs);
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+    iovec.iov_len = 18 * 4;
+  else
+    iovec.iov_len = sizeof (regs);
 
   ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec);
   if (ret < 0)
     perror_with_name (_("Unable to fetch general registers."));
 
-  for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
-    if (REG_VALID == regcache_register_status (regcache, regno))
-      regcache_raw_collect (regcache, regno,
-                           (char *) &regs[regno - AARCH64_X0_REGNUM]);
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+    aarch32_gp_regcache_collect (regcache, (uint32_t *) regs, 1);
+  else
+    {
+      int regno;
+
+      for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
+       if (REG_VALID == regcache_register_status (regcache, regno))
+         regcache_raw_collect (regcache, regno,
+                               &regs[regno - AARCH64_X0_REGNUM]);
+    }
 
   ret = ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, &iovec);
   if (ret < 0)
@@ -511,25 +542,46 @@ store_gregs_to_thread (const struct regcache *regcache)
 static void
 fetch_fpregs_from_thread (struct regcache *regcache)
 {
-  int ret, regno, tid;
+  int ret, tid;
   elf_fpregset_t regs;
   struct iovec iovec;
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+
+  /* Make sure REGS can hold all VFP registers contents on both aarch64
+     and arm.  */
+  gdb_static_assert (sizeof regs >= VFP_REGS_SIZE);
 
   tid = get_thread_id (inferior_ptid);
 
   iovec.iov_base = &regs;
-  iovec.iov_len = sizeof (regs);
 
-  ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
-  if (ret < 0)
-    perror_with_name (_("Unable to fetch FP/SIMD registers."));
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+    {
+      iovec.iov_len = VFP_REGS_SIZE;
+
+      ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_VFP, &iovec);
+      if (ret < 0)
+       perror_with_name (_("Unable to fetch VFP registers."));
+
+      aarch32_vfp_regcache_supply (regcache, (gdb_byte *) &regs, 32);
+    }
+  else
+    {
+      int regno;
+
+      iovec.iov_len = sizeof (regs);
 
-  for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
-    regcache_raw_supply (regcache, regno,
-                        (char *) &regs.vregs[regno - AARCH64_V0_REGNUM]);
+      ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
+      if (ret < 0)
+       perror_with_name (_("Unable to fetch vFP/SIMD registers."));
 
-  regcache_raw_supply (regcache, AARCH64_FPSR_REGNUM, (char *) &regs.fpsr);
-  regcache_raw_supply (regcache, AARCH64_FPCR_REGNUM, (char *) &regs.fpcr);
+      for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
+       regcache_raw_supply (regcache, regno,
+                            &regs.vregs[regno - AARCH64_V0_REGNUM]);
+
+      regcache_raw_supply (regcache, AARCH64_FPSR_REGNUM, &regs.fpsr);
+      regcache_raw_supply (regcache, AARCH64_FPCR_REGNUM, &regs.fpcr);
+    }
 }
 
 /* Store to the current thread the valid fp/simd register
@@ -538,32 +590,63 @@ fetch_fpregs_from_thread (struct regcache *regcache)
 static void
 store_fpregs_to_thread (const struct regcache *regcache)
 {
-  int ret, regno, tid;
+  int ret, tid;
   elf_fpregset_t regs;
   struct iovec iovec;
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
 
+  /* Make sure REGS can hold all VFP registers contents on both aarch64
+     and arm.  */
+  gdb_static_assert (sizeof regs >= VFP_REGS_SIZE);
   tid = get_thread_id (inferior_ptid);
 
   iovec.iov_base = &regs;
-  iovec.iov_len = sizeof (regs);
 
-  ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
-  if (ret < 0)
-    perror_with_name (_("Unable to fetch FP/SIMD registers."));
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+    {
+      iovec.iov_len = VFP_REGS_SIZE;
+
+      ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_VFP, &iovec);
+      if (ret < 0)
+       perror_with_name (_("Unable to fetch VFP registers."));
+
+      aarch32_vfp_regcache_collect (regcache, (gdb_byte *) &regs, 32);
+    }
+  else
+    {
+      int regno;
 
-  for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
-    if (REG_VALID == regcache_register_status (regcache, regno))
-      regcache_raw_collect (regcache, regno,
-                           (char *) &regs.vregs[regno - AARCH64_V0_REGNUM]);
+      iovec.iov_len = sizeof (regs);
 
-  if (REG_VALID == regcache_register_status (regcache, AARCH64_FPSR_REGNUM))
-    regcache_raw_collect (regcache, AARCH64_FPSR_REGNUM, (char *) &regs.fpsr);
-  if (REG_VALID == regcache_register_status (regcache, AARCH64_FPCR_REGNUM))
-    regcache_raw_collect (regcache, AARCH64_FPCR_REGNUM, (char *) &regs.fpcr);
+      ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
+      if (ret < 0)
+       perror_with_name (_("Unable to fetch FP/SIMD registers."));
 
-  ret = ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iovec);
-  if (ret < 0)
-    perror_with_name (_("Unable to store FP/SIMD registers."));
+      for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
+       if (REG_VALID == regcache_register_status (regcache, regno))
+         regcache_raw_collect (regcache, regno,
+                               (char *) &regs.vregs[regno - AARCH64_V0_REGNUM]);
+
+      if (REG_VALID == regcache_register_status (regcache, AARCH64_FPSR_REGNUM))
+       regcache_raw_collect (regcache, AARCH64_FPSR_REGNUM,
+                             (char *) &regs.fpsr);
+      if (REG_VALID == regcache_register_status (regcache, AARCH64_FPCR_REGNUM))
+       regcache_raw_collect (regcache, AARCH64_FPCR_REGNUM,
+                             (char *) &regs.fpcr);
+    }
+
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+    {
+      ret = ptrace (PTRACE_SETREGSET, tid, NT_ARM_VFP, &iovec);
+      if (ret < 0)
+       perror_with_name (_("Unable to store VFP registers."));
+    }
+  else
+    {
+      ret = ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iovec);
+      if (ret < 0)
+       perror_with_name (_("Unable to store FP/SIMD registers."));
+    }
 }
 
 /* Implement the "to_fetch_register" target_ops method.  */
@@ -823,11 +906,54 @@ aarch64_linux_child_post_startup_inferior (struct target_ops *self,
   super_post_startup_inferior (self, ptid);
 }
 
+extern struct target_desc *tdesc_arm_with_vfpv3;
+extern struct target_desc *tdesc_arm_with_neon;
+
 /* Implement the "to_read_description" target_ops method.  */
 
 static const struct target_desc *
 aarch64_linux_read_description (struct target_ops *ops)
 {
+  CORE_ADDR at_phent;
+
+  if (target_auxv_search (ops, AT_PHENT, &at_phent) == 1)
+    {
+      if (at_phent == sizeof (Elf64_External_Phdr))
+       return tdesc_aarch64;
+      else
+       {
+         CORE_ADDR arm_hwcap = 0;
+
+         if (target_auxv_search (ops, AT_HWCAP, &arm_hwcap) != 1)
+           return ops->beneath->to_read_description (ops->beneath);
+
+#ifndef COMPAT_HWCAP_VFP
+#define COMPAT_HWCAP_VFP        (1 << 6)
+#endif
+#ifndef COMPAT_HWCAP_NEON
+#define COMPAT_HWCAP_NEON       (1 << 12)
+#endif
+#ifndef COMPAT_HWCAP_VFPv3
+#define COMPAT_HWCAP_VFPv3      (1 << 13)
+#endif
+
+         if (arm_hwcap & COMPAT_HWCAP_VFP)
+           {
+             char *buf;
+             const struct target_desc *result = NULL;
+
+             if (arm_hwcap & COMPAT_HWCAP_NEON)
+               result = tdesc_arm_with_neon;
+             else if (arm_hwcap & COMPAT_HWCAP_VFPv3)
+               result = tdesc_arm_with_vfpv3;
+
+             return result;
+           }
+
+         return NULL;
+       }
+    }
+
   return tdesc_aarch64;
 }
 
index aca046126e28598838e3de19435b6c746c4391fb..f0ab98cace1a412d00803a514593be6a05ee40ca 100644 (file)
@@ -342,11 +342,6 @@ store_wmmx_regs (const struct regcache *regcache)
     }
 }
 
-/* Fetch and store VFP Registers.  The kernel object has space for 32
-   64-bit registers, and the FPSCR.  This is even when on a VFPv2 or
-   VFPv3D16 target.  */
-#define VFP_REGS_SIZE (32 * 8 + 4)
-
 static void
 fetch_vfp_regs (struct regcache *regcache)
 {
index 6a8aa7df304d46f9eb0377434d813c2b687f673e..cbe322f7ad49ef10563b6429ac79b3b5d76a295f 100644 (file)
@@ -19,7 +19,7 @@
 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o aarch64-linux-nat.o \
+NATDEPFILES= inf-ptrace.o fork-child.o aarch64-linux-nat.o aarch32-linux-nat.o \
        proc-service.o linux-thread-db.o linux-nat.o linux-fork.o \
        linux-procfs.o linux-ptrace.o linux-osdata.o linux-waitpid.o \
        linux-personality.o linux-namespaces.o
index 4e4d6a90684154b5bc97775bbe5289c1f4ef2f8c..f2c1a2d8bda41dc766326dee148928c7e6c7af61 100644 (file)
@@ -44,6 +44,7 @@ aarch64*-*-elf)
 aarch64*-*-linux*)
        # Target: AArch64 linux
        gdb_target_obs="aarch64-tdep.o aarch64-linux-tdep.o \
+                       arm-tdep.o arm-linux-tdep.o \
                        glibc-tdep.o linux-tdep.o solib-svr4.o \
                        symfile-mem.o linux-record.o"
        build_gdbserver=yes