gdb/
[deliverable/binutils-gdb.git] / gdb / amd64-linux-tdep.c
index f527b7eab2952282448d4105ab7c9c1d2e6caa37..0adc22b7e69f1a79f275d157a40608d8fc28e518 100644 (file)
@@ -1,7 +1,6 @@
 /* Target-dependent code for GNU/Linux x86-64.
 
-   Copyright (C) 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009
-   Free Software Foundation, Inc.
+   Copyright (C) 2001, 2003-2012 Free Software Foundation, Inc.
    Contributed by Jiri Smid, SuSE Labs.
 
    This file is part of GDB.
 #include "symtab.h"
 #include "gdbtypes.h"
 #include "reggroups.h"
+#include "regset.h"
 #include "amd64-linux-tdep.h"
+#include "i386-linux-tdep.h"
 #include "linux-tdep.h"
+#include "i386-xstate.h"
 
 #include "gdb_string.h"
 
 #include "amd64-tdep.h"
 #include "solib-svr4.h"
 #include "xml-syscall.h"
+#include "glibc-tdep.h"
+
+#include "features/i386/amd64-linux.c"
+#include "features/i386/amd64-avx-linux.c"
+#include "features/i386/x32-linux.c"
+#include "features/i386/x32-avx-linux.c"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
 #include "record.h"
 #include "linux-record.h"
 
+/* Supported register note sections.  */
+static struct core_regset_section amd64_linux_regset_sections[] =
+{
+  { ".reg", 27 * 8, "general-purpose" },
+  { ".reg2", 512, "floating-point" },
+  { ".reg-xstate", I386_XSTATE_MAX_SIZE, "XSAVE extended state" },
+  { NULL, 0 }
+};
+
 /* Mapping between the general-purpose registers in `struct user'
    format and GDB's register cache layout.  */
 
 /* From <sys/reg.h>.  */
-static int amd64_linux_gregset_reg_offset[] =
+int amd64_linux_gregset_reg_offset[] =
 {
   10 * 8,                      /* %rax */
   5 * 8,                       /* %rbx */
@@ -57,7 +74,7 @@ static int amd64_linux_gregset_reg_offset[] =
   14 * 8,                      /* %rdi */
   4 * 8,                       /* %rbp */
   19 * 8,                      /* %rsp */
-  9 * 8,                       /* %r8 ... */
+  9 * 8,                       /* %r8 ...  */
   8 * 8,
   7 * 8,
   6 * 8,
@@ -72,7 +89,14 @@ static int amd64_linux_gregset_reg_offset[] =
   23 * 8,                      /* %ds */
   24 * 8,                      /* %es */
   25 * 8,                      /* %fs */
-  26 * 8                       /* %gs */
+  26 * 8,                      /* %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,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  15 * 8                       /* "orig_rax" */
 };
 \f
 
@@ -135,7 +159,7 @@ static int
 amd64_linux_sigtramp_p (struct frame_info *this_frame)
 {
   CORE_ADDR pc = get_frame_pc (this_frame);
-  char *name;
+  const char *name;
 
   find_pc_partial_function (pc, &name, NULL, NULL);
 
@@ -234,26 +258,6 @@ 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)
@@ -262,7 +266,7 @@ amd64_linux_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
     return (group == system_reggroup
             || group == save_reggroup
             || group == restore_reggroup);
-  return default_register_reggroup_p (gdbarch, regnum, group);
+  return i386_register_reggroup_p (gdbarch, regnum, group);
 }
 
 /* Set the program counter for process PTID to PC.  */
@@ -289,25 +293,57 @@ amd64_linux_write_pc (struct regcache *regcache, CORE_ADDR pc)
   regcache_cooked_write_unsigned (regcache, AMD64_LINUX_ORIG_RAX_REGNUM, -1);
 }
 
-/* Parse the arguments of current system call instruction and record
-   the values of the registers and memory that will be changed into
-   "record_arch_list".  This instruction is "syscall".
-
-   Return -1 if something wrong.  */
+/* Record all registers but IP register for process-record.  */
 
-static struct linux_record_tdep amd64_linux_record_tdep;
+static int
+amd64_all_but_ip_registers_record (struct regcache *regcache)
+{
+  if (record_arch_list_add_reg (regcache, AMD64_RAX_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_RCX_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_RDX_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_RBX_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_RSP_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_RBP_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_RSI_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_RDI_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_R8_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_R9_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_R10_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_R11_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_R12_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_R13_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_R14_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_R15_REGNUM))
+    return -1;
+  if (record_arch_list_add_reg (regcache, AMD64_EFLAGS_REGNUM))
+    return -1;
 
-#define RECORD_ARCH_GET_FS     0x1003
-#define RECORD_ARCH_GET_GS     0x1004
+  return 0;
+}
 
 /* amd64_canonicalize_syscall maps from the native amd64 Linux set 
    of syscall ids into a canonical set of syscall ids used by 
    process record.  */
 
 static enum gdb_syscall
