/* Target-dependent code for GNU/Linux x86-64.
- Copyright (C) 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ Copyright (C) 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
Contributed by Jiri Smid, SuSE Labs.
#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 "solib-svr4.h"
#include "xml-syscall.h"
+#include "features/i386/amd64-linux.c"
+#include "features/i386/amd64-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", 144, "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 */
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
-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)
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. */
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
}
}
+/* 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)
{
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;
-
- 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;
+ case amd64_sys_rt_sigreturn:
+ if (amd64_all_but_ip_registers_record (regcache))
+ return -1;
+ return 0;
+ break;
+
+ case amd64_sys_arch_prctl:
+ if (syscall_native == amd64_sys_arch_prctl)
+ {
+ 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;
+ }
+ break;
}
+ syscall_gdb = amd64_canonicalize_syscall (syscall_native);
+
if (syscall_gdb < 0)
{
printf_unfiltered (_("Process record and replay target doesn't "
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
+
+int
+amd64_linux_record_signal (struct gdbarch *gdbarch,
+ struct regcache *regcache,
+ enum target_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:
+ return tdesc_amd64_avx_linux;
+ default:
+ return tdesc_amd64_linux;
+ }
+}
+
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);
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;
+
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);
+ tdep->xsave_xcr0_offset = I386_LINUX_XSAVE_XCR0_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);
+
+ tdep->register_reggroup_p = amd64_linux_register_reggroup_p;
/* Functions for 'catch syscall'. */
set_xml_syscall_file_name (XML_SYSCALL_FILENAME_AMD64);
/* GNU/Linux uses SVR4-style shared libraries. */
set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
+ /* 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);
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
{
gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64,
GDB_OSABI_LINUX, amd64_linux_init_abi);
+
+ /* Initialize the Linux target description */
+ initialize_tdesc_amd64_linux ();
+ initialize_tdesc_amd64_avx_linux ();
}