aboutsummaryrefslogtreecommitdiff
path: root/contrib/elf2dmp/qemu_elf.c
diff options
context:
space:
mode:
authorViktor Prutyanov <viktor.prutyanov@virtuozzo.com>2018-08-29 15:41:25 +0300
committerPaolo Bonzini <pbonzini@redhat.com>2018-10-02 19:09:12 +0200
commit3fa2d384c245bcee3a9ecfa11f298b76ea4c9d57 (patch)
treee6dc921bb305a9d2eb6f6a2d24dcd6f553856883 /contrib/elf2dmp/qemu_elf.c
parenta52fbc37a46691762540b043c4cf5f9e7eb1a244 (diff)
downloadqemu-3fa2d384c245bcee3a9ecfa11f298b76ea4c9d57.zip
qemu-3fa2d384c245bcee3a9ecfa11f298b76ea4c9d57.tar.gz
qemu-3fa2d384c245bcee3a9ecfa11f298b76ea4c9d57.tar.bz2
contrib: add elf2dmp tool
elf2dmp is a converter from ELF dump (produced by 'dump-guest-memory') to Windows MEMORY.DMP format (also know as 'Complete Memory Dump') which can be opened in WinDbg. This tool can help if VMCoreInfo device/driver is absent in Windows VM and 'dump-guest-memory -w' is not available but dump can be created in ELF format. The tool works as follows: 1. Determine the system paging root looking at GS_BASE or KERNEL_GS_BASE to locate the PRCB structure and finds the kernel CR3 nearby if QEMU CPU state CR3 is not suitable. 2. Find an address within the kernel image by dereferencing the first IDT entry and scans virtual memory upwards until the start of the kernel. 3. Download a PDB matching the kernel from the Microsoft symbol store, and figure out the layout of certain relevant structures necessary for the dump. 4. Populate the corresponding structures in the memory image and create the appropriate dump header. Signed-off-by: Viktor Prutyanov <viktor.prutyanov@virtuozzo.com> Message-Id: <1535546488-30208-3-git-send-email-viktor.prutyanov@virtuozzo.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'contrib/elf2dmp/qemu_elf.c')
-rw-r--r--contrib/elf2dmp/qemu_elf.c164
1 files changed, 164 insertions, 0 deletions
diff --git a/contrib/elf2dmp/qemu_elf.c b/contrib/elf2dmp/qemu_elf.c
new file mode 100644
index 0000000..e9c0d25
--- /dev/null
+++ b/contrib/elf2dmp/qemu_elf.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2018 Virtuozzo International GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "err.h"
+#include "qemu_elf.h"
+
+#define QEMU_NOTE_NAME "QEMU"
+
+#ifndef ROUND_UP
+#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
+#endif
+
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+#endif
+
+#define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \
+ ((DIV_ROUND_UP((hdr_size), 4) + \
+ DIV_ROUND_UP((name_size), 4) + \
+ DIV_ROUND_UP((desc_size), 4)) * 4)
+
+int is_system(QEMUCPUState *s)
+{
+ return s->gs.base >> 63;
+}
+
+static char *nhdr_get_name(Elf64_Nhdr *nhdr)
+{
+ return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4);
+}
+
+static void *nhdr_get_desc(Elf64_Nhdr *nhdr)
+{
+ return nhdr_get_name(nhdr) + ROUND_UP(nhdr->n_namesz, 4);
+}
+
+static Elf64_Nhdr *nhdr_get_next(Elf64_Nhdr *nhdr)
+{
+ return (void *)((uint8_t *)nhdr + ELF_NOTE_SIZE(sizeof(*nhdr),
+ nhdr->n_namesz, nhdr->n_descsz));
+}
+
+Elf64_Phdr *elf64_getphdr(void *map)
+{
+ Elf64_Ehdr *ehdr = map;
+ Elf64_Phdr *phdr = (void *)((uint8_t *)map + ehdr->e_phoff);
+
+ return phdr;
+}
+
+Elf64_Half elf_getphdrnum(void *map)
+{
+ Elf64_Ehdr *ehdr = map;
+
+ return ehdr->e_phnum;
+}
+
+static int init_states(QEMU_Elf *qe)
+{
+ Elf64_Phdr *phdr = elf64_getphdr(qe->map);
+ Elf64_Nhdr *start = (void *)((uint8_t *)qe->map + phdr[0].p_offset);
+ Elf64_Nhdr *end = (void *)((uint8_t *)start + phdr[0].p_memsz);
+ Elf64_Nhdr *nhdr;
+ size_t cpu_nr = 0;
+
+ if (phdr[0].p_type != PT_NOTE) {
+ eprintf("Failed to find PT_NOTE\n");
+ return 1;
+ }
+
+ qe->has_kernel_gs_base = 1;
+
+ for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
+ if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
+ QEMUCPUState *state = nhdr_get_desc(nhdr);
+
+ if (state->size < sizeof(*state)) {
+ eprintf("CPU #%zu: QEMU CPU state size %u doesn't match\n",
+ cpu_nr, state->size);
+ /*
+ * We assume either every QEMU CPU state has KERNEL_GS_BASE or
+ * no one has.
+ */
+ qe->has_kernel_gs_base = 0;
+ }
+ cpu_nr++;
+ }
+ }
+
+ printf("%zu CPU states has been found\n", cpu_nr);
+
+ qe->state = malloc(sizeof(*qe->state) * cpu_nr);
+ if (!qe->state) {
+ return 1;
+ }
+
+ cpu_nr = 0;
+
+ for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
+ if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
+ qe->state[cpu_nr] = nhdr_get_desc(nhdr);
+ cpu_nr++;
+ }
+ }
+
+ qe->state_nr = cpu_nr;
+
+ return 0;
+}
+
+static void exit_states(QEMU_Elf *qe)
+{
+ free(qe->state);
+}
+
+int QEMU_Elf_init(QEMU_Elf *qe, const char *filename)
+{
+ int err = 0;
+ struct stat st;
+
+ qe->fd = open(filename, O_RDONLY, 0);
+ if (qe->fd == -1) {
+ eprintf("Failed to open ELF dump file \'%s\'\n", filename);
+ return 1;
+ }
+
+ fstat(qe->fd, &st);
+ qe->size = st.st_size;
+
+ qe->map = mmap(NULL, qe->size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, qe->fd, 0);
+ if (qe->map == MAP_FAILED) {
+ eprintf("Failed to map ELF file\n");
+ err = 1;
+ goto out_fd;
+ }
+
+ if (init_states(qe)) {
+ eprintf("Failed to extract QEMU CPU states\n");
+ err = 1;
+ goto out_unmap;
+ }
+
+ return 0;
+
+out_unmap:
+ munmap(qe->map, qe->size);
+out_fd:
+ close(qe->fd);
+
+ return err;
+}
+
+void QEMU_Elf_exit(QEMU_Elf *qe)
+{
+ exit_states(qe);
+ munmap(qe->map, qe->size);
+ close(qe->fd);
+}