include/elf/
[deliverable/binutils-gdb.git] / gdb / amd64-linux-tdep.c
index 9a1636a89981632d195940ea9f3b62df944dd8ad..badc0b43e7739ab897e76a42d0bb0085d33af12a 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 "inferior.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"
 
 #include "amd64-tdep.h"
-#include "amd64-linux-tdep.h"
-
-/* Register indexes to 'struct user' come from <sys/reg.h>.  */
-
-#define USER_R15    0
-#define USER_R14    1
-#define USER_R13    2
-#define USER_R12    3
-#define USER_RBP    4
-#define USER_RBX    5
-#define USER_R11    6
-#define USER_R10    7
-#define USER_R9     8
-#define USER_R8     9
-#define USER_RAX    10
-#define USER_RCX    11
-#define USER_RDX    12
-#define USER_RSI    13
-#define USER_RDI    14
-#define USER_RIP    16
-#define USER_CS     17
-#define USER_EFLAGS 18
-#define USER_RSP    19
-#define USER_SS     20
-#define USER_DS     23
-#define USER_ES     24
-#define USER_FS     25
-#define USER_GS     26
+#include "solib-svr4.h"
 
 /* Mapping between the general-purpose registers in `struct user'
-   format and GDB's register array layout.  */
+   format and GDB's register cache layout.  */
 
-static int user_to_gdb_regmap[] =
+/* From <sys/reg.h>.  */
+static int amd64_linux_gregset_reg_offset[] =
 {
-  USER_RAX, USER_RBX, USER_RCX, USER_RDX,
-  USER_RSI, USER_RDI, USER_RBP, USER_RSP,
-  USER_R8, USER_R9, USER_R10, USER_R11,
-  USER_R12, USER_R13, USER_R14, USER_R15,
-  USER_RIP, USER_EFLAGS,
-  USER_CS, USER_SS,
-  USER_DS, USER_ES, USER_FS, USER_GS
+  10 * 8,                      /* %rax */
+  5 * 8,                       /* %rbx */
+  11 * 8,                      /* %rcx */
+  12 * 8,                      /* %rdx */
+  13 * 8,                      /* %rsi */
+  14 * 8,                      /* %rdi */
+  4 * 8,                       /* %rbp */
+  19 * 8,                      /* %rsp */
+  9 * 8,                       /* %r8 ... */
+  8 * 8,
+  7 * 8,
+  6 * 8,
+  3 * 8,
+  2 * 8,
+  1 * 8,
+  0 * 8,                       /* ... %r15 */
+  16 * 8,                      /* %rip */
+  18 * 8,                      /* %eflags */
+  17 * 8,                      /* %cs */
+  20 * 8,                      /* %ss */
+  23 * 8,                      /* %ds */
+  24 * 8,                      /* %es */
+  25 * 8,                      /* %fs */
+  26 * 8                       /* %gs */
 };
+\f
 
