* i386-tdep.c (i386_match_insn_block): Use length of the proper
[platform/upstream/binutils.git] / gdb / amd64-linux-nat.c
index 5c9e558..a869f85 100644 (file)
@@ -1,7 +1,7 @@
 /* Native-dependent code for GNU/Linux x86-64.
 
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+   2011 Free Software Foundation, Inc.
    Contributed by Jiri Smid, SuSE Labs.
 
    This file is part of GDB.
 #include "inferior.h"
 #include "gdbcore.h"
 #include "regcache.h"
+#include "regset.h"
 #include "linux-nat.h"
 #include "amd64-linux-tdep.h"
 
 #include "gdb_assert.h"
 #include "gdb_string.h"
+#include "elf/common.h"
+#include <sys/uio.h>
 #include <sys/ptrace.h>
 #include <sys/debugreg.h>
 #include <sys/syscall.h>
@@ -37,7 +40,7 @@
    <asm/ptrace.h> because the latter redefines FS and GS for no apparent
    reason, and those definitions don't match the ones that libpthread_db
    uses, which come from <sys/reg.h>.  */
-/* ezannoni-2003-07-09: I think this is fixed. The extraneous defs have
+/* ezannoni-2003-07-09: I think this is fixed.  The extraneous defs have
    been removed from ptrace.h in the kernel.  However, better safe than
    sorry.  */
 #include <asm/ptrace.h>
 #include "i386-linux-tdep.h"
 #include "amd64-nat.h"
 #include "i386-nat.h"
+#include "i386-xstate.h"
 
-/* Mapping between the general-purpose registers in GNU/Linux x86-64
-   `struct user' format and GDB's register cache layout.  */
+#ifndef PTRACE_GETREGSET
+#define PTRACE_GETREGSET       0x4204
+#endif
 
-static int amd64_linux_gregset64_reg_offset[] =
-{
-  RAX * 8, RBX * 8,            /* %rax, %rbx */
-  RCX * 8, RDX * 8,            /* %rcx, %rdx */
-  RSI * 8, RDI * 8,            /* %rsi, %rdi */
-  RBP * 8, RSP * 8,            /* %rbp, %rsp */
-  R8 * 8, R9 * 8,              /* %r8 ... */
-  R10 * 8, R11 * 8,
-  R12 * 8, R13 * 8,
-  R14 * 8, R15 * 8,            /* ... %r15 */
-  RIP * 8, EFLAGS * 8,         /* %rip, %eflags */
-  CS * 8, SS * 8,              /* %cs, %ss */
-  DS * 8, ES * 8,              /* %ds, %es */
-  FS * 8, GS * 8,              /* %fs, %gs */
-  -1, -1, -1, -1, -1, -1, -1, -1,
-  -1, -1, -1, -1, -1, -1, -1, -1,
-  -1, -1, -1, -1, -1, -1, -1, -1,
-  -1, -1, -1, -1, -1, -1, -1, -1, -1,
-  ORIG_RAX * 8
-};
-\f
+#ifndef PTRACE_SETREGSET
+#define PTRACE_SETREGSET       0x4205
+#endif
+
+/* Does the current host support PTRACE_GETREGSET?  */
+static int have_ptrace_getregset = -1;
 
 /* Mapping between the general-purpose registers in GNU/Linux x86-64
    `struct user' format and GDB's register cache layout for GNU/Linux
@@ -99,6 +89,7 @@ static int amd64_linux_gregset32_reg_offset[] =
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
   ORIG_RAX * 8                 /* "orig_eax" */
 };
 \f
