Move duplicated code into new files
authorGary Benson <gbenson@redhat.com>
Thu, 10 Jul 2014 14:43:56 +0000 (15:43 +0100)
committerGary Benson <gbenson@redhat.com>
Fri, 11 Jul 2014 11:24:26 +0000 (12:24 +0100)
This commit moves the duplicated code in {i386,amd64}-linux-nat.c
into the new files x86-linux-nat.[ch].  Additionally, a new
file i386-linux-nat.h was required to expose a value required
by the 32-bit code in x86-linux-nat.c.

gdb/
2014-07-11  Gary Benson  <gbenson@redhat.com>

* i386-linux-nat.h: New file.
* x86-linux-nat.h: Likewise.
* x86-linux-nat.c: Likewise.
* Makefile.in (HFILES_NO_SRCDIR): Add the above new headers.
* config/i386/linux.mh (NATDEPFILES): Add x86-linux-nat.o.
* config/i386/linux64.mh (NATDEPFILES): Likewise.
* amd64-linux-nat.c (x86-linux-nat.h): New include.
(PTRACE_GETREGSET): Now in x86-linux-nat.h.
(PTRACE_SETREGSET): Likewise.
(arch_lwp_info): Now in x86-linux-nat.c.
(have_ptrace_getregset): Now in x86-linux-nat.h.
(x86_linux_dr_get): Now in x86-linux-nat.c.
(x86_linux_dr_set): Likewise.
(x86_linux_dr_get_addr): Likewise.
(x86_linux_dr_get_control): Likewise.
(x86_linux_dr_get_status): Likewise.
(update_debug_registers_callback): Likewise.
(x86_linux_dr_set_control): Likewise.
(x86_linux_dr_set_addr): Likewise.
(x86_linux_prepare_to_resume): Likewise.
(x86_linux_new_thread): Likewise.
(x86_linux_new_fork): Likewise.
(x86_linux_get_thread_area): Likewise.
(super_post_startup_inferior): Likewise.
(x86_linux_child_post_startup_inferior): Likewise.
(AMD64_LINUX_USER64_CS): Likewise.
(AMD64_LINUX_X32_DS): Likewise.
(x86_linux_read_description): Likewise.
(x86_linux_enable_btrace): Likewise.
(x86_linux_disable_btrace): Likewise.
(x86_linux_teardown_btrace): Likewise.
(x86_linux_read_btrace): Likewise.
(x86_linux_create_target): Likewise.
(x86_linux_add_target): Likewise.
* i386-linux-nat.c (x86-linux-nat.h): New include.
(PTRACE_GETREGSET): Now in x86-linux-nat.h.
(PTRACE_SETREGSET): Likewise.
(arch_lwp_info): Now in x86-linux-nat.c.
(have_ptrace_getregset): Now in x86-linux-nat.h.
(x86_linux_dr_get): Now in x86-linux-nat.c.
(x86_linux_dr_set): Likewise.
(x86_linux_dr_get_addr): Likewise.
(x86_linux_dr_get_control): Likewise.
(x86_linux_dr_get_status): Likewise.
(update_debug_registers_callback): Likewise.
(x86_linux_dr_set_control): Likewise.
(x86_linux_dr_set_addr): Likewise.
(x86_linux_prepare_to_resume): Likewise.
(x86_linux_new_thread): Likewise.
(x86_linux_new_fork): Likewise.
(x86_linux_get_thread_area): Likewise.
(super_post_startup_inferior): Likewise.
(x86_linux_child_post_startup_inferior): Likewise.
(AMD64_LINUX_USER64_CS): Likewise.
(AMD64_LINUX_X32_DS): Likewise.
(x86_linux_read_description): Likewise.
(x86_linux_enable_btrace): Likewise.
(x86_linux_disable_btrace): Likewise.
(x86_linux_teardown_btrace): Likewise.
(x86_linux_read_btrace): Likewise.
(x86_linux_create_target): Likewise.
(x86_linux_add_target): Likewise.

gdb/ChangeLog
gdb/Makefile.in
gdb/amd64-linux-nat.c
gdb/config/i386/linux.mh
gdb/config/i386/linux64.mh
gdb/i386-linux-nat.c
gdb/i386-linux-nat.h [new file with mode: 0644]
gdb/x86-linux-nat.c [new file with mode: 0644]
gdb/x86-linux-nat.h [new file with mode: 0644]

index 490e652c53dd6505cb33842223aaef2f0e087188..418b3e3dc5164983761d98a82dd014e70615aa3b 100644 (file)
@@ -1,3 +1,68 @@
+2014-07-11  Gary Benson  <gbenson@redhat.com>
+
+       * i386-linux-nat.h: New file.
+       * x86-linux-nat.h: Likewise.
+       * x86-linux-nat.c: Likewise.
+       * Makefile.in (HFILES_NO_SRCDIR): Add the above new headers.
+       * config/i386/linux.mh (NATDEPFILES): Add x86-linux-nat.o.
+       * config/i386/linux64.mh (NATDEPFILES): Likewise.
+       * amd64-linux-nat.c (x86-linux-nat.h): New include.
+       (PTRACE_GETREGSET): Now in x86-linux-nat.h.
+       (PTRACE_SETREGSET): Likewise.
+       (arch_lwp_info): Now in x86-linux-nat.c.
+       (have_ptrace_getregset): Now in x86-linux-nat.h.
+       (x86_linux_dr_get): Now in x86-linux-nat.c.
+       (x86_linux_dr_set): Likewise.
+       (x86_linux_dr_get_addr): Likewise.
+       (x86_linux_dr_get_control): Likewise.
+       (x86_linux_dr_get_status): Likewise.
+       (update_debug_registers_callback): Likewise.
+       (x86_linux_dr_set_control): Likewise.
+       (x86_linux_dr_set_addr): Likewise.
+       (x86_linux_prepare_to_resume): Likewise.
+       (x86_linux_new_thread): Likewise.
+       (x86_linux_new_fork): Likewise.
+       (x86_linux_get_thread_area): Likewise.
+       (super_post_startup_inferior): Likewise.
+       (x86_linux_child_post_startup_inferior): Likewise.
+       (AMD64_LINUX_USER64_CS): Likewise.
+       (AMD64_LINUX_X32_DS): Likewise.
+       (x86_linux_read_description): Likewise.
+       (x86_linux_enable_btrace): Likewise.
+       (x86_linux_disable_btrace): Likewise.
+       (x86_linux_teardown_btrace): Likewise.
+       (x86_linux_read_btrace): Likewise.
+       (x86_linux_create_target): Likewise.
+       (x86_linux_add_target): Likewise.
+       * i386-linux-nat.c (x86-linux-nat.h): New include.
+       (PTRACE_GETREGSET): Now in x86-linux-nat.h.
+       (PTRACE_SETREGSET): Likewise.
+       (arch_lwp_info): Now in x86-linux-nat.c.
+       (have_ptrace_getregset): Now in x86-linux-nat.h.
+       (x86_linux_dr_get): Now in x86-linux-nat.c.
+       (x86_linux_dr_set): Likewise.
+       (x86_linux_dr_get_addr): Likewise.
+       (x86_linux_dr_get_control): Likewise.
+       (x86_linux_dr_get_status): Likewise.
+       (update_debug_registers_callback): Likewise.
+       (x86_linux_dr_set_control): Likewise.
+       (x86_linux_dr_set_addr): Likewise.
+       (x86_linux_prepare_to_resume): Likewise.
+       (x86_linux_new_thread): Likewise.
+       (x86_linux_new_fork): Likewise.
+       (x86_linux_get_thread_area): Likewise.
+       (super_post_startup_inferior): Likewise.
+       (x86_linux_child_post_startup_inferior): Likewise.
+       (AMD64_LINUX_USER64_CS): Likewise.
+       (AMD64_LINUX_X32_DS): Likewise.
+       (x86_linux_read_description): Likewise.
+       (x86_linux_enable_btrace): Likewise.
+       (x86_linux_disable_btrace): Likewise.
+       (x86_linux_teardown_btrace): Likewise.
+       (x86_linux_read_btrace): Likewise.
+       (x86_linux_create_target): Likewise.
+       (x86_linux_add_target): Likewise.
+
 2014-07-11  Gary Benson  <gbenson@redhat.com>
 
        * amd64-linux-nat.c: Comment and whitespace changes.
