PATCH: 6/6 [3rd try]: Add AVX support (gdbserver changes)

H.J. Lu
2010-03-29T01:09:50Z

Hi,

Here are gdbserver changes to support AVX. OK to install?

Thanks.

H.J.
---
2010-03-28 H.J. Lu <hongjiu.lu@intel.com>

* Makefile.in (clean): Updated.
(i386-avx.o): New.
(i386-avx.c): Likewise.
(i386-avx-linux.o): Likewise.
(i386-avx-linux.c): Likewise.
(amd64-avx.o): Likewise.
(amd64-avx.c): Likewise.
(amd64-avx-linux.o): Likewise.
(amd64-avx-linux.c): Likewise.

* configure.srv (srv_i386_regobj): Add i386-avx.o.
(srv_i386_linux_regobj): Add i386-avx-linux.o.
(srv_amd64_regobj): Add amd64-avx.o.
(srv_amd64_linux_regobj): Add amd64-avx-linux.o.
(srv_i386_32bit_xmlfiles): Add i386/32bit-avx.xml.
(srv_i386_64bit_xmlfiles): Add i386/64bit-avx.xml.
(srv_i386_xmlfiles): Add i386/i386-avx.xml.
(srv_amd64_xmlfiles): Add i386/amd64-avx.xml.
(srv_i386_linux_xmlfiles): Add i386/i386-avx-linux.xml.
(srv_amd64_linux_xmlfiles): Add i386/amd64-avx-linux.xml.

* i387-fp.c: Include "i386-xstate.h".
(i387_xsave): New.
(i387_cache_to_xsave): Likewise.
(i387_xsave_to_cache): Likewise.
(x86_xcr0): Likewise.

* i387-fp.h (i387_cache_to_xsave): Likewise.
(i387_xsave_to_cache): Likewise.
(x86_xcr0): Likewise.

* linux-arm-low.c (target_regsets): Initialize nt_type to 0.
* linux-crisv32-low.c (target_regsets): Likewise.
* linux-m68k-low.c (target_regsets): Likewise.
* linux-mips-low.c (target_regsets): Likewise.
* linux-ppc-low.c (target_regsets): Likewise.
* linux-s390-low.c (target_regsets): Likewise.
* linux-sh-low.c (target_regsets): Likewise.
* linux-sparc-low.c (target_regsets): Likewise.
* linux-xtensa-low.c (target_regsets): Likewise.

* linux-low.c: Include <sys/uio.h>.
(regsets_fetch_inferior_registers): Support nt_type.
(regsets_store_inferior_registers): Likewise.
(linux_process_qsupported): New.
(linux_target_ops): Add linux_process_qsupported.

* linux-low.h (regset_info): Add nt_type.
(linux_target_ops): Add process_qsupported.

* linux-x86-low.c: Include "i386-xstate.h", "elf/common.h",
<sys/uio.h> and <unistd.h>.
(init_registers_i386_avx_linux): New.
(init_registers_amd64_avx_linux): Likewise.
(xmltarget_amd64_linux_no_xml): Likewise.
(xmltarget_i386_linux_no_xml): Likewise.
(PTRACE_GETREGSET): Likewise.
(PTRACE_SETREGSET): Likewise.
(x86_fill_xstateregset): Likewise.
(x86_store_xstateregset): Likewise.
(x86_linux_process_qsupported): Likewise.
(target_regsets): Add NT_X86_XSTATE entry and Initialize nt_type.
(x86_arch_setup): Set gdbserver_xmltarget.
(the_low_target): Add x86_linux_process_qsupported.

* server.c (handle_query): Call target_process_qsupported.

* target.h (target_ops): Add process_qsupported.
(target_process_qsupported): New.

diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 7fecced..2ec9784 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -217,6 +217,8 @@ clean:
rm -f powerpc-isa205-vsx64l.c
rm -f s390-linux32.c s390-linux64.c s390x-linux64.c
rm -f xml-builtin.c stamp-xml
+ rm -f i386-avx.c i386-avx-linux.c
+ rm -f amd64-avx.c amd64-avx-linux.c

