* win32-i386-low.c: Add 64-bit support.
[deliverable/binutils-gdb.git] / gdb / gdbserver / linux-x86-low.c
index f77e9dafef02c30f83309d8b2b181c139d947a8a..f254f3838981b795845a5b5bbef8240633829e3f 100644 (file)
@@ -1,6 +1,6 @@
 /* GNU/Linux/x86-64 specific low level interface, for the remote server
    for GDB.
-   Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009
+   Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009, 2010
    Free Software Foundation, Inc.
 
    This file is part of GDB.
 #include "linux-low.h"
 #include "i387-fp.h"
 #include "i386-low.h"
+#include "i386-xstate.h"
+#include "elf/common.h"
 
 #include "gdb_proc_service.h"
 
-/* Defined in auto-generated file reg-i386-linux.c.  */
+/* Defined in auto-generated file i386-linux.c.  */
 void init_registers_i386_linux (void);
-/* Defined in auto-generated file reg-x86-64-linux.c.  */
-void init_registers_x86_64_linux (void);
+/* Defined in auto-generated file amd64-linux.c.  */
+void init_registers_amd64_linux (void);
+/* Defined in auto-generated file i386-avx-linux.c.  */
+void init_registers_i386_avx_linux (void);
+/* Defined in auto-generated file amd64-avx-linux.c.  */
+void init_registers_amd64_avx_linux (void);
+/* Defined in auto-generated file i386-mmx-linux.c.  */
+void init_registers_i386_mmx_linux (void);
+
+/* Backward compatibility for gdb without XML support.  */
+
+static const char *xmltarget_i386_linux_no_xml = "@<target>\
+<architecture>i386</architecture>\
+<osabi>GNU/Linux</osabi>\
+</target>";
+
+#ifdef __x86_64__
+static const char *xmltarget_amd64_linux_no_xml = "@<target>\
+<architecture>i386:x86-64</architecture>\
+<osabi>GNU/Linux</osabi>\
+</target>";
+#endif
 
 #include <sys/reg.h>
 #include <sys/procfs.h>
 #include <sys/ptrace.h>
+#include <sys/uio.h>
+
+#ifndef PTRACE_GETREGSET
+#define PTRACE_GETREGSET       0x4204
+#endif
+
+#ifndef PTRACE_SETREGSET
+#define PTRACE_SETREGSET       0x4205
+#endif
+
 
 #ifndef PTRACE_GET_THREAD_AREA
 #define PTRACE_GET_THREAD_AREA 25
@@ -173,7 +205,7 @@ i386_cannot_fetch_register (int regno)
 }
 
 static void
-x86_fill_gregset (void *buf)
+x86_fill_gregset (struct regcache *regcache, void *buf)
 {
   int i;
 
@@ -182,19 +214,20 @@ x86_fill_gregset (void *buf)
     {
       for (i = 0; i < X86_64_NUM_REGS; i++)
        if (x86_64_regmap[i] != -1)
-         collect_register (i, ((char *) buf) + x86_64_regmap[i]);
+         collect_register (regcache, i, ((char *) buf) + x86_64_regmap[i]);
       return;
     }
 #endif
 
   for (i = 0; i < I386_NUM_REGS; i++)
-    collect_register (i, ((char *) buf) + i386_regmap[i]);
+    collect_register (regcache, i, ((char *) buf) + i386_regmap[i]);
 
-  collect_register_by_name ("orig_eax", ((char *) buf) + ORIG_EAX * 4);
+  collect_register_by_name (regcache, "orig_eax",
+                           ((char *) buf) + ORIG_EAX * 4);
 }
 
 static void
-x86_store_gregset (const void *buf)
+x86_store_gregset (struct regcache *regcache, const void *buf)
 {
   int i;
 
@@ -203,53 +236,66 @@ x86_store_gregset (const void *buf)
     {
       for (i = 0; i < X86_64_NUM_REGS; i++)
        if (x86_64_regmap[i] != -1)
-         supply_register (i, ((char *) buf) + x86_64_regmap[i]);
+         supply_register (regcache, i, ((char *) buf) + x86_64_regmap[i]);
       return;
     }
 #endif
 
   for (i = 0; i < I386_NUM_REGS; i++)
-    supply_register (i, ((char *) buf) + i386_regmap[i]);
+    supply_register (regcache, i, ((char *) buf) + i386_regmap[i]);
 
-  supply_register_by_name ("orig_eax", ((char *) buf) + ORIG_EAX * 4);
+  supply_register_by_name (regcache, "orig_eax",
+                          ((char *) buf) + ORIG_EAX * 4);
 }
 
 static void
