aarch64 multi-arch support (part 2): siginfo fixup
[deliverable/binutils-gdb.git] / gdb / gdbserver / linux-aarch64-low.c
index 79eb44c8121e0a7fd2dfa900d8e1b3d5426d7b6d..73b248c5ed03d5862f7cc179be37f22ba52f4c72 100644 (file)
 
 #include "server.h"
 #include "linux-low.h"
+#include "nat/aarch64-linux.h"
 #include "nat/aarch64-linux-hw-point.h"
+#include "linux-aarch32-low.h"
 #include "elf/common.h"
 
 #include <signal.h>
 #include <sys/user.h>
-#include <sys/ptrace.h>
+#include "nat/gdb_ptrace.h"
 #include <asm/ptrace.h>
 #include <sys/uio.h>
 
@@ -69,6 +71,16 @@ struct arch_process_info
   struct aarch64_debug_reg_state debug_reg_state;
 };
 
+/* Return true if the size of register 0 is 8 byte.  */
+
+static int
+is_64bit_tdesc (void)
+{
+  struct regcache *regcache = get_thread_regcache (current_thread, 0);
+
+  return register_size (regcache->tdesc, 0) == 8;
+}
+
 /* Implementation of linux_target_ops method "cannot_store_register".  */
 
 static int
@@ -144,12 +156,24 @@ extern int debug_threads;
 static CORE_ADDR
 aarch64_get_pc (struct regcache *regcache)
 {
-  unsigned long pc;
+  if (register_size (regcache->tdesc, 0) == 8)
+    {
+      unsigned long pc;
+
+      collect_register_by_name (regcache, "pc", &pc);
+      if (debug_threads)
+       debug_printf ("stop pc is %08lx\n", pc);
+      return pc;
+    }
+  else
+    {
+      unsigned int pc;
 
-  collect_register_by_name (regcache, "pc", &pc);
-  if (debug_threads)
-    debug_printf ("stop pc is %08lx\n", pc);
-  return pc;
+      collect_register_by_name (regcache, "pc", &pc);
+      if (debug_threads)
+       debug_printf ("stop pc is %04x\n", pc);
+      return pc;
+    }
 }
 
 /* Implementation of linux_target_ops method "set_pc".  */
@@ -157,8 +181,16 @@ aarch64_get_pc (struct regcache *regcache)
 static void
 aarch64_set_pc (struct regcache *regcache, CORE_ADDR pc)
 {
-  unsigned long newpc = pc;
-  supply_register_by_name (regcache, "pc", &newpc);
+  if (register_size (regcache->tdesc, 0) == 8)
+    {
+      unsigned long newpc = pc;
+      supply_register_by_name (regcache, "pc", &newpc);
+    }
+  else
+    {
+      unsigned int newpc = pc;
+      supply_register_by_name (regcache, "pc", &newpc);
+    }
 }
 
 #define aarch64_breakpoint_len 4
@@ -203,125 +235,14 @@ aarch64_init_debug_reg_state (struct aarch64_debug_reg_state *state)
     }
 }
 
