gdb/
[binutils-gdb.git] / gdb / gdbserver / linux-x86-64-low.c
index 1f80d990cc9134c61fee2934fce5d6f5f9fab01c..e067b9c5ede7f44aa37c2c0f93f2cde561f52092 100644 (file)
@@ -1,6 +1,6 @@
 /* GNU/Linux/x86-64 specific low level interface, for the remote server
    for GDB.
-   Copyright 2002
+   Copyright (C) 2002, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of GDB.
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
 
 #include "server.h"
 #include "linux-low.h"
 #include "i387-fp.h"
 
+#include "gdb_proc_service.h"
+
 #include <sys/reg.h>
 #include <sys/procfs.h>
 #include <sys/ptrace.h>
 
-#define        X86_64_NUM_GREGS 22
-
-static int x86_64_regmap[X86_64_NUM_GREGS] = {
-  RAX, RBX, RCX, RDX,
-  RSI, RDI, RBP, RSP,
-  R8, R9, R10, R11,
-  R12, R13, R14, R15,
-  RIP, EFLAGS,
-  DS, ES, FS, GS
+/* This definition comes from prctl.h, but some kernels may not have it.  */
+#ifndef PTRACE_ARCH_PRCTL
+#define PTRACE_ARCH_PRCTL      30
+#endif
+
+/* The following definitions come from prctl.h, but may be absent
+   for certain configurations.  */
+#ifndef ARCH_GET_FS
+#define ARCH_SET_GS 0x1001
+#define ARCH_SET_FS 0x1002
+#define ARCH_GET_FS 0x1003
+#define ARCH_GET_GS 0x1004
+#endif
+
+static int x86_64_regmap[] = {
+  RAX * 8, RBX * 8, RCX * 8, RDX * 8,
+  RSI * 8, RDI * 8, RBP * 8, RSP * 8,
+  R8 * 8, R9 * 8, R10 * 8, R11 * 8,
+  R12 * 8, R13 * 8, R14 * 8, R15 * 8,
+  RIP * 8, EFLAGS * 8, CS * 8, SS * 8, 
+  DS * 8, ES * 8, FS * 8, GS * 8,
+  -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
 };
 
+#define X86_64_NUM_GREGS (sizeof(x86_64_regmap)/sizeof(int))
+
+/* Called by libthread_db.  */
+
+ps_err_e
+ps_get_thread_area (const struct ps_prochandle *ph,
+                    lwpid_t lwpid, int idx, void **base)
+{
+  switch (idx)
+    {
+    case FS:
+      if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_FS) == 0)
+       return PS_OK;
+      break;
+    case GS:
+      if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_GS) == 0)
+       return PS_OK;
+      break;
+    default:
+      return PS_BADADDR;
+    }
+  return PS_ERR;
+}
+
 static void
 x86_64_fill_gregset (void *buf)
 {
   int i;
 
   for (i = 0; i < X86_64_NUM_GREGS; i++)
-    collect_register (i, ((char *) buf) + x86_64_regmap[i]);
+    if (x86_64_regmap[i] != -1)
+      collect_register (i, ((char *) buf) + x86_64_regmap[i]);
 }
 
 static void
-x86_64_store_gregset (void *buf)
+x86_64_store_gregset (const void *buf)
 {
   int i;
 
   for (i = 0; i < X86_64_NUM_GREGS; i++)
-    supply_register (i, ((char *) buf) + x86_64_regmap[i]);
+    if (x86_64_regmap[i] != -1)
+      supply_register (i, ((char *) buf) + x86_64_regmap[i]);
 }
 
 static void
@@ -64,7 +109,7 @@ x86_64_fill_fpregset (void *buf)
 }
 
 static void
-x86_64_store_fpregset (void *buf)
+x86_64_store_fpregset (const void *buf)
 {
   i387_fxsave_to_cache (buf);
 }
@@ -79,9 +124,53 @@ struct regset_info target_regsets[] = {
   { 0, 0, -1, -1, NULL, NULL }
 };
 
+static const unsigned char x86_64_breakpoint[] = { 0xCC };
+#define x86_64_breakpoint_len 1
+                
+extern int debug_threads;
+
+static CORE_ADDR
+x86_64_get_pc ()
+{
+  unsigned long pc;
+
+  collect_register_by_name ("rip", &pc);
+
+  if (debug_threads)
+    fprintf (stderr, "stop pc (before any decrement) is %08lx\n", pc);
+  return pc;
+}
+
+static void
+x86_64_set_pc (CORE_ADDR newpc)
+{
+  if (debug_threads)
+    fprintf (stderr, "set pc to %08lx\n", (long) newpc);
+  supply_register_by_name ("rip", &newpc);
+}
+
+static int
+x86_64_breakpoint_at (CORE_ADDR pc)
+{
+  unsigned char c;
+
+  read_inferior_memory (pc, &c, 1);
+  if (c == 0xCC)
+    return 1;
+
+  return 0;
+}
+
 struct linux_target_ops the_low_target = {
   -1,
   NULL,
   NULL,
   NULL,
+  x86_64_get_pc,
+  x86_64_set_pc,
+  x86_64_breakpoint,  
+  x86_64_breakpoint_len,
+  NULL,                                 
+  1,
+  x86_64_breakpoint_at,
 };