/* Target-dependent code for GNU/Linux i386.
- Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009
+ Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
This file is part of GDB.
#include "frame.h"
#include "value.h"
#include "regcache.h"
+#include "regset.h"
#include "inferior.h"
#include "osabi.h"
#include "reggroups.h"
#include "solib-svr4.h"
#include "symtab.h"
#include "arch-utils.h"
-#include "regset.h"
+#include "xml-syscall.h"
+
+#include "i387-tdep.h"
+#include "i386-xstate.h"
+
+/* The syscall's XML filename for i386. */
+#define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
#include "record.h"
#include "linux-record.h"
#include <stdint.h>
+#include "features/i386/i386-linux.c"
+#include "features/i386/i386-mmx-linux.c"
+#include "features/i386/i386-avx-linux.c"
+
/* Supported register note sections. */
static struct core_regset_section i386_linux_regset_sections[] =
{
- { ".reg", 144 },
- { ".reg2", 108 },
- { ".reg-xfp", 512 },
+ { ".reg", 68, "general-purpose" },
+ { ".reg2", 108, "floating-point" },
{ NULL, 0 }
};
-/* Return the name of register REG. */
-
-static const char *
-i386_linux_register_name (struct gdbarch *gdbarch, int reg)
+static struct core_regset_section i386_linux_sse_regset_sections[] =
{
- /* Deal with the extra "orig_eax" pseudo register. */
- if (reg == I386_LINUX_ORIG_EAX_REGNUM)
- return "orig_eax";
+ { ".reg", 68, "general-purpose" },
+ { ".reg-xfp", 512, "extended floating-point" },
+ { NULL, 0 }
+};
- return i386_register_name (gdbarch, reg);
-}
+static struct core_regset_section i386_linux_avx_regset_sections[] =
+{
+ { ".reg", 68, "general-purpose" },
+ { ".reg-xstate", I386_XSTATE_MAX_SIZE, "XSAVE extended state" },
+ { NULL, 0 }
+};
/* Return non-zero, when the register is in the corresponding register
group. Put the LINUX_ORIG_EAX register in the system group. */
regcache_cooked_write_unsigned (regcache, I386_LINUX_ORIG_EAX_REGNUM, -1);
}
+/* Record all registers but IP register for process-record. */
+
+static int
+i386_all_but_ip_registers_record (struct regcache *regcache)
+{
+ if (record_arch_list_add_reg (regcache, I386_EAX_REGNUM))
+ return -1;
+ if (record_arch_list_add_reg (regcache, I386_ECX_REGNUM))
+ return -1;
+ if (record_arch_list_add_reg (regcache, I386_EDX_REGNUM))
+ return -1;
+ if (record_arch_list_add_reg (regcache, I386_EBX_REGNUM))
+ return -1;
+ if (record_arch_list_add_reg (regcache, I386_ESP_REGNUM))
+ return -1;
+ if (record_arch_list_add_reg (regcache, I386_EBP_REGNUM))
+ return -1;
+ if (record_arch_list_add_reg (regcache, I386_ESI_REGNUM))
+ return -1;
+ if (record_arch_list_add_reg (regcache, I386_EDI_REGNUM))
+ return -1;
+ if (record_arch_list_add_reg (regcache, I386_EFLAGS_REGNUM))
+ return -1;
+
+ return 0;
+}
+
+/* i386_canonicalize_syscall maps from the native i386 Linux set
+ of syscall ids into a canonical set of syscall ids used by
+ process record (a mostly trivial mapping, since the canonical
+ set was originally taken from the i386 set). */
+
+static enum gdb_syscall
+i386_canonicalize_syscall (int syscall)
+{
+ enum { i386_syscall_max = 499 };
+
+ if (syscall <= i386_syscall_max)
+ return syscall;
+ else
+ return -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 "int 0x80" (Linux
i386_linux_intx80_sysenter_record (struct regcache *regcache)
{
int ret;
- uint32_t tmpu32;
+ LONGEST syscall_native;
+ enum gdb_syscall syscall_gdb;
+
+ regcache_raw_read_signed (regcache, I386_EAX_REGNUM, &syscall_native);
- regcache_raw_read (regcache, I386_EAX_REGNUM, (gdb_byte *)&tmpu32);
+ syscall_gdb = i386_canonicalize_syscall (syscall_native);
- ret = record_linux_system_call (tmpu32, regcache,
+ if (syscall_gdb < 0)
+ {
+ printf_unfiltered (_("Process record and replay target doesn't "
+ "support syscall number %s\n"),
+ plongest (syscall_native));
+ return -1;
+ }
+
+ if (syscall_gdb == gdb_sys_sigreturn
+ || syscall_gdb == gdb_sys_rt_sigreturn)
+ {
+ if (i386_all_but_ip_registers_record (regcache))
+ return -1;
+ return 0;
+ }
+
+ ret = record_linux_system_call (syscall_gdb, regcache,
&i386_linux_record_tdep);
if (ret)
return ret;
return 0;
}
+
+#define I386_LINUX_xstate 270
+#define I386_LINUX_frame_size 732
+
+int
+i386_linux_record_signal (struct gdbarch *gdbarch,
+ struct regcache *regcache,
+ enum target_signal signal)
+{
+ ULONGEST esp;
+
+ if (i386_all_but_ip_registers_record (regcache))
+ return -1;
+
+ if (record_arch_list_add_reg (regcache, I386_EIP_REGNUM))
+ return -1;
+
+ /* Record the change in the stack. */
+ regcache_raw_read_unsigned (regcache, I386_ESP_REGNUM, &esp);
+ /* This is for xstate.
+ sp -= sizeof (struct _fpstate); */
+ esp -= I386_LINUX_xstate;
+ /* This is for frame_size.
+ sp -= sizeof (struct rt_sigframe); */
+ esp -= I386_LINUX_frame_size;
+ if (record_arch_list_add_mem (esp,
+ I386_LINUX_xstate + I386_LINUX_frame_size))
+ return -1;
+
+ if (record_arch_list_add_end ())
+ return -1;
+
+ return 0;
+}
\f
+static LONGEST
+i386_linux_get_syscall_number (struct gdbarch *gdbarch,
+ ptid_t ptid)
+{
+ struct regcache *regcache = get_thread_regcache (ptid);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ /* The content of a register. */
+ gdb_byte buf[4];
+ /* The result. */
+ LONGEST ret;
+
+ /* Getting the system call number from the register.
+ When dealing with x86 architecture, this information
+ is stored at %eax register. */
+ regcache_cooked_read (regcache, I386_LINUX_ORIG_EAX_REGNUM, buf);
+
+ ret = extract_signed_integer (buf, 4, byte_order);
+
+ return ret;
+}
+
/* The register sets used in GNU/Linux ELF core-dumps are identical to
the register sets in `struct user' that are used for a.out
core-dumps. These are also used by ptrace(2). The corresponding
format and GDB's register cache layout. */
/* From <sys/reg.h>. */
-static int i386_linux_gregset_reg_offset[] =
+int i386_linux_gregset_reg_offset[] =
{
6 * 4, /* %eax */
1 * 4, /* %ecx */
-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,
11 * 4 /* "orig_eax" */
};
0 * 4 /* %gs */
};
+/* Get XSAVE extended state xcr0 from core dump. */
+
+uint64_t
+i386_linux_core_read_xcr0 (struct gdbarch *gdbarch,
+ struct target_ops *target, bfd *abfd)
+{
+ asection *xstate = bfd_get_section_by_name (abfd, ".reg-xstate");
+ uint64_t xcr0;
+
+ if (xstate)
+ {
+ size_t size = bfd_section_size (abfd, xstate);
+
+ /* Check extended state size. */
+ if (size < I386_XSTATE_AVX_SIZE)
+ xcr0 = I386_XSTATE_SSE_MASK;
+ else
+ {
+ char contents[8];
+
+ if (! bfd_get_section_contents (abfd, xstate, contents,
+ I386_LINUX_XSAVE_XCR0_OFFSET,
+ 8))
+ {
+ warning (_("Couldn't read `xcr0' bytes from `.reg-xstate' section in core file."));
+ return 0;
+ }
+
+ xcr0 = bfd_get_64 (abfd, contents);
+ }
+ }
+ else
+ xcr0 = 0;
+
+ return xcr0;
+}
+
+/* Get Linux/x86 target description from core dump. */
+
+static const struct target_desc *
+i386_linux_core_read_description (struct gdbarch *gdbarch,
+ struct target_ops *target,
+ bfd *abfd)
+{
+ /* Linux/i386. */
+ uint64_t xcr0 = i386_linux_core_read_xcr0 (gdbarch, target, abfd);
+ switch ((xcr0 & I386_XSTATE_AVX_MASK))
+ {
+ case I386_XSTATE_AVX_MASK:
+ return tdesc_i386_avx_linux;
+ case I386_XSTATE_SSE_MASK:
+ return tdesc_i386_linux;
+ case I386_XSTATE_X87_MASK:
+ return tdesc_i386_mmx_linux;
+ default:
+ break;
+ }
+
+ if (bfd_get_section_by_name (abfd, ".reg-xfp") != NULL)
+ return tdesc_i386_linux;
+ else
+ return tdesc_i386_mmx_linux;
+}
+
static void
i386_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);
+
+ linux_init_abi (info, gdbarch);
/* GNU/Linux uses ELF. */
i386_elf_init_abi (info, gdbarch);
- /* Since we have the extra "orig_eax" register on GNU/Linux, we have
- to adjust a few things. */
+ /* Reserve a number for orig_eax. */
+ set_gdbarch_num_regs (gdbarch, I386_LINUX_NUM_REGS);
+
+ if (! tdesc_has_registers (tdesc))
+ tdesc = tdesc_i386_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,
+ I386_LINUX_ORIG_EAX_REGNUM,
+ "orig_eax");
+ if (!valid_p)
+ return;
+
+ /* Add the %orig_eax register used for syscall restarting. */
set_gdbarch_write_pc (gdbarch, i386_linux_write_pc);
- set_gdbarch_num_regs (gdbarch, I386_LINUX_NUM_REGS);
- set_gdbarch_register_name (gdbarch, i386_linux_register_name);
- set_gdbarch_register_reggroup_p (gdbarch, i386_linux_register_reggroup_p);
+
+ tdep->register_reggroup_p = i386_linux_register_reggroup_p;
tdep->gregset_reg_offset = i386_linux_gregset_reg_offset;
tdep->gregset_num_regs = ARRAY_SIZE (i386_linux_gregset_reg_offset);
tdep->sc_reg_offset = i386_linux_sc_reg_offset;
tdep->sc_num_regs = ARRAY_SIZE (i386_linux_sc_reg_offset);
+ tdep->xsave_xcr0_offset = I386_LINUX_XSAVE_XCR0_OFFSET;
+
set_gdbarch_process_record (gdbarch, i386_process_record);
+ set_gdbarch_process_record_signal (gdbarch, i386_linux_record_signal);
/* Initialize the i386_linux_record_tdep. */
/* These values are the size of the type that will be used in a system
call. They are obtained from Linux Kernel source. */
+ i386_linux_record_tdep.size_pointer
+ = gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;
i386_linux_record_tdep.size__old_kernel_stat = 32;
i386_linux_record_tdep.size_tms = 16;
i386_linux_record_tdep.size_loff_t = 8;
i386_linux_record_tdep.size_statfs = 64;
i386_linux_record_tdep.size_statfs64 = 84;
i386_linux_record_tdep.size_sockaddr = 16;
- i386_linux_record_tdep.size_int = 4;
- i386_linux_record_tdep.size_long = 4;
- i386_linux_record_tdep.size_ulong = 4;
+ i386_linux_record_tdep.size_int
+ = gdbarch_int_bit (gdbarch) / TARGET_CHAR_BIT;
+ i386_linux_record_tdep.size_long
+ = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT;
+ i386_linux_record_tdep.size_ulong
+ = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT;
i386_linux_record_tdep.size_msghdr = 28;
i386_linux_record_tdep.size_itimerval = 16;
i386_linux_record_tdep.size_stat = 88;
i386_linux_record_tdep.size_io_event = 32;
i386_linux_record_tdep.size_iocb = 64;
i386_linux_record_tdep.size_epoll_event = 12;
- i386_linux_record_tdep.size_itimerspec = i386_linux_record_tdep.size_timespec * 2;
+ i386_linux_record_tdep.size_itimerspec
+ = i386_linux_record_tdep.size_timespec * 2;
i386_linux_record_tdep.size_mq_attr = 32;
i386_linux_record_tdep.size_siginfo = 128;
i386_linux_record_tdep.size_termios = 36;
i386_linux_record_tdep.size_serial_struct = 60;
i386_linux_record_tdep.size_serial_icounter_struct = 80;
i386_linux_record_tdep.size_hayes_esp_config = 12;
+ i386_linux_record_tdep.size_size_t = 4;
+ i386_linux_record_tdep.size_iovec = 8;
/* These values are the second argument of system call "sys_ioctl".
They are obtained from Linux Kernel source. */
i386_linux_record_tdep.arg3 = I386_EDX_REGNUM;
i386_linux_record_tdep.arg4 = I386_ESI_REGNUM;
i386_linux_record_tdep.arg5 = I386_EDI_REGNUM;
+ i386_linux_record_tdep.arg6 = I386_EBP_REGNUM;
tdep->i386_intx80_record = i386_linux_intx80_sysenter_record;
tdep->i386_sysenter_record = i386_linux_intx80_sysenter_record;
svr4_fetch_objfile_link_map);
/* Install supported register note sections. */
- set_gdbarch_core_regset_sections (gdbarch, i386_linux_regset_sections);
+ if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx"))
+ set_gdbarch_core_regset_sections (gdbarch, i386_linux_avx_regset_sections);
+ else if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.sse"))
+ set_gdbarch_core_regset_sections (gdbarch, i386_linux_sse_regset_sections);
+ else
+ set_gdbarch_core_regset_sections (gdbarch, i386_linux_regset_sections);
+
+ set_gdbarch_core_read_description (gdbarch,
+ i386_linux_core_read_description);
/* Displaced stepping. */
set_gdbarch_displaced_step_copy_insn (gdbarch,
- simple_displaced_step_copy_insn);
+ i386_displaced_step_copy_insn);
set_gdbarch_displaced_step_fixup (gdbarch, i386_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);
+ /* Functions for 'catch syscall'. */
+ set_xml_syscall_file_name (XML_SYSCALL_FILENAME_I386);
+ set_gdbarch_get_syscall_number (gdbarch,
+ i386_linux_get_syscall_number);
+
set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
}
{
gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_LINUX,
i386_linux_init_abi);
+
+ /* Initialize the Linux target description */
+ initialize_tdesc_i386_linux ();
+ initialize_tdesc_i386_mmx_linux ();
+ initialize_tdesc_i386_avx_linux ();
}