-struct aarch64_dr_update_callback_param
-{
-  int pid;
-  int is_watchpoint;
-  unsigned int idx;
-};
-
-/* Callback function which records the information about the change of
-   one hardware breakpoint/watchpoint setting for the thread ENTRY.
-   The information is passed in via PTR.
-   N.B.  The actual updating of hardware debug registers is not
-   carried out until the moment the thread is resumed.  */
-
-static int
-debug_reg_change_callback (struct inferior_list_entry *entry, void *ptr)
-{
-  struct thread_info *thread = (struct thread_info *) entry;
-  struct lwp_info *lwp = get_thread_lwp (thread);
-  struct aarch64_dr_update_callback_param *param_p
-    = (struct aarch64_dr_update_callback_param *) ptr;
-  int pid = param_p->pid;
-  int idx = param_p->idx;
-  int is_watchpoint = param_p->is_watchpoint;
-  struct arch_lwp_info *info = lwp->arch_private;
-  dr_changed_t *dr_changed_ptr;
-  dr_changed_t dr_changed;
-
-  if (show_debug_regs)
-    {
-      fprintf (stderr, "debug_reg_change_callback: \n\tOn entry:\n");
-      fprintf (stderr, "\tpid%d, tid: %ld, dr_changed_bp=0x%llx, "
-              "dr_changed_wp=0x%llx\n",
-              pid, lwpid_of (thread), info->dr_changed_bp,
-              info->dr_changed_wp);
-    }
-
-  dr_changed_ptr = is_watchpoint ? &info->dr_changed_wp
-    : &info->dr_changed_bp;
-  dr_changed = *dr_changed_ptr;
-
-  /* Only update the threads of this process.  */
-  if (pid_of (thread) == pid)
-    {
-      gdb_assert (idx >= 0
-                 && (idx <= (is_watchpoint ? aarch64_num_wp_regs
-                             : aarch64_num_bp_regs)));
-
-      /* The following assertion is not right, as there can be changes
-        that have not been made to the hardware debug registers
-        before new changes overwrite the old ones.  This can happen,
-        for instance, when the breakpoint/watchpoint hit one of the
-        threads and the user enters continue; then what happens is:
-        1) all breakpoints/watchpoints are removed for all threads;
-        2) a single step is carried out for the thread that was hit;
-        3) all of the points are inserted again for all threads;
-        4) all threads are resumed.
-        The 2nd step will only affect the one thread in which the
-        bp/wp was hit, which means only that one thread is resumed;
-        remember that the actual updating only happen in
-        aarch64_linux_prepare_to_resume, so other threads remain
-        stopped during the removal and insertion of bp/wp.  Therefore
-        for those threads, the change of insertion of the bp/wp
-        overwrites that of the earlier removals.  (The situation may
-        be different when bp/wp is steppable, or in the non-stop
-        mode.)  */
-      /* gdb_assert (DR_N_HAS_CHANGED (dr_changed, idx) == 0);  */
-
-      /* The actual update is done later just before resuming the lwp,
-         we just mark that one register pair needs updating.  */
-      DR_MARK_N_CHANGED (dr_changed, idx);
-      *dr_changed_ptr = dr_changed;
-
-      /* If the lwp isn't stopped, force it to momentarily pause, so
-         we can update its debug registers.  */
-      if (!lwp->stopped)
-       linux_stop_lwp (lwp);
-    }
-
-  if (show_debug_regs)
-    {
-      fprintf (stderr, "\tOn exit:\n\tpid%d, tid: %ld, dr_changed_bp=0x%llx, "
-              "dr_changed_wp=0x%llx\n",
-              pid, lwpid_of (thread), info->dr_changed_bp,
-              info->dr_changed_wp);
-    }
-
-  return 0;
-}
-
-/* Notify each thread that their IDXth breakpoint/watchpoint register
-   pair needs to be updated.  The message will be recorded in each
-   thread's arch-specific data area, the actual updating will be done
-   when the thread is resumed.  */
-
-void
-aarch64_notify_debug_reg_change (const struct aarch64_debug_reg_state *state,
-                                int is_watchpoint, unsigned int idx)
-{
-  struct aarch64_dr_update_callback_param param;
-
-  /* Only update the threads of this process.  */
-  param.pid = pid_of (current_thread);
-
-  param.is_watchpoint = is_watchpoint;
-  param.idx = idx;
-
-  find_inferior (&all_threads, debug_reg_change_callback, (void *) &param);
-}
-
-
 /* Return the pointer to the debug register state structure in the
    current process' arch-specific data area.  */
 
-static struct aarch64_debug_reg_state *
-aarch64_get_debug_reg_state ()
+struct aarch64_debug_reg_state *
+aarch64_get_debug_reg_state (pid_t pid)
 {
-  struct process_info *proc;
+  struct process_info *proc = find_process_pid (pid);
 
-  proc = current_process ();
   return &proc->priv->arch_private->debug_reg_state;
 }
 
