Re: [PATCH v2 3/3] elf2dmp: fill ContextBuffer with faulting CPU context
Akihiko Odaki <[email protected]> Thu, 6 Aug 2026 12:23:59 +0900
| Newsgroups | gmane.comp.emulators.qemu |
|---|---|
| Message-ID | <[email protected]> |
On 2026/08/05 18:05, Zhengrong Li wrote: > When is_crash_occurred_cpu is set in QEMUCPUState, copy that CPU's > context into WinDumpHeader64 ContextBuffer so WinDbg opens on the > faulting CPU instead of CPU 0. > > Also extract KiBugCheckData from the guest kernel to populate > BugcheckCode and BugcheckParameters in the dump header. > > Changes since v1: > - Add size-based field detection in init_states() for forward compatibility > - Use has_is_crash_occurred_cpu flag instead of version check > - Set header context before best-effort guest memory operations to avoid > losing faulting CPU context on partial dump failures > - Use LIVE_SYSTEM_DUMP when BugcheckCode is zero > - Use PRIx32 for BugcheckCode format specifier > - Use linux.alibaba.com identity > > Signed-off-by: Zhengrong Li <[email protected]> > --- > contrib/elf2dmp/kdbg.h | 9 +++++++ > contrib/elf2dmp/main.c | 50 ++++++++++++++++++++++++++++++++++++-- > contrib/elf2dmp/qemu_elf.c | 20 +++++++++------ > contrib/elf2dmp/qemu_elf.h | 1 + > 4 files changed, 71 insertions(+), 9 deletions(-) > > diff --git a/contrib/elf2dmp/kdbg.h b/contrib/elf2dmp/kdbg.h > index 002e3d0cd5..15e4fb1140 100644 > --- a/contrib/elf2dmp/kdbg.h > +++ b/contrib/elf2dmp/kdbg.h > @@ -195,4 +195,13 @@ typedef struct KDDEBUGGER_DATA64 { > uint16_t OffsetPrcbContext; > } KDDEBUGGER_DATA64; > > +typedef struct KIBUGCHECK_INFO { > + uint32_t BugcheckCode; > + uint32_t unused0; > + uint64_t BugcheckParameter1; > + uint64_t BugcheckParameter2; > + uint64_t BugcheckParameter3; > + uint64_t BugcheckParameter4; > +} KIBUGCHECK_INFO; > + > #endif /* KDBG_H */ > diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c > index a62abadcc0..59ffc0f2e8 100644 > --- a/contrib/elf2dmp/main.c > +++ b/contrib/elf2dmp/main.c > @@ -338,11 +338,26 @@ static bool fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, > * A dump may still contain valuable information even if it lacks contexts of > * some CPUs due to dump corruption or a failure before starting CPUs. > */ > -static void fill_context(KDDEBUGGER_DATA64 *kdbg, > +static void fill_context(WinDumpHeader64 *hdr, KDDEBUGGER_DATA64 *kdbg, > struct va_space *vs, QEMU_Elf *qe) > { > int i; > + int fault_cpu = -1; > > + /* First pass: identify faulting CPU and set header context early */ > + for (i = 0; i < qe->state_nr; i++) { > + QEMUCPUState *s = qe->state[i]; > + if (qe->has_is_crash_occurred_cpu && s->is_crash_occurred_cpu) { > + WinContext64 ctx; > + win_context_init_from_qemu_cpu_state(&ctx, s); > + memcpy(hdr->ContextBuffer, &ctx, sizeof(ctx)); > + fault_cpu = i; fault_cpu is assigned but never read. Consequently: error: variable ‘fault_cpu’ set but not used [-Werror=unused-but-set-variable] make -j8 contrib/elf2dmp/elf2dmp fails. > + printf("Faulting CPU identified: #%d\n", i); > + break; > + } > + } > + > + /* Second pass: fill context for all CPUs (best-effort) */ > for (i = 0; i < qe->state_nr; i++) { > uint64_t Prcb; > uint64_t Context; > @@ -512,6 +527,7 @@ int main(int argc, char *argv[]) > uint64_t KdVersionBlock; > bool kernel_found = false; > OMFSignatureRSDS rsds; > + uint64_t KiBugCheckData; > > if (argc != 3) { > eprintf("usage:\n\t%s elf_file dmp_file\n", argv[0]); > @@ -611,7 +627,37 @@ int main(int argc, char *argv[]) > goto out_kdbg; > } > > - fill_context(kdbg, &vs, &qemu_elf); > + if (!SYM_RESOLVE(KernBase, &pdb, KiBugCheckData)) { > + eprintf("Failed to get KiBugCheckData.\n"); > + } else { > + KIBUGCHECK_INFO data = { 0 }; > + if (va_space_rw(&vs, KiBugCheckData, &data, sizeof(data), 0)) { > + header.BugcheckCode = data.BugcheckCode; > + header.BugcheckParameter1 = data.BugcheckParameter1; > + header.BugcheckParameter2 = data.BugcheckParameter2; > + header.BugcheckParameter3 = data.BugcheckParameter3; > + header.BugcheckParameter4 = data.BugcheckParameter4; > + > + /* > + * If BugcheckCode wasn't saved, we consider guest OS as alive. > + */ > + if (!header.BugcheckCode) { > + header.BugcheckCode = LIVE_SYSTEM_DUMP; > + } > + > + printf("KiBugCheckData: 0x%016" PRIx64 > + ", BugcheckCode: 0x%08" PRIx32 ", Args:" > + " 0x%016" PRIx64 " 0x%016" PRIx64 > + " 0x%016" PRIx64 " 0x%016" PRIx64 "\n", > + KiBugCheckData, header.BugcheckCode, > + data.BugcheckParameter1, data.BugcheckParameter2, > + data.BugcheckParameter3, data.BugcheckParameter4); > + } else { > + eprintf("Failed to va_space_rw KiBugCheckData.\n"); > + } > + } > + > + fill_context(&header, kdbg, &vs, &qemu_elf); > > if (!write_dump(&ps, &header, argv[2])) { > eprintf("Failed to save dump\n"); > diff --git a/contrib/elf2dmp/qemu_elf.c b/contrib/elf2dmp/qemu_elf.c > index c9bad6e82c..8eb2654356 100644 > --- a/contrib/elf2dmp/qemu_elf.c > +++ b/contrib/elf2dmp/qemu_elf.c > @@ -71,7 +71,8 @@ static bool init_states(QEMU_Elf *qe) > return false; > } > > - qe->has_kernel_gs_base = 1; > + qe->has_kernel_gs_base = 0; > + qe->has_is_crash_occurred_cpu = 0; > offset = phdr[0].p_offset; > states = g_ptr_array_new(); > > @@ -103,14 +104,19 @@ static bool init_states(QEMU_Elf *qe) > nhdr->n_descsz >= offsetof(QEMUCPUState, kernel_gs_base)) { > state_size = MIN(state->size, nhdr->n_descsz); > > - if (state_size < sizeof(*state)) { > + if (state_size >= offsetof(QEMUCPUState, kernel_gs_base) + > + sizeof(state->kernel_gs_base)) { > + qe->has_kernel_gs_base = 1; > + } > + > + if (state_size >= offsetof(QEMUCPUState, is_crash_occurred_cpu) + > + sizeof(state->is_crash_occurred_cpu)) { > + qe->has_is_crash_occurred_cpu = 1; > + } > + > + if (state_size != sizeof(*state)) { > eprintf("CPU #%u: QEMU CPU state size %u doesn't match\n", > states->len, state_size); > - /* > - * We assume either every QEMU CPU state has KERNEL_GS_BASE or > - * no one has. > - */ > - qe->has_kernel_gs_base = 0; > } Field availability is ORed across all CPU notes and then applied to every note. With a 440-byte CPU 0 note followed by a 448-byte CPU 1 note, CPU 1 sets has_is_crash_occurred_cpu, after which fill_context() reads byte 440 beyond CPU 0’s descriptor. That byte normally belongs to the next note header and is nonzero, so CPU 0 can be falsely selected. The same aggregation can make fix_dtb() read a missing kernel_gs_base. Please follow what's previously done for kernel_gs_base. Regards, Akihiko Odaki > g_ptr_array_add(states, state); > } > diff --git a/contrib/elf2dmp/qemu_elf.h b/contrib/elf2dmp/qemu_elf.h > index 3b28332e67..120de4a2f4 100644 > --- a/contrib/elf2dmp/qemu_elf.h > +++ b/contrib/elf2dmp/qemu_elf.h > @@ -42,6 +42,7 @@ typedef struct QEMU_Elf { > QEMUCPUState **state; > size_t state_nr; > int has_kernel_gs_base; > + int has_is_crash_occurred_cpu; > } QEMU_Elf; > > bool QEMU_Elf_init(QEMU_Elf *qe, const char *filename);