-x86_fill_fpregset (void *buf)
+x86_fill_fpregset (struct regcache *regcache, void *buf)
 {
 #ifdef __x86_64__
-  i387_cache_to_fxsave (buf);
+  i387_cache_to_fxsave (regcache, buf);
 #else
-  i387_cache_to_fsave (buf);
+  i387_cache_to_fsave (regcache, buf);
 #endif
 }
 
 static void
-x86_store_fpregset (const void *buf)
+x86_store_fpregset (struct regcache *regcache, const void *buf)
 {
 #ifdef __x86_64__
-  i387_fxsave_to_cache (buf);
+  i387_fxsave_to_cache (regcache, buf);
 #else
-  i387_fsave_to_cache (buf);
+  i387_fsave_to_cache (regcache, buf);
 #endif
 }
 
 #ifndef __x86_64__
 
 static void
-x86_fill_fpxregset (void *buf)
+x86_fill_fpxregset (struct regcache *regcache, void *buf)
 {
-  i387_cache_to_fxsave (buf);
+  i387_cache_to_fxsave (regcache, buf);
 }
 
 static void
-x86_store_fpxregset (const void *buf)
+x86_store_fpxregset (struct regcache *regcache, const void *buf)
 {
-  i387_fxsave_to_cache (buf);
+  i387_fxsave_to_cache (regcache, buf);
 }
 
 #endif
 
+static void
+x86_fill_xstateregset (struct regcache *regcache, void *buf)
+{
+  i387_cache_to_xsave (regcache, buf);
+}
+
+static void
+x86_store_xstateregset (struct regcache *regcache, const void *buf)
+{
+  i387_xsave_to_cache (regcache, buf);
+}
+
 /* ??? The non-biarch i386 case stores all the i387 regs twice.
    Once in i387_.*fsave.* and once in i387_.*fxsave.*.
    This is, presumably, to handle the case where PTRACE_[GS]ETFPXREGS
@@ -262,56 +308,58 @@ x86_store_fpxregset (const void *buf)
 struct regset_info target_regsets[] =
 {
 #ifdef HAVE_PTRACE_GETREGS
-  { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t),
+  { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
     GENERAL_REGS,
     x86_fill_gregset, x86_store_gregset },
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_X86_XSTATE, 0,
+    EXTENDED_REGS, x86_fill_xstateregset, x86_store_xstateregset },
 # ifndef __x86_64__
 #  ifdef HAVE_PTRACE_GETFPXREGS
-  { PTRACE_GETFPXREGS, PTRACE_SETFPXREGS, sizeof (elf_fpxregset_t),
+  { PTRACE_GETFPXREGS, PTRACE_SETFPXREGS, 0, sizeof (elf_fpxregset_t),
     EXTENDED_REGS,
     x86_fill_fpxregset, x86_store_fpxregset },
 #  endif
 # endif
-  { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof (elf_fpregset_t),
+  { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, sizeof (elf_fpregset_t),
     FP_REGS,
     x86_fill_fpregset, x86_store_fpregset },
 #endif /* HAVE_PTRACE_GETREGS */
-  { 0, 0, -1, -1, NULL, NULL }
+  { 0, 0, 0, -1, -1, NULL, NULL }
 };
 
 static CORE_ADDR
-x86_get_pc (void)
+x86_get_pc (struct regcache *regcache)
 {
   int use_64bit = register_size (0) == 8;
 
   if (use_64bit)
     {
       unsigned long pc;
-      collect_register_by_name ("rip", &pc);
+      collect_register_by_name (regcache, "rip", &pc);
       return (CORE_ADDR) pc;
     }
   else
     {
       unsigned int pc;
-      collect_register_by_name ("eip", &pc);
+      collect_register_by_name (regcache, "eip", &pc);
       return (CORE_ADDR) pc;
     }
 }
 
 static void
-x86_set_pc (CORE_ADDR pc)
+x86_set_pc (struct regcache *regcache, CORE_ADDR pc)
 {
   int use_64bit = register_size (0) == 8;
 
   if (use_64bit)
     {
       unsigned long newpc = pc;
-      supply_register_by_name ("rip", &newpc);
+      supply_register_by_name (regcache, "rip", &newpc);
     }
   else
     {
       unsigned int newpc = pc;
-      supply_register_by_name ("eip", &newpc);
+      supply_register_by_name (regcache, "eip", &newpc);
     }
 }
 \f
