* infrun.c (handle_inferior_event): Clear trap_expected after
[deliverable/binutils-gdb.git] / gdb / amd64-linux-tdep.c
index be7ace2d065b39ef563346fa9ea47f1c9b1ad4e6..92f52655d57c9cb7bf443cde0d224656057b010a 100644 (file)
@@ -1,13 +1,14 @@
 /* Target-dependent code for GNU/Linux x86-64.
 
-   Copyright 2001, 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   Free Software Foundation, Inc.
    Contributed by Jiri Smid, SuSE Labs.
 
    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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "arch-utils.h"
 #include "frame.h"
 #include "gdbcore.h"
 #include "regcache.h"
 #include "osabi.h"
 #include "symtab.h"
+#include "gdbtypes.h"
+#include "reggroups.h"
+#include "amd64-linux-tdep.h"
+#include "linux-tdep.h"
 
 #include "gdb_string.h"
 
@@ -72,7 +76,7 @@ static int amd64_linux_gregset_reg_offset[] =
 #define LINUX_SIGTRAMP_INSN1   0x0f    /* syscall */
 #define LINUX_SIGTRAMP_OFFSET1 7
 
-static const unsigned char linux_sigtramp_code[] =
+static const gdb_byte linux_sigtramp_code[] =
 {
   /* mov $__NR_rt_sigreturn, %rax */
   LINUX_SIGTRAMP_INSN0, 0xc7, 0xc0, 0x0f, 0x00, 0x00, 0x00,
@@ -86,9 +90,10 @@ static const unsigned char linux_sigtramp_code[] =
    the routine.  Otherwise, return 0.  */
 
 static CORE_ADDR
-amd64_linux_sigtramp_start (CORE_ADDR pc)
+amd64_linux_sigtramp_start (struct frame_info *this_frame)
 {
-  unsigned char buf[LINUX_SIGTRAMP_LEN];
+  CORE_ADDR pc = get_frame_pc (this_frame);
+  gdb_byte buf[LINUX_SIGTRAMP_LEN];
 
   /* We only recognize a signal trampoline if PC is at the start of
      one of the two instructions.  We optimize for finding the PC at
@@ -97,7 +102,7 @@ amd64_linux_sigtramp_start (CORE_ADDR pc)
      PC is not at the start of the instruction sequence, there will be
      a few trailing readable bytes on the stack.  */
 
-  if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0)
+  if (!safe_frame_unwind_memory (this_frame, pc, buf, sizeof buf))
     return 0;
 
   if (buf[0] != LINUX_SIGTRAMP_INSN0)
@@ -106,8 +111,7 @@ amd64_linux_sigtramp_start (CORE_ADDR pc)
        return 0;
 
       pc -= LINUX_SIGTRAMP_OFFSET1;
-
-      if (read_memory_nobpt (pc, (char *) buf, LINUX_SIGTRAMP_LEN) != 0)
+      if (!safe_frame_unwind_memory (this_frame, pc, buf, sizeof buf))
        return 0;
     }
 
@@ -117,13 +121,13 @@ amd64_linux_sigtramp_start (CORE_ADDR pc)
   return pc;
 }
 
-/* Return whether the frame preciding NEXT_FRAME corresponds to a
-   GNU/Linux sigtramp routine.  */
+/* Return whether THIS_FRAME corresponds to a GNU/Linux sigtramp
+   routine.  */
 
 static int
-amd64_linux_sigtramp_p (struct frame_info *next_frame)
+amd64_linux_sigtramp_p (struct frame_info *this_frame)
 {
-  CORE_ADDR pc = frame_pc_unwind (next_frame);
+  CORE_ADDR pc = get_frame_pc (this_frame);
   char *name;
 
   find_pc_partial_function (pc, &name, NULL, NULL);
@@ -135,7 +139,7 @@ amd64_linux_sigtramp_p (struct frame_info *next_frame)
      __sigaction, or __libc_sigaction (all aliases to the same
      function).  */
   if (name == NULL || strstr (name, "sigaction") != NULL)
-    return (amd64_linux_sigtramp_start (pc) != 0);
+    return (amd64_linux_sigtramp_start (this_frame) != 0);
 
   return (strcmp ("__restore_rt", name) == 0);
 }
@@ -143,17 +147,19 @@ amd64_linux_sigtramp_p (struct frame_info *next_frame)
 /* Offset to struct sigcontext in ucontext, from <asm/ucontext.h>.  */
 #define AMD64_LINUX_UCONTEXT_SIGCONTEXT_OFFSET 40
 
