Without this, only the crash frame's window is spilled to the
stack; all deeper call frames remain in the register file and
are absent from the core's memory segments. Stack unwinding
fails past the first DWARF step because the callers' register
save areas contain stale/garbage data.
The real kernel calls flush_all_user_windows() at the top of
do_coredump(). Mirror that via a weak target_flush_windows()
hook called from dump_core_and_abort(), with the SPARC override
calling the existing flush_windows() in cpu_loop.c.
---
linux-user/signal-common.h | 1 +
linux-user/signal.c | 9 ++++++++
linux-user/sparc/cpu_loop.c | 10 +++++++++
linux-user/sparc/elfload.c | 39 +++++++++++++++++++++++++++++------
linux-user/sparc/target_elf.h | 17 +++++++++++----
5 files changed, 66 insertions(+), 10 deletions(-)
diff --git ./linux-user/signal-common.h ./linux-user/signal-common.h
index 8a44714251..54df4ccfdb 100644
--- ./linux-user/signal-common.h
+++ ./linux-user/signal-common.h
@@ -62,6 +62,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUArchState *env);
+void target_flush_windows(CPUArchState *env);
void process_pending_signals(CPUArchState *cpu_env);
void signal_init(const char *rtsig_map);
void queue_signal(CPUArchState *env, int sig, int si_type,
diff --git ./linux-user/signal.c ./linux-user/signal.c
index f0a22577e5..a7355fdeb8 100644
--- ./linux-user/signal.c
+++ ./linux-user/signal.c
@@ -502,6 +502,14 @@ void target_to_host_siginfo(siginfo_t *info, const
target_siginfo_t *tinfo)
info->si_value.sival_ptr = (void *)(long)sival_ptr;
}
+/*
+ * Weak arch hook: flush all register windows to the guest stack before
+ * writing a core dump. Required on SPARC where the register file holds
+ * multiple call-frames that have not been spilled to memory yet.
+ * Architectures without register windows leave this as a no-op.
+ */
+void __attribute__((weak)) target_flush_windows(CPUArchState *env) {}
+
/* returns 1 if given signal should dump core if not handled */
static int core_dump_signal(int sig)
{
@@ -828,6 +836,7 @@ void dump_core_and_abort(CPUArchState *env, int target_sig)
/* dump core if supported by target binary format */
if (core_dump_signal(target_sig) && (ts->bprm->core_dump != NULL)) {
+ target_flush_windows(env);
stop_all_tasks();
core_dumped =
((*ts->bprm->core_dump)(target_sig, env) == 0);
diff --git ./linux-user/sparc/cpu_loop.c ./linux-user/sparc/cpu_loop.c
index ab633eeae3..2d16304c08 100644
--- ./linux-user/sparc/cpu_loop.c
+++ ./linux-user/sparc/cpu_loop.c
@@ -149,6 +149,16 @@ static void flush_windows(CPUSPARCState *env)
#endif
}
+/* Override the weak hook in linux-user/signal.c to flush all SPARC register
+ * windows to the guest stack before a core dump is written. Without this,
+ * only the active window (captured in NT_PRSTATUS) ends up in memory; all
+ * deeper call frames remain in the register file and are absent from the
+ * core's memory segments, breaking stack unwind beyond one frame. */
+void target_flush_windows(CPUArchState *env)
+{
+ flush_windows((CPUSPARCState *)env);
+}
+
static void next_instruction(CPUSPARCState *env)
{
env->pc = env->npc;
diff --git ./linux-user/sparc/elfload.c ./linux-user/sparc/elfload.c
index e6387ec891..181f1e00b5 100644
--- ./linux-user/sparc/elfload.c
+++ ./linux-user/sparc/elfload.c
@@ -12,16 +12,41 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const
CPUArchState *env)
CPUSPARCState *e = (CPUSPARCState *)env;
int i;
+ memset(r, 0, sizeof(*r));
+
#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
+ /* Linux kernel layout for sparc64 (arch/sparc/include/asm/elf_64.h):
+ * [0..7] G0-G7
+ * [8..15] O0-O7
+ * [16..23] L0-L7
+ * [24..31] I0-I7
+ * [32] TSTATE
+ * [33] TPC
+ * [34] TNPC
+ * [35] Y
+ */
for (i = 0; i < 8; i++) {
- r->regs[i] = tswap64(env->gregs[i]);
- r->regs[8 + i] = tswap64(env->regwptr[WREG_O0 + i]);
+ r->regs[i] = tswap64(env->gregs[i]);
+ r->regs[8 + i] = tswap64(env->regwptr[WREG_O0 + i]);
+ r->regs[16 + i] = tswap64(env->regwptr[WREG_L0 + i]);
+ r->regs[24 + i] = tswap64(env->regwptr[WREG_I0 + i]);
}
- r->regs[16] = tswap64(sparc64_tstate(e));
- r->regs[17] = tswap64(env->pc);
- r->regs[18] = tswap64(env->npc);
- r->regs[19] = tswap64(env->y);
+ r->regs[32] = tswap64(sparc64_tstate(e));
+ r->regs[33] = tswap64(env->pc);
+ r->regs[34] = tswap64(env->npc);
+ r->regs[35] = tswap64(env->y);
#else
+ /* Linux kernel layout for sparc32 (arch/sparc/include/asm/elf_32.h):
+ * [0] PSR
+ * [1] PC
+ * [2] NPC
+ * [3] Y
+ * [4..11] G0-G7
+ * [12..19] O0-O7
+ * [20..27] L0-L7
+ * [28..35] I0-I7
+ * [36..37] reserved (stack_check)
+ */
r->regs[0] = tswap32(cpu_get_psr(e));
r->regs[1] = tswap32(env->pc);
r->regs[2] = tswap32(env->npc);
@@ -29,6 +54,8 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const
CPUArchState *env)
for (i = 0; i < 8; i++) {
r->regs[4 + i] = tswap32(env->gregs[i]);
r->regs[12 + i] = tswap32(env->regwptr[WREG_O0 + i]);
+ r->regs[20 + i] = tswap32(env->regwptr[WREG_L0 + i]);
+ r->regs[28 + i] = tswap32(env->regwptr[WREG_I0 + i]);
}
#endif
}
diff --git ./linux-user/sparc/target_elf.h ./linux-user/sparc/target_elf.h
index edb0b3103c..365af864b0 100644
--- ./linux-user/sparc/target_elf.h
+++ ./linux-user/sparc/target_elf.h
@@ -24,12 +24,21 @@
#define HAVE_ELF_CORE_DUMP 1
/*
- * Matches the kernel's elf_gregset_t (ELF_NGREG = 20).
- * sparc32/sparc32plus: psr, pc, npc, y, u_regs[16] (g0-g7, o0-o7)
- * sparc64: u_regs[16] (g0-g7, o0-o7), tstate, pc, npc, y
+ * Matches the kernel's elf_gregset_t.
+ * sparc32/sparc32plus (ELF_NGREG = 38):
+ * psr, pc, npc, y, u_regs[16] (g0-g7, o0-o7),
+ * reg_window[16] (l0-l7, i0-i7), stack_check[2]
+ * sparc64 (ELF_NGREG = 36):
+ * u_regs[16] (g0-g7, o0-o7), reg_window[16] (l0-l7, i0-i7),
+ * tstate, tpc, tnpc, y
*/
+#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
+# define TARGET_ELF_NGREG 36
+#else
+# define TARGET_ELF_NGREG 38
+#endif
typedef struct target_elf_gregset_t {
- abi_ulong regs[20];
+ abi_ulong regs[TARGET_ELF_NGREG];
} target_elf_gregset_t;
#endif
--
2.53.0