@@ -323,7 +371,7 @@ x86_breakpoint_at (CORE_ADDR pc)
 {
   unsigned char c;
 
-  read_inferior_memory (pc, &c, 1);
+  (*the_target->read_memory) (pc, &c, 1);
   if (c == 0xCC)
     return 1;
 
@@ -429,6 +477,8 @@ x86_insert_point (char type, CORE_ADDR addr, int len)
   struct process_info *proc = current_process ();
   switch (type)
     {
+    case '0':
+      return set_gdb_breakpoint_at (addr);
     case '2':
     case '3':
     case '4':
@@ -446,6 +496,8 @@ x86_remove_point (char type, CORE_ADDR addr, int len)
   struct process_info *proc = current_process ();
   switch (type)
     {
+    case '0':
+      return delete_gdb_breakpoint_at (addr);
     case '2':
     case '3':
     case '4':
@@ -505,10 +557,11 @@ x86_linux_new_thread (void)
 static void
 x86_linux_prepare_to_resume (struct lwp_info *lwp)
 {
+  ptid_t ptid = ptid_of (lwp);
+
   if (lwp->arch_private->debug_registers_changed)
     {
       int i;
-      ptid_t ptid = ptid_of (lwp);
       int pid = ptid_get_pid (ptid);
       struct process_info *proc = find_process_pid (pid);
       struct i386_debug_reg_state *state = &proc->private->arch_private->debug_reg_state;
@@ -520,6 +573,9 @@ x86_linux_prepare_to_resume (struct lwp_info *lwp)
 
       lwp->arch_private->debug_registers_changed = 0;
     }
+
+  if (lwp->stopped_by_watchpoint)
+    x86_linux_dr_set (ptid, DR_STATUS, 0);
 }
 \f
 /* When GDBSERVER is built as a 64-bit application on linux, the
@@ -770,6 +826,168 @@ x86_siginfo_fixup (struct siginfo *native, void *inf, int direction)
   return 0;
 }
 \f
+static int use_xml;
+
+/* Update gdbserver_xmltarget.  */
+
+static void
+x86_linux_update_xmltarget (void)
+{
+  int pid;
+  struct regset_info *regset;
+  static unsigned long long xcr0;
+  static int have_ptrace_getregset = -1;
+#if !defined(__x86_64__) && defined(HAVE_PTRACE_GETFPXREGS)
+  static int have_ptrace_getfpxregs = -1;
+#endif
+
+  if (!current_inferior)
+    return;
+
+  /* Before changing the register cache internal layout or the target
+     regsets, flush the contents of the current valid caches back to
+     the threads.  */
+  regcache_invalidate ();
+
+  pid = pid_of (get_thread_lwp (current_inferior));
+#ifdef __x86_64__
+  if (num_xmm_registers == 8)
+    init_registers_i386_linux ();
+  else
+    init_registers_amd64_linux ();
+#else
+    {
+# ifdef HAVE_PTRACE_GETFPXREGS
+      if (have_ptrace_getfpxregs == -1)
+       {
+         elf_fpxregset_t fpxregs;
+
+         if (ptrace (PTRACE_GETFPXREGS, pid, 0, (int) &fpxregs) < 0)
+           {
+             have_ptrace_getfpxregs = 0;
+             x86_xcr0 = I386_XSTATE_X87_MASK;
+
+             /* Disable PTRACE_GETFPXREGS.  */
+             for (regset = target_regsets;
+                  regset->fill_function != NULL; regset++)
+               if (regset->get_request == PTRACE_GETFPXREGS)
+                 {
+                   regset->size = 0;
+                   break;
+                 }
+           }
+         else
+           have_ptrace_getfpxregs = 1;
+       }
+
+      if (!have_ptrace_getfpxregs)
+       {
+         init_registers_i386_mmx_linux ();
+         return;
+       }
+# endif
+      init_registers_i386_linux ();
+    }
+#endif
+
+  if (!use_xml)
+    {
+      /* Don't use XML.  */
+#ifdef __x86_64__
+      if (num_xmm_registers == 8)
+       gdbserver_xmltarget = xmltarget_i386_linux_no_xml;
+      else
+       gdbserver_xmltarget = xmltarget_amd64_linux_no_xml;
+#else
+      gdbserver_xmltarget = xmltarget_i386_linux_no_xml;
+#endif
+
+      x86_xcr0 = I386_XSTATE_SSE_MASK;
+
+      return;
+    }
+
+  /* Check if XSAVE extended state is supported.  */
+  if (have_ptrace_getregset == -1)
+    {
+      unsigned long long xstateregs[I386_XSTATE_SSE_SIZE / sizeof (long long)];
+      struct iovec iov;
+
+      iov.iov_base = xstateregs;
+      iov.iov_len = sizeof (xstateregs);
+
+      /* Check if PTRACE_GETREGSET works.  */
+      if (ptrace (PTRACE_GETREGSET, pid, (unsigned int) NT_X86_XSTATE,
+                 &iov) < 0)
+       {
+         have_ptrace_getregset = 0;
+         return;
+       }
+      else
+       have_ptrace_getregset = 1;
+
+      /* Get XCR0 from XSAVE extended state at byte 464.  */
+      xcr0 = xstateregs[464 / sizeof (long long)];
+
+      /* Use PTRACE_GETREGSET if it is available.  */
+      for (regset = target_regsets;
+          regset->fill_function != NULL; regset++)
+       if (regset->get_request == PTRACE_GETREGSET)
+         regset->size = I386_XSTATE_SIZE (xcr0);
+       else if (regset->type != GENERAL_REGS)
+         regset->size = 0;
+    }
+
+  if (have_ptrace_getregset)
+    {
+      /* AVX is the highest feature we support.  */
+      if ((xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK)
+       {
+         x86_xcr0 = xcr0;
+
+#ifdef __x86_64__
+         /* I386 has 8 xmm regs.  */
+         if (num_xmm_registers == 8)
+           init_registers_i386_avx_linux ();
+         else
+           init_registers_amd64_avx_linux ();
+#else
+         init_registers_i386_avx_linux ();
+#endif
+       }
+    }
+}
+
+/* Process qSupported query, "xmlRegisters=".  Update the buffer size for
+   PTRACE_GETREGSET.  */
+
+static void
+x86_linux_process_qsupported (const char *query)
+{
+  /* Return if gdb doesn't support XML.  If gdb sends "xmlRegisters="
+     with "i386" in qSupported query, it supports x86 XML target
+     descriptions.  */
+  use_xml = 0;
+  if (query != NULL && strncmp (query, "xmlRegisters=", 13) == 0)
+    {
+      char *copy = xstrdup (query + 13);
+      char *p;
+
+      for (p = strtok (copy, ","); p != NULL; p = strtok (NULL, ","))
+       {
+         if (strcmp (p, "i386") == 0)
+           {
+             use_xml = 1;
+             break;
+           }
+       } 
+
+      free (copy);
+    }
+
+  x86_linux_update_xmltarget ();
+}
+
 /* Initialize gdbserver for the architecture of the inferior.  */
 
 static void
@@ -790,8 +1008,6 @@ x86_arch_setup (void)
     }
   else if (use_64bit)
     {
-      init_registers_x86_64_linux ();
-
       /* Amd64 doesn't have HAVE_LINUX_USRREGS.  */
       the_low_target.num_regs = -1;
       the_low_target.regmap = NULL;
@@ -801,14 +1017,13 @@ x86_arch_setup (void)
       /* Amd64 has 16 xmm regs.  */
       num_xmm_registers = 16;
 
+      x86_linux_update_xmltarget ();
       return;
     }
 #endif
 
   /* Ok we have a 32-bit inferior.  */
 
-  init_registers_i386_linux ();
-
   the_low_target.num_regs = I386_NUM_REGS;
   the_low_target.regmap = i386_regmap;
   the_low_target.cannot_fetch_register = i386_cannot_fetch_register;
@@ -816,6 +1031,14 @@ x86_arch_setup (void)
 
   /* I386 has 8 xmm regs.  */
   num_xmm_registers = 8;
+
+  x86_linux_update_xmltarget ();
+}
+
+static int
+x86_supports_tracepoints (void)
+{
+  return 1;
 }
 
 /* This is initialized assuming an amd64 target.
@@ -848,5 +1071,7 @@ struct linux_target_ops the_low_target =
   x86_siginfo_fixup,
   x86_linux_new_process,
   x86_linux_new_thread,
-  x86_linux_prepare_to_resume
+  x86_linux_prepare_to_resume,
+  x86_linux_process_qsupported,
+  x86_supports_tracepoints
 };
This page took 0.030474 seconds and 4 git commands to generate.