[PATCH v3 3/3] elf2dmp: fill ContextBuffer with faulting CPU context

Zhengrong Li <[email protected]>
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
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 | 10 +++++++-
 contrib/elf2dmp/qemu_elf.h |  2 ++
 4 files changed, 68 insertions(+), 3 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..f69023554b 100644
--- a/contrib/elf2dmp/qemu_elf.c
+++ b/contrib/elf2dmp/qemu_elf.c
@@ -103,7 +103,15 @@ 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)) {
+            /*
+             * 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 < offsetof(QEMUCPUState, kernel_gs_base) +
+                              sizeof(state->kernel_gs_base)) {
                 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);
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.