Index: contrib/binutils/binutils/readelf.c =================================================================== --- contrib/binutils/binutils/readelf.c +++ contrib/binutils/binutils/readelf.c @@ -9159,6 +9159,8 @@ return _("NT_PROCSTAT_PSSTRINGS (ps_strings data)"); case NT_PROCSTAT_AUXV: return _("NT_PROCSTAT_AUXV (auxv data)"); + case NT_X86_XSTATE: + return _("NT_X86_XSTATE (x86 XSAVE extended state)"); default: return get_note_type(e_type); } Index: contrib/binutils/include/elf/common.h =================================================================== --- contrib/binutils/include/elf/common.h +++ contrib/binutils/include/elf/common.h @@ -414,6 +414,7 @@ #define NT_PROCSTAT_OSREL 14 #define NT_PROCSTAT_PSSTRINGS 15 #define NT_PROCSTAT_AUXV 16 +#define NT_X86_XSTATE 0x202 /* Note segments for core files on NetBSD systems. Note name Index: sys/amd64/amd64/elf_machdep.c =================================================================== --- sys/amd64/amd64/elf_machdep.c +++ sys/amd64/amd64/elf_machdep.c @@ -44,6 +44,7 @@ #include #include +#include #include struct sysentvec elf64_freebsd_sysvec = { @@ -133,11 +134,34 @@ &kfreebsd_brand_info); void -elf64_dump_thread(struct thread *td __unused, void *dst __unused, - size_t *off __unused) +elf64_dump_thread(struct thread *td, void *dst, size_t *off) { -} + char *buf, *savefpu; + size_t len; + + len = 0; + buf = dst; + if (use_xsave) { + if (buf != NULL) { + fpugetregs(td); + savefpu = (char *)get_pcb_user_save_td(td); + /* + * The thread should not use the FPU again since + * it is dumping core, so it is ok to modify the + * saved state in the PCB in-place. + */ + *(uint64_t *)(savefpu + X86_XSTATE_XCR0_OFFSET) = + xsave_mask; + len += elf64_populate_note(NT_X86_XSTATE, savefpu, + buf, cpu_max_ext_state_size); + buf += len; + } else + len += elf64_populate_note(NT_X86_XSTATE, NULL, NULL, + cpu_max_ext_state_size); + } + *off = len; +} /* Process one elf relocation with addend. */ static int Index: sys/amd64/amd64/fpu.c =================================================================== --- sys/amd64/amd64/fpu.c +++ sys/amd64/amd64/fpu.c @@ -127,6 +127,13 @@ */ CTASSERT(sizeof(struct pcb) % XSAVE_AREA_ALIGN == 0); +/* + * Ensure the copy of XCR0 saved in a core is contained in the padding + * area. + */ +CTASSERT(X86_XSTATE_XCR0_OFFSET >= offsetof(struct savefpu, sv_pad) && + X86_XSTATE_XCR0_OFFSET + sizeof(uint64_t) <= sizeof(struct savefpu)); + static void fpu_clean_state(void); SYSCTL_INT(_hw, HW_FLOATINGPT, floatingpoint, CTLFLAG_RD, Index: sys/amd64/amd64/ptrace_machdep.c =================================================================== --- sys/amd64/amd64/ptrace_machdep.c +++ sys/amd64/amd64/ptrace_machdep.c @@ -49,14 +49,14 @@ return (EOPNOTSUPP); switch (req) { - case PT_GETXSTATE: + case PT_GETXSTATE_OLD: fpugetregs(td); savefpu = (char *)(get_pcb_user_save_td(td) + 1); error = copyout(savefpu, addr, cpu_max_ext_state_size - sizeof(struct savefpu)); break; - case PT_SETXSTATE: + case PT_SETXSTATE_OLD: if (data > cpu_max_ext_state_size - sizeof(struct savefpu)) { error = EINVAL; break; @@ -70,6 +70,32 @@ free(savefpu, M_TEMP); break; + case PT_GETXSTATE_INFO: + error = copyout(&xsave_mask, addr, sizeof(xsave_mask)); + if (error == 0) + curthread->td_retval[0] = cpu_max_ext_state_size; + break; + + case PT_GETXSTATE: + fpugetregs(td); + savefpu = (char *)(get_pcb_user_save_td(td)); + error = copyout(savefpu, addr, cpu_max_ext_state_size); + break; + + case PT_SETXSTATE: + if (data > cpu_max_ext_state_size) { + error = EINVAL; + break; + } + savefpu = malloc(data, M_TEMP, M_WAITOK); + error = copyin(addr, savefpu, data); + if (error == 0) + error = fpusetregs(td, (struct savefpu *)savefpu, + savefpu + sizeof(struct savefpu), data - + sizeof(struct savefpu)); + free(savefpu, M_TEMP); + break; + default: error = EINVAL; break; @@ -81,8 +107,6 @@ #ifdef COMPAT_FREEBSD32 #define PT_I386_GETXMMREGS (PT_FIRSTMACH + 0) #define PT_I386_SETXMMREGS (PT_FIRSTMACH + 1) -#define PT_I386_GETXSTATE (PT_FIRSTMACH + 2) -#define PT_I386_SETXSTATE (PT_FIRSTMACH + 3) static int cpu32_ptrace(struct thread *td, int req, void *addr, int data) @@ -104,12 +128,12 @@ fpstate->sv_env.en_mxcsr &= cpu_mxcsr_mask; break; - case PT_I386_GETXSTATE: - error = cpu_ptrace_xstate(td, PT_GETXSTATE, addr, data); - break; - - case PT_I386_SETXSTATE: - error = cpu_ptrace_xstate(td, PT_SETXSTATE, addr, data); + case PT_GETXSTATE_OLD: + case PT_SETXSTATE_OLD: + case PT_GETXSTATE_INFO: + case PT_GETXSTATE: + case PT_SETXSTATE: + error = cpu_ptrace_xstate(td, req, addr, data); break; default: @@ -131,13 +155,16 @@ return (cpu32_ptrace(td, req, addr, data)); #endif - /* Support old values of PT_GETXSTATE and PT_SETXSTATE. */ + /* Support old values of PT_GETXSTATE_OLD and PT_SETXSTATE_OLD. */ if (req == PT_FIRSTMACH + 0) - req = PT_GETXSTATE; + req = PT_GETXSTATE_OLD; if (req == PT_FIRSTMACH + 1) - req = PT_SETXSTATE; + req = PT_SETXSTATE_OLD; switch (req) { + case PT_GETXSTATE_OLD: + case PT_SETXSTATE_OLD: + case PT_GETXSTATE_INFO: case PT_GETXSTATE: case PT_SETXSTATE: error = cpu_ptrace_xstate(td, req, addr, data); Index: sys/compat/ia32/ia32_sysvec.c =================================================================== --- sys/compat/ia32/ia32_sysvec.c +++ sys/compat/ia32/ia32_sysvec.c @@ -187,9 +187,33 @@ &kia32_brand_info); void -elf32_dump_thread(struct thread *td __unused, void *dst __unused, - size_t *off __unused) +elf32_dump_thread(struct thread *td, void *dst, size_t *off) { + char *buf, *savefpu; + size_t len; + + len = 0; + buf = dst; + if (use_xsave) { + if (buf != NULL) { + fpugetregs(td); + savefpu = (char *)get_pcb_user_save_td(td); + + /* + * The thread should not use the FPU again since + * it is dumping core, so it is ok to modify the + * saved state in the PCB in-place. + */ + *(uint64_t *)(savefpu + X86_XSTATE_XCR0_OFFSET) = + xsave_mask; + len += elf32_populate_note(NT_X86_XSTATE, savefpu, + buf, cpu_max_ext_state_size); + buf += len; + } else + len += elf32_populate_note(NT_X86_XSTATE, NULL, NULL, + cpu_max_ext_state_size); + } + *off = len; } void Index: sys/i386/i386/elf_machdep.c =================================================================== --- sys/i386/i386/elf_machdep.c +++ sys/i386/i386/elf_machdep.c @@ -45,6 +45,7 @@ #include #include +#include struct sysentvec elf32_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, @@ -134,11 +135,34 @@ void -elf32_dump_thread(struct thread *td __unused, void *dst __unused, - size_t *off __unused) +elf32_dump_thread(struct thread *td, void *dst, size_t *off) { -} + char *buf, *savefpu; + size_t len; + + len = 0; + buf = dst; + if (use_xsave) { + if (buf != NULL) { + npxgetregs(td); + savefpu = (char *)get_pcb_user_save_td(td); + /* + * The thread should not use the FPU again since + * it is dumping core, so it is ok to modify the + * saved state in the PCB in-place. + */ + *(uint64_t *)(savefpu + X86_XSTATE_XCR0_OFFSET) = + xsave_mask; + len += elf32_populate_note(NT_X86_XSTATE, savefpu, + buf, cpu_max_ext_state_size); + buf += len; + } else + len += elf32_populate_note(NT_X86_XSTATE, NULL, NULL, + cpu_max_ext_state_size); + } + *off = len; +} /* Process one elf relocation with addend. */ static int Index: sys/i386/i386/ptrace_machdep.c =================================================================== --- sys/i386/i386/ptrace_machdep.c +++ sys/i386/i386/ptrace_machdep.c @@ -53,14 +53,14 @@ return (EOPNOTSUPP); switch (req) { - case PT_GETXSTATE: + case PT_GETXSTATE_OLD: npxgetregs(td); savefpu = (char *)(get_pcb_user_save_td(td) + 1); error = copyout(savefpu, addr, cpu_max_ext_state_size - sizeof(union savefpu)); break; - case PT_SETXSTATE: + case PT_SETXSTATE_OLD: if (data > cpu_max_ext_state_size - sizeof(union savefpu)) { error = EINVAL; break; @@ -74,6 +74,32 @@ free(savefpu, M_TEMP); break; + case PT_GETXSTATE_INFO: + error = copyout(&xsave_mask, addr, sizeof(xsave_mask)); + if (error == 0) + curthread->td_retval[0] = cpu_max_ext_state_size; + break; + + case PT_GETXSTATE: + npxgetregs(td); + savefpu = (char *)(get_pcb_user_save_td(td)); + error = copyout(savefpu, addr, cpu_max_ext_state_size); + break; + + case PT_SETXSTATE: + if (data > cpu_max_ext_state_size) { + error = EINVAL; + break; + } + savefpu = malloc(data, M_TEMP, M_WAITOK); + error = copyin(addr, savefpu, data); + if (error == 0) + error = npxsetregs(td, (union savefpu *)savefpu, + savefpu + sizeof(union savefpu), data - + sizeof(union savefpu)); + free(savefpu, M_TEMP); + break; + default: error = EINVAL; break; @@ -106,6 +132,9 @@ fpstate->sv_env.en_mxcsr &= cpu_mxcsr_mask; break; + case PT_GETXSTATE_OLD: + case PT_SETXSTATE_OLD: + case PT_GETXSTATE_INFO: case PT_GETXSTATE: case PT_SETXSTATE: error = cpu_ptrace_xstate(td, req, addr, data); Index: sys/i386/isa/npx.c =================================================================== --- sys/i386/isa/npx.c +++ sys/i386/isa/npx.c @@ -201,6 +201,13 @@ */ CTASSERT(sizeof(struct pcb) % XSAVE_AREA_ALIGN == 0); +/* + * Ensure the copy of XCR0 saved in a core is contained in the padding + * area. + */ +CTASSERT(X86_XSTATE_XCR0_OFFSET >= offsetof(struct savexmm, sv_pad) && + X86_XSTATE_XCR0_OFFSET + sizeof(uint64_t) <= sizeof(struct savexmm)); + static void fpu_clean_state(void); #endif Index: sys/kern/imgact_elf.c =================================================================== --- sys/kern/imgact_elf.c +++ sys/kern/imgact_elf.c @@ -1593,6 +1593,44 @@ return (notesize); } +static size_t +append_note_data(void *src, void *dst, size_t len) +{ + size_t padded_len; + + padded_len = roundup2(len, ELF_NOTE_ROUNDSIZE); + if (dst != NULL) { + bcopy(src, dst, len); + bzero((char *)dst + len, padded_len - len); + } + return (padded_len); +} + +size_t +__elfN(populate_note)(int type, void *src, void *dst, size_t size) +{ + Elf_Note *note; + char *buf; + size_t notesize; + + buf = dst; + if (buf != NULL) { + note = (Elf_Note *)buf; + note->n_namesz = 8; /* strlen("FreeBSD") + 1 */ + note->n_descsz = size; + note->n_type = type; + buf += sizeof(*note); + buf += append_note_data("FreeBSD", buf, strlen("FreeBSD") + 1); + append_note_data(src, buf, size); + } + + notesize = sizeof(Elf_Note) + /* note header */ + roundup2(8, ELF_NOTE_ROUNDSIZE) + /* note name ("FreeBSD") */ + roundup2(size, ELF_NOTE_ROUNDSIZE); /* note description */ + + return (notesize); +} + static void __elfN(putnote)(struct note_info *ninfo, struct sbuf *sb) { Index: sys/sys/elf_common.h =================================================================== --- sys/sys/elf_common.h +++ sys/sys/elf_common.h @@ -508,6 +508,7 @@ #define NT_PROCSTAT_OSREL 14 /* Procstat osreldate data. */ #define NT_PROCSTAT_PSSTRINGS 15 /* Procstat ps_strings data. */ #define NT_PROCSTAT_AUXV 16 /* Procstat auxv data. */ +#define NT_X86_XSTATE 0x202 /* x86 XSAVE extended state. */ /* Symbol Binding - ELFNN_ST_BIND - st_info */ #define STB_LOCAL 0 /* Local symbol */ Index: sys/sys/imgact_elf.h =================================================================== --- sys/sys/imgact_elf.h +++ sys/sys/imgact_elf.h @@ -90,6 +90,7 @@ int __elfN(remove_brand_entry)(Elf_Brandinfo *entry); int __elfN(freebsd_fixup)(register_t **, struct image_params *); int __elfN(coredump)(struct thread *, struct vnode *, off_t, int); +size_t __elfN(populate_note)(int, void *, void *, size_t); /* Machine specific function to dump per-thread information. */ void __elfN(dump_thread)(struct thread *, void *, size_t *); Index: sys/x86/include/fpu.h =================================================================== --- sys/x86/include/fpu.h +++ sys/x86/include/fpu.h @@ -63,15 +63,7 @@ struct env87 sv_env; /* floating point control/status */ struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */ uint8_t sv_pad0[4]; /* saved status word (now unused) */ - /* - * Bogus padding for emulators. Emulators should use their own - * struct and arrange to store into this struct (ending here) - * before it is inspected for ptracing or for core dumps. Some - * emulators overwrite the whole struct. We have no good way of - * knowing how much padding to leave. Leave just enough for the - * GPL emulator's i387_union (176 bytes total). - */ - uint8_t sv_pad[64]; /* padding; used by emulators */ + uint8_t sv_pad[64]; }; /* Contents of each SSE extended accumulator. */ @@ -215,4 +207,11 @@ #define __INITIAL_MXCSR__ 0x1F80 #define __INITIAL_MXCSR_MASK__ 0xFFBF +/* + * The current value of %xcr0 is saved in the sv_pad[] field of the FPU + * state in the NT_X86_XSTATE note in core dumps. This offset is chosen + * to match the offset used by NT_X86_XSTATE in other systems. + */ +#define X86_XSTATE_XCR0_OFFSET 464 + #endif /* !_X86_FPU_H_ */ Index: sys/x86/include/ptrace.h =================================================================== --- sys/x86/include/ptrace.h +++ sys/x86/include/ptrace.h @@ -37,14 +37,19 @@ /* * On amd64 (PT_FIRSTMACH + 0) and (PT_FIRSTMACH + 1) are old values for - * PT_GETXSTATE and PT_SETXSTATE. They should not be (re)used. + * PT_GETXSTATE_OLD and PT_SETXSTATE_OLD. They should not be (re)used. */ #ifdef __i386__ #define PT_GETXMMREGS (PT_FIRSTMACH + 0) #define PT_SETXMMREGS (PT_FIRSTMACH + 1) #endif -#define PT_GETXSTATE (PT_FIRSTMACH + 2) -#define PT_SETXSTATE (PT_FIRSTMACH + 3) +#ifdef _KERNEL +#define PT_GETXSTATE_OLD (PT_FIRSTMACH + 2) +#define PT_SETXSTATE_OLD (PT_FIRSTMACH + 3) +#endif +#define PT_GETXSTATE_INFO (PT_FIRSTMACH + 4) +#define PT_GETXSTATE (PT_FIRSTMACH + 5) +#define PT_SETXSTATE (PT_FIRSTMACH + 6) #endif Index: usr.bin/gcore/elfcore.c =================================================================== --- usr.bin/gcore/elfcore.c +++ usr.bin/gcore/elfcore.c @@ -101,6 +101,9 @@ static void *elf_note_prpsinfo(void *, size_t *); static void *elf_note_prstatus(void *, size_t *); static void *elf_note_thrmisc(void *, size_t *); +#if defined(__i386__) || defined(__amd64__) +static void *elf_note_x86_xstate(void *, size_t *); +#endif static void *elf_note_procstat_auxv(void *, size_t *); static void *elf_note_procstat_files(void *, size_t *); static void *elf_note_procstat_groups(void *, size_t *); @@ -341,6 +344,9 @@ elf_putnote(NT_PRSTATUS, elf_note_prstatus, tids + i, sb); elf_putnote(NT_FPREGSET, elf_note_fpregset, tids + i, sb); elf_putnote(NT_THRMISC, elf_note_thrmisc, tids + i, sb); +#if defined(__i386__) || defined(__amd64__) + elf_putnote(NT_X86_XSTATE, elf_note_x86_xstate, tids + i, sb); +#endif } #ifndef ELFCORE_COMPAT_32 @@ -615,6 +621,33 @@ return (thrmisc); } +#if defined(__i386__) || defined(__amd64__) +static void * +elf_note_x86_xstate(void *arg, size_t *sizep) +{ + lwpid_t tid; + char *xstate; + static int xstate_len = -1; + static uint64_t xcr0; + + tid = *(lwpid_t *)arg; + if (xstate_len == -1) { + xstate_len = ptrace(PT_GETXSTATE_INFO, tid, (void *)&xcr0, 0); + if (xstate_len == -1) + xstate_len = 0; + } + if (xstate_len == 0) { + *sizep = 0; + return (NULL); + } + xstate = calloc(1, xstate_len); + ptrace(PT_GETXSTATE, tid, xstate, 0); + *(uint64_t *)(xstate + X86_XSTATE_XCR0_OFFSET) = xcr0; + *sizep = xstate_len; + return (xstate); +} +#endif + static void * procstat_sysctl(void *arg, int what, size_t structsz, size_t *sizep) {