@@ -183,10 +174,26 @@ amd64_linux_fetch_inferior_registers (struct target_ops *ops,
     {
       elf_fpregset_t fpregs;
 
-      if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
-       perror_with_name (_("Couldn't get floating point status"));
+      if (have_ptrace_getregset)
+       {
+         char xstateregs[I386_XSTATE_MAX_SIZE];
+         struct iovec iov;
+
+         iov.iov_base = xstateregs;
+         iov.iov_len = sizeof (xstateregs);
+         if (ptrace (PTRACE_GETREGSET, tid,
+                     (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
+           perror_with_name (_("Couldn't get extended state status"));
 
-      amd64_supply_fxsave (regcache, -1, &fpregs);
+         amd64_supply_xsave (regcache, -1, xstateregs);
+       }
+      else
+       {
+         if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
+           perror_with_name (_("Couldn't get floating point status"));
+
+         amd64_supply_fxsave (regcache, -1, &fpregs);
+       }
     }
 }
 
@@ -226,15 +233,33 @@ amd64_linux_store_inferior_registers (struct target_ops *ops,
     {
       elf_fpregset_t fpregs;
 
-      if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
-       perror_with_name (_("Couldn't get floating point status"));
+      if (have_ptrace_getregset)
+       {
+         char xstateregs[I386_XSTATE_MAX_SIZE];
+         struct iovec iov;
+
+         iov.iov_base = xstateregs;
+         iov.iov_len = sizeof (xstateregs);
+         if (ptrace (PTRACE_GETREGSET, tid,
+                     (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
+           perror_with_name (_("Couldn't get extended state status"));
+
+         amd64_collect_xsave (regcache, regnum, xstateregs, 0);
 
-      amd64_collect_fxsave (regcache, regnum, &fpregs);
+         if (ptrace (PTRACE_SETREGSET, tid,
+                     (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
+           perror_with_name (_("Couldn't write extended state status"));
+       }
+      else
+       {
+         if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
+           perror_with_name (_("Couldn't get floating point status"));
 
-      if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0)
-       perror_with_name (_("Couldn't write floating point status"));
+         amd64_collect_fxsave (regcache, regnum, &fpregs);
 
-      return;
+         if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0)
+           perror_with_name (_("Couldn't write floating point status"));
+       }
     }
 }
 \f
@@ -549,8 +574,10 @@ compat_siginfo_from_siginfo (compat_siginfo_t *to, siginfo_t *from)
   to->si_errno = from->si_errno;
   to->si_code = from->si_code;
 
-  if (to->si_code < 0)
+  if (to->si_code == SI_TIMER)
     {
+      to->cpt_si_timerid = from->si_timerid;
+      to->cpt_si_overrun = from->si_overrun;
       to->cpt_si_ptr = (intptr_t) from->si_ptr;
     }
   else if (to->si_code == SI_USER)
@@ -558,10 +585,10 @@ compat_siginfo_from_siginfo (compat_siginfo_t *to, siginfo_t *from)
       to->cpt_si_pid = from->si_pid;
       to->cpt_si_uid = from->si_uid;
     }
-  else if (to->si_code == SI_TIMER)
+  else if (to->si_code < 0)
     {
-      to->cpt_si_timerid = from->si_timerid;
-      to->cpt_si_overrun = from->si_overrun;
+      to->cpt_si_pid = from->si_pid;
+      to->cpt_si_uid = from->si_uid;
       to->cpt_si_ptr = (intptr_t) from->si_ptr;
     }
   else
@@ -603,8 +630,10 @@ siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from)
   to->si_errno = from->si_errno;
   to->si_code = from->si_code;
 
-  if (to->si_code < 0)
+  if (to->si_code == SI_TIMER)
     {
+      to->si_timerid = from->cpt_si_timerid;
+      to->si_overrun = from->cpt_si_overrun;
       to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr;
     }
   else if (to->si_code == SI_USER)
@@ -612,10 +641,10 @@ siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from)
       to->si_pid = from->cpt_si_pid;
       to->si_uid = from->cpt_si_uid;
     }
-  else if (to->si_code == SI_TIMER)
+  if (to->si_code < 0)
     {
-      to->si_timerid = from->cpt_si_timerid;
-      to->si_overrun = from->cpt_si_overrun;
+      to->si_pid = from->cpt_si_pid;
+      to->si_uid = from->cpt_si_uid;
       to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr;
     }
   else
@@ -674,6 +703,77 @@ amd64_linux_siginfo_fixup (struct siginfo *native, gdb_byte *inf, int direction)
     return 0;
 }
 
+/* Get Linux/x86 target description from running target.
+
+   Value of CS segment register:
+     1. 64bit process: 0x33.
+     2. 32bit process: 0x23.
+ */
+
+#define AMD64_LINUX_USER64_CS  0x33
+
+static const struct target_desc *
+amd64_linux_read_description (struct target_ops *ops)
+{
+  unsigned long cs;
+  int tid;
+  int is_64bit;
+  static uint64_t xcr0;
+
+  /* GNU/Linux LWP ID's are process ID's.  */
+  tid = TIDGET (inferior_ptid);
+  if (tid == 0)
+    tid = PIDGET (inferior_ptid); /* Not a threaded program.  */
+
+  /* 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;
+
+  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, (long) &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 (have_ptrace_getregset
+      && (xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK)
+    {
+      if (is_64bit)
+       return tdesc_amd64_avx_linux;
+      else
+       return tdesc_i386_avx_linux;
+    }
+  else
+    {
+      if (is_64bit)
+       return tdesc_amd64_linux;
+      else
+       return tdesc_i386_linux;
+    }
+}
+
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 void _initialize_amd64_linux_nat (void);
 
@@ -684,13 +784,11 @@ _initialize_amd64_linux_nat (void)
 
   amd64_native_gregset32_reg_offset = amd64_linux_gregset32_reg_offset;
   amd64_native_gregset32_num_regs = I386_LINUX_NUM_REGS;
-  amd64_native_gregset64_reg_offset = amd64_linux_gregset64_reg_offset;
+  amd64_native_gregset64_reg_offset = amd64_linux_gregset_reg_offset;
   amd64_native_gregset64_num_regs = AMD64_LINUX_NUM_REGS;
 
   gdb_assert (ARRAY_SIZE (amd64_linux_gregset32_reg_offset)
              == amd64_native_gregset32_num_regs);
-  gdb_assert (ARRAY_SIZE (amd64_linux_gregset64_reg_offset)
-             == amd64_native_gregset64_num_regs);
 
   /* Fill in the generic GNU/Linux methods.  */
   t = linux_target ();
@@ -712,6 +810,8 @@ _initialize_amd64_linux_nat (void)
   t->to_fetch_registers = amd64_linux_fetch_inferior_registers;
   t->to_store_registers = amd64_linux_store_inferior_registers;
 
+  t->to_read_description = amd64_linux_read_description;
+
   /* Register the target.  */
   linux_nat_add_target (t);
   linux_nat_set_new_thread (t, amd64_linux_new_thread);