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

H.J. Lu
2010-03-30T16:48:47Z

On Sun, Mar 28, 2010 at 06:09:35PM -0700, H.J. Lu wrote:
> Hi,
>
> Here are gdbserver changes to support AVX. OK to install?
>
> Thanks.
>

Here is the updated gdbserver change. I tested it with

# gdbserver --multi host:10000

connecting from

1. gdb without AVX support.
2. gdb with AVX support,
3. gdb without XML support.

to debug 32bit and 64bit binaries. Everything works correctly.
OK to install?

Thanks.

H.J.
--
2010-03-30 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"
and <sys/uio.h>.
(init_registers_i386_avx_linux): New.
(init_registers_amd64_avx_linux): Likewise.
(xmltarget_i386_linux_no_xml): Likewise.
(xmltarget_amd64_linux_no_xml): Likewise.
(PTRACE_GETREGSET): Likewise.
(PTRACE_SETREGSET): Likewise.
(x86_fill_xstateregset): Likewise.
(x86_store_xstateregset): Likewise.
(use_xml): Likewise.
(x86_linux_update_xmltarget): Likewise.
(x86_linux_process_qsupported): Likewise.
(target_regsets): Add NT_X86_XSTATE entry and Initialize nt_type.
(x86_arch_setup): Don't call init_registers_amd64_linux nor
init_registers_i386_linux here. Call
x86_linux_update_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..5d75365 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,35 @@
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_i386_linux_no_xml = "@<target>\
+<architecture>i386</architecture>\
+<osabi>GNU/Linux</osabi>\
+</target>";
+static const char *xmltarget_amd64_linux_no_xml = "@<target>\
+<architecture>i386:x86-64</architecture>\
+<osabi>GNU/Linux</osabi>\
+</target>";

#include <sys/reg.h>
#include <sys/procfs.h>
#include <sys/ptrace.h>
+#include <sys/uio.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 +279,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 +303,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 +817,121 @@ x86_siginfo_fixup (struct siginfo *native, void *inf, int direction)
return 0;
}
\f
+static int use_xml;
+
+/* Update gdbserver_xmltarget. */
+
+static void
+x86_linux_update_xmltarget (void)
+{
+ static unsigned long long xcr0;
+ static int have_ptrace_getregset = -1;
+
+ if (!current_inferior)
+ return;
+
+#ifdef __x86_64__
+ if (num_xmm_registers == 8)
+ init_registers_i386_linux ();
+ else
+ init_registers_amd64_linux ();
+#else
+ init_registers_i386_linux ();
+#endif
+
+ if (!use_xml)
+ {
+ /* Don't use XML. */
+#ifdef __x86_64__
+ if (num_xmm_registers == 8)
+ gdbserver_xmltarget = xmltarget_i386_linux_no_xml;
+ else
+ gdbserver_xmltarget = xmltarget_amd64_linux_no_xml;
+#else
+ gdbserver_xmltarget = xmltarget_i386_linux_no_xml;
+#endif
+
+ x86_xcr0 = I386_XSTATE_SSE_MASK;
+
+ return;
+ }
+
+ /* Update gdbserver_xmltarget with XML support. */
+#ifdef __x86_64__
+ if (num_xmm_registers == 8)
+ gdbserver_xmltarget = "i386-linux.xml";
+ else
+ gdbserver_xmltarget = "amd64-linux.xml";
+#else
+ gdbserver_xmltarget = "i386-linux.xml";
+#endif
+
+ /* Check if XSAVE extended state is supported. */
+ if (have_ptrace_getregset == -1)
+ {
+ int pid = pid_of (get_thread_lwp (current_inferior));
+ unsigned long long xstateregs[I386_XSTATE_SSE_SIZE / sizeof (long long)];
+ struct iovec iov;
+ struct regset_info *regset;
+
+ iov.iov_base = xstateregs;
+ iov.iov_len = sizeof (xstateregs);
+
+ /* Check if PTRACE_GETREGSET works. */
+ if (ptrace (PTRACE_GETREGSET, pid, (unsigned int) NT_X86_XSTATE,
+ &iov) < 0)
+ {
+ have_ptrace_getregset = 0;
+ return;
+ }
+ else
+ have_ptrace_getregset = 1;
+
+ /* 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;
+ }
+
+ if (have_ptrace_getregset)
+ {
+ /* 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
+ }
+ }
+}
+
+/* Process qSupported query, "xmlRegisters=". Update the buffer size for
+ PTRACE_GETREGSET. */
+
+static void
+x86_linux_process_qsupported (const char *query)
+{
+ /* Return if gdb doesn't support XML. If gdb sends "xmlRegisters="
+ in qSupported query, it supports x86 XML target descriptions. */
+ use_xml = query != NULL && strncmp (query, "xmlRegisters=", 13) == 0;
+
+ x86_linux_update_xmltarget ();
+}
+
/* Initialize gdbserver for the architecture of the inferior. */

static void
@@ -796,8 +952,6 @@ x86_arch_setup (void)
}
else if (use_64bit)
{
- init_registers_amd64_linux ();
-
/* Amd64 doesn't have HAVE_LINUX_USRREGS. */
the_low_target.num_regs = -1;
the_low_target.regmap = NULL;
@@ -807,14 +961,13 @@ x86_arch_setup (void)
/* Amd64 has 16 xmm regs. */
num_xmm_registers = 16;

+ x86_linux_update_xmltarget ();
return;
}
#endif

/* Ok we have a 32-bit inferior. */

- init_registers_i386_linux ();
-
the_low_target.num_regs = I386_NUM_REGS;
the_low_target.regmap = i386_regmap;
the_low_target.cannot_fetch_register = i386_cannot_fetch_register;
@@ -822,6 +975,8 @@ x86_arch_setup (void)

/* I386 has 8 xmm regs. */
num_xmm_registers = 8;
+
+ x86_linux_update_xmltarget ();
}

/* This is initialized assuming an amd64 target.
@@ -854,5 +1009,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..1bdd6a6 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1277,6 +1277,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
{
char *p = &own_buf[10];

+ /* Start processing qSupported packet. */
+ target_process_qsupported (NULL);
+
/* Process each feature being provided by GDB. The first
feature will follow a ':', and latter features will follow
';'. */
@@ -1292,6 +1295,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);