Re: [PATCH v3 3/3] elf2dmp: fill ContextBuffer with faulting CPU context
Akihiko Odaki <[email protected]>
| Newsgroups | gmane.comp.emulators.qemu |
|---|---|
| Message-ID | <[email protected]> |
On 2026/08/06 17:47, 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. A per-CPU size check ensures the > field is present before reading, supporting heterogeneous dumps. > > Also extract KiBugCheckData from the guest kernel to populate > BugcheckCode and BugcheckParameters in the dump header. > > The embedded state->size is clamped to MIN(state->size, n_descsz) > in init_states() to prevent reading beyond the note descriptor. > > Signed-off-by: Zhengrong Li <[email protected]> > --- > contrib/elf2dmp/kdbg.h | 9 +++++++ > contrib/elf2dmp/main.c | 50 ++++++++++++++++++++++++++++++++++++-- > contrib/elf2dmp/qemu_elf.c | 7 ++++++ > contrib/elf2dmp/qemu_elf.h | 2 ++ > 4 files changed, 66 insertions(+), 2 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..e25709b45d 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; > > + /* First pass: identify faulting CPU and set header context early */ > + for (i = 0; i < qe->state_nr; i++) { > + QEMUCPUState *s = qe->state[i]; > + if (s->size >= offsetof(QEMUCPUState, is_crash_occurred_cpu) + > + sizeof(s->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)); > + 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..b181d418bd 100644 > --- a/contrib/elf2dmp/qemu_elf.c > +++ b/contrib/elf2dmp/qemu_elf.c > @@ -103,6 +103,13 @@ static bool init_states(QEMU_Elf *qe) > nhdr->n_descsz >= offsetof(QEMUCPUState, kernel_gs_base)) { > state_size = MIN(state->size, nhdr->n_descsz); > > + /* > + * Clamp the embedded size to the actual note descriptor size > + * so that downstream size checks (e.g. fill_context) never > + * read beyond the descriptor boundary. > + */ > + state->size = state_size; > + > if (state_size < sizeof(*state)) { This compares the validated size with the new 448-byte whole structure. Valid old 440-byte notes contain kernel_gs_base, but are classified as lacking it. Please change this to compare against the end of kernel_gs_base, not sizeof(*state). Regards, Akihiko Odaki > eprintf("CPU #%u: QEMU CPU state size %u doesn't match\n", > states->len, state_size); > diff --git a/contrib/elf2dmp/qemu_elf.h b/contrib/elf2dmp/qemu_elf.h > index adc50238b4..3b28332e67 100644 > --- a/contrib/elf2dmp/qemu_elf.h > +++ b/contrib/elf2dmp/qemu_elf.h > @@ -27,6 +27,8 @@ typedef struct QEMUCPUState { > QEMUCPUSegment ldt, tr, gdt, idt; > uint64_t cr[5]; > uint64_t kernel_gs_base; > + uint8_t is_crash_occurred_cpu; > + uint8_t pad[7]; > } QEMUCPUState; > > int is_system(QEMUCPUState *s);