maintainer-clean realclean distclean: clean
rm -f nm.h tm.h xm.h config.status config.h stamp-h config.log
@@ -351,6 +353,12 @@ i386.c : $(srcdir)/../regformats/i386/i386.dat $(regdat_sh)
i386-linux.o : i386-linux.c $(regdef_h)
i386-linux.c : $(srcdir)/../regformats/i386/i386-linux.dat $(regdat_sh)
$(SHELL) $(regdat_sh) $(srcdir)/../regformats/i386/i386-linux.dat i386-linux.c
+i386-avx.o : i386-avx.c $(regdef_h)
+i386-avx.c : $(srcdir)/../regformats/i386/i386-avx.dat $(regdat_sh)
+ $(SHELL) $(regdat_sh) $(srcdir)/../regformats/i386/i386-avx.dat i386-avx.c
+i386-avx-linux.o : i386-avx-linux.c $(regdef_h)
+i386-avx-linux.c : $(srcdir)/../regformats/i386/i386-avx-linux.dat $(regdat_sh)
+ $(SHELL) $(regdat_sh) $(srcdir)/../regformats/i386/i386-avx-linux.dat i386-avx-linux.c
reg-ia64.o : reg-ia64.c $(regdef_h)
reg-ia64.c : $(srcdir)/../regformats/reg-ia64.dat $(regdat_sh)
$(SHELL) $(regdat_sh) $(srcdir)/../regformats/reg-ia64.dat reg-ia64.c
@@ -438,6 +446,12 @@ amd64.c : $(srcdir)/../regformats/i386/amd64.dat $(regdat_sh)
amd64-linux.o : amd64-linux.c $(regdef_h)
amd64-linux.c : $(srcdir)/../regformats/i386/amd64-linux.dat $(regdat_sh)
$(SHELL) $(regdat_sh) $(srcdir)/../regformats/i386/amd64-linux.dat amd64-linux.c
+amd64-avx.o : amd64-avx.c $(regdef_h)
+amd64-avx.c : $(srcdir)/../regformats/i386/amd64-avx.dat $(regdat_sh)
+ $(SHELL) $(regdat_sh) $(srcdir)/../regformats/i386/amd64-avx.dat amd64-avx.c
+amd64-avx-linux.o : amd64-avx-linux.c $(regdef_h)
+amd64-avx-linux.c : $(srcdir)/../regformats/i386/amd64-avx-linux.dat $(regdat_sh)
+ $(SHELL) $(regdat_sh) $(srcdir)/../regformats/i386/amd64-avx-linux.dat amd64-avx-linux.c
reg-xtensa.o : reg-xtensa.c $(regdef_h)
reg-xtensa.c : $(srcdir)/../regformats/reg-xtensa.dat $(regdat_sh)
$(SHELL) $(regdat_sh) $(srcdir)/../regformats/reg-xtensa.dat reg-xtensa.c
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index f7c80bd..8bc9aeb 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -22,17 +22,17 @@
# Default hostio_last_error implementation
srv_hostio_err_objs="hostio-errno.o"

-srv_i386_regobj=i386.o
-srv_i386_linux_regobj=i386-linux.o
-srv_amd64_regobj=amd64.o
-srv_amd64_linux_regobj=amd64-linux.o
+srv_i386_regobj="i386.o i386-avx.o"
+srv_i386_linux_regobj="i386-linux.o i386-avx-linux.o"
+srv_amd64_regobj="amd64.o x86-64-avx.o"
+srv_amd64_linux_regobj="amd64-linux.o amd64-avx-linux.o"

-srv_i386_32bit_xmlfiles="i386/32bit-core.xml i386/32bit-sse.xml"
-srv_i386_64bit_xmlfiles="i386/64bit-core.xml i386/64bit-sse.xml"
-srv_i386_xmlfiles="i386/i386.xml $srv_i386_32bit_xmlfiles"
-srv_amd64_xmlfiles="i386/amd64.xml $srv_i386_64bit_xmlfiles"
-srv_i386_linux_xmlfiles="i386/i386-linux.xml i386/32bit-linux.xml $srv_i386_32bit_xmlfiles"
-srv_amd64_linux_xmlfiles="i386/amd64-linux.xml i386/64bit-linux.xml $srv_i386_64bit_xmlfiles"
+srv_i386_32bit_xmlfiles="i386/32bit-core.xml i386/32bit-sse.xml i386/32bit-avx.xml"
+srv_i386_64bit_xmlfiles="i386/64bit-core.xml i386/64bit-sse.xml i386/64bit-avx.xml"
+srv_i386_xmlfiles="i386/i386.xml i386/i386-avx.xml $srv_i386_32bit_xmlfiles"
+srv_amd64_xmlfiles="i386/amd64.xml i386/amd64-avx.xml $srv_i386_64bit_xmlfiles"
+srv_i386_linux_xmlfiles="i386/i386-linux.xml i386/i386-avx-linux.xml i386/32bit-linux.xml $srv_i386_32bit_xmlfiles"
+srv_amd64_linux_xmlfiles="i386/amd64-linux.xml i386/amd64-avx-linux.xml i386/64bit-linux.xml $srv_i386_64bit_xmlfiles"

# Input is taken from the "${target}" variable.

diff --git a/gdb/gdbserver/i387-fp.c b/gdb/gdbserver/i387-fp.c
index 7ef4ba3..5461022 100644
--- a/gdb/gdbserver/i387-fp.c
+++ b/gdb/gdbserver/i387-fp.c
@@ -19,6 +19,7 @@

#include "server.h"
#include "i387-fp.h"
+#include "i386-xstate.h"

int num_xmm_registers = 8;

@@ -72,6 +73,46 @@ struct i387_fxsave {
unsigned char xmm_space[256];
};

