* [PATCH 0/3] linux-user: write the x86 floating-point registers to a core dump
@ 2026-09-10 1:34 Matt Turner
2026-09-10 1:34 ` [PATCH 1/3] target/i386: split the FNINIT out of do_fsave() Matt Turner
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Matt Turner @ 2026-09-10 1:34 UTC (permalink / raw)
To: qemu-devel
Cc: Helge Deller, Laurent Vivier, Paolo Bonzini, Pierrick Bouvier,
Richard Henderson, Zhao Liu
Guest cores for i386 and x86_64 carried only the general-purpose
registers, so a debugger opening one reported the x87 stack, and on
x86_64 the SSE registers too, as unavailable.
These add HAVE_ELF_CORE_FPREGS for both, following the same shape as the
recent alpha, mips, riscv and sh4 conversions. x86_64 can reuse
cpu_x86_fxsave() as-is; i386 needs the FSAVE image without the FNINIT
that FSAVE performs afterward, so the first patch splits that out.
The SSE registers are still missing from an i386 core: the kernel puts
those in a separate NT_PRXFPREG note, which the generic core dump code
has no support for.
Matt Turner (3):
target/i386: split the FNINIT out of do_fsave()
linux-user/i386: write the floating-point registers to a core dump
linux-user/x86_64: write the floating-point registers to a core dump
linux-user/i386/elfload.c | 7 +++++++
linux-user/i386/target_elf.h | 18 ++++++++++++++++++
linux-user/x86_64/elfload.c | 11 +++++++++++
linux-user/x86_64/target_elf.h | 20 ++++++++++++++++++++
target/i386/cpu.h | 2 ++
target/i386/tcg/fpu_helper.c | 13 +++++++++++--
6 files changed, 69 insertions(+), 2 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/3] target/i386: split the FNINIT out of do_fsave()
2026-09-10 1:34 [PATCH 0/3] linux-user: write the x86 floating-point registers to a core dump Matt Turner
@ 2026-09-10 1:34 ` Matt Turner
2026-09-10 1:34 ` [PATCH 2/3] linux-user/i386: write the floating-point registers to a core dump Matt Turner
2026-09-10 1:34 ` [PATCH 3/3] linux-user/x86_64: " Matt Turner
2 siblings, 0 replies; 4+ messages in thread
From: Matt Turner @ 2026-09-10 1:34 UTC (permalink / raw)
To: qemu-devel
Cc: Helge Deller, Laurent Vivier, Paolo Bonzini, Pierrick Bouvier,
Richard Henderson, Zhao Liu
do_fsave() ends with an FNINIT, which is what the FSAVE instruction and
the signal frame setup both want, but it makes the helper unusable for a
caller that only wants to read the state out: recording the registers
would destroy them.
Move the FNINIT into helper_fsave() and cpu_x86_fsave(), and expose the
store alone as cpu_x86_fsave_noinit(). No functional change; the next
patch uses the new entry point to write the x87 registers to a guest
core dump.
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
target/i386/cpu.h | 2 ++
target/i386/tcg/fpu_helper.c | 13 +++++++++++--
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git ./target/i386/cpu.h ./target/i386/cpu.h
index 9ce8ca0038..2a543ee808 100644
--- ./target/i386/cpu.h
+++ ./target/i386/cpu.h
@@ -2717,6 +2717,8 @@ int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector,
*/
void cpu_x86_load_seg(CPUX86State *s, X86Seg seg_reg, int selector);
void cpu_x86_fsave(CPUX86State *s, void *host, size_t len);
+/* As cpu_x86_fsave(), but leaving the FPU state undisturbed. */
+void cpu_x86_fsave_noinit(CPUX86State *s, void *host, size_t len);
void cpu_x86_frstor(CPUX86State *s, void *host, size_t len);
void cpu_x86_fxsave(CPUX86State *s, void *host, size_t len);
void cpu_x86_fxrstor(CPUX86State *s, void *host, size_t len);
diff --git ./target/i386/tcg/fpu_helper.c ./target/i386/tcg/fpu_helper.c
index b812125efa..f7cf4220ff 100644
--- ./target/i386/tcg/fpu_helper.c
+++ ./target/i386/tcg/fpu_helper.c
@@ -2536,6 +2536,10 @@ void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32)
do_fldenv(&ac, ptr, data32);
}
+/*
+ * Store the environment and the register stack, as FSAVE does, but
+ * without the FNINIT that FSAVE performs afterward.
+ */
static void do_fsave(X86Access *ac, target_ulong ptr, int data32)
{
CPUX86State *env = ac->env;
@@ -2548,8 +2552,6 @@ static void do_fsave(X86Access *ac, target_ulong ptr, int data32)
do_fstt(ac, ptr, tmp);
ptr += 10;
}
-
- do_fninit(env);
}
void helper_fsave(CPUX86State *env, target_ulong ptr, int data32)
@@ -2559,6 +2561,7 @@ void helper_fsave(CPUX86State *env, target_ulong ptr, int data32)
access_prepare(&ac, env, ptr, size, MMU_DATA_STORE, GETPC());
do_fsave(&ac, ptr, data32);
+ do_fninit(env);
}
static void do_frstor(X86Access *ac, target_ulong ptr, int data32)
@@ -3085,6 +3088,12 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
#if defined(CONFIG_USER_ONLY)
void cpu_x86_fsave(CPUX86State *env, void *host, size_t len)
+{
+ cpu_x86_fsave_noinit(env, host, len);
+ do_fninit(env);
+}
+
+void cpu_x86_fsave_noinit(CPUX86State *env, void *host, size_t len)
{
X86Access ac = {
.haddr1 = host,
--
2.54.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/3] linux-user/i386: write the floating-point registers to a core dump
2026-09-10 1:34 [PATCH 0/3] linux-user: write the x86 floating-point registers to a core dump Matt Turner
2026-09-10 1:34 ` [PATCH 1/3] target/i386: split the FNINIT out of do_fsave() Matt Turner
@ 2026-09-10 1:34 ` Matt Turner
2026-09-10 1:34 ` [PATCH 3/3] linux-user/x86_64: " Matt Turner
2 siblings, 0 replies; 4+ messages in thread
From: Matt Turner @ 2026-09-10 1:34 UTC (permalink / raw)
To: qemu-devel
Cc: Helge Deller, Laurent Vivier, Paolo Bonzini, Pierrick Bouvier,
Richard Henderson, Zhao Liu
A guest core carried only the general-purpose registers, so a debugger
opening one reported the x87 stack as unavailable.
Implement HAVE_ELF_CORE_FPREGS for i386: define target_elf_fpregset_t to
match the kernel's elf_fpregset_t, i.e. struct user_i387_struct from
arch/x86/include/asm/user_32.h, which is the legacy FSAVE image without
the trailing software status word that FSAVE does not write either.
cpu_x86_fsave_noinit() produces exactly that, already in target byte
order.
The SSE registers are still absent: the kernel dumps those in a separate
NT_PRXFPREG note, which the generic core dump code has no support for.
Checked with gdb on a core from a program that faults with 1.5 and 2.25
live on the x87 stack: "info float" reads both back, along with the tag
word and the instruction and operand pointers.
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
linux-user/i386/elfload.c | 7 +++++++
linux-user/i386/target_elf.h | 18 ++++++++++++++++++
2 files changed, 25 insertions(+)
diff --git ./linux-user/i386/elfload.c ./linux-user/i386/elfload.c
index 2e10f38a41..3c8a65e35c 100644
--- ./linux-user/i386/elfload.c
+++ ./linux-user/i386/elfload.c
@@ -25,6 +25,13 @@ const char *get_elf_platform(CPUState *cs)
return elf_platform[family - 3];
}
+void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPUX86State *env)
+{
+ /* The FSAVE image exactly, stored in target byte order. */
+ QEMU_BUILD_BUG_ON(sizeof(*r) != 4 * 7 + 8 * 10);
+ cpu_x86_fsave_noinit((CPUX86State *)env, r, sizeof(*r));
+}
+
void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env)
{
r->pt.bx = tswapal(env->regs[R_EBX]);
diff --git ./linux-user/i386/target_elf.h ./linux-user/i386/target_elf.h
index eafac8f382..931ea23ffa 100644
--- ./linux-user/i386/target_elf.h
+++ ./linux-user/i386/target_elf.h
@@ -27,6 +27,24 @@ typedef struct target_elf_gregset_t {
struct target_user_regs_struct pt;
} target_elf_gregset_t;
+/*
+ * Matches the kernel's elf_fpregset_t, i.e. struct user_i387_struct from
+ * arch/x86/include/asm/user_32.h. This is the legacy FSAVE image without
+ * the trailing software status word, which FSAVE does not write either.
+ */
+#define HAVE_ELF_CORE_FPREGS 1
+
+typedef struct target_elf_fpregset_t {
+ uint32_t cwd; /* FPU control word */
+ uint32_t swd; /* FPU status word */
+ uint32_t twd; /* FPU tag word */
+ uint32_t fip; /* FPU IP offset */
+ uint32_t fcs; /* FPU IP selector */
+ uint32_t foo; /* FPU operand pointer offset */
+ uint32_t fos; /* FPU operand pointer selector */
+ uint32_t st_space[20]; /* 8 * 10 bytes for st0-st7 */
+} target_elf_fpregset_t;
+
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
--
2.54.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 3/3] linux-user/x86_64: write the floating-point registers to a core dump
2026-09-10 1:34 [PATCH 0/3] linux-user: write the x86 floating-point registers to a core dump Matt Turner
2026-09-10 1:34 ` [PATCH 1/3] target/i386: split the FNINIT out of do_fsave() Matt Turner
2026-09-10 1:34 ` [PATCH 2/3] linux-user/i386: write the floating-point registers to a core dump Matt Turner
@ 2026-09-10 1:34 ` Matt Turner
2 siblings, 0 replies; 4+ messages in thread
From: Matt Turner @ 2026-09-10 1:34 UTC (permalink / raw)
To: qemu-devel
Cc: Helge Deller, Laurent Vivier, Paolo Bonzini, Pierrick Bouvier,
Richard Henderson, Zhao Liu
A guest core carried only the general-purpose registers, so a debugger
opening one reported the x87 stack and every SSE register as
unavailable.
Implement HAVE_ELF_CORE_FPREGS for x86_64: define target_elf_fpregset_t
to match the kernel's elf_fpregset_t, i.e. struct user_i387_struct from
arch/x86/include/asm/user_64.h, which is the 512 byte FXSAVE image, and
fill it with the existing cpu_x86_fxsave(). As on the signal path, the
helper writes in target byte order, so there is nothing to swap.
QEMU's FXSAVE writes zero for the instruction and operand pointers and
never writes the opcode field, as it does everywhere else, so those
three fields are not recovered from a core.
Checked with gdb on a core from a program that faults with live values
on the x87 stack and in xmm0 and xmm1: "info float" reads back the stack
and the tag word, and the xmm registers and $mxcsr read correctly.
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
linux-user/x86_64/elfload.c | 11 +++++++++++
linux-user/x86_64/target_elf.h | 20 ++++++++++++++++++++
2 files changed, 31 insertions(+)
diff --git ./linux-user/x86_64/elfload.c ./linux-user/x86_64/elfload.c
index 121a8167ac..4560106faf 100644
--- ./linux-user/x86_64/elfload.c
+++ ./linux-user/x86_64/elfload.c
@@ -42,6 +42,17 @@ bool init_guest_commpage(void)
return true;
}
+void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPUX86State *env)
+{
+ QEMU_BUILD_BUG_ON(sizeof(*r) != sizeof(X86LegacyXSaveArea));
+ /*
+ * The helper stores the image in target byte order. cpu_x86_fxsave()
+ * folds the softfloat exception flags back into env->mxcsr, so the
+ * const has to go.
+ */
+ cpu_x86_fxsave((CPUX86State *)env, r, sizeof(*r));
+}
+
void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env)
{
r->pt.r15 = tswapal(env->regs[15]);
diff --git ./linux-user/x86_64/target_elf.h ./linux-user/x86_64/target_elf.h
index 840bddf5ec..23f84f6a91 100644
--- ./linux-user/x86_64/target_elf.h
+++ ./linux-user/x86_64/target_elf.h
@@ -27,4 +27,24 @@ typedef struct target_elf_gregset_t {
struct target_user_regs_struct pt;
} target_elf_gregset_t;
+/*
+ * Matches the kernel's elf_fpregset_t, i.e. struct user_i387_struct from
+ * arch/x86/include/asm/user_64.h, which is the 512 byte FXSAVE image.
+ */
+#define HAVE_ELF_CORE_FPREGS 1
+
+typedef struct target_elf_fpregset_t {
+ uint16_t cwd; /* FPU control word */
+ uint16_t swd; /* FPU status word */
+ uint16_t twd; /* abridged tag word, not the x87 one */
+ uint16_t fop; /* last instruction opcode */
+ uint64_t rip; /* instruction pointer */
+ uint64_t rdp; /* data pointer */
+ uint32_t mxcsr;
+ uint32_t mxcsr_mask;
+ uint32_t st_space[32]; /* 8 * 16 bytes for st0-st7 */
+ uint32_t xmm_space[64]; /* 16 * 16 bytes for xmm0-xmm15 */
+ uint32_t padding[24];
+} target_elf_fpregset_t;
+
#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-10 1:35 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-10 1:34 [PATCH 0/3] linux-user: write the x86 floating-point registers to a core dump Matt Turner
2026-09-10 1:34 ` [PATCH 1/3] target/i386: split the FNINIT out of do_fsave() Matt Turner
2026-09-10 1:34 ` [PATCH 2/3] linux-user/i386: write the floating-point registers to a core dump Matt Turner
2026-09-10 1:34 ` [PATCH 3/3] linux-user/x86_64: " Matt Turner
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.