@@ -333,6 +254,22 @@ aarch64_supports_z_point_type (char z_type)
   switch (z_type)
     {
     case Z_PACKET_SW_BP:
+      {
+       if (!extended_protocol && is_64bit_tdesc ())
+         {
+           /* Only enable Z0 packet in non-multi-arch debugging.  If
+              extended protocol is used, don't enable Z0 packet because
+              GDBserver may attach to 32-bit process.  */
+           return 1;
+         }
+       else
+         {
+           /* Disable Z0 packet so that GDBserver doesn't have to handle
+              different breakpoint instructions (aarch64, arm, thumb etc)
+              in multi-arch debugging.  */
+           return 0;
+         }
+      }
     case Z_PACKET_HW_BP:
     case Z_PACKET_WRITE_WP:
     case Z_PACKET_READ_WP:
@@ -354,7 +291,8 @@ aarch64_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
 {
   int ret;
   enum target_hw_bp_type targ_type;
-  struct aarch64_debug_reg_state *state = aarch64_get_debug_reg_state ();
+  struct aarch64_debug_reg_state *state
+    = aarch64_get_debug_reg_state (pid_of (current_thread));
 
   if (show_debug_regs)
     fprintf (stderr, "insert_point on entry (addr=0x%08lx, len=%d)\n",
@@ -364,17 +302,21 @@ aarch64_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
   targ_type = raw_bkpt_type_to_target_hw_bp_type (type);
 
   if (targ_type != hw_execute)
-    ret =
-      aarch64_handle_watchpoint (targ_type, addr, len, 1 /* is_insert */,
-                                state);
+    {
+      if (aarch64_linux_region_ok_for_watchpoint (addr, len))
+       ret = aarch64_handle_watchpoint (targ_type, addr, len,
+                                        1 /* is_insert */, state);
+      else
+       ret = -1;
+    }
   else
     ret =
       aarch64_handle_breakpoint (targ_type, addr, len, 1 /* is_insert */,
                                 state);
 
   if (show_debug_regs)
-    aarch64_show_debug_reg_state (aarch64_get_debug_reg_state (),
-                                 "insert_point", addr, len, targ_type);
+    aarch64_show_debug_reg_state (state, "insert_point", addr, len,
+                                 targ_type);
 
   return ret;
 }
@@ -390,7 +332,8 @@ aarch64_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
 {
   int ret;
   enum target_hw_bp_type targ_type;
-  struct aarch64_debug_reg_state *state = aarch64_get_debug_reg_state ();
+  struct aarch64_debug_reg_state *state
+    = aarch64_get_debug_reg_state (pid_of (current_thread));
 
   if (show_debug_regs)
     fprintf (stderr, "remove_point on entry (addr=0x%08lx, len=%d)\n",
@@ -410,8 +353,8 @@ aarch64_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
                                 state);
 
   if (show_debug_regs)
-    aarch64_show_debug_reg_state (aarch64_get_debug_reg_state (),
-                                 "remove_point", addr, len, targ_type);
+    aarch64_show_debug_reg_state (state, "remove_point", addr, len,
+                                 targ_type);
 
   return ret;
 }
@@ -437,7 +380,7 @@ aarch64_stopped_data_address (void)
     return (CORE_ADDR) 0;
 
   /* Check if the address matches any watched address.  */
-  state = aarch64_get_debug_reg_state ();
+  state = aarch64_get_debug_reg_state (pid_of (current_thread));
   for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
     {
       const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
@@ -487,32 +430,37 @@ ps_get_thread_area (const struct ps_prochandle *ph,
   return PS_OK;
 }
 
-/* Implementation of linux_target_ops method "linux_new_process".  */
+/* Implementation of linux_target_ops method "siginfo_fixup".  */
 
-static struct arch_process_info *
-aarch64_linux_new_process (void)
+static int
+aarch64_linux_siginfo_fixup (siginfo_t *native, void *inf, int direction)
 {
-  struct arch_process_info *info = xcalloc (1, sizeof (*info));
+  /* Is the inferior 32-bit?  If so, then fixup the siginfo object.  */
+  if (!is_64bit_tdesc ())
+    {
+      if (direction == 0)
+       aarch64_compat_siginfo_from_siginfo ((struct compat_siginfo *) inf,
+                                            native);
+      else
+       aarch64_siginfo_from_compat_siginfo (native,
+                                            (struct compat_siginfo *) inf);
 
-  aarch64_init_debug_reg_state (&info->debug_reg_state);
+      return 1;
+    }
 
-  return info;
+  return 0;
 }
 
-/* Implementation of linux_target_ops method "linux_new_thread".  */
+/* Implementation of linux_target_ops method "linux_new_process".  */
 
-static void
-aarch64_linux_new_thread (struct lwp_info *lwp)
+static struct arch_process_info *
+aarch64_linux_new_process (void)
 {
-  struct arch_lwp_info *info = xcalloc (1, sizeof (*info));
+  struct arch_process_info *info = XCNEW (struct arch_process_info);
 
-  /* Mark that all the hardware breakpoint/watchpoint register pairs
-     for this thread need to be initialized (with data from
-     aarch_process_info.debug_reg_state).  */
-  DR_MARK_ALL_CHANGED (info->dr_changed_bp, aarch64_num_bp_regs);
-  DR_MARK_ALL_CHANGED (info->dr_changed_wp, aarch64_num_wp_regs);
+  aarch64_init_debug_reg_state (&info->debug_reg_state);
 
-  lwp->arch_private = info;
+  return info;
 }
 
 /* Implementation of linux_target_ops method "linux_new_fork".  */
@@ -544,42 +492,24 @@ aarch64_linux_new_fork (struct process_info *parent,
   *child->priv->arch_private = *parent->priv->arch_private;
 }
 
-/* Implementation of linux_target_ops method "linux_prepare_to_resume".
-
-   If the debug regs have changed, update the thread's copies.  */
+/* Return the right target description according to the ELF file of
+   current thread.  */
 
-static void
-aarch64_linux_prepare_to_resume (struct lwp_info *lwp)
+static const struct target_desc *
+aarch64_linux_read_description (void)
 {
-  struct thread_info *thread = get_lwp_thread (lwp);
-  ptid_t ptid = ptid_of (thread);
-  struct arch_lwp_info *info = lwp->arch_private;
+  unsigned int machine;
+  int is_elf64;
+  int tid;
 
-  if (DR_HAS_CHANGED (info->dr_changed_bp)
-      || DR_HAS_CHANGED (info->dr_changed_wp))
-    {
-      int tid = ptid_get_lwp (ptid);
-      struct process_info *proc = find_process_pid (ptid_get_pid (ptid));
-      struct aarch64_debug_reg_state *state
-       = &proc->priv->arch_private->debug_reg_state;
-
-      if (show_debug_regs)
-       fprintf (stderr, "prepare_to_resume thread %ld\n", lwpid_of (thread));
-
-      /* Watchpoints.  */
-      if (DR_HAS_CHANGED (info->dr_changed_wp))
-       {
-         aarch64_linux_set_debug_regs (state, tid, 1);
-         DR_CLEAR_CHANGED (info->dr_changed_wp);
-       }
-
-      /* Breakpoints.  */
-      if (DR_HAS_CHANGED (info->dr_changed_bp))
-       {
-         aarch64_linux_set_debug_regs (state, tid, 0);
-         DR_CLEAR_CHANGED (info->dr_changed_bp);
-       }
-    }
+  tid = lwpid_of (current_thread);
+
+  is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
+
+  if (is_elf64)
+    return tdesc_aarch64;
+  else
+    return tdesc_arm_with_neon;
 }
 
 /* Implementation of linux_target_ops method "arch_setup".  */
@@ -587,7 +517,7 @@ aarch64_linux_prepare_to_resume (struct lwp_info *lwp)
 static void
 aarch64_arch_setup (void)
 {
-  current_process ()->tdesc = tdesc_aarch64;
+  current_process ()->tdesc = aarch64_linux_read_description ();
 
   aarch64_linux_get_debug_reg_capacity (lwpid_of (current_thread));
 }
@@ -611,7 +541,7 @@ static struct regsets_info aarch64_regsets_info =
     NULL, /* disabled_regsets */
   };
 
-static struct regs_info regs_info =
+static struct regs_info regs_info_aarch64 =
   {
     NULL, /* regset_bitmap */
     NULL, /* usrregs */
@@ -623,7 +553,10 @@ static struct regs_info regs_info =
 static const struct regs_info *
 aarch64_regs_info (void)
 {
-  return &regs_info;
+  if (is_64bit_tdesc ())
+    return &regs_info_aarch64;
+  else
+    return &regs_info_aarch32;
 }
 
 /* Implementation of linux_target_ops method "supports_tracepoints".  */
@@ -631,7 +564,13 @@ aarch64_regs_info (void)
 static int
 aarch64_supports_tracepoints (void)
 {
-  return 1;
+  if (current_thread == NULL)
+    return 1;
+  else
+    {
+      /* We don't support tracepoints on aarch32 now.  */
+      return is_64bit_tdesc ();
+    }
 }
 
 /* Implementation of linux_target_ops method "supports_range_stepping".  */
@@ -663,7 +602,7 @@ struct linux_target_ops the_low_target =
   aarch64_stopped_data_address,
   NULL, /* collect_ptrace_register */
   NULL, /* supply_ptrace_register */
-  NULL, /* siginfo_fixup */
+  aarch64_linux_siginfo_fixup,
   aarch64_linux_new_process,
   aarch64_linux_new_thread,
   aarch64_linux_new_fork,
@@ -682,5 +621,7 @@ initialize_low_arch (void)
 {
   init_registers_aarch64 ();
 
+  initialize_low_arch_aarch32 ();
+
   initialize_regsets_info (&aarch64_regsets_info);
 }
This page took 0.036956 seconds and 4 git commands to generate.