-/* Assuming NEXT_FRAME is a frame following a GNU/Linux sigtramp
-   routine, return the address of the associated sigcontext structure.  */
+/* Assuming THIS_FRAME is a GNU/Linux sigtramp routine, return the
+   address of the associated sigcontext structure.  */
 
 static CORE_ADDR
-amd64_linux_sigcontext_addr (struct frame_info *next_frame)
+amd64_linux_sigcontext_addr (struct frame_info *this_frame)
 {
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   CORE_ADDR sp;
-  char buf[8];
+  gdb_byte buf[8];
 
-  frame_unwind_register (next_frame, SP_REGNUM, buf);
-  sp = extract_unsigned_integer (buf, 8);
+  get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
+  sp = extract_unsigned_integer (buf, 8, byte_order);
 
   /* The sigcontext structure is part of the user context.  A pointer
      to the user context is passed as the third argument to the signal
@@ -199,6 +205,61 @@ static int amd64_linux_sc_reg_offset[] =
   -1                           /* %gs */
 };
 
+/* Replacement register functions which know about %orig_rax.  */
+
+static const char *
+amd64_linux_register_name (struct gdbarch *gdbarch, int reg)
+{
+  if (reg == AMD64_LINUX_ORIG_RAX_REGNUM)
+    return "orig_rax";
+
+  return amd64_register_name (gdbarch, reg);
+}
+
+static struct type *
+amd64_linux_register_type (struct gdbarch *gdbarch, int reg)
+{
+  if (reg == AMD64_LINUX_ORIG_RAX_REGNUM)
+    return builtin_type (gdbarch)->builtin_int64;
+
+  return amd64_register_type (gdbarch, reg);
+}
+
+static int
+amd64_linux_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+                                struct reggroup *group)
+{ 
+  if (regnum == AMD64_LINUX_ORIG_RAX_REGNUM)
+    return (group == system_reggroup
+            || group == save_reggroup
+            || group == restore_reggroup);
+  return default_register_reggroup_p (gdbarch, regnum, group);
+}
+
+/* Set the program counter for process PTID to PC.  */
+
+static void
+amd64_linux_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+  regcache_cooked_write_unsigned (regcache, AMD64_RIP_REGNUM, pc);
+
+  /* We must be careful with modifying the program counter.  If we
+     just interrupted a system call, the kernel might try to restart
+     it when we resume the inferior.  On restarting the system call,
+     the kernel will try backing up the program counter even though it
+     no longer points at the system call.  This typically results in a
+     SIGSEGV or SIGILL.  We can prevent this by writing `-1' in the
+     "orig_rax" pseudo-register.
+
+     Note that "orig_rax" is saved when setting up a dummy call frame.
+     This means that it is properly restored when that frame is
+     popped, and that the interrupted system call will be restarted
+     when we resume the inferior on return from a function call from
+     within GDB.  In all other cases the system call will not be
+     restarted.  */
+  regcache_cooked_write_unsigned (regcache, AMD64_LINUX_ORIG_RAX_REGNUM, -1);
+}
+
 static void
 amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
@@ -218,6 +279,28 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   /* GNU/Linux uses SVR4-style shared libraries.  */
   set_solib_svr4_fetch_link_map_offsets
     (gdbarch, svr4_lp64_fetch_link_map_offsets);
+
+  /* Add the %orig_rax register used for syscall restarting.  */
+  set_gdbarch_write_pc (gdbarch, amd64_linux_write_pc);
+  set_gdbarch_num_regs (gdbarch, AMD64_LINUX_NUM_REGS);
+  set_gdbarch_register_name (gdbarch, amd64_linux_register_name);
+  set_gdbarch_register_type (gdbarch, amd64_linux_register_type);
+  set_gdbarch_register_reggroup_p (gdbarch, amd64_linux_register_reggroup_p);
+
+  /* Enable TLS support.  */
+  set_gdbarch_fetch_tls_load_module_address (gdbarch,
+                                             svr4_fetch_objfile_link_map);
+
+  /* Displaced stepping.  */
+  set_gdbarch_displaced_step_copy_insn (gdbarch,
+                                        amd64_displaced_step_copy_insn);
+  set_gdbarch_displaced_step_fixup (gdbarch, amd64_displaced_step_fixup);
+  set_gdbarch_displaced_step_free_closure (gdbarch,
+                                           simple_displaced_step_free_closure);
+  set_gdbarch_displaced_step_location (gdbarch,
+                                       displaced_step_at_entry_point);
+
+  set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
 }
 \f
 
This page took 0.025924 seconds and 4 git commands to generate.