-amd64_canonicalize_syscall (enum amd64_syscall syscall)
+amd64_canonicalize_syscall (enum amd64_syscall syscall_number)
 {
-  switch (syscall) {
+  switch (syscall_number) {
   case amd64_sys_read:
     return gdb_sys_read;
 
@@ -1111,6 +1147,17 @@ amd64_canonicalize_syscall (enum amd64_syscall syscall)
   }
 }
 
+/* Parse the arguments of current system call instruction and record
+   the values of the registers and memory that will be changed into
+   "record_arch_list".  This instruction is "syscall".
+
+   Return -1 if something wrong.  */
+
+static struct linux_record_tdep amd64_linux_record_tdep;
+
+#define RECORD_ARCH_GET_FS     0x1003
+#define RECORD_ARCH_GET_GS     0x1004
+
 static int
 amd64_linux_syscall_record (struct regcache *regcache)
 {
@@ -1120,27 +1167,38 @@ amd64_linux_syscall_record (struct regcache *regcache)
 
   regcache_raw_read_unsigned (regcache, AMD64_RAX_REGNUM, &syscall_native);
 
-  syscall_gdb = amd64_canonicalize_syscall (syscall_native);
-
-  if (syscall_native == amd64_sys_arch_prctl) 
+  switch (syscall_native)
     {
-      ULONGEST arg3;
+    case amd64_sys_rt_sigreturn:
+      if (amd64_all_but_ip_registers_record (regcache))
+        return -1;
+      return 0;
+      break;
 
-      regcache_raw_read_unsigned (regcache, amd64_linux_record_tdep.arg3,
-                                 &arg3);
-      if (arg3 == RECORD_ARCH_GET_FS || arg3 == RECORD_ARCH_GET_GS)
+    case amd64_sys_arch_prctl:
       {
-       CORE_ADDR addr;
-
-       regcache_raw_read_unsigned (regcache, amd64_linux_record_tdep.arg2,
-                                   &addr);
-       if (record_arch_list_add_mem (addr, 
-                                     amd64_linux_record_tdep.size_ulong))
-         return -1;
+       ULONGEST arg3;
+
+       regcache_raw_read_unsigned (regcache, amd64_linux_record_tdep.arg3,
+                                   &arg3);
+       if (arg3 == RECORD_ARCH_GET_FS || arg3 == RECORD_ARCH_GET_GS)
+         {
+           CORE_ADDR addr;
+
+           regcache_raw_read_unsigned (regcache,
+                                       amd64_linux_record_tdep.arg2,
+                                       &addr);
+           if (record_arch_list_add_mem (addr,
+                                         amd64_linux_record_tdep.size_ulong))
+             return -1;
+         }
+       goto record_regs;
       }
-      goto record_regs;
+      break;
     }
 
+  syscall_gdb = amd64_canonicalize_syscall (syscall_native);
+
   if (syscall_gdb < 0)
     {
       printf_unfiltered (_("Process record and replay target doesn't "
@@ -1163,36 +1221,90 @@ amd64_linux_syscall_record (struct regcache *regcache)
   if (record_arch_list_add_reg (regcache, AMD64_R11_REGNUM))
     return -1;
 
+  return 0;
+}
+
+#define AMD64_LINUX_redzone    128
+#define AMD64_LINUX_xstate     512
+#define AMD64_LINUX_frame_size 560
+
+static int
+amd64_linux_record_signal (struct gdbarch *gdbarch,
+                           struct regcache *regcache,
+                           enum gdb_signal signal)
+{
+  ULONGEST rsp;
+
+  if (amd64_all_but_ip_registers_record (regcache))
+    return -1;
+
+  if (record_arch_list_add_reg (regcache, AMD64_RIP_REGNUM))
+    return -1;
+
+  /* Record the change in the stack.  */
+  regcache_raw_read_unsigned (regcache, AMD64_RSP_REGNUM, &rsp);
+  /* redzone
+     sp -= 128; */
+  rsp -= AMD64_LINUX_redzone;
+  /* This is for xstate.
+     sp -= sizeof (struct _fpstate);  */
+  rsp -= AMD64_LINUX_xstate;
+  /* This is for frame_size.
+     sp -= sizeof (struct rt_sigframe);  */
+  rsp -= AMD64_LINUX_frame_size;
+  if (record_arch_list_add_mem (rsp, AMD64_LINUX_redzone
+                                     + AMD64_LINUX_xstate
+                                     + AMD64_LINUX_frame_size))
+    return -1;
+
+  if (record_arch_list_add_end ())
+    return -1;
 
   return 0;
 }
 
+/* Get Linux/x86 target description from core dump.  */
+
+static const struct target_desc *
+amd64_linux_core_read_description (struct gdbarch *gdbarch,
+                                 struct target_ops *target,
+                                 bfd *abfd)
+{
+  /* Linux/x86-64.  */
+  uint64_t xcr0 = i386_linux_core_read_xcr0 (gdbarch, target, abfd);
+  switch ((xcr0 & I386_XSTATE_AVX_MASK))
+    {
+    case I386_XSTATE_AVX_MASK:
+      if (gdbarch_ptr_bit (gdbarch) == 32)
+       return tdesc_x32_avx_linux;
+      else
+       return tdesc_amd64_avx_linux;
+    default:
+      if (gdbarch_ptr_bit (gdbarch) == 32)
+       return tdesc_x32_linux;
+      else
+       return tdesc_amd64_linux;
+    }
+}
+
 static void
-amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch)
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  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);
+  linux_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);
+  tdep->xsave_xcr0_offset = I386_LINUX_XSAVE_XCR0_OFFSET;
 
   /* 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);
+
+  tdep->register_reggroup_p = amd64_linux_register_reggroup_p;
 
   /* Functions for 'catch syscall'.  */
   set_xml_syscall_file_name (XML_SYSCALL_FILENAME_AMD64);
@@ -1206,6 +1318,15 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   /* GNU/Linux uses SVR4-style shared libraries.  */
   set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
 
+  /* GNU/Linux uses the dynamic linker included in the GNU C Library.  */
+  set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
+
+  /* Install supported register note sections.  */
+  set_gdbarch_core_regset_sections (gdbarch, amd64_linux_regset_sections);
+
+  set_gdbarch_core_read_description (gdbarch,
+                                    amd64_linux_core_read_description);
+
   /* Displaced stepping.  */
   set_gdbarch_displaced_step_copy_insn (gdbarch,
                                         amd64_displaced_step_copy_insn);
@@ -1218,6 +1339,7 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
 
   set_gdbarch_process_record (gdbarch, i386_process_record);
+  set_gdbarch_process_record_signal (gdbarch, amd64_linux_record_signal);
 
   /* Initialize the amd64_linux_record_tdep.  */
   /* These values are the size of the type that will be used in a system
@@ -1388,6 +1510,88 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 
   tdep->i386_syscall_record = amd64_linux_syscall_record;
 }
+
+static void
+amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  const struct target_desc *tdesc = info.target_desc;
+  struct tdesc_arch_data *tdesc_data = (void *) info.tdep_info;
+  const struct tdesc_feature *feature;
+  int valid_p;
+
+  gdb_assert (tdesc_data);
+
+  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);
+
+  /* Reserve a number for orig_rax.  */
+  set_gdbarch_num_regs (gdbarch, AMD64_LINUX_NUM_REGS);
+
+  if (! tdesc_has_registers (tdesc))
+    tdesc = tdesc_amd64_linux;
+  tdep->tdesc = tdesc;
+
+  feature = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.linux");
+  if (feature == NULL)
+    return;
+
+  valid_p = tdesc_numbered_register (feature, tdesc_data,
+                                    AMD64_LINUX_ORIG_RAX_REGNUM,
+                                    "orig_rax");
+  if (!valid_p)
+    return;
+
+  amd64_linux_init_abi_common (info, gdbarch);
+
+  /* GNU/Linux uses SVR4-style shared libraries.  */
+  set_solib_svr4_fetch_link_map_offsets
+    (gdbarch, svr4_lp64_fetch_link_map_offsets);
+}
+
+static void
+amd64_x32_linux_init_abi(struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  const struct target_desc *tdesc = info.target_desc;
+  struct tdesc_arch_data *tdesc_data = (void *) info.tdep_info;
+  const struct tdesc_feature *feature;
+  int valid_p;
+
+  gdb_assert (tdesc_data);
+
+  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_x32_init_abi (info, gdbarch);
+
+  /* Reserve a number for orig_rax.  */
+  set_gdbarch_num_regs (gdbarch, AMD64_LINUX_NUM_REGS);
+
+  if (! tdesc_has_registers (tdesc))
+    tdesc = tdesc_x32_linux;
+  tdep->tdesc = tdesc;
+
+  feature = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.linux");
+  if (feature == NULL)
+    return;
+
+  valid_p = tdesc_numbered_register (feature, tdesc_data,
+                                    AMD64_LINUX_ORIG_RAX_REGNUM,
+                                    "orig_rax");
+  if (!valid_p)
+    return;
+
+  amd64_linux_init_abi_common (info, gdbarch);
+
+  /* GNU/Linux uses SVR4-style shared libraries.  */
+  set_solib_svr4_fetch_link_map_offsets
+    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
+}
 \f
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
@@ -1398,4 +1602,12 @@ _initialize_amd64_linux_tdep (void)
 {
   gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64,
                          GDB_OSABI_LINUX, amd64_linux_init_abi);
+  gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x64_32,
+                         GDB_OSABI_LINUX, amd64_x32_linux_init_abi);
+
+  /* Initialize the Linux target description.  */
+  initialize_tdesc_amd64_linux ();
+  initialize_tdesc_amd64_avx_linux ();
+  initialize_tdesc_x32_linux ();
+  initialize_tdesc_x32_avx_linux ();
 }
This page took 0.03025 seconds and 4 git commands to generate.