-/* Fill GDB's register array with the general-purpose register values
-   in *GREGSETP.  */
-
-void
-amd64_linux_supply_gregset (char *regp)
-{
-  int i;
-
-  for (i = 0; i < AMD64_NUM_GREGS; i++)
-    supply_register (i, regp + (user_to_gdb_regmap[i] * 8));
-}
-
-/* Fill register REGNO (if it is a general-purpose register) in
-   *GREGSETPS with the value in GDB's register array.  If REGNO is -1,
-   do this for all registers.  */
-
-void
-amd64_linux_fill_gregset (char *regp, int regno)
-{
-  int i;
-
-  for (i = 0; i < AMD64_NUM_GREGS; i++)
-    if (regno == -1 || regno == i)
-      regcache_collect (i, regp + (user_to_gdb_regmap[i] * 8));
-}
-
-/* The register sets used in GNU/Linux ELF core-dumps are identical to
-   the register sets used by `ptrace'.  The corresponding types are
-   `elf_gregset_t' for the general-purpose registers (with
-   `elf_greg_t' the type of a single GP register) and `elf_fpregset_t'
-   for the floating-point registers.  */
-
-static void
-fetch_core_registers (char *core_reg_sect, unsigned core_reg_size,
-                        int which, CORE_ADDR ignore)
-{
-  switch (which)
-    {
-    case 0:  /* Integer registers.  */
-      if (core_reg_size != 216)
-       warning ("Wrong size register set in core file.");
-      else
-       amd64_linux_supply_gregset (core_reg_sect);
-      break;
-
-    case 2:  /* Floating point registers.  */
-    case 3:  /* "Extended" floating point registers.  This is gdb-speak
-               for SSE/SSE2. */
-      if (core_reg_size != 512)
-       warning ("Wrong size XMM register set in core file.");
-      else
-       amd64_supply_fxsave (current_regcache, -1, core_reg_sect);
-      break;
-
-    default:
-      /* Don't know what kind of register request this is; just ignore it.  */
-      break;
-    }
-}
-
-static struct core_fns amd64_core_fns = 
-{
-  bfd_target_elf_flavour,              /* core_flavour */
-  default_check_format,                        /* check_format */
-  default_core_sniffer,                        /* core_sniffer */
-  fetch_core_registers,                        /* core_read_registers */
-  NULL                                 /* next */
-};
+/* Support for signal handlers.  */
 
 #define LINUX_SIGTRAMP_INSN0   0x48    /* mov $NNNNNNNN, %rax */
 #define LINUX_SIGTRAMP_OFFSET0 0
 #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,
@@ -160,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
@@ -171,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)
@@ -180,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;
     }
 
@@ -191,11 +121,17 @@ amd64_linux_sigtramp_start (CORE_ADDR pc)
   return pc;
 }
 
-/* Return whether PC is in a GNU/Linux sigtramp routine.  */
+/* Return whether THIS_FRAME corresponds to a GNU/Linux sigtramp
+   routine.  */
 
 static int
-amd64_linux_pc_in_sigtramp (CORE_ADDR pc, char *name)
+amd64_linux_sigtramp_p (struct frame_info *this_frame)
 {
+  CORE_ADDR pc = get_frame_pc (this_frame);
+  char *name;
+
+  find_pc_partial_function (pc, &name, NULL, NULL);
+
   /* If we have NAME, we can optimize the search.  The trampoline is
      named __restore_rt.  However, it isn't dynamically exported from
      the shared C library, so the trampoline may appear to be part of
@@ -203,7 +139,7 @@ amd64_linux_pc_in_sigtramp (CORE_ADDR pc, char *name)
      __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);
 }
@@ -211,16 +147,16 @@ amd64_linux_pc_in_sigtramp (CORE_ADDR pc, char *name)
 /* 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)
 {
   CORE_ADDR sp;
-  char buf[8];
+  gdb_byte buf[8];
 
-  frame_unwind_register (next_frame, SP_REGNUM, buf);
+  get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
   sp = extract_unsigned_integer (buf, 8);
 
   /* The sigcontext structure is part of the user context.  A pointer
@@ -267,17 +203,102 @@ 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_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)
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
-  amd64_init_abi (info, gdbarch);
 
-  set_gdbarch_pc_in_sigtramp (gdbarch, amd64_linux_pc_in_sigtramp);
+  tdep->gregset_reg_offset = amd64_linux_gregset_reg_offset;
+  tdep->gregset_num_regs = ARRAY_SIZE (amd64_linux_gregset_reg_offset);
+  tdep->sizeof_gregset = 27 * 8;
+
+  amd64_init_abi (info, gdbarch);
 
+  tdep->sigtramp_p = amd64_linux_sigtramp_p;
   tdep->sigcontext_addr = amd64_linux_sigcontext_addr;
   tdep->sc_reg_offset = amd64_linux_sc_reg_offset;
   tdep->sc_num_regs = ARRAY_SIZE (amd64_linux_sc_reg_offset);
+
+  /* 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
 
@@ -287,8 +308,6 @@ extern void _initialize_amd64_linux_tdep (void);
 void
 _initialize_amd64_linux_tdep (void)
 {
-  add_core_fns (&amd64_core_fns);
-
   gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64,
                          GDB_OSABI_LINUX, amd64_linux_init_abi);
 }
This page took 0.027792 seconds and 4 git commands to generate.