+struct i387_xsave {
+ /* All these are only sixteen bits, plus padding, except for fop (which
+ is only eleven bits), and fooff / fioff (which are 32 bits each). */
+ unsigned short fctrl;
+ unsigned short fstat;
+ unsigned short ftag;
+ unsigned short fop;
+ unsigned int fioff;
+ unsigned short fiseg;
+ unsigned short pad1;
+ unsigned int fooff;
+ unsigned short foseg;
+ unsigned short pad12;
+
+ unsigned int mxcsr;
+ unsigned int mxcsr_mask;
+
+ /* Space for eight 80-bit FP values in 128-bit spaces. */
+ unsigned char st_space[128];
+
+ /* Space for eight 128-bit XMM values, or 16 on x86-64. */
+ unsigned char xmm_space[256];
+
+ unsigned char reserved1[48];
+
+ /* The extended control register 0 (the XFEATURE_ENABLED_MASK
+ register). */
+ unsigned long long xcr0;
+
+ unsigned char reserved2[40];
+
+ /* The XSTATE_BV bit vector. */
+ unsigned long long xstate_bv;
+
+ unsigned char reserved3[56];
+
+ /* Space for eight upper 128-bit YMM values, or 16 on x86-64. */
+ unsigned char ymmh_space[256];
+};
+
void
i387_cache_to_fsave (struct regcache *regcache, void *buf)
{
@@ -199,6 +240,128 @@ i387_cache_to_fxsave (struct regcache *regcache, void *buf)
fp->foseg = val;
}

