/* Native-dependent code for GNU/Linux i386.
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
- 2009, 2010 Free Software Foundation, Inc.
+ 2009, 2010, 2011 Free Software Foundation, Inc.
This file is part of GDB.
#include "inferior.h"
#include "gdbcore.h"
#include "regcache.h"
+#include "regset.h"
#include "target.h"
#include "linux-nat.h"
#include "gdb_assert.h"
#include "gdb_string.h"
+#include "elf/common.h"
+#include <sys/uio.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/procfs.h>
/* Defines ps_err_e, struct ps_prochandle. */
#include "gdb_proc_service.h"
+
+#include "i386-xstate.h"
+
+#ifndef PTRACE_GETREGSET
+#define PTRACE_GETREGSET 0x4204
+#endif
+
+#ifndef PTRACE_SETREGSET
+#define PTRACE_SETREGSET 0x4205
+#endif
+
+/* Does the current host support PTRACE_GETREGSET? */
+static int have_ptrace_getregset = -1;
\f
/* The register sets used in GNU/Linux ELF core-dumps are identical to
those names are now used for the register sets used in the
`mcontext_t' type, and have a different size and layout. */
-/* Mapping between the general-purpose registers in `struct user'
- format and GDB's register array layout. */
-static int regmap[] =
-{
- EAX, ECX, EDX, EBX,
- UESP, EBP, ESI, EDI,
- EIP, EFL, CS, SS,
- DS, ES, FS, GS,
- -1, -1, -1, -1, /* st0, st1, st2, st3 */
- -1, -1, -1, -1, /* st4, st5, st6, st7 */
- -1, -1, -1, -1, /* fctrl, fstat, ftag, fiseg */
- -1, -1, -1, -1, /* fioff, foseg, fooff, fop */
- -1, -1, -1, -1, /* xmm0, xmm1, xmm2, xmm3 */
- -1, -1, -1, -1, /* xmm4, xmm5, xmm6, xmm6 */
- -1, /* mxcsr */
- ORIG_EAX
-};
-
/* Which ptrace request retrieves which registers?
These apply to the corresponding SET requests as well. */
#define GETFPXREGS_SUPPLIES(regno) \
(I386_ST0_REGNUM <= (regno) && (regno) < I386_SSE_NUM_REGS)
+#define GETXSTATEREGS_SUPPLIES(regno) \
+ (I386_ST0_REGNUM <= (regno) && (regno) < I386_AVX_NUM_REGS)
+
/* Does the current host support the GETREGS request? */
int have_ptrace_getregs =
#ifdef HAVE_PTRACE_GETREGS
for this to be a simple variable. */
int have_ptrace_getfpxregs =
#ifdef HAVE_PTRACE_GETFPXREGS
- 1
+ -1
#else
0
#endif
int val;
gdb_assert (!have_ptrace_getregs);
- if (regmap[regno] == -1)
+ if (i386_linux_gregset_reg_offset[regno] == -1)
{
regcache_raw_supply (regcache, regno, NULL);
return;
tid = PIDGET (inferior_ptid); /* Not a threaded program. */
errno = 0;
- val = ptrace (PTRACE_PEEKUSER, tid, 4 * regmap[regno], 0);
+ val = ptrace (PTRACE_PEEKUSER, tid,
+ i386_linux_gregset_reg_offset[regno], 0);
if (errno != 0)
error (_("Couldn't read register %s (#%d): %s."),
gdbarch_register_name (get_regcache_arch (regcache), regno),
regcache_raw_supply (regcache, regno, &val);
}
-/* Store one register. */
+/* Store one register. */
static void
store_register (const struct regcache *regcache, int regno)
int val;
gdb_assert (!have_ptrace_getregs);
- if (regmap[regno] == -1)
+ if (i386_linux_gregset_reg_offset[regno] == -1)
return;
/* GNU/Linux LWP ID's are process ID's. */
errno = 0;
regcache_raw_collect (regcache, regno, &val);
- ptrace (PTRACE_POKEUSER, tid, 4 * regmap[regno], val);
+ ptrace (PTRACE_POKEUSER, tid,
+ i386_linux_gregset_reg_offset[regno], val);
if (errno != 0)
error (_("Couldn't write register %s (#%d): %s."),
gdbarch_register_name (get_regcache_arch (regcache), regno),
void
supply_gregset (struct regcache *regcache, const elf_gregset_t *gregsetp)
{
- const elf_greg_t *regp = (const elf_greg_t *) gregsetp;
+ const gdb_byte *regp = (const gdb_byte *) gregsetp;
int i;
for (i = 0; i < I386_NUM_GREGS; i++)
- regcache_raw_supply (regcache, i, regp + regmap[i]);
+ regcache_raw_supply (regcache, i,
+ regp + i386_linux_gregset_reg_offset[i]);
if (I386_LINUX_ORIG_EAX_REGNUM
< gdbarch_num_regs (get_regcache_arch (regcache)))
- regcache_raw_supply (regcache, I386_LINUX_ORIG_EAX_REGNUM,
- regp + ORIG_EAX);
+ regcache_raw_supply (regcache, I386_LINUX_ORIG_EAX_REGNUM, regp
+ + i386_linux_gregset_reg_offset[I386_LINUX_ORIG_EAX_REGNUM]);
}
/* Fill register REGNO (if it is a general-purpose register) in
fill_gregset (const struct regcache *regcache,
elf_gregset_t *gregsetp, int regno)
{
- elf_greg_t *regp = (elf_greg_t *) gregsetp;
+ gdb_byte *regp = (gdb_byte *) gregsetp;
int i;
for (i = 0; i < I386_NUM_GREGS; i++)
if (regno == -1 || regno == i)
- regcache_raw_collect (regcache, i, regp + regmap[i]);
+ regcache_raw_collect (regcache, i,
+ regp + i386_linux_gregset_reg_offset[i]);
if ((regno == -1 || regno == I386_LINUX_ORIG_EAX_REGNUM)
&& I386_LINUX_ORIG_EAX_REGNUM
< gdbarch_num_regs (get_regcache_arch (regcache)))
- regcache_raw_collect (regcache, I386_LINUX_ORIG_EAX_REGNUM,
- regp + ORIG_EAX);
+ regcache_raw_collect (regcache, I386_LINUX_ORIG_EAX_REGNUM, regp
+ + i386_linux_gregset_reg_offset[I386_LINUX_ORIG_EAX_REGNUM]);
}
#ifdef HAVE_PTRACE_GETREGS
#else
-static void fetch_fpregs (struct regcache *regcache, int tid) {}
-static void store_fpregs (const struct regcache *regcache, int tid, int regno) {}
+static void
+fetch_fpregs (struct regcache *regcache, int tid)
+{
+}
+
+static void
+store_fpregs (const struct regcache *regcache, int tid, int regno)
+{
+}
#endif
\f
/* Transfering floating-point and SSE registers to and from GDB. */
-#ifdef HAVE_PTRACE_GETFPXREGS
-
-/* Fill GDB's register array with the floating-point and SSE register
- values in *FPXREGSETP. */
+/* Fetch all registers covered by the PTRACE_GETREGSET request from
+ process/thread TID and store their values in GDB's register array.
+ Return non-zero if successful, zero otherwise. */
-void
-supply_fpxregset (struct regcache *regcache,
- const elf_fpxregset_t *fpxregsetp)
+static int
+fetch_xstateregs (struct regcache *regcache, int tid)
{
- i387_supply_fxsave (regcache, -1, fpxregsetp);
+ char xstateregs[I386_XSTATE_MAX_SIZE];
+ struct iovec iov;
+
+ if (!have_ptrace_getregset)
+ return 0;
+
+ iov.iov_base = xstateregs;
+ iov.iov_len = sizeof(xstateregs);
+ if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE,
+ &iov) < 0)
+ perror_with_name (_("Couldn't read extended state status"));
+
+ i387_supply_xsave (regcache, -1, xstateregs);
+ return 1;
}
-/* Fill register REGNO (if it is a floating-point or SSE register) in
- *FPXREGSETP with the value in GDB's register array. If REGNO is
- -1, do this for all registers. */
+/* Store all valid registers in GDB's register array covered by the
+ PTRACE_SETREGSET request into the process/thread specified by TID.
+ Return non-zero if successful, zero otherwise. */
-void
-fill_fpxregset (const struct regcache *regcache,
- elf_fpxregset_t *fpxregsetp, int regno)
+static int
+store_xstateregs (const struct regcache *regcache, int tid, int regno)
{
- i387_collect_fxsave (regcache, regno, fpxregsetp);
+ char xstateregs[I386_XSTATE_MAX_SIZE];
+ struct iovec iov;
+
+ if (!have_ptrace_getregset)
+ return 0;
+
+ iov.iov_base = xstateregs;
+ iov.iov_len = sizeof(xstateregs);
+ if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE,
+ &iov) < 0)
+ perror_with_name (_("Couldn't read extended state status"));
+
+ i387_collect_xsave (regcache, regno, xstateregs, 0);
+
+ if (ptrace (PTRACE_SETREGSET, tid, (unsigned int) NT_X86_XSTATE,
+ (int) &iov) < 0)
+ perror_with_name (_("Couldn't write extended state status"));
+
+ return 1;
}
+#ifdef HAVE_PTRACE_GETFPXREGS
+
/* Fetch all registers covered by the PTRACE_GETFPXREGS request from
process/thread TID and store their values in GDB's register array.
Return non-zero if successful, zero otherwise. */
perror_with_name (_("Couldn't read floating-point and SSE registers"));
}
- supply_fpxregset (regcache, (const elf_fpxregset_t *) &fpxregs);
+ i387_supply_fxsave (regcache, -1, (const elf_fpxregset_t *) &fpxregs);
return 1;
}
perror_with_name (_("Couldn't read floating-point and SSE registers"));
}
- fill_fpxregset (regcache, &fpxregs, regno);
+ i387_collect_fxsave (regcache, regno, &fpxregs);
if (ptrace (PTRACE_SETFPXREGS, tid, 0, &fpxregs) == -1)
perror_with_name (_("Couldn't write floating-point and SSE registers"));
#else
-static int fetch_fpxregs (struct regcache *regcache, int tid) { return 0; }
-static int store_fpxregs (const struct regcache *regcache, int tid, int regno) { return 0; }
+static int
+fetch_fpxregs (struct regcache *regcache, int tid)
+{
+ return 0;
+}
+
+static int
+store_fpxregs (const struct regcache *regcache, int tid, int regno)
+{
+ return 0;
+}
#endif /* HAVE_PTRACE_GETFPXREGS */
\f
return;
}
+ if (fetch_xstateregs (regcache, tid))
+ return;
if (fetch_fpxregs (regcache, tid))
return;
fetch_fpregs (regcache, tid);
return;
}
+ if (GETXSTATEREGS_SUPPLIES (regno))
+ {
+ if (fetch_xstateregs (regcache, tid))
+ return;
+ }
+
if (GETFPXREGS_SUPPLIES (regno))
{
if (fetch_fpxregs (regcache, tid))
if (regno == -1)
{
store_regs (regcache, tid, regno);
+ if (store_xstateregs (regcache, tid, regno))
+ return;
if (store_fpxregs (regcache, tid, regno))
return;
store_fpregs (regcache, tid, regno);
return;
}
+ if (GETXSTATEREGS_SUPPLIES (regno))
+ {
+ if (store_xstateregs (regcache, tid, regno))
+ return;
+ }
+
if (GETFPXREGS_SUPPLIES (regno))
{
if (store_fpxregs (regcache, tid, regno))
call.
Is this function needed? I'm guessing that the `base' is the
- address of a a descriptor that libthread_db uses to find the
+ address of a descriptor that libthread_db uses to find the
thread local address base that GDB needs. Perhaps that
descriptor is defined by the ABI. Anyway, given that
libthread_db calls this function without prompting (gdb
static const struct target_desc *
i386_linux_read_description (struct target_ops *ops)
{
- return tdesc_i386_linux;
+ int tid;
+ static uint64_t xcr0;
+
+ /* GNU/Linux LWP ID's are process ID's. */
+ tid = TIDGET (inferior_ptid);
+ if (tid == 0)
+ tid = PIDGET (inferior_ptid); /* Not a threaded program. */
+
+#ifdef HAVE_PTRACE_GETFPXREGS
+ if (have_ptrace_getfpxregs == -1)
+ {
+ elf_fpxregset_t fpxregs;
+
+ if (ptrace (PTRACE_GETFPXREGS, tid, 0, (int) &fpxregs) < 0)
+ {
+ have_ptrace_getfpxregs = 0;
+ have_ptrace_getregset = 0;
+ return tdesc_i386_mmx_linux;
+ }
+ }
+#endif
+
+ if (have_ptrace_getregset == -1)
+ {
+ uint64_t xstateregs[(I386_XSTATE_SSE_SIZE / sizeof (uint64_t))];
+ struct iovec iov;
+
+ iov.iov_base = xstateregs;
+ iov.iov_len = sizeof (xstateregs);
+
+ /* Check if PTRACE_GETREGSET works. */
+ if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE,
+ &iov) < 0)
+ have_ptrace_getregset = 0;
+ else
+ {
+ have_ptrace_getregset = 1;
+
+ /* Get XCR0 from XSAVE extended state. */
+ xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+ / sizeof (long long))];
+ }
+ }
+
+ /* Check the native XCR0 only if PTRACE_GETREGSET is available. */
+ if (have_ptrace_getregset
+ && (xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK)
+ return tdesc_i386_avx_linux;
+ else
+ return tdesc_i386_linux;
}
void