index 8838e9912f14679b9fc78208ed01ada1a4da22d7..ce155016298fd10ff492e141b2102f7ec6339321 100644 (file)
@@ -934,7 +934,8 @@ nat/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h \
 gdb_bfd.h sparc-ravenscar-thread.h ppc-ravenscar-thread.h nat/linux-btrace.h \
 ctf.h nat/i386-cpuid.h nat/i386-gcc-cpuid.h target/resume.h \
 target/wait.h target/waitstatus.h nat/linux-nat.h nat/linux-waitpid.h \
-common/print-utils.h common/rsp-low.h nat/i386-dregs.h
+common/print-utils.h common/rsp-low.h nat/i386-dregs.h x86-linux-nat.h \
+i386-linux-nat.h
 
 # Header files that already have srcdir in them, or which are in objdir.
 
index 42af924ad941e281f46b6275dc93550426b2fa48..6b8d43a6dda97eb297d6d04a171c9f2471cb6c18 100644 (file)
 #include "i386-nat.h"
 #include "i386-xstate.h"
 
-#ifndef PTRACE_GETREGSET
-#define PTRACE_GETREGSET       0x4204
-#endif
-
-#ifndef PTRACE_SETREGSET
-#define PTRACE_SETREGSET       0x4205
-#endif
-
-/* Per-thread arch-specific data we want to keep.  */
-
-struct arch_lwp_info
-{
-  /* Non-zero if our copy differs from what's recorded in the thread.  */
-  int debug_registers_changed;
-};
-
-/* Does the current host support PTRACE_GETREGSET?  */
-static int have_ptrace_getregset = -1;
+#include "x86-linux-nat.h"
 
 /* Mapping between the general-purpose registers in GNU/Linux x86-64
    `struct user' format and GDB's register cache layout for GNU/Linux
@@ -278,262 +261,6 @@ amd64_linux_store_inferior_registers (struct target_ops *ops,
 }
 \f
 
-/* Support for debug registers.  */
-
-/* Get debug register REGNUM value from only the one LWP of PTID.  */
-
-static unsigned long
-x86_linux_dr_get (ptid_t ptid, int regnum)
-{
-  int tid;
-  unsigned long value;
-
-  tid = ptid_get_lwp (ptid);
-  if (tid == 0)
-    tid = ptid_get_pid (ptid);
-
-  errno = 0;
-  value = ptrace (PTRACE_PEEKUSER, tid,
-                 offsetof (struct user, u_debugreg[regnum]), 0);
-  if (errno != 0)
-    perror_with_name (_("Couldn't read debug register"));
-
-  return value;
-}
-
-/* Set debug register REGNUM to VALUE in only the one LWP of PTID.  */
-
-static void
-x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
-{
-  int tid;
-
-  tid = ptid_get_lwp (ptid);
-  if (tid == 0)
-    tid = ptid_get_pid (ptid);
-
-  errno = 0;
-  ptrace (PTRACE_POKEUSER, tid,
-         offsetof (struct user, u_debugreg[regnum]), value);
-  if (errno != 0)
-    perror_with_name (_("Couldn't write debug register"));
-}
-
-/* Return the inferior's debug register REGNUM.  */
-
-static CORE_ADDR
-x86_linux_dr_get_addr (int regnum)
-{
-  /* DR6 and DR7 are retrieved with some other way.  */
-  gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
-
-  return x86_linux_dr_get (inferior_ptid, regnum);
-}
-
-/* Return the inferior's DR7 debug control register.  */
-
-static unsigned long
-x86_linux_dr_get_control (void)
-{
-  return x86_linux_dr_get (inferior_ptid, DR_CONTROL);
-}
-
-/* Get DR_STATUS from only the one LWP of INFERIOR_PTID.  */
-
-static unsigned long
-x86_linux_dr_get_status (void)
-{
-  return x86_linux_dr_get (inferior_ptid, DR_STATUS);
-}
-
-/* Callback for iterate_over_lwps.  Update the debug registers of
-   LWP.  */
-
-static int
-update_debug_registers_callback (struct lwp_info *lwp, void *arg)
-{
-  if (lwp->arch_private == NULL)
-    lwp->arch_private = XCNEW (struct arch_lwp_info);
-
-  /* The actual update is done later just before resuming the lwp, we
-     just mark that the registers need updating.  */
-  lwp->arch_private->debug_registers_changed = 1;
-
-  /* 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);
-
-  /* Continue the iteration.  */
-  return 0;
-}
-
-/* Set DR_CONTROL to CONTROL in all LWPs of the current inferior.  */
-
-static void
-x86_linux_dr_set_control (unsigned long control)
-{
-  ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
-
-  iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
-}
-
-/* Set address REGNUM (zero based) to ADDR in all LWPs of the current
-   inferior.  */
-
-static void
-x86_linux_dr_set_addr (int regnum, CORE_ADDR addr)
-{
-  ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
-
-  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
-
-  iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
-}
-
-/* Called when resuming a thread.
-   If the debug regs have changed, update the thread's copies.  */
-
-static void
-x86_linux_prepare_to_resume (struct lwp_info *lwp)
-{
-  int clear_status = 0;
-
-  /* NULL means this is the main thread still going through the shell,
-     or, no watchpoint has been set yet.  In that case, there's
-     nothing to do.  */
-  if (lwp->arch_private == NULL)
-    return;
-
-  if (lwp->arch_private->debug_registers_changed)
-    {
-      struct i386_debug_reg_state *state
-       = i386_debug_reg_state (ptid_get_pid (lwp->ptid));
-      int i;
-
-      /* On Linux kernel before 2.6.33 commit
-        72f674d203cd230426437cdcf7dd6f681dad8b0d
-        if you enable a breakpoint by the DR_CONTROL bits you need to have
-        already written the corresponding DR_FIRSTADDR...DR_LASTADDR registers.
-
-        Ensure DR_CONTROL gets written as the very last register here.  */
-
-      /* Clear DR_CONTROL first.  In some cases, setting DR0-3 to a
-        value that doesn't match what is enabled in DR_CONTROL
-        results in EINVAL.  */
-      x86_linux_dr_set (lwp->ptid, DR_CONTROL, 0);
-
-      for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
-       if (state->dr_ref_count[i] > 0)
-         {
-           x86_linux_dr_set (lwp->ptid, i, state->dr_mirror[i]);
-
-           /* If we're setting a watchpoint, any change the inferior
-              had done itself to the debug registers needs to be
-              discarded, otherwise, i386_stopped_data_address can get
-              confused.  */
-           clear_status = 1;
-         }
-
-      /* If DR_CONTROL is supposed to be zero, we've already set it
-        above.  */
-      if (state->dr_control_mirror != 0)
-       x86_linux_dr_set (lwp->ptid, DR_CONTROL, state->dr_control_mirror);
-
-      lwp->arch_private->debug_registers_changed = 0;
-    }
-
-  if (clear_status || lwp->stopped_by_watchpoint)
-    x86_linux_dr_set (lwp->ptid, DR_STATUS, 0);
-}
-
-static void
-x86_linux_new_thread (struct lwp_info *lp)
-{
-  struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
-
-  info->debug_registers_changed = 1;
-
-  lp->arch_private = info;
-}
-
-/* linux_nat_new_fork hook.   */
-
-static void
-x86_linux_new_fork (struct lwp_info *parent, pid_t child_pid)
-{
-  pid_t parent_pid;
-  struct i386_debug_reg_state *parent_state;
-  struct i386_debug_reg_state *child_state;
-
-  /* NULL means no watchpoint has ever been set in the parent.  In
-     that case, there's nothing to do.  */
-  if (parent->arch_private == NULL)
-    return;
-
-  /* Linux kernel before 2.6.33 commit
-     72f674d203cd230426437cdcf7dd6f681dad8b0d
-     will inherit hardware debug registers from parent
-     on fork/vfork/clone.  Newer Linux kernels create such tasks with
-     zeroed debug registers.
-
-     GDB core assumes the child inherits the watchpoints/hw
-     breakpoints of the parent, and will remove them all from the
-     forked off process.  Copy the debug registers mirrors into the
-     new process so that all breakpoints and watchpoints can be
-     removed together.  The debug registers mirror will become zeroed
-     in the end before detaching the forked off process, thus making
-     this compatible with older Linux kernels too.  */
-
-  parent_pid = ptid_get_pid (parent->ptid);
-  parent_state = i386_debug_reg_state (parent_pid);
-  child_state = i386_debug_reg_state (child_pid);
-  *child_state = *parent_state;
-}
-
-\f
-
-/* Helper for ps_get_thread_area.  Sets BASE_ADDR to a pointer to
-   the thread local storage (or its descriptor) and returns PS_OK
-   on success.  Returns PS_ERR on failure.  */
-
-static ps_err_e
-x86_linux_get_thread_area (pid_t pid, void *addr, unsigned int *base_addr)
-{
-  /* NOTE: cagney/2003-08-26: The definition of this buffer is found
-     in the kernel header <asm-i386/ldt.h>.  It, after padding, is 4 x
-     4 byte integers in size: `entry_number', `base_addr', `limit',
-     and a bunch of status bits.
-
-     The values returned by this ptrace call should be part of the
-     regcache buffer, and ps_get_thread_area should channel its
-     request through the regcache.  That way remote targets could
-     provide the value using the remote protocol and not this direct
-     call.
-
-     Is this function needed?  I'm guessing that the `base' is the
-     address of a descriptor that libthread_db uses to find the
-     thread local address base that GDB needs.  Perhaps that
-     descriptor is defined by the ABI.  Anyway, given that
-     libthread_db calls this function without prompting (gdb
-     requesting tls base) I guess it needs info in there anyway.  */
-  unsigned int desc[4];
-
-  /* This code assumes that "int" is 32 bits and that
-     GET_THREAD_AREA returns no more than 4 int values.  */
-  gdb_assert (sizeof (int) == 4);
-
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 25
-#endif
-
-  if (ptrace (PTRACE_GET_THREAD_AREA, pid, addr, &desc) < 0)
-    return PS_ERR;
-
-  *base_addr = desc[1];
-  return PS_OK;
-}
-
 /* This function is called by libthread_db as part of its handling of
    a request for a thread's local storage address.  */
 