+void
+i387_cache_to_xsave (struct regcache *regcache, void *buf)
+{
+ struct i387_xsave *fp = (struct i387_xsave *) buf;
+ int i;
+ unsigned long val, val2;
+ unsigned int clear_bv;
+ unsigned long long xstate_bv = 0;
+ char raw[16];
+ char *p;
+
+ /* The supported bits in `xstat_bv' are 1 byte. Clear part in
+ vector registers if its bit in xstat_bv is zero. */
+ clear_bv = (~fp->xstate_bv) & x86_xcr0;
+
+ /* Clear part in x87 and vector registers if its bit in xstat_bv is
+ zero. */
+ if (clear_bv)
+ {
+ if ((clear_bv & I386_XSTATE_X87))
+ for (i = 0; i < 8; i++)
+ memset (((char *) &fp->st_space[0]) + i * 16, 0, 10);
+
+ if ((clear_bv & I386_XSTATE_SSE))
+ for (i = 0; i < num_xmm_registers; i++)
+ memset (((char *) &fp->xmm_space[0]) + i * 16, 0, 16);
+
+ if ((clear_bv & I386_XSTATE_AVX))
+ for (i = 0; i < num_xmm_registers; i++)
+ memset (((char *) &fp->ymmh_space[0]) + i * 16, 0, 16);
+ }
+
+ /* Check if any x87 registers are changed. */
+ if ((x86_xcr0 & I386_XSTATE_X87))
+ {
+ int st0_regnum = find_regno ("st0");
+
+ for (i = 0; i < 8; i++)
+ {
+ collect_register (regcache, i + st0_regnum, raw);
+ p = ((char *) &fp->st_space[0]) + i * 16;
+ if (memcmp (raw, p, 10))
+ {
+ xstate_bv |= I386_XSTATE_X87;
+ memcpy (p, raw, 10);
+ }
+ }
+ }
+
+ /* Check if any SSE registers are changed. */
+ if ((x86_xcr0 & I386_XSTATE_SSE))
+ {
+ int xmm0_regnum = find_regno ("xmm0");
+
+ for (i = 0; i < num_xmm_registers; i++)
+ {
+ collect_register (regcache, i + xmm0_regnum, raw);
+ p = ((char *) &fp->xmm_space[0]) + i * 16;
+ if (memcmp (raw, p, 16))
+ {
+ xstate_bv |= I386_XSTATE_SSE;
+ memcpy (p, raw, 16);
+ }
+ }
+ }
+
+ /* Check if any AVX registers are changed. */
+ if ((x86_xcr0 & I386_XSTATE_AVX))
+ {
+ int ymm0h_regnum = find_regno ("ymm0h");
+
+ for (i = 0; i < num_xmm_registers; i++)
+ {
+ collect_register (regcache, i + ymm0h_regnum, raw);
+ p = ((char *) &fp->ymmh_space[0]) + i * 16;
+ if (memcmp (raw, p, 16))
+ {
+ xstate_bv |= I386_XSTATE_AVX;
+ memcpy (p, raw, 16);
+ }
+ }
+ }
+
+ /* Update the corresponding bits in xstate_bv if any SSE/AVX
+ registers are changed. */
+ fp->xstate_bv |= xstate_bv;
+
+ collect_register_by_name (regcache, "fioff", &fp->fioff);
+ collect_register_by_name (regcache, "fooff", &fp->fooff);
+ collect_register_by_name (regcache, "mxcsr", &fp->mxcsr);
+
+ /* This one's 11 bits... */
+ collect_register_by_name (regcache, "fop", &val2);
+ fp->fop = (val2 & 0x7FF) | (fp->fop & 0xF800);
+
+ /* Some registers are 16-bit. */
+ collect_register_by_name (regcache, "fctrl", &val);
+ fp->fctrl = val;
+
+ collect_register_by_name (regcache, "fstat", &val);
+ fp->fstat = val;
+
+ /* Convert to the simplifed tag form stored in fxsave data. */
+ collect_register_by_name (regcache, "ftag", &val);
+ val &= 0xFFFF;
+ val2 = 0;
+ for (i = 7; i >= 0; i--)
+ {
+ int tag = (val >> (i * 2)) & 3;
+
+ if (tag != 3)
+ val2 |= (1 << i);
+ }
+ fp->ftag = val2;
+
+ collect_register_by_name (regcache, "fiseg", &val);
+ fp->fiseg = val;
+
+ collect_register_by_name (regcache, "foseg", &val);
+ fp->foseg = val;
+}
+
static int
i387_ftag (struct i387_fxsave *fp, int regno)
{
@@ -296,3 +459,107 @@ i387_fxsave_to_cache (struct regcache *regcache, const void *buf)
val = (fp->fop) & 0x7FF;
supply_register_by_name (regcache, "fop", &val);
}
+
+void
+i387_xsave_to_cache (struct regcache *regcache, const void *buf)
+{
+ struct i387_xsave *fp = (struct i387_xsave *) buf;
+ struct i387_fxsave *fxp = (struct i387_fxsave *) buf;
+ int i, top;
+ unsigned long val;
+ unsigned int clear_bv;
+ char *p;
+
+ /* The supported bits in `xstat_bv' are 1 byte. Clear part in
+ vector registers if its bit in xstat_bv is zero. */
+ clear_bv = (~fp->xstate_bv) & x86_xcr0;
+
+ /* Check if any x87 registers are changed. */
+ if ((x86_xcr0 & I386_XSTATE_X87))
+ {
+ int st0_regnum = find_regno ("st0");
+
+ if ((clear_bv & I386_XSTATE_X87))
+ p = NULL;
+ else
+ p = (char *) buf;
+
+ for (i = 0; i < 8; i++)
+ {
+ if (p)
+ p = ((char *) &fp->st_space[0]) + i * 16;
+ supply_register (regcache, i + st0_regnum, p);
+ }
+ }
+
+ if ((x86_xcr0 & I386_XSTATE_SSE))
+ {
+ int xmm0_regnum = find_regno ("xmm0");
+
+ if ((clear_bv & I386_XSTATE_SSE))
+ p = NULL;
+ else
+ p = (char *) buf;
+
+ for (i = 0; i < num_xmm_registers; i++)
+ {
+ if (p)
+ p = ((char *) &fp->xmm_space[0]) + i * 16;
+ supply_register (regcache, i + xmm0_regnum, p);
+ }
+ }
+
+ if ((x86_xcr0 & I386_XSTATE_AVX))
+ {
+ int ymm0h_regnum = find_regno ("ymm0h");
+
+ if ((clear_bv & I386_XSTATE_AVX))
+ p = NULL;
+ else
+ p = (char *) buf;
+
+ for (i = 0; i < num_xmm_registers; i++)
+ {
+ if (p)
+ p = ((char *) &fp->ymmh_space[0]) + i * 16;
+ supply_register (regcache, i + ymm0h_regnum, p);
+ }
+ }
+
+ supply_register_by_name (regcache, "fioff", &fp->fioff);
+ supply_register_by_name (regcache, "fooff", &fp->fooff);
+ supply_register_by_name (regcache, "mxcsr", &fp->mxcsr);
+
+ /* Some registers are 16-bit. */
+ val = fp->fctrl & 0xFFFF;
+ supply_register_by_name (regcache, "fctrl", &val);
+
+ val = fp->fstat & 0xFFFF;
+ supply_register_by_name (regcache, "fstat", &val);
+
+ /* Generate the form of ftag data that GDB expects. */
+ top = (fp->fstat >> 11) & 0x7;
+ val = 0;
+ for (i = 7; i >= 0; i--)
+ {
+ int tag;
+ if (fp->ftag & (1 << i))
+ tag = i387_ftag (fxp, (i + 8 - top) % 8);
+ else
+ tag = 3;
+ val |= tag << (2 * i);
+ }
+ supply_register_by_name (regcache, "ftag", &val);
+
+ val = fp->fiseg & 0xFFFF;
+ supply_register_by_name (regcache, "fiseg", &val);
+
+ val = fp->foseg & 0xFFFF;
+ supply_register_by_name (regcache, "foseg", &val);
+
+ val = (fp->fop) & 0x7FF;
+ supply_register_by_name (regcache, "fop", &val);
+}
+
+/* Default to SSE. */
+unsigned long long x86_xcr0 = I386_XSTATE_SSE_MASK;
diff --git a/gdb/gdbserver/i387-fp.h b/gdb/gdbserver/i387-fp.h
index d1e0681..ed1a322 100644
--- a/gdb/gdbserver/i387-fp.h
+++ b/gdb/gdbserver/i387-fp.h
@@ -26,6 +26,11 @@ void i387_fsave_to_cache (struct regcache *regcache, const void *buf);
void i387_cache_to_fxsave (struct regcache *regcache, void *buf);
void i387_fxsave_to_cache (struct regcache *regcache, const void *buf);

+void i387_cache_to_xsave (struct regcache *regcache, void *buf);
+void i387_xsave_to_cache (struct regcache *regcache, const void *buf);
+
+extern unsigned long long x86_xcr0;
+
extern int num_xmm_registers;

#endif /* I387_FP_H */
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 54668f8..32bd7bb 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -354,16 +354,16 @@ arm_arch_setup (void)
}

