From: H.J. Lu Date: Wed, 7 Apr 2010 18:49:46 +0000 (+0000) Subject: Add x86 AVX support to gdbserver. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=1570b33e44a894b781674d0906a733584ad780e0;p=binutils-gdb.git Add x86 AVX support to gdbserver. 2010-04-07 H.J. Lu * 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 . (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 . (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/ChangeLog b/gdb/gdbserver/ChangeLog index 1102e126114..8a26b272731 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,79 @@ +2010-04-07 H.J. Lu + + * 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 . + (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 . + (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. + 2010-04-03 Pedro Alves * inferiors.c (add_thread): Set last_status kind to diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index 7feccedd43c..2ec97849385 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 f7c80bde5eb..8bc9aeb5ef7 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 7ef4ba3b7a1..54610226633 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 d1e068182fe..ed1a3222f0c 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 54668f83793..32bd7bbf555 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 6ba48b64a7b..d426c326141 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 38af9d0beb8..f1592445e43 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -39,6 +39,7 @@ #include #include #include +#include #ifndef ELFMAG0 /* Don't include here. If it got included by gdb_proc_service.h then ELFMAG0 will have been defined. If it didn't get included by @@ -2977,14 +2978,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]) { @@ -2993,10 +2995,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) { @@ -3034,14 +3047,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]) { @@ -3054,10 +3068,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) @@ -3067,9 +3092,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 } @@ -4133,6 +4158,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, @@ -4176,7 +4208,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 d7aa41876b1..52623bf6d00 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 14e3864ab7b..6c98bb17eeb 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 70f67007075..1c04b2e89e2 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 10a1309e390..000b20fbf1d 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, + { PTRACE_GETVRREGS, PTRACE_SETVRREGS, 0, 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 5460f571646..eb865dc35cc 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 9d27e7f9063..87a0dd2cf58 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 0bb5f2fd5a7..e0bfe81d201 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 37fe60f24a5..3853b2513e6 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 = "@\ +i386\ +GNU/Linux\ +"; +static const char *xmltarget_amd64_linux_no_xml = "@\ +i386:x86-64\ +GNU/Linux\ +"; #include #include #include +#include + +#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 @@ -780,6 +821,128 @@ x86_siginfo_fixup (struct siginfo *native, void *inf, int direction) return 0; } +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; + } + + /* 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=" + with "i386" in qSupported query, it supports x86 XML target + descriptions. */ + use_xml = 0; + if (query != NULL && strncmp (query, "xmlRegisters=", 13) == 0) + { + char *copy = xstrdup (query + 13); + char *p; + + for (p = strtok (copy, ","); p != NULL; p = strtok (NULL, ",")) + { + if (strcmp (p, "i386") == 0) + { + use_xml = 1; + break; + } + } + + free (copy); + } + + x86_linux_update_xmltarget (); +} + /* Initialize gdbserver for the architecture of the inferior. */ static void @@ -800,8 +963,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; @@ -811,14 +972,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; @@ -826,6 +986,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. @@ -858,5 +1020,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 c5ed3516ba5..8d0e73ad778 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 c6fc0059db2..568640e31f8 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -1289,6 +1289,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 ';'. */ @@ -1304,6 +1307,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 ac686520c63..6109b1caa8d 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);