@@ -614,17 +341,6 @@ ps_get_thread_area (const struct ps_prochandle *ph,
 }
 \f
 
-static void (*super_post_startup_inferior) (struct target_ops *self,
-                                           ptid_t ptid);
-
-static void
-x86_linux_child_post_startup_inferior (struct target_ops *self, ptid_t ptid)
-{
-  i386_cleanup_dregs ();
-  super_post_startup_inferior (self, ptid);
-}
-\f
-
 /* When GDB is built as a 64-bit application on linux, the
    PTRACE_GETSIGINFO data is always presented in 64-bit layout.  Since
    debugging a 32-bit inferior with a 64-bit GDB should look the same
@@ -1069,254 +785,7 @@ amd64_linux_siginfo_fixup (siginfo_t *native, gdb_byte *inf, int direction)
   else
     return 0;
 }
-
-#ifdef __x86_64__
-/* Value of CS segment register:
-     64bit process: 0x33
-     32bit process: 0x23  */
-#define AMD64_LINUX_USER64_CS 0x33
-
-/* Value of DS segment register:
-     LP64 process: 0x0
-     X32 process: 0x2b  */
-#define AMD64_LINUX_X32_DS 0x2b
-#endif
-
-/* Get Linux/x86 target description from running target.  */
-
-static const struct target_desc *
-x86_linux_read_description (struct target_ops *ops)
-{
-  int tid;
-  int is_64bit = 0;
-#ifdef __x86_64__
-  int is_x32;
-#endif
-  static uint64_t xcr0;
-  uint64_t xcr0_features_bits;
-
-  /* GNU/Linux LWP ID's are process ID's.  */
-  tid = ptid_get_lwp (inferior_ptid);
-  if (tid == 0)
-    tid = ptid_get_pid (inferior_ptid); /* Not a threaded program.  */
-
-#ifdef __x86_64__
-  {
-    unsigned long cs;
-    unsigned long ds;
-
-    /* Get CS register.  */
-    errno = 0;
-    cs = ptrace (PTRACE_PEEKUSER, tid,
-                offsetof (struct user_regs_struct, cs), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get CS register"));
-
-    is_64bit = cs == AMD64_LINUX_USER64_CS;
-
-    /* Get DS register.  */
-    errno = 0;
-    ds = ptrace (PTRACE_PEEKUSER, tid,
-                offsetof (struct user_regs_struct, ds), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get DS register"));
-
-    is_x32 = ds == AMD64_LINUX_X32_DS;
-
-    if (sizeof (void *) == 4 && is_64bit && !is_x32)
-      error (_("Can't debug 64-bit process with 32-bit GDB"));
-  }
-#elif HAVE_PTRACE_GETFPXREGS
-  if (have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
-       {
-         have_ptrace_getfpxregs = 0;
-         have_ptrace_getregset = 0;
-         return tdesc_i386_mmx_linux;
-       }
-    }
-#endif
-
-  if (have_ptrace_getregset == -1)
-    {
-      uint64_t xstateregs[(I386_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-                 (unsigned int) NT_X86_XSTATE, &iov) < 0)
-       have_ptrace_getregset = 0;
-      else
-       {
-         have_ptrace_getregset = 1;
-
-         /* Get XCR0 from XSAVE extended state.  */
-         xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-                            / sizeof (uint64_t))];
-       }
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
-     PTRACE_GETREGSET is not available then set xcr0_features_bits to
-     zero so that the "no-features" descriptions are returned by the
-     switches below.  */
-  if (have_ptrace_getregset)
-    xcr0_features_bits = xcr0 & I386_XSTATE_ALL_MASK;
-  else
-    xcr0_features_bits = 0;
-
-  if (is_64bit)
-    {
-#ifdef __x86_64__
-      switch (xcr0_features_bits)
-       {
-       case I386_XSTATE_MPX_AVX512_MASK:
-       case I386_XSTATE_AVX512_MASK:
-         if (is_x32)
-           return tdesc_x32_avx512_linux;
-         else
-           return tdesc_amd64_avx512_linux;
-       case I386_XSTATE_MPX_MASK:
-         if (is_x32)
-           return tdesc_x32_avx_linux; /* No MPX on x32 using AVX.  */
-         else
-           return tdesc_amd64_mpx_linux;
-       case I386_XSTATE_AVX_MASK:
-         if (is_x32)
-           return tdesc_x32_avx_linux;
-         else
-           return tdesc_amd64_avx_linux;
-       default:
-         if (is_x32)
-           return tdesc_x32_linux;
-         else
-           return tdesc_amd64_linux;
-       }
-#endif
-    }
-  else
-    {
-      switch (xcr0_features_bits)
-       {
-       case I386_XSTATE_MPX_AVX512_MASK:
-       case I386_XSTATE_AVX512_MASK:
-         return tdesc_i386_avx512_linux;
-       case I386_XSTATE_MPX_MASK:
-         return tdesc_i386_mpx_linux;
-       case I386_XSTATE_AVX_MASK:
-         return tdesc_i386_avx_linux;
-       default:
-         return tdesc_i386_linux;
-       }
-    }
-
-  gdb_assert_not_reached ("failed to return tdesc");
-}
-
-/* Enable branch tracing.  */
-
-static struct btrace_target_info *
-x86_linux_enable_btrace (struct target_ops *self, ptid_t ptid)
-{
-  struct btrace_target_info *tinfo;
-  struct gdbarch *gdbarch;
-
-  errno = 0;
-  tinfo = linux_enable_btrace (ptid);
-
-  if (tinfo == NULL)
-    error (_("Could not enable branch tracing for %s: %s."),
-          target_pid_to_str (ptid), safe_strerror (errno));
-
-  /* Fill in the size of a pointer in bits.  */
-  gdbarch = target_thread_architecture (ptid);
-  tinfo->ptr_bits = gdbarch_ptr_bit (gdbarch);
-
-  return tinfo;
-}
-
-/* Disable branch tracing.  */
-
-static void
-x86_linux_disable_btrace (struct target_ops *self,
-                         struct btrace_target_info *tinfo)
-{
-  enum btrace_error errcode = linux_disable_btrace (tinfo);
-
-  if (errcode != BTRACE_ERR_NONE)
-    error (_("Could not disable branch tracing."));
-}
-
-/* Teardown branch tracing.  */
-
-static void
-x86_linux_teardown_btrace (struct target_ops *self,
-                          struct btrace_target_info *tinfo)
-{
-  /* Ignore errors.  */
-  linux_disable_btrace (tinfo);
-}
-
-static enum btrace_error
-x86_linux_read_btrace (struct target_ops *self,
-                      VEC (btrace_block_s) **data,
-                      struct btrace_target_info *btinfo,
-                      enum btrace_read_type type)
-{
-  return linux_read_btrace (data, btinfo, type);
-}
-
-/* Create an x86 GNU/Linux target.  */
-
-static struct target_ops *
-x86_linux_create_target (void)
-{
-  /* Fill in the generic GNU/Linux methods.  */
-  struct target_ops *t = linux_target ();
-
-  /* Initialize the debug register function vectors.  */
-  i386_use_watchpoints (t);
-  i386_dr_low.set_control = x86_linux_dr_set_control;
-  i386_dr_low.set_addr = x86_linux_dr_set_addr;
-  i386_dr_low.get_addr = x86_linux_dr_get_addr;
-  i386_dr_low.get_status = x86_linux_dr_get_status;
-  i386_dr_low.get_control = x86_linux_dr_get_control;
-  i386_set_debug_register_length (sizeof (void *));
-
-  /* Override the GNU/Linux inferior startup hook.  */
-  super_post_startup_inferior = t->to_post_startup_inferior;
-  t->to_post_startup_inferior = x86_linux_child_post_startup_inferior;
-
-  /* Add the description reader.  */
-  t->to_read_description = x86_linux_read_description;
-
-  /* Add btrace methods.  */
-  t->to_supports_btrace = linux_supports_btrace;
-  t->to_enable_btrace = x86_linux_enable_btrace;
-  t->to_disable_btrace = x86_linux_disable_btrace;
-  t->to_teardown_btrace = x86_linux_teardown_btrace;
-  t->to_read_btrace = x86_linux_read_btrace;
-
-  return t;
-}
-
-/* Add an x86 GNU/Linux target.  */
-
-static void
-x86_linux_add_target (struct target_ops *t)
-{
-  linux_nat_add_target (t);
-  linux_nat_set_new_thread (t, x86_linux_new_thread);
-  linux_nat_set_new_fork (t, x86_linux_new_fork);
-  linux_nat_set_forget_process (t, i386_forget_process);
-  linux_nat_set_prepare_to_resume (t, x86_linux_prepare_to_resume);
-}
+\f
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 void _initialize_amd64_linux_nat (void);
index be18dcfdcddb26a059af2489ae2a1e047cb06213..536ed3d48af043276ec57be2330099b0ee5ea018 100644 (file)
@@ -2,7 +2,7 @@
 
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o \
-       i386-nat.o i386-dregs.o i386-linux-nat.o \
+       i386-nat.o i386-dregs.o i386-linux-nat.o x86-linux-nat.o \
        proc-service.o linux-thread-db.o \
        linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
        linux-btrace.o linux-waitpid.o
index 3126b75a2c0c33db3de2b82516423d5d1e289deb..8faca421bd56d254b10822ef0e5b2519c25adabc 100644 (file)
@@ -1,6 +1,7 @@
 # Host: GNU/Linux x86-64
 NATDEPFILES= inf-ptrace.o fork-child.o \
        i386-nat.o i386-dregs.o amd64-nat.o amd64-linux-nat.o \
+       x86-linux-nat.o \
        linux-nat.o linux-osdata.o \
        proc-service.o linux-thread-db.o linux-fork.o \
        linux-procfs.o linux-ptrace.o linux-btrace.o \
index 1ee75038976640c469827162660fdf6336b6e4cf..154cd12849e7fab0739c5b96abf200fafb7a56c5 100644 (file)
 
 #include "i386-xstate.h"
 
-#ifndef PTRACE_GETREGSET
-#define PTRACE_GETREGSET       0x4204
-#endif
-
-#ifndef PTRACE_SETREGSET
-#define PTRACE_SETREGSET       0x4205
-#endif
-
-/* Per-thread arch-specific data we want to keep.  */
-
-struct arch_lwp_info
-{
-  /* Non-zero if our copy differs from what's recorded in the thread.  */
-  int debug_registers_changed;
-};
-
-/* Does the current host support PTRACE_GETREGSET?  */
-static int have_ptrace_getregset = -1;
-\f
+#include "x86-linux-nat.h"
 
 /* The register sets used in GNU/Linux ELF core-dumps are identical to
    the register sets in `struct user' that is used for a.out
@@ -642,262 +624,6 @@ i386_linux_store_inferior_registers (struct target_ops *ops,
 }
 \f
 
-/* Support for debug registers.  */
-
-/* Get debug register REGNUM value from only the one LWP of PTID.  */
-
-static unsigned long
-x86_linux_dr_get (ptid_t ptid, int regnum)
-{
-  int tid;
-  unsigned long value;
-
-  tid = ptid_get_lwp (ptid);
-  if (tid == 0)
-    tid = ptid_get_pid (ptid);
-
-  errno = 0;
-  value = ptrace (PTRACE_PEEKUSER, tid,
-                 offsetof (struct user, u_debugreg[regnum]), 0);
-  if (errno != 0)
-    perror_with_name (_("Couldn't read debug register"));
-
-  return value;
-}
-
-/* Set debug register REGNUM to VALUE in only the one LWP of PTID.  */
-
-static void
-x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
-{
-  int tid;
-
-  tid = ptid_get_lwp (ptid);
-  if (tid == 0)
-    tid = ptid_get_pid (ptid);
-
-  errno = 0;
-  ptrace (PTRACE_POKEUSER, tid,
-         offsetof (struct user, u_debugreg[regnum]), value);
-  if (errno != 0)
-    perror_with_name (_("Couldn't write debug register"));
-}
-
-/* Return the inferior's debug register REGNUM.  */
-
-static CORE_ADDR
-x86_linux_dr_get_addr (int regnum)
-{
-  /* DR6 and DR7 are retrieved with some other way.  */
-  gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
-
-  return x86_linux_dr_get (inferior_ptid, regnum);
-}
-
-/* Return the inferior's DR7 debug control register.  */
-
-static unsigned long
-x86_linux_dr_get_control (void)
-{
-  return x86_linux_dr_get (inferior_ptid, DR_CONTROL);
-}
-
-/* Get DR_STATUS from only the one LWP of INFERIOR_PTID.  */
-
-static unsigned long
-x86_linux_dr_get_status (void)
-{
-  return x86_linux_dr_get (inferior_ptid, DR_STATUS);
-}
-
-/* Callback for iterate_over_lwps.  Update the debug registers of
-   LWP.  */
-
-static int
-update_debug_registers_callback (struct lwp_info *lwp, void *arg)
-{
-  if (lwp->arch_private == NULL)
-    lwp->arch_private = XCNEW (struct arch_lwp_info);
-
-  /* The actual update is done later just before resuming the lwp, we
-     just mark that the registers need updating.  */
-  lwp->arch_private->debug_registers_changed = 1;
-
-  /* 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);
-
-  /* Continue the iteration.  */
-  return 0;
-}
-
-/* Set DR_CONTROL to CONTROL in all LWPs of the current inferior.  */
-
-static void
-x86_linux_dr_set_control (unsigned long control)
-{
-  ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
-
-  iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
-}
-
-/* Set address REGNUM (zero based) to ADDR in all LWPs of the current
-   inferior.  */
-
-static void
-x86_linux_dr_set_addr (int regnum, CORE_ADDR addr)
-{
-  ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
-
-  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
-
-  iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
-}
-
-/* Called when resuming a thread.
-   If the debug regs have changed, update the thread's copies.  */
-
-static void
-x86_linux_prepare_to_resume (struct lwp_info *lwp)
-{
-  int clear_status = 0;
-
-  /* NULL means this is the main thread still going through the shell,
-     or, no watchpoint has been set yet.  In that case, there's
-     nothing to do.  */
-  if (lwp->arch_private == NULL)
-    return;
-
-  if (lwp->arch_private->debug_registers_changed)
-    {
-      struct i386_debug_reg_state *state
-       = i386_debug_reg_state (ptid_get_pid (lwp->ptid));
-      int i;
-
-      /* On Linux kernel before 2.6.33 commit
-        72f674d203cd230426437cdcf7dd6f681dad8b0d
-        if you enable a breakpoint by the DR_CONTROL bits you need to have
-        already written the corresponding DR_FIRSTADDR...DR_LASTADDR registers.
-
-        Ensure DR_CONTROL gets written as the very last register here.  */
-
-      /* Clear DR_CONTROL first.  In some cases, setting DR0-3 to a
-        value that doesn't match what is enabled in DR_CONTROL
-        results in EINVAL.  */
-      x86_linux_dr_set (lwp->ptid, DR_CONTROL, 0);
-
-      for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
-       if (state->dr_ref_count[i] > 0)
-         {
-           x86_linux_dr_set (lwp->ptid, i, state->dr_mirror[i]);
-
-           /* If we're setting a watchpoint, any change the inferior
-              had done itself to the debug registers needs to be
-              discarded, otherwise, i386_stopped_data_address can get
-              confused.  */
-           clear_status = 1;
-         }
-
-      /* If DR_CONTROL is supposed to be zero, we've already set it
-        above.  */
-      if (state->dr_control_mirror != 0)
-       x86_linux_dr_set (lwp->ptid, DR_CONTROL, state->dr_control_mirror);
-
-      lwp->arch_private->debug_registers_changed = 0;
-    }
-
-  if (clear_status || lwp->stopped_by_watchpoint)
-    x86_linux_dr_set (lwp->ptid, DR_STATUS, 0);
-}
-
-static void
-x86_linux_new_thread (struct lwp_info *lp)
-{
-  struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
-
-  info->debug_registers_changed = 1;
-
-  lp->arch_private = info;
-}
-
-/* linux_nat_new_fork hook.   */
-
-static void
-x86_linux_new_fork (struct lwp_info *parent, pid_t child_pid)
-{
-  pid_t parent_pid;
-  struct i386_debug_reg_state *parent_state;
-  struct i386_debug_reg_state *child_state;
-
-  /* NULL means no watchpoint has ever been set in the parent.  In
-     that case, there's nothing to do.  */
-  if (parent->arch_private == NULL)
-    return;
-
-  /* Linux kernel before 2.6.33 commit
-     72f674d203cd230426437cdcf7dd6f681dad8b0d
-     will inherit hardware debug registers from parent
-     on fork/vfork/clone.  Newer Linux kernels create such tasks with
-     zeroed debug registers.
-
-     GDB core assumes the child inherits the watchpoints/hw
-     breakpoints of the parent, and will remove them all from the
-     forked off process.  Copy the debug registers mirrors into the
-     new process so that all breakpoints and watchpoints can be
-     removed together.  The debug registers mirror will become zeroed
-     in the end before detaching the forked off process, thus making
-     this compatible with older Linux kernels too.  */
-
-  parent_pid = ptid_get_pid (parent->ptid);
-  parent_state = i386_debug_reg_state (parent_pid);
-  child_state = i386_debug_reg_state (child_pid);
-  *child_state = *parent_state;
-}
-
-\f
-
-/* Helper for ps_get_thread_area.  Sets BASE_ADDR to a pointer to
-   the thread local storage (or its descriptor) and returns PS_OK
-   on success.  Returns PS_ERR on failure.  */
-
-static ps_err_e
-x86_linux_get_thread_area (pid_t pid, void *addr, unsigned int *base_addr)
-{
-  /* NOTE: cagney/2003-08-26: The definition of this buffer is found
-     in the kernel header <asm-i386/ldt.h>.  It, after padding, is 4 x
-     4 byte integers in size: `entry_number', `base_addr', `limit',
-     and a bunch of status bits.
-
-     The values returned by this ptrace call should be part of the
-     regcache buffer, and ps_get_thread_area should channel its
-     request through the regcache.  That way remote targets could
-     provide the value using the remote protocol and not this direct
-     call.
-
-     Is this function needed?  I'm guessing that the `base' is the
-     address of a descriptor that libthread_db uses to find the
-     thread local address base that GDB needs.  Perhaps that
-     descriptor is defined by the ABI.  Anyway, given that
-     libthread_db calls this function without prompting (gdb
-     requesting tls base) I guess it needs info in there anyway.  */
-  unsigned int desc[4];
-
-  /* This code assumes that "int" is 32 bits and that
-     GET_THREAD_AREA returns no more than 4 int values.  */
-  gdb_assert (sizeof (int) == 4);
-
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 25
-#endif
-
-  if (ptrace (PTRACE_GET_THREAD_AREA, pid, addr, &desc) < 0)
-    return PS_ERR;
-
-  *base_addr = desc[1];
-  return PS_OK;
-}
-
 /* Called by libthread_db.  Returns a pointer to the thread local
    storage (or its descriptor).  */
 
@@ -1013,264 +739,7 @@ i386_linux_resume (struct target_ops *ops,
   if (ptrace (request, pid, 0, gdb_signal_to_host (signal)) == -1)
     perror_with_name (("ptrace"));
 }
-
-static void (*super_post_startup_inferior) (struct target_ops *self,
-                                           ptid_t ptid);
-
-static void
-x86_linux_child_post_startup_inferior (struct target_ops *self, ptid_t ptid)
-{
-  i386_cleanup_dregs ();
-  super_post_startup_inferior (self, ptid);
-}
-
-#ifdef __x86_64__
-/* Value of CS segment register:
-     64bit process: 0x33
-     32bit process: 0x23  */
-#define AMD64_LINUX_USER64_CS 0x33
-
-/* Value of DS segment register:
-     LP64 process: 0x0
-     X32 process: 0x2b  */
-#define AMD64_LINUX_X32_DS 0x2b
-#endif
-
-/* Get Linux/x86 target description from running target.  */
-
-static const struct target_desc *
-x86_linux_read_description (struct target_ops *ops)
-{
-  int tid;
-  int is_64bit = 0;
-#ifdef __x86_64__
-  int is_x32;
-#endif
-  static uint64_t xcr0;
-  uint64_t xcr0_features_bits;
-
-  /* GNU/Linux LWP ID's are process ID's.  */
-  tid = ptid_get_lwp (inferior_ptid);
-  if (tid == 0)
-    tid = ptid_get_pid (inferior_ptid); /* Not a threaded program.  */
-
-#ifdef __x86_64__
-  {
-    unsigned long cs;
-    unsigned long ds;
-
-    /* Get CS register.  */
-    errno = 0;
-    cs = ptrace (PTRACE_PEEKUSER, tid,
-                offsetof (struct user_regs_struct, cs), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get CS register"));
-
-    is_64bit = cs == AMD64_LINUX_USER64_CS;
-
-    /* Get DS register.  */
-    errno = 0;
-    ds = ptrace (PTRACE_PEEKUSER, tid,
-                offsetof (struct user_regs_struct, ds), 0);
-    if (errno != 0)
-      perror_with_name (_("Couldn't get DS register"));
-
-    is_x32 = ds == AMD64_LINUX_X32_DS;
-
-    if (sizeof (void *) == 4 && is_64bit && !is_x32)
-      error (_("Can't debug 64-bit process with 32-bit GDB"));
-  }
-#elif HAVE_PTRACE_GETFPXREGS
-  if (have_ptrace_getfpxregs == -1)
-    {
-      elf_fpxregset_t fpxregs;
-
-      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
-       {
-         have_ptrace_getfpxregs = 0;
-         have_ptrace_getregset = 0;
-         return tdesc_i386_mmx_linux;
-       }
-    }
-#endif
-
-  if (have_ptrace_getregset == -1)
-    {
-      uint64_t xstateregs[(I386_XSTATE_SSE_SIZE / sizeof (uint64_t))];
-      struct iovec iov;
-
-      iov.iov_base = xstateregs;
-      iov.iov_len = sizeof (xstateregs);
-
-      /* Check if PTRACE_GETREGSET works.  */
-      if (ptrace (PTRACE_GETREGSET, tid,
-                 (unsigned int) NT_X86_XSTATE, &iov) < 0)
-       have_ptrace_getregset = 0;
-      else
-       {
-         have_ptrace_getregset = 1;
-
-         /* Get XCR0 from XSAVE extended state.  */
-         xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
-                            / sizeof (uint64_t))];
-       }
-    }
-
-  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
-     PTRACE_GETREGSET is not available then set xcr0_features_bits to
-     zero so that the "no-features" descriptions are returned by the
-     switches below.  */
-  if (have_ptrace_getregset)
-    xcr0_features_bits = xcr0 & I386_XSTATE_ALL_MASK;
-  else
-    xcr0_features_bits = 0;
-
-  if (is_64bit)
-    {
-#ifdef __x86_64__
-      switch (xcr0_features_bits)
-       {
-       case I386_XSTATE_MPX_AVX512_MASK:
-       case I386_XSTATE_AVX512_MASK:
-         if (is_x32)
-           return tdesc_x32_avx512_linux;
-         else
-           return tdesc_amd64_avx512_linux;
-       case I386_XSTATE_MPX_MASK:
-         if (is_x32)
-           return tdesc_x32_avx_linux; /* No MPX on x32 using AVX.  */
-         else
-           return tdesc_amd64_mpx_linux;
-       case I386_XSTATE_AVX_MASK:
-         if (is_x32)
-           return tdesc_x32_avx_linux;
-         else
-           return tdesc_amd64_avx_linux;
-       default:
-         if (is_x32)
-           return tdesc_x32_linux;
-         else
-           return tdesc_amd64_linux;
-       }
-#endif
-    }
-  else
-    {
-      switch (xcr0_features_bits)
-       {
-       case I386_XSTATE_MPX_AVX512_MASK:
-       case I386_XSTATE_AVX512_MASK:
-         return tdesc_i386_avx512_linux;
-       case I386_XSTATE_MPX_MASK:
-         return tdesc_i386_mpx_linux;
-       case I386_XSTATE_AVX_MASK:
-         return tdesc_i386_avx_linux;
-       default:
-         return tdesc_i386_linux;
-       }
-    }
-
-  gdb_assert_not_reached ("failed to return tdesc");
-}
-
-/* Enable branch tracing.  */
-
-static struct btrace_target_info *
-x86_linux_enable_btrace (struct target_ops *self, ptid_t ptid)
-{
-  struct btrace_target_info *tinfo;
-  struct gdbarch *gdbarch;
-
-  errno = 0;
-  tinfo = linux_enable_btrace (ptid);
-
-  if (tinfo == NULL)
-    error (_("Could not enable branch tracing for %s: %s."),
-          target_pid_to_str (ptid), safe_strerror (errno));
-
-  /* Fill in the size of a pointer in bits.  */
-  gdbarch = target_thread_architecture (ptid);
-  tinfo->ptr_bits = gdbarch_ptr_bit (gdbarch);
-
-  return tinfo;
-}
-
-/* Disable branch tracing.  */
-
-static void
-x86_linux_disable_btrace (struct target_ops *self,
-                         struct btrace_target_info *tinfo)
-{
-  enum btrace_error errcode = linux_disable_btrace (tinfo);
-
-  if (errcode != BTRACE_ERR_NONE)
-    error (_("Could not disable branch tracing."));
-}
-
-/* Teardown branch tracing.  */
-
-static void
-x86_linux_teardown_btrace (struct target_ops *self,
-                          struct btrace_target_info *tinfo)
-{
-  /* Ignore errors.  */
-  linux_disable_btrace (tinfo);
-}
-
-static enum btrace_error
-x86_linux_read_btrace (struct target_ops *self,
-                      VEC (btrace_block_s) **data,
-                      struct btrace_target_info *btinfo,
-                      enum btrace_read_type type)
-{
-  return linux_read_btrace (data, btinfo, type);
-}
-
-/* Create an x86 GNU/Linux target.  */
-
-static struct target_ops *
-x86_linux_create_target (void)
-{
-  /* Fill in the generic GNU/Linux methods.  */
-  struct target_ops *t = linux_target ();
-
-  /* Initialize the debug register function vectors.  */
-  i386_use_watchpoints (t);
-  i386_dr_low.set_control = x86_linux_dr_set_control;
-  i386_dr_low.set_addr = x86_linux_dr_set_addr;
-  i386_dr_low.get_addr = x86_linux_dr_get_addr;
-  i386_dr_low.get_status = x86_linux_dr_get_status;
-  i386_dr_low.get_control = x86_linux_dr_get_control;
-  i386_set_debug_register_length (sizeof (void *));
-
-  /* Override the GNU/Linux inferior startup hook.  */
-  super_post_startup_inferior = t->to_post_startup_inferior;
-  t->to_post_startup_inferior = x86_linux_child_post_startup_inferior;
-
-  /* Add the description reader.  */
-  t->to_read_description = x86_linux_read_description;
-
-  /* Add btrace methods.  */
-  t->to_supports_btrace = linux_supports_btrace;
-  t->to_enable_btrace = x86_linux_enable_btrace;
-  t->to_disable_btrace = x86_linux_disable_btrace;
-  t->to_teardown_btrace = x86_linux_teardown_btrace;
-  t->to_read_btrace = x86_linux_read_btrace;
-
-  return t;
-}
-
-/* Add an x86 GNU/Linux target.  */
-
-static void
-x86_linux_add_target (struct target_ops *t)
-{
-  linux_nat_add_target (t);
-  linux_nat_set_new_thread (t, x86_linux_new_thread);
-  linux_nat_set_new_fork (t, x86_linux_new_fork);
-  linux_nat_set_forget_process (t, i386_forget_process);
-  linux_nat_set_prepare_to_resume (t, x86_linux_prepare_to_resume);
-}
+\f
 
 /* -Wmissing-prototypes */
 extern initialize_file_ftype _initialize_i386_linux_nat;