struct regset_info target_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, 18 * 4,
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, 18 * 4,
GENERAL_REGS,
arm_fill_gregset, arm_store_gregset },
- { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 16 * 8 + 6 * 4,
+ { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 0, 16 * 8 + 6 * 4,
EXTENDED_REGS,
arm_fill_wmmxregset, arm_store_wmmxregset },
- { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 32 * 8 + 4,
+ { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, 32 * 8 + 4,
EXTENDED_REGS,
arm_fill_vfpregset, arm_store_vfpregset },
- { 0, 0, -1, -1, NULL, NULL }
+ { 0, 0, 0, -1, -1, NULL, NULL }
};

struct linux_target_ops the_low_target = {
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index 6ba48b6..d426c32 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -365,9 +365,9 @@ cris_store_gregset (const void *buf)
typedef unsigned long elf_gregset_t[cris_num_regs];

struct regset_info target_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t),
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
GENERAL_REGS, cris_fill_gregset, cris_store_gregset },
- { 0, 0, -1, -1, NULL, NULL }
+ { 0, 0, 0, -1, -1, NULL, NULL }
};

struct linux_target_ops the_low_target = {
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index ad68179..f5d4c41 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -39,6 +39,7 @@
#include <dirent.h>
#include <sys/stat.h>
#include <sys/vfs.h>
+#include <sys/uio.h>
#ifndef ELFMAG0
/* Don't include <linux/elf.h> here. If it got included by gdb_proc_service.h
then ELFMAG0 will have been defined. If it didn't get included by
@@ -2957,14 +2958,15 @@ regsets_fetch_inferior_registers (struct regcache *regcache)
struct regset_info *regset;
int saw_general_regs = 0;
int pid;
+ struct iovec iov;

regset = target_regsets;

pid = lwpid_of (get_thread_lwp (current_inferior));
while (regset->size >= 0)
{
- void *buf;
- int res;
+ void *buf, *data;
+ int nt_type, res;

if (regset->size == 0 || disabled_regsets[regset - target_regsets])
{
@@ -2973,10 +2975,21 @@ regsets_fetch_inferior_registers (struct regcache *regcache)
}

buf = xmalloc (regset->size);
+
+ nt_type = regset->nt_type;
+ if (nt_type)
+ {
+ iov.iov_base = buf;
+ iov.iov_len = regset->size;
+ data = (void *) &iov;
+ }
+ else
+ data = buf;
+
#ifndef __sparc__
- res = ptrace (regset->get_request, pid, 0, buf);
+ res = ptrace (regset->get_request, pid, nt_type, data);
#else
- res = ptrace (regset->get_request, pid, buf, 0);
+ res = ptrace (regset->get_request, pid, data, nt_type);
#endif
if (res < 0)
{
@@ -3014,14 +3027,15 @@ regsets_store_inferior_registers (struct regcache *regcache)
struct regset_info *regset;
int saw_general_regs = 0;
int pid;
+ struct iovec iov;

regset = target_regsets;

pid = lwpid_of (get_thread_lwp (current_inferior));
while (regset->size >= 0)
{
- void *buf;
- int res;
+ void *buf, *data;
+ int nt_type, res;

if (regset->size == 0 || disabled_regsets[regset - target_regsets])
{
@@ -3034,10 +3048,21 @@ regsets_store_inferior_registers (struct regcache *regcache)
/* First fill the buffer with the current register set contents,
in case there are any items in the kernel's regset that are
not in gdbserver's regcache. */
+
+ nt_type = regset->nt_type;
+ if (nt_type)
+ {
+ iov.iov_base = buf;
+ iov.iov_len = regset->size;
+ data = (void *) &iov;
+ }
+ else
+ data = buf;
+
#ifndef __sparc__
- res = ptrace (regset->get_request, pid, 0, buf);
+ res = ptrace (regset->get_request, pid, nt_type, data);
#else
- res = ptrace (regset->get_request, pid, buf, 0);
+ res = ptrace (regset->get_request, pid, &iov, data);
#endif

if (res == 0)
@@ -3047,9 +3072,9 @@ regsets_store_inferior_registers (struct regcache *regcache)

/* Only now do we write the register set. */
#ifndef __sparc__
- res = ptrace (regset->set_request, pid, 0, buf);
+ res = ptrace (regset->set_request, pid, nt_type, data);
#else
- res = ptrace (regset->set_request, pid, buf, 0);
+ res = ptrace (regset->set_request, pid, data, nt_type);
#endif
}

@@ -4113,6 +4138,13 @@ linux_core_of_thread (ptid_t ptid)
return core;
}

+static void
+linux_process_qsupported (const char *query)
+{
+ if (the_low_target.process_qsupported != NULL)
+ the_low_target.process_qsupported (query);
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
@@ -4156,7 +4188,8 @@ static struct target_ops linux_target_ops = {
#else
NULL,
#endif
- linux_core_of_thread
+ linux_core_of_thread,
+ linux_process_qsupported
};

static void
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index d7aa418..52623bf 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -35,6 +35,9 @@ enum regset_type {
struct regset_info
{
int get_request, set_request;
+ /* If NT_TYPE isn't 0, it will be passed to ptrace as the 3rd
+ argument and the 4th argument should be "const struct iovec *". */
+ int nt_type;
int size;
enum regset_type type;
regset_fill_func fill_function;
@@ -111,6 +114,9 @@ struct linux_target_ops

/* Hook to call prior to resuming a thread. */
void (*prepare_to_resume) (struct lwp_info *);
+
+ /* Hook to support target specific qSupported. */
+ void (*process_qsupported) (const char *);
};

extern struct linux_target_ops the_low_target;
diff --git a/gdb/gdbserver/linux-m68k-low.c b/gdb/gdbserver/linux-m68k-low.c
index 14e3864..6c98bb1 100644
--- a/gdb/gdbserver/linux-m68k-low.c
+++ b/gdb/gdbserver/linux-m68k-low.c
@@ -112,14 +112,14 @@ m68k_store_fpregset (struct regcache *regcache, const void *buf)

struct regset_info target_regsets[] = {
#ifdef HAVE_PTRACE_GETREGS
- { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t),
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
GENERAL_REGS,
m68k_fill_gregset, m68k_store_gregset },
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof (elf_fpregset_t),
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, sizeof (elf_fpregset_t),
FP_REGS,
m68k_fill_fpregset, m68k_store_fpregset },
#endif /* HAVE_PTRACE_GETREGS */
- { 0, 0, -1, -1, NULL, NULL }
+ { 0, 0, 0, -1, -1, NULL, NULL }
};

static const unsigned char m68k_breakpoint[] = { 0x4E, 0x4F };
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index 70f6700..1c04b2e 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -343,12 +343,12 @@ mips_store_fpregset (struct regcache *regcache, const void *buf)

struct regset_info target_regsets[] = {
#ifdef HAVE_PTRACE_GETREGS
- { PTRACE_GETREGS, PTRACE_SETREGS, 38 * 8, GENERAL_REGS,
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, 38 * 8, GENERAL_REGS,
mips_fill_gregset, mips_store_gregset },
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 33 * 8, FP_REGS,
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, 33 * 8, FP_REGS,
mips_fill_fpregset, mips_store_fpregset },
#endif /* HAVE_PTRACE_GETREGS */
- { 0, 0, -1, -1, NULL, NULL }
+ { 0, 0, 0, -1, -1, NULL, NULL }
};

struct linux_target_ops the_low_target = {
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index 10a1309..0dab604 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -593,14 +593,14 @@ struct regset_info target_regsets[] = {
fetch them every time, but still fall back to PTRACE_PEEKUSER for the
general registers. Some kernels support these, but not the newer
PPC_PTRACE_GETREGS. */
- { PTRACE_GETVSXREGS, PTRACE_SETVSXREGS, SIZEOF_VSXREGS, EXTENDED_REGS,
+ { PTRACE_GETVSXREGS, PTRACE_SETVSXREGS, 0, SIZEOF_VSXREGS, EXTENDED_REGS,
ppc_fill_vsxregset, ppc_store_vsxregset },
{ PTRACE_GETVRREGS, PTRACE_SETVRREGS, SIZEOF_VRREGS, EXTENDED_REGS,
ppc_fill_vrregset, ppc_store_vrregset },
- { PTRACE_GETEVRREGS, PTRACE_SETEVRREGS, 32 * 4 + 8 + 4, EXTENDED_REGS,
+ { PTRACE_GETEVRREGS, PTRACE_SETEVRREGS, 0, 32 * 4 + 8 + 4, EXTENDED_REGS,
ppc_fill_evrregset, ppc_store_evrregset },
- { 0, 0, 0, GENERAL_REGS, ppc_fill_gregset, NULL },
- { 0, 0, -1, -1, NULL, NULL }
+ { 0, 0, 0, 0, GENERAL_REGS, ppc_fill_gregset, NULL },
+ { 0, 0, 0, -1, -1, NULL, NULL }
};

struct linux_target_ops the_low_target = {
diff --git a/gdb/gdbserver/linux-s390-low.c b/gdb/gdbserver/linux-s390-low.c
index 5460f57..eb865dc 100644
--- a/gdb/gdbserver/linux-s390-low.c
+++ b/gdb/gdbserver/linux-s390-low.c
@@ -181,8 +181,8 @@ static void s390_fill_gregset (struct regcache *regcache, void *buf)
}

struct regset_info target_regsets[] = {
- { 0, 0, 0, GENERAL_REGS, s390_fill_gregset, NULL },
- { 0, 0, -1, -1, NULL, NULL }
+ { 0, 0, 0, 0, GENERAL_REGS, s390_fill_gregset, NULL },
+ { 0, 0, 0, -1, -1, NULL, NULL }
};

diff --git a/gdb/gdbserver/linux-sh-low.c b/gdb/gdbserver/linux-sh-low.c
index 9d27e7f..87a0dd2 100644
--- a/gdb/gdbserver/linux-sh-low.c
+++ b/gdb/gdbserver/linux-sh-low.c
@@ -104,8 +104,8 @@ static void sh_fill_gregset (struct regcache *regcache, void *buf)
}

struct regset_info target_regsets[] = {
- { 0, 0, 0, GENERAL_REGS, sh_fill_gregset, NULL },
- { 0, 0, -1, -1, NULL, NULL }
+ { 0, 0, 0, 0, GENERAL_REGS, sh_fill_gregset, NULL },
+ { 0, 0, 0, -1, -1, NULL, NULL }
};

struct linux_target_ops the_low_target = {
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index 0bb5f2f..e0bfe81 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -260,13 +260,13 @@ sparc_reinsert_addr (void)

struct regset_info target_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t),
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
GENERAL_REGS,
sparc_fill_gregset, sparc_store_gregset },
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof (fpregset_t),
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, sizeof (fpregset_t),
FP_REGS,
sparc_fill_fpregset, sparc_store_fpregset },
- { 0, 0, -1, -1, NULL, NULL }
+ { 0, 0, 0, -1, -1, NULL, NULL }
};