diff --git a/gdb/i386-linux-nat.h b/gdb/i386-linux-nat.h
new file mode 100644 (file)
index 0000000..f2c29bd
--- /dev/null
@@ -0,0 +1,26 @@
+/* Native-dependent code for GNU/Linux i386.
+
+   Copyright (C) 1999-2014 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 I386_LINUX_NAT_H
+#define I386_LINUX_NAT_H 1
+
+/* Does the current host support the GETFPXREGS request? */
+extern int have_ptrace_getfpxregs;
+
+#endif
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
new file mode 100644 (file)
index 0000000..7527370
--- /dev/null
@@ -0,0 +1,568 @@
+/* Native-dependent code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 1999-2014 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 "inferior.h"
+#include "elf/common.h"
+#include "gdb_proc_service.h"
+#include <sys/ptrace.h>
+#include <sys/user.h>
+#include <sys/procfs.h>
+
+#include "i386-nat.h"
+#include "linux-nat.h"
+#ifndef __x86_64__
+#include "i386-linux-nat.h"
+#endif
+#include "x86-linux-nat.h"
+#include "i386-linux-tdep.h"
+#ifdef __x86_64__
+#include "amd64-linux-tdep.h"
+#endif
+#include "i386-xstate.h"
+#include "nat/linux-btrace.h"
+
+/* Per-thread arch-specific data we want to keep.  */
+
+struct arch_lwp_info
+{
+  /* Non-zero if our copy differs from what's recorded in the thread.  */
+  int debug_registers_changed;
+};
+
+/* Does the current host support PTRACE_GETREGSET?  */
+int have_ptrace_getregset = -1;
+\f
+
+/* Support for debug registers.  */
+
+/* Get debug register REGNUM value from only the one LWP of PTID.  */
+
+static unsigned long
+x86_linux_dr_get (ptid_t ptid, int regnum)
+{
+  int tid;
+  unsigned long value;
+
+  tid = ptid_get_lwp (ptid);
+  if (tid == 0)
+    tid = ptid_get_pid (ptid);
+
+  errno = 0;
+  value = ptrace (PTRACE_PEEKUSER, tid,
+                 offsetof (struct user, u_debugreg[regnum]), 0);
+  if (errno != 0)
+    perror_with_name (_("Couldn't read debug register"));
+
+  return value;
+}
+
+/* Set debug register REGNUM to VALUE in only the one LWP of PTID.  */
+
+static void
+x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
+{
+  int tid;
+
+  tid = ptid_get_lwp (ptid);
+  if (tid == 0)
+    tid = ptid_get_pid (ptid);
+
+  errno = 0;
+  ptrace (PTRACE_POKEUSER, tid,
+         offsetof (struct user, u_debugreg[regnum]), value);
+  if (errno != 0)
+    perror_with_name (_("Couldn't write debug register"));
+}
+
+/* Return the inferior's debug register REGNUM.  */
+
+static CORE_ADDR
+x86_linux_dr_get_addr (int regnum)
+{
+  /* DR6 and DR7 are retrieved with some other way.  */
+  gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
+
+  return x86_linux_dr_get (inferior_ptid, regnum);
+}
+
+/* Return the inferior's DR7 debug control register.  */
+
+static unsigned long
+x86_linux_dr_get_control (void)
+{
+  return x86_linux_dr_get (inferior_ptid, DR_CONTROL);
+}
+
+/* Get DR_STATUS from only the one LWP of INFERIOR_PTID.  */
+
+static unsigned long
+x86_linux_dr_get_status (void)
+{
+  return x86_linux_dr_get (inferior_ptid, DR_STATUS);
+}
+
+/* Callback for iterate_over_lwps.  Update the debug registers of
+   LWP.  */
+
+static int
+update_debug_registers_callback (struct lwp_info *lwp, void *arg)
+{
+  if (lwp->arch_private == NULL)
+    lwp->arch_private = XCNEW (struct arch_lwp_info);
+
+  /* The actual update is done later just before resuming the lwp, we
+     just mark that the registers need updating.  */
+  lwp->arch_private->debug_registers_changed = 1;
+
+  /* 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);
+
+  /* Continue the iteration.  */
+  return 0;
+}
+
+/* Set DR_CONTROL to CONTROL in all LWPs of the current inferior.  */
+
+static void
+x86_linux_dr_set_control (unsigned long control)
+{
+  ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+
+  iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
+}
+
+/* Set address REGNUM (zero based) to ADDR in all LWPs of the current
+   inferior.  */
+
+static void
+x86_linux_dr_set_addr (int regnum, CORE_ADDR addr)
+{
+  ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+
+  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+
+  iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL);
+}
+
+/* Called when resuming a thread.
+   If the debug regs have changed, update the thread's copies.  */
+
+static void
+x86_linux_prepare_to_resume (struct lwp_info *lwp)
+{
+  int clear_status = 0;
+
+  /* NULL means this is the main thread still going through the shell,
+     or, no watchpoint has been set yet.  In that case, there's
+     nothing to do.  */
+  if (lwp->arch_private == NULL)
+    return;
+
+  if (lwp->arch_private->debug_registers_changed)
+    {
+      struct i386_debug_reg_state *state
+       = i386_debug_reg_state (ptid_get_pid (lwp->ptid));
+      int i;
+
+      /* On Linux kernel before 2.6.33 commit
+        72f674d203cd230426437cdcf7dd6f681dad8b0d
+        if you enable a breakpoint by the DR_CONTROL bits you need to have
+        already written the corresponding DR_FIRSTADDR...DR_LASTADDR registers.
+
+        Ensure DR_CONTROL gets written as the very last register here.  */
+
+      /* Clear DR_CONTROL first.  In some cases, setting DR0-3 to a
+        value that doesn't match what is enabled in DR_CONTROL
+        results in EINVAL.  */
+      x86_linux_dr_set (lwp->ptid, DR_CONTROL, 0);
+
+      for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
+       if (state->dr_ref_count[i] > 0)
+         {
+           x86_linux_dr_set (lwp->ptid, i, state->dr_mirror[i]);
+
+           /* If we're setting a watchpoint, any change the inferior
+              had done itself to the debug registers needs to be
+              discarded, otherwise, i386_stopped_data_address can get
+              confused.  */
+           clear_status = 1;
+         }
+
+      /* If DR_CONTROL is supposed to be zero, we've already set it
+        above.  */
+      if (state->dr_control_mirror != 0)
+       x86_linux_dr_set (lwp->ptid, DR_CONTROL, state->dr_control_mirror);
+
+      lwp->arch_private->debug_registers_changed = 0;
+    }
+
+  if (clear_status || lwp->stopped_by_watchpoint)
+    x86_linux_dr_set (lwp->ptid, DR_STATUS, 0);
+}
+
+static void
+x86_linux_new_thread (struct lwp_info *lp)
+{
+  struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
+
+  info->debug_registers_changed = 1;
+
+  lp->arch_private = info;
+}
+\f
+
+/* linux_nat_new_fork hook.   */
+
+static void
+x86_linux_new_fork (struct lwp_info *parent, pid_t child_pid)
+{
+  pid_t parent_pid;
+  struct i386_debug_reg_state *parent_state;
+  struct i386_debug_reg_state *child_state;
+
+  /* NULL means no watchpoint has ever been set in the parent.  In
+     that case, there's nothing to do.  */
+  if (parent->arch_private == NULL)
+    return;
+
+  /* Linux kernel before 2.6.33 commit
+     72f674d203cd230426437cdcf7dd6f681dad8b0d
+     will inherit hardware debug registers from parent
+     on fork/vfork/clone.  Newer Linux kernels create such tasks with
+     zeroed debug registers.
+
+     GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  The debug registers mirror will become zeroed
+     in the end before detaching the forked off process, thus making
+     this compatible with older Linux kernels too.  */
+
+  parent_pid = ptid_get_pid (parent->ptid);
+  parent_state = i386_debug_reg_state (parent_pid);
+  child_state = i386_debug_reg_state (child_pid);
+  *child_state = *parent_state;
+}
+\f
+
+static void (*super_post_startup_inferior) (struct target_ops *self,
+                                           ptid_t ptid);
+
+static void
+x86_linux_child_post_startup_inferior (struct target_ops *self, ptid_t ptid)
+{
+  i386_cleanup_dregs ();
+  super_post_startup_inferior (self, ptid);
+}
+
+#ifdef __x86_64__
+/* Value of CS segment register:
+     64bit process: 0x33
+     32bit process: 0x23  */
+#define AMD64_LINUX_USER64_CS 0x33
+
+/* Value of DS segment register:
+     LP64 process: 0x0
+     X32 process: 0x2b  */
+#define AMD64_LINUX_X32_DS 0x2b
+#endif
+
+/* Get Linux/x86 target description from running target.  */
+
+static const struct target_desc *
+x86_linux_read_description (struct target_ops *ops)
+{
+  int tid;
+  int is_64bit = 0;
+#ifdef __x86_64__
+  int is_x32;
+#endif
+  static uint64_t xcr0;
+  uint64_t xcr0_features_bits;
+
+  /* GNU/Linux LWP ID's are process ID's.  */
+  tid = ptid_get_lwp (inferior_ptid);
+  if (tid == 0)
+    tid = ptid_get_pid (inferior_ptid); /* Not a threaded program.  */
+
+#ifdef __x86_64__
+  {
+    unsigned long cs;
+    unsigned long ds;
+
+    /* Get CS register.  */
+    errno = 0;
+    cs = ptrace (PTRACE_PEEKUSER, tid,
+                offsetof (struct user_regs_struct, cs), 0);
+    if (errno != 0)
+      perror_with_name (_("Couldn't get CS register"));
+
+    is_64bit = cs == AMD64_LINUX_USER64_CS;
+
+    /* Get DS register.  */
+    errno = 0;
+    ds = ptrace (PTRACE_PEEKUSER, tid,
+                offsetof (struct user_regs_struct, ds), 0);
+    if (errno != 0)
+      perror_with_name (_("Couldn't get DS register"));
+
+    is_x32 = ds == AMD64_LINUX_X32_DS;
+
+    if (sizeof (void *) == 4 && is_64bit && !is_x32)
+      error (_("Can't debug 64-bit process with 32-bit GDB"));
+  }
+#elif HAVE_PTRACE_GETFPXREGS
+  if (have_ptrace_getfpxregs == -1)
+    {
+      elf_fpxregset_t fpxregs;
+
+      if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
+       {
+         have_ptrace_getfpxregs = 0;
+         have_ptrace_getregset = 0;
+         return tdesc_i386_mmx_linux;
+       }
+    }
+#endif
+
+  if (have_ptrace_getregset == -1)
+    {
+      uint64_t xstateregs[(I386_XSTATE_SSE_SIZE / sizeof (uint64_t))];
+      struct iovec iov;
+
+      iov.iov_base = xstateregs;
+      iov.iov_len = sizeof (xstateregs);
+
+      /* Check if PTRACE_GETREGSET works.  */
+      if (ptrace (PTRACE_GETREGSET, tid,
+                 (unsigned int) NT_X86_XSTATE, &iov) < 0)
+       have_ptrace_getregset = 0;
+      else
+       {
+         have_ptrace_getregset = 1;
+
+         /* Get XCR0 from XSAVE extended state.  */
+         xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+                            / sizeof (uint64_t))];
+       }
+    }
+
+  /* Check the native XCR0 only if PTRACE_GETREGSET is available.  If
+     PTRACE_GETREGSET is not available then set xcr0_features_bits to
+     zero so that the "no-features" descriptions are returned by the
+     switches below.  */
+  if (have_ptrace_getregset)
+    xcr0_features_bits = xcr0 & I386_XSTATE_ALL_MASK;
+  else
+    xcr0_features_bits = 0;
+
+  if (is_64bit)
+    {
+#ifdef __x86_64__
+      switch (xcr0_features_bits)
+       {
+       case I386_XSTATE_MPX_AVX512_MASK:
+       case I386_XSTATE_AVX512_MASK:
+         if (is_x32)
+           return tdesc_x32_avx512_linux;
+         else
+           return tdesc_amd64_avx512_linux;
+       case I386_XSTATE_MPX_MASK:
+         if (is_x32)
+           return tdesc_x32_avx_linux; /* No MPX on x32 using AVX.  */
+         else
+           return tdesc_amd64_mpx_linux;
+       case I386_XSTATE_AVX_MASK:
+         if (is_x32)
+           return tdesc_x32_avx_linux;
+         else
+           return tdesc_amd64_avx_linux;
+       default:
+         if (is_x32)
+           return tdesc_x32_linux;
+         else
+           return tdesc_amd64_linux;
+       }
+#endif
+    }
+  else
+    {
+      switch (xcr0_features_bits)
+       {
+       case I386_XSTATE_MPX_AVX512_MASK:
+       case I386_XSTATE_AVX512_MASK:
+         return tdesc_i386_avx512_linux;
+       case I386_XSTATE_MPX_MASK:
+         return tdesc_i386_mpx_linux;
+       case I386_XSTATE_AVX_MASK:
+         return tdesc_i386_avx_linux;
+       default:
+         return tdesc_i386_linux;
+       }
+    }
+
+  gdb_assert_not_reached ("failed to return tdesc");
+}
+\f
+
+/* Enable branch tracing.  */
+
+static struct btrace_target_info *
+x86_linux_enable_btrace (struct target_ops *self, ptid_t ptid)
+{
+  struct btrace_target_info *tinfo;
+  struct gdbarch *gdbarch;
+
+  errno = 0;
+  tinfo = linux_enable_btrace (ptid);
+
+  if (tinfo == NULL)
+    error (_("Could not enable branch tracing for %s: %s."),
+          target_pid_to_str (ptid), safe_strerror (errno));
+
+  /* Fill in the size of a pointer in bits.  */
+  gdbarch = target_thread_architecture (ptid);
+  tinfo->ptr_bits = gdbarch_ptr_bit (gdbarch);
+
+  return tinfo;
+}
+
+/* Disable branch tracing.  */
+
+static void
+x86_linux_disable_btrace (struct target_ops *self,
+                         struct btrace_target_info *tinfo)
+{
+  enum btrace_error errcode = linux_disable_btrace (tinfo);
+
+  if (errcode != BTRACE_ERR_NONE)
+    error (_("Could not disable branch tracing."));
+}
+
+/* Teardown branch tracing.  */
+
+static void
+x86_linux_teardown_btrace (struct target_ops *self,
+                          struct btrace_target_info *tinfo)
+{
+  /* Ignore errors.  */
+  linux_disable_btrace (tinfo);
+}
+
+static enum btrace_error
+x86_linux_read_btrace (struct target_ops *self,
+                      VEC (btrace_block_s) **data,
+                      struct btrace_target_info *btinfo,
+                      enum btrace_read_type type)
+{
+  return linux_read_btrace (data, btinfo, type);
+}
+\f
+
+/* Helper for ps_get_thread_area.  Sets BASE_ADDR to a pointer to
+   the thread local storage (or its descriptor) and returns PS_OK
+   on success.  Returns PS_ERR on failure.  */
+
+ps_err_e
+x86_linux_get_thread_area (pid_t pid, void *addr, unsigned int *base_addr)
+{
+  /* NOTE: cagney/2003-08-26: The definition of this buffer is found
+     in the kernel header <asm-i386/ldt.h>.  It, after padding, is 4 x
+     4 byte integers in size: `entry_number', `base_addr', `limit',
+     and a bunch of status bits.
+
+     The values returned by this ptrace call should be part of the
+     regcache buffer, and ps_get_thread_area should channel its
+     request through the regcache.  That way remote targets could
+     provide the value using the remote protocol and not this direct
+     call.
+
+     Is this function needed?  I'm guessing that the `base' is the
+     address of a descriptor that libthread_db uses to find the
+     thread local address base that GDB needs.  Perhaps that
+     descriptor is defined by the ABI.  Anyway, given that
+     libthread_db calls this function without prompting (gdb
+     requesting tls base) I guess it needs info in there anyway.  */
+  unsigned int desc[4];
+
+  /* This code assumes that "int" is 32 bits and that
+     GET_THREAD_AREA returns no more than 4 int values.  */
+  gdb_assert (sizeof (int) == 4);
+
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
+#endif
+
+  if (ptrace (PTRACE_GET_THREAD_AREA, pid, addr, &desc) < 0)
+    return PS_ERR;
+
+  *base_addr = desc[1];
+  return PS_OK;
+}
+\f
+
+/* Create an x86 GNU/Linux target.  */
+
+struct target_ops *
+x86_linux_create_target (void)
+{
+  /* Fill in the generic GNU/Linux methods.  */
+  struct target_ops *t = linux_target ();
+
+  /* Initialize the debug register function vectors.  */
+  i386_use_watchpoints (t);
+  i386_dr_low.set_control = x86_linux_dr_set_control;
+  i386_dr_low.set_addr = x86_linux_dr_set_addr;
+  i386_dr_low.get_addr = x86_linux_dr_get_addr;
+  i386_dr_low.get_status = x86_linux_dr_get_status;
+  i386_dr_low.get_control = x86_linux_dr_get_control;
+  i386_set_debug_register_length (sizeof (void *));
+
+  /* Override the GNU/Linux inferior startup hook.  */
+  super_post_startup_inferior = t->to_post_startup_inferior;
+  t->to_post_startup_inferior = x86_linux_child_post_startup_inferior;
+
+  /* Add the description reader.  */
+  t->to_read_description = x86_linux_read_description;
+
+  /* Add btrace methods.  */
+  t->to_supports_btrace = linux_supports_btrace;
+  t->to_enable_btrace = x86_linux_enable_btrace;
+  t->to_disable_btrace = x86_linux_disable_btrace;
+  t->to_teardown_btrace = x86_linux_teardown_btrace;
+  t->to_read_btrace = x86_linux_read_btrace;
+
+  return t;
+}
+
+/* Add an x86 GNU/Linux target.  */
+
+void
+x86_linux_add_target (struct target_ops *t)
+{
+  linux_nat_add_target (t);
+  linux_nat_set_new_thread (t, x86_linux_new_thread);
+  linux_nat_set_new_fork (t, x86_linux_new_fork);
+  linux_nat_set_forget_process (t, i386_forget_process);
+  linux_nat_set_prepare_to_resume (t, x86_linux_prepare_to_resume);
+}
diff --git a/gdb/x86-linux-nat.h b/gdb/x86-linux-nat.h
new file mode 100644 (file)
index 0000000..16679af
--- /dev/null
@@ -0,0 +1,51 @@
+/* Native-dependent code for GNU/Linux x86 (i386 and x86-64).
+
+   Copyright (C) 1999-2014 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 X86_LINUX_NAT_H
+#define X86_LINUX_NAT_H 1
+
+#ifndef PTRACE_GETREGSET
+#define PTRACE_GETREGSET       0x4204
+#endif
+
+#ifndef PTRACE_SETREGSET
+#define PTRACE_SETREGSET       0x4205
+#endif
+
+/* Does the current host support PTRACE_GETREGSET?  */
+extern int have_ptrace_getregset;
+\f
+
+/* Helper for ps_get_thread_area.  Sets BASE_ADDR to a pointer to
+   the thread local storage (or its descriptor) and returns PS_OK
+   on success.  Returns PS_ERR on failure.  */
+
+extern ps_err_e x86_linux_get_thread_area (pid_t pid, void *addr,
+                                          unsigned int *base_addr);
+\f
+
+/* Create an x86 GNU/Linux target.  */
+
+extern struct target_ops *x86_linux_create_target (void);
+
+/* Add an x86 GNU/Linux target.  */
+
+extern void x86_linux_add_target (struct target_ops *t);
+
+#endif