struct linux_target_ops the_low_target = {
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index fe5d46e..fbdbecb 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -24,6 +24,8 @@
#include "linux-low.h"
#include "i387-fp.h"
#include "i386-low.h"
+#include "i386-xstate.h"
+#include "elf/common.h"

#include "gdb_proc_service.h"

@@ -31,10 +33,36 @@
void init_registers_i386_linux (void);
/* Defined in auto-generated file amd64-linux.c. */
void init_registers_amd64_linux (void);
+/* Defined in auto-generated file i386-avx-linux.c. */
+void init_registers_i386_avx_linux (void);
+/* Defined in auto-generated file amd64-avx-linux.c. */
+void init_registers_amd64_avx_linux (void);
+
+/* Backward compatibility for gdb without XML support. */
+
+static const char *xmltarget_amd64_linux_no_xml = "@<target>\
+<architecture>i386:x86-64</architecture>\
+<osabi>GNU/Linux</osabi>\
+</target>";
+static const char *xmltarget_i386_linux_no_xml = "@<target>\
+<architecture>i386</architecture>\
+<osabi>GNU/Linux</osabi>\
+</target>";

#include <sys/reg.h>
#include <sys/procfs.h>
#include <sys/ptrace.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#ifndef PTRACE_GETREGSET
+#define PTRACE_GETREGSET 0x4204
+#endif
+
+#ifndef PTRACE_SETREGSET
+#define PTRACE_SETREGSET 0x4205
+#endif
+

#ifndef PTRACE_GET_THREAD_AREA
#define PTRACE_GET_THREAD_AREA 25
@@ -252,6 +280,18 @@ x86_store_fpxregset (struct regcache *regcache, const void *buf)

#endif

+static void
+x86_fill_xstateregset (struct regcache *regcache, void *buf)
+{
+ i387_cache_to_xsave (regcache, buf);
+}
+
+static void
+x86_store_xstateregset (struct regcache *regcache, const void *buf)
+{
+ i387_xsave_to_cache (regcache, buf);
+}
+
/* ??? The non-biarch i386 case stores all the i387 regs twice.
Once in i387_.*fsave.* and once in i387_.*fxsave.*.
This is, presumably, to handle the case where PTRACE_[GS]ETFPXREGS
@@ -264,21 +304,23 @@ x86_store_fpxregset (struct regcache *regcache, const void *buf)
struct regset_info target_regsets[] =
{
#ifdef HAVE_PTRACE_GETREGS
- { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t),
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
GENERAL_REGS,
x86_fill_gregset, x86_store_gregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_X86_XSTATE, 0,
+ EXTENDED_REGS, x86_fill_xstateregset, x86_store_xstateregset },
# ifndef __x86_64__
# ifdef HAVE_PTRACE_GETFPXREGS
- { PTRACE_GETFPXREGS, PTRACE_SETFPXREGS, sizeof (elf_fpxregset_t),
+ { PTRACE_GETFPXREGS, PTRACE_SETFPXREGS, 0, sizeof (elf_fpxregset_t),
EXTENDED_REGS,
x86_fill_fpxregset, x86_store_fpxregset },
# endif
# endif
- { PTRACE_GETFPREGS, PTRACE_SETFPREGS, sizeof (elf_fpregset_t),
+ { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, sizeof (elf_fpregset_t),
FP_REGS,
x86_fill_fpregset, x86_store_fpregset },
#endif /* HAVE_PTRACE_GETREGS */
- { 0, 0, -1, -1, NULL, NULL }
+ { 0, 0, 0, -1, -1, NULL, NULL }
};

static CORE_ADDR
@@ -776,6 +818,66 @@ x86_siginfo_fixup (struct siginfo *native, void *inf, int direction)
return 0;
}
\f
+/* Process qSupported query, "xmlRegisters=". Update the buffer size for
+ PTRACE_GETREGSET. */
+
+static void
+x86_linux_process_qsupported (const char *query)
+{
+ uint64_t xstateregs[I386_XSTATE_SSE_SIZE / sizeof (uint64_t)];
+ struct iovec iov;
+
+ /* Return if gdb doesn't support XML. If gdb sends "xmlRegisters="
+ in qSupported query, it supports x86 XML target descriptions. */
+ if (strncmp (query, "xmlRegisters=", 13) != 0)
+ return;
+
+ /* Update gdbserver_xmltarget with XML support. */
+ if (num_xmm_registers == 8)
+ gdbserver_xmltarget = "i386-linux.xml";
+ else
+ gdbserver_xmltarget = "amd64-linux.xml";
+
+ /* Check if XSAVE extended state is supported. */
+ iov.iov_base = xstateregs;
+ iov.iov_len = sizeof (xstateregs);
+
+ /* Check if PTRACE_GETREGSET works. */
+ if (ptrace (PTRACE_GETREGSET, getpid (),
+ (unsigned int) NT_X86_XSTATE, &iov) == 0)
+ {
+ struct regset_info *regset;
+ unsigned long long xcr0;
+
+ /* Get XCR0 from XSAVE extended state at byte 464. */
+ xcr0 = xstateregs[464 / sizeof (long long)];
+
+ /* Use PTRACE_GETREGSET if it is available. */
+ for (regset = target_regsets;
+ regset->fill_function != NULL; regset++)
+ if (regset->get_request == PTRACE_GETREGSET)
+ regset->size = I386_XSTATE_SIZE (xcr0);
+ else if (regset->type != GENERAL_REGS)
+ regset->size = 0;
+
+ /* AVX is the highest feature we support. */
+ if ((xcr0 & I386_XSTATE_AVX_MASK) == I386_XSTATE_AVX_MASK)
+ {
+ x86_xcr0 = xcr0;
+
+#ifdef __x86_64__
+ /* I386 has 8 xmm regs. */
+ if (num_xmm_registers == 8)
+ init_registers_i386_avx_linux ();
+ else
+ init_registers_amd64_avx_linux ();
+#else
+ init_registers_i386_avx_linux ();
+#endif
+ }
+ }
+}
+
/* Initialize gdbserver for the architecture of the inferior. */

static void
@@ -798,6 +900,10 @@ x86_arch_setup (void)
{
init_registers_amd64_linux ();

+ /* Assume that gdb doesn't support XML. We will update it in
+ x86_linux_process_qsupported if gdb does support XML. */
+ gdbserver_xmltarget = xmltarget_amd64_linux_no_xml;
+
/* Amd64 doesn't have HAVE_LINUX_USRREGS. */
the_low_target.num_regs = -1;
the_low_target.regmap = NULL;
@@ -815,6 +921,10 @@ x86_arch_setup (void)

init_registers_i386_linux ();

+ /* Assume that gdb doesn't support XML. We will update it in
+ x86_linux_process_qsupported if gdb does support XML. */
+ gdbserver_xmltarget = xmltarget_i386_linux_no_xml;
+
the_low_target.num_regs = I386_NUM_REGS;
the_low_target.regmap = i386_regmap;
the_low_target.cannot_fetch_register = i386_cannot_fetch_register;
@@ -854,5 +964,6 @@ struct linux_target_ops the_low_target =
x86_siginfo_fixup,
x86_linux_new_process,
x86_linux_new_thread,
- x86_linux_prepare_to_resume
+ x86_linux_prepare_to_resume,
+ x86_linux_process_qsupported
};
diff --git a/gdb/gdbserver/linux-xtensa-low.c b/gdb/gdbserver/linux-xtensa-low.c
index c5ed351..8d0e73a 100644
--- a/gdb/gdbserver/linux-xtensa-low.c
+++ b/gdb/gdbserver/linux-xtensa-low.c
@@ -131,13 +131,13 @@ xtensa_store_xtregset (struct regcache *regcache, const void *buf)
}

struct regset_info target_regsets[] = {
- { PTRACE_GETREGS, PTRACE_SETREGS, sizeof (elf_gregset_t),
+ { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
GENERAL_REGS,
xtensa_fill_gregset, xtensa_store_gregset },
- { PTRACE_GETXTREGS, PTRACE_SETXTREGS, XTENSA_ELF_XTREG_SIZE,
+ { PTRACE_GETXTREGS, PTRACE_SETXTREGS, 0, XTENSA_ELF_XTREG_SIZE,
EXTENDED_REGS,
xtensa_fill_xtregset, xtensa_store_xtregset },
- { 0, 0, -1, -1, NULL, NULL }
+ { 0, 0, 0, -1, -1, NULL, NULL }
};

#if XCHAL_HAVE_BE
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 232085a..e596b33 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1292,6 +1292,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_multi_process ())
multi_process = 1;
}
+ else
+ target_process_qsupported (p);
}

sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1);
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index ac68652..6109b1c 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -286,6 +286,9 @@ struct target_ops

/* Returns the core given a thread, or -1 if not known. */
int (*core_of_thread) (ptid_t);
+
+ /* Target specific qSupported support. */
+ void (*process_qsupported) (const char *);
};

extern struct target_ops *the_target;
@@ -326,6 +329,10 @@ void set_target_ops (struct target_ops *);
(the_target->supports_multi_process ? \
(*the_target->supports_multi_process) () : 0)

+#define target_process_qsupported(query) \
+ if (the_target->process_qsupported) \
+ the_target->process_qsupported (query)
+
/* Start non-stop mode, returns 0 on success, -1 on failure. */

int start_non_stop (int nonstop);