summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--Makefile.objs1
-rwxr-xr-xconfigure3
-rw-r--r--contrib/elf2dmp/Makefile.objs1
-rw-r--r--contrib/elf2dmp/addrspace.c233
-rw-r--r--contrib/elf2dmp/addrspace.h44
-rw-r--r--contrib/elf2dmp/download.c47
-rw-r--r--contrib/elf2dmp/download.h13
-rw-r--r--contrib/elf2dmp/err.h13
-rw-r--r--contrib/elf2dmp/kdbg.h194
-rw-r--r--contrib/elf2dmp/main.c589
-rw-r--r--contrib/elf2dmp/pdb.c322
-rw-r--r--contrib/elf2dmp/pdb.h241
-rw-r--r--contrib/elf2dmp/pe.h121
-rw-r--r--contrib/elf2dmp/qemu_elf.c164
-rw-r--r--contrib/elf2dmp/qemu_elf.h51
16 files changed, 2042 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index 3730092817..1144d6e3ba 100644
--- a/Makefile
+++ b/Makefile
@@ -415,6 +415,7 @@ dummy := $(call unnest-vars,, \
                 chardev-obj-y \
                 util-obj-y \
                 qga-obj-y \
+                elf2dmp-obj-y \
                 ivshmem-client-obj-y \
                 ivshmem-server-obj-y \
                 libvhost-user-obj-y \
@@ -710,6 +711,10 @@ ifneq ($(EXESUF),)
 qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
 endif
 
+elf2dmp: LIBS = $(CURL_LIBS)
+elf2dmp: $(elf2dmp-obj-y)
+	$(call LINK, $^)
+
 ifdef CONFIG_IVSHMEM
 ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
 	$(call LINK, $^)
diff --git a/Makefile.objs b/Makefile.objs
index ce9c79235e..1e1ff387d7 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -186,6 +186,7 @@ qga-vss-dll-obj-y = qga/
 
 ######################################################################
 # contrib
+elf2dmp-obj-y = contrib/elf2dmp/
 ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/
 ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/
 libvhost-user-obj-y = contrib/libvhost-user/
diff --git a/configure b/configure
index 4fc1feaa6f..f3d4b799a5 100755
--- a/configure
+++ b/configure
@@ -5721,6 +5721,9 @@ if test "$want_tools" = "yes" ; then
   if [ "$ivshmem" = "yes" ]; then
     tools="ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools"
   fi
+  if [ "$posix" = "yes" ] && [ "$curl" = "yes" ]; then
+    tools="elf2dmp $tools"
+  fi
 fi
 if test "$softmmu" = yes ; then
   if test "$linux" = yes; then
diff --git a/contrib/elf2dmp/Makefile.objs b/contrib/elf2dmp/Makefile.objs
new file mode 100644
index 0000000000..e3140f58cf
--- /dev/null
+++ b/contrib/elf2dmp/Makefile.objs
@@ -0,0 +1 @@
+elf2dmp-obj-y = main.o addrspace.o download.o pdb.o qemu_elf.o
diff --git a/contrib/elf2dmp/addrspace.c b/contrib/elf2dmp/addrspace.c
new file mode 100644
index 0000000000..8a76069cb5
--- /dev/null
+++ b/contrib/elf2dmp/addrspace.c
@@ -0,0 +1,233 @@
+/*
+ * 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 "addrspace.h"
+
+static struct pa_block *pa_space_find_block(struct pa_space *ps, uint64_t pa)
+{
+    size_t i;
+    for (i = 0; i < ps->block_nr; i++) {
+        if (ps->block[i].paddr <= pa &&
+                pa <= ps->block[i].paddr + ps->block[i].size) {
+            return ps->block + i;
+        }
+    }
+
+    return NULL;
+}
+
+static uint8_t *pa_space_resolve(struct pa_space *ps, uint64_t pa)
+{
+    struct pa_block *block = pa_space_find_block(ps, pa);
+
+    if (!block) {
+        return NULL;
+    }
+
+    return block->addr + (pa - block->paddr);
+}
+
+int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf)
+{
+    Elf64_Half phdr_nr = elf_getphdrnum(qemu_elf->map);
+    Elf64_Phdr *phdr = elf64_getphdr(qemu_elf->map);
+    size_t block_i = 0;
+    size_t i;
+
+    ps->block_nr = 0;
+
+    for (i = 0; i < phdr_nr; i++) {
+        if (phdr[i].p_type == PT_LOAD) {
+            ps->block_nr++;
+        }
+    }
+
+    ps->block = malloc(sizeof(*ps->block) * ps->block_nr);
+    if (!ps->block) {
+        return 1;
+    }
+
+    for (i = 0; i < phdr_nr; i++) {
+        if (phdr[i].p_type == PT_LOAD) {
+            ps->block[block_i] = (struct pa_block) {
+                .addr = (uint8_t *)qemu_elf->map + phdr[i].p_offset,
+                .paddr = phdr[i].p_paddr,
+                .size = phdr[i].p_filesz,
+            };
+            block_i++;
+        }
+    }
+
+    return 0;
+}
+
+void pa_space_destroy(struct pa_space *ps)
+{
+    ps->block_nr = 0;
+    free(ps->block);
+}
+
+void va_space_set_dtb(struct va_space *vs, uint64_t dtb)
+{
+    vs->dtb = dtb & 0x00ffffffffff000;
+}
+
+void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb)
+{
+    vs->ps = ps;
+    va_space_set_dtb(vs, dtb);
+}
+
+static uint64_t get_pml4e(struct va_space *vs, uint64_t va)
+{
+    uint64_t pa = (vs->dtb & 0xffffffffff000) | ((va & 0xff8000000000) >> 36);
+
+    return *(uint64_t *)pa_space_resolve(vs->ps, pa);
+}
+
+static uint64_t get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e)
+{
+    uint64_t pdpte_paddr = (pml4e & 0xffffffffff000) |
+        ((va & 0x7FC0000000) >> 27);
+
+    return *(uint64_t *)pa_space_resolve(vs->ps, pdpte_paddr);
+}
+
+static uint64_t pde_index(uint64_t va)
+{
+    return (va >> 21) & 0x1FF;
+}
+
+static uint64_t pdba_base(uint64_t pdpe)
+{
+    return pdpe & 0xFFFFFFFFFF000;
+}
+
+static uint64_t get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe)
+{
+    uint64_t pgd_entry = pdba_base(pdpe) + pde_index(va) * 8;
+
+    return *(uint64_t *)pa_space_resolve(vs->ps, pgd_entry);
+}
+
+static uint64_t pte_index(uint64_t va)
+{
+    return (va >> 12) & 0x1FF;
+}
+
+static uint64_t ptba_base(uint64_t pde)
+{
+    return pde & 0xFFFFFFFFFF000;
+}
+
+static uint64_t get_pte(struct va_space *vs, uint64_t va, uint64_t pgd)
+{
+    uint64_t pgd_val = ptba_base(pgd) + pte_index(va) * 8;
+
+    return *(uint64_t *)pa_space_resolve(vs->ps, pgd_val);
+}
+
+static uint64_t get_paddr(uint64_t va, uint64_t pte)
+{
+    return (pte & 0xFFFFFFFFFF000) | (va & 0xFFF);
+}
+
+static bool is_present(uint64_t entry)
+{
+    return entry & 0x1;
+}
+
+static bool page_size_flag(uint64_t entry)
+{
+    return entry & (1 << 7);
+}
+
+static uint64_t get_1GB_paddr(uint64_t va, uint64_t pdpte)
+{
+    return (pdpte & 0xfffffc0000000) | (va & 0x3fffffff);
+}
+
+static uint64_t get_2MB_paddr(uint64_t va, uint64_t pgd_entry)
+{
+    return (pgd_entry & 0xfffffffe00000) | (va & 0x00000001fffff);
+}
+
+static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va)
+{
+    uint64_t pml4e, pdpe, pgd, pte;
+
+    pml4e = get_pml4e(vs, va);
+    if (!is_present(pml4e)) {
+        return INVALID_PA;
+    }
+
+    pdpe = get_pdpi(vs, va, pml4e);
+    if (!is_present(pdpe)) {
+        return INVALID_PA;
+    }
+
+    if (page_size_flag(pdpe)) {
+        return get_1GB_paddr(va, pdpe);
+    }
+
+    pgd = get_pgd(vs, va, pdpe);
+    if (!is_present(pgd)) {
+        return INVALID_PA;
+    }
+
+    if (page_size_flag(pgd)) {
+        return get_2MB_paddr(va, pgd);
+    }
+
+    pte = get_pte(vs, va, pgd);
+    if (!is_present(pte)) {
+        return INVALID_PA;
+    }
+
+    return get_paddr(va, pte);
+}
+
+void *va_space_resolve(struct va_space *vs, uint64_t va)
+{
+    uint64_t pa = va_space_va2pa(vs, va);
+
+    if (pa == INVALID_PA) {
+        return NULL;
+    }
+
+    return pa_space_resolve(vs->ps, pa);
+}
+
+int va_space_rw(struct va_space *vs, uint64_t addr,
+        void *buf, size_t size, int is_write)
+{
+    while (size) {
+        uint64_t page = addr & PFN_MASK;
+        size_t s = (page + PAGE_SIZE) - addr;
+        void *ptr;
+
+        s = (s > size) ? size : s;
+
+        ptr = va_space_resolve(vs, addr);
+        if (!ptr) {
+            return 1;
+        }
+
+        if (is_write) {
+            memcpy(ptr, buf, s);
+        } else {
+            memcpy(buf, ptr, s);
+        }
+
+        size -= s;
+        buf = (uint8_t *)buf + s;
+        addr += s;
+    }
+
+    return 0;
+}
diff --git a/contrib/elf2dmp/addrspace.h b/contrib/elf2dmp/addrspace.h
new file mode 100644
index 0000000000..d87f6a18c6
--- /dev/null
+++ b/contrib/elf2dmp/addrspace.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018 Virtuozzo International GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ *
+ */
+
+#ifndef ADDRSPACE_H
+#define ADDRSPACE_H
+
+#include "qemu_elf.h"
+
+#define PAGE_BITS 12
+#define PAGE_SIZE (1ULL << PAGE_BITS)
+#define PFN_MASK (~(PAGE_SIZE - 1))
+
+#define INVALID_PA  UINT64_MAX
+
+struct pa_block {
+    uint8_t *addr;
+    uint64_t paddr;
+    uint64_t size;
+};
+
+struct pa_space {
+    size_t block_nr;
+    struct pa_block *block;
+};
+
+struct va_space {
+    uint64_t dtb;
+    struct pa_space *ps;
+};
+
+int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf);
+void pa_space_destroy(struct pa_space *ps);
+
+void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb);
+void va_space_set_dtb(struct va_space *vs, uint64_t dtb);
+void *va_space_resolve(struct va_space *vs, uint64_t va);
+int va_space_rw(struct va_space *vs, uint64_t addr,
+        void *buf, size_t size, int is_write);
+
+#endif /* ADDRSPACE_H */
diff --git a/contrib/elf2dmp/download.c b/contrib/elf2dmp/download.c
new file mode 100644
index 0000000000..d09e607431
--- /dev/null
+++ b/contrib/elf2dmp/download.c
@@ -0,0 +1,47 @@
+/*
+ * 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 <curl/curl.h>
+#include "download.h"
+
+int download_url(const char *name, const char *url)
+{
+    int err = 0;
+    FILE *file;
+    CURL *curl = curl_easy_init();
+
+    if (!curl) {
+        return 1;
+    }
+
+    file = fopen(name, "wb");
+    if (!file) {
+        err = 1;
+        goto out_curl;
+    }
+
+    curl_easy_setopt(curl, CURLOPT_URL, url);
+    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
+    curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
+    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
+
+    if (curl_easy_perform(curl) != CURLE_OK) {
+        err = 1;
+        fclose(file);
+        unlink(name);
+        goto out_curl;
+    }
+
+    err = fclose(file);
+
+out_curl:
+    curl_easy_cleanup(curl);
+
+    return err;
+}
diff --git a/contrib/elf2dmp/download.h b/contrib/elf2dmp/download.h
new file mode 100644
index 0000000000..5c274925f7
--- /dev/null
+++ b/contrib/elf2dmp/download.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2018 Virtuozzo International GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ *
+ */
+
+#ifndef DOWNLOAD_H
+#define DOWNLOAD_H
+
+int download_url(const char *name, const char *url);
+
+#endif /* DOWNLOAD_H */
diff --git a/contrib/elf2dmp/err.h b/contrib/elf2dmp/err.h
new file mode 100644
index 0000000000..5456bd5a30
--- /dev/null
+++ b/contrib/elf2dmp/err.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2018 Virtuozzo International GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ *
+ */
+
+#ifndef ERR_H
+#define ERR_H
+
+#define eprintf(...) fprintf(stderr, __VA_ARGS__)
+
+#endif /* ERR_H */
diff --git a/contrib/elf2dmp/kdbg.h b/contrib/elf2dmp/kdbg.h
new file mode 100644
index 0000000000..851b57c321
--- /dev/null
+++ b/contrib/elf2dmp/kdbg.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2018 Virtuozzo International GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ *
+ */
+
+#ifndef KDBG_H
+#define KDBG_H
+
+typedef struct DBGKD_GET_VERSION64 {
+    uint16_t  MajorVersion;
+    uint16_t  MinorVersion;
+    uint8_t   ProtocolVersion;
+    uint8_t   KdSecondaryVersion;
+    uint16_t  Flags;
+    uint16_t  MachineType;
+    uint8_t   MaxPacketType;
+    uint8_t   MaxStateChange;
+    uint8_t   MaxManipulate;
+    uint8_t   Simulation;
+    uint16_t  Unused[1];
+    uint64_t KernBase;
+    uint64_t PsLoadedModuleList;
+    uint64_t DebuggerDataList;
+} DBGKD_GET_VERSION64;
+
+typedef struct DBGKD_DEBUG_DATA_HEADER64 {
+    struct LIST_ENTRY64 {
+       struct LIST_ENTRY64 *Flink;
+       struct LIST_ENTRY64 *Blink;
+    } List;
+    uint32_t           OwnerTag;
+    uint32_t           Size;
+} DBGKD_DEBUG_DATA_HEADER64;
+
+typedef struct KDDEBUGGER_DATA64 {
+    DBGKD_DEBUG_DATA_HEADER64 Header;
+
+    uint64_t KernBase;
+    uint64_t BreakpointWithStatus;
+    uint64_t SavedContext;
+    uint16_t ThCallbackStack;
+    uint16_t NextCallback;
+    uint16_t FramePointer;
+    uint16_t PaeEnabled:1;
+    uint64_t KiCallUserMode;
+    uint64_t KeUserCallbackDispatcher;
+    uint64_t PsLoadedModuleList;
+    uint64_t PsActiveProcessHead;
+    uint64_t PspCidTable;
+    uint64_t ExpSystemResourcesList;
+    uint64_t ExpPagedPoolDescriptor;
+    uint64_t ExpNumberOfPagedPools;
+    uint64_t KeTimeIncrement;
+    uint64_t KeBugCheckCallbackListHead;
+    uint64_t KiBugcheckData;
+    uint64_t IopErrorLogListHead;
+    uint64_t ObpRootDirectoryObject;
+    uint64_t ObpTypeObjectType;
+    uint64_t MmSystemCacheStart;
+    uint64_t MmSystemCacheEnd;
+    uint64_t MmSystemCacheWs;
+    uint64_t MmPfnDatabase;
+    uint64_t MmSystemPtesStart;
+    uint64_t MmSystemPtesEnd;
+    uint64_t MmSubsectionBase;
+    uint64_t MmNumberOfPagingFiles;
+    uint64_t MmLowestPhysicalPage;
+    uint64_t MmHighestPhysicalPage;
+    uint64_t MmNumberOfPhysicalPages;
+    uint64_t MmMaximumNonPagedPoolInBytes;
+    uint64_t MmNonPagedSystemStart;
+    uint64_t MmNonPagedPoolStart;
+    uint64_t MmNonPagedPoolEnd;
+    uint64_t MmPagedPoolStart;
+    uint64_t MmPagedPoolEnd;
+    uint64_t MmPagedPoolInformation;
+    uint64_t MmPageSize;
+    uint64_t MmSizeOfPagedPoolInBytes;
+    uint64_t MmTotalCommitLimit;
+    uint64_t MmTotalCommittedPages;
+    uint64_t MmSharedCommit;
+    uint64_t MmDriverCommit;
+    uint64_t MmProcessCommit;
+    uint64_t MmPagedPoolCommit;
+    uint64_t MmExtendedCommit;
+    uint64_t MmZeroedPageListHead;
+    uint64_t MmFreePageListHead;
+    uint64_t MmStandbyPageListHead;
+    uint64_t MmModifiedPageListHead;
+    uint64_t MmModifiedNoWritePageListHead;
+    uint64_t MmAvailablePages;
+    uint64_t MmResidentAvailablePages;
+    uint64_t PoolTrackTable;
+    uint64_t NonPagedPoolDescriptor;
+    uint64_t MmHighestUserAddress;
+    uint64_t MmSystemRangeStart;
+    uint64_t MmUserProbeAddress;
+    uint64_t KdPrintCircularBuffer;
+    uint64_t KdPrintCircularBufferEnd;
+    uint64_t KdPrintWritePointer;
+    uint64_t KdPrintRolloverCount;
+    uint64_t MmLoadedUserImageList;
+
+    /* NT 5.1 Addition */
+
+    uint64_t NtBuildLab;
+    uint64_t KiNormalSystemCall;
+
+    /* NT 5.0 hotfix addition */
+
+    uint64_t KiProcessorBlock;
+    uint64_t MmUnloadedDrivers;
+    uint64_t MmLastUnloadedDriver;
+    uint64_t MmTriageActionTaken;
+    uint64_t MmSpecialPoolTag;
+    uint64_t KernelVerifier;
+    uint64_t MmVerifierData;
+    uint64_t MmAllocatedNonPagedPool;
+    uint64_t MmPeakCommitment;
+    uint64_t MmTotalCommitLimitMaximum;
+    uint64_t CmNtCSDVersion;
+
+    /* NT 5.1 Addition */
+
+    uint64_t MmPhysicalMemoryBlock;
+    uint64_t MmSessionBase;
+    uint64_t MmSessionSize;
+    uint64_t MmSystemParentTablePage;
+
+    /* Server 2003 addition */
+
+    uint64_t MmVirtualTranslationBase;
+    uint16_t OffsetKThreadNextProcessor;
+    uint16_t OffsetKThreadTeb;
+    uint16_t OffsetKThreadKernelStack;
+    uint16_t OffsetKThreadInitialStack;
+    uint16_t OffsetKThreadApcProcess;
+    uint16_t OffsetKThreadState;
+    uint16_t OffsetKThreadBStore;
+    uint16_t OffsetKThreadBStoreLimit;
+    uint16_t SizeEProcess;
+    uint16_t OffsetEprocessPeb;
+    uint16_t OffsetEprocessParentCID;
+    uint16_t OffsetEprocessDirectoryTableBase;
+    uint16_t SizePrcb;
+    uint16_t OffsetPrcbDpcRoutine;
+    uint16_t OffsetPrcbCurrentThread;
+    uint16_t OffsetPrcbMhz;
+    uint16_t OffsetPrcbCpuType;
+    uint16_t OffsetPrcbVendorString;
+    uint16_t OffsetPrcbProcStateContext;
+    uint16_t OffsetPrcbNumber;
+    uint16_t SizeEThread;
+    uint64_t KdPrintCircularBufferPtr;
+    uint64_t KdPrintBufferSize;
+    uint64_t KeLoaderBlock;
+    uint16_t SizePcr;
+    uint16_t OffsetPcrSelfPcr;
+    uint16_t OffsetPcrCurrentPrcb;
+    uint16_t OffsetPcrContainedPrcb;
+    uint16_t OffsetPcrInitialBStore;
+    uint16_t OffsetPcrBStoreLimit;
+    uint16_t OffsetPcrInitialStack;
+    uint16_t OffsetPcrStackLimit;
+    uint16_t OffsetPrcbPcrPage;
+    uint16_t OffsetPrcbProcStateSpecialReg;
+    uint16_t GdtR0Code;
+    uint16_t GdtR0Data;
+    uint16_t GdtR0Pcr;
+    uint16_t GdtR3Code;
+    uint16_t GdtR3Data;
+    uint16_t GdtR3Teb;
+    uint16_t GdtLdt;
+    uint16_t GdtTss;
+    uint16_t Gdt64R3CmCode;
+    uint16_t Gdt64R3CmTeb;
+    uint64_t IopNumTriageDumpDataBlocks;
+    uint64_t IopTriageDumpDataBlocks;
+
+    /* Longhorn addition */
+
+    uint64_t VfCrashDataBlock;
+    uint64_t MmBadPagesDetected;
+    uint64_t MmZeroedPageSingleBitErrorsDetected;
+
+    /* Windows 7 addition */
+
+    uint64_t EtwpDebuggerData;
+    uint16_t OffsetPrcbContext;
+} KDDEBUGGER_DATA64;
+
+#endif /* KDBG_H */
diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c
new file mode 100644
index 0000000000..9b93dab662
--- /dev/null
+++ b/contrib/elf2dmp/main.c
@@ -0,0 +1,589 @@
+/*
+ * 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 "addrspace.h"
+#include "pe.h"
+#include "pdb.h"
+#include "kdbg.h"
+#include "download.h"
+#include "qemu/win_dump_defs.h"
+
+#define SYM_URL_BASE    "https://msdl.microsoft.com/download/symbols/"
+#define PDB_NAME    "ntkrnlmp.pdb"
+
+#define INITIAL_MXCSR   0x1f80
+
+typedef struct idt_desc {
+    uint16_t offset1;   /* offset bits 0..15 */
+    uint16_t selector;
+    uint8_t ist;
+    uint8_t type_attr;
+    uint16_t offset2;   /* offset bits 16..31 */
+    uint32_t offset3;   /* offset bits 32..63 */
+    uint32_t rsrvd;
+} __attribute__ ((packed)) idt_desc_t;
+
+static uint64_t idt_desc_addr(idt_desc_t desc)
+{
+    return (uint64_t)desc.offset1 | ((uint64_t)desc.offset2 << 16) |
+          ((uint64_t)desc.offset3 << 32);
+}
+
+static const uint64_t SharedUserData = 0xfffff78000000000;
+
+#define KUSD_OFFSET_SUITE_MASK 0x2d0
+#define KUSD_OFFSET_PRODUCT_TYPE 0x264
+
+#define SYM_RESOLVE(base, r, s) ((s = pdb_resolve(base, r, #s)),\
+    s ? printf(#s" = 0x%016lx\n", s) : eprintf("Failed to resolve "#s"\n"), s)
+
+static uint64_t rol(uint64_t x, uint64_t y)
+{
+    return (x << y) | (x >> (64 - y));
+}
+
+/*
+ * Decoding algorithm can be found in Volatility project
+ */
+static void kdbg_decode(uint64_t *dst, uint64_t *src, size_t size,
+        uint64_t kwn, uint64_t kwa, uint64_t kdbe)
+{
+    size_t i;
+    assert(size % sizeof(uint64_t) == 0);
+    for (i = 0; i < size / sizeof(uint64_t); i++) {
+        uint64_t block;
+
+        block = src[i];
+        block = rol(block ^ kwn, (uint8_t)kwn);
+        block = __builtin_bswap64(block ^ kdbe) ^ kwa;
+        dst[i] = block;
+    }
+}
+
+static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb,
+        struct va_space *vs, uint64_t KdDebuggerDataBlock)
+{
+    const char OwnerTag[4] = "KDBG";
+    KDDEBUGGER_DATA64 *kdbg = NULL;
+    DBGKD_DEBUG_DATA_HEADER64 kdbg_hdr;
+    bool decode = false;
+    uint64_t kwn, kwa, KdpDataBlockEncoded;
+
+    if (va_space_rw(vs,
+                KdDebuggerDataBlock + offsetof(KDDEBUGGER_DATA64, Header),
+                &kdbg_hdr, sizeof(kdbg_hdr), 0)) {
+        eprintf("Failed to extract KDBG header\n");
+        return NULL;
+    }
+
+    if (memcmp(&kdbg_hdr.OwnerTag, OwnerTag, sizeof(OwnerTag))) {
+        uint64_t KiWaitNever, KiWaitAlways;
+
+        decode = true;
+
+        if (!SYM_RESOLVE(KernBase, pdb, KiWaitNever) ||
+                !SYM_RESOLVE(KernBase, pdb, KiWaitAlways) ||
+                !SYM_RESOLVE(KernBase, pdb, KdpDataBlockEncoded)) {
+            return NULL;
+        }
+
+        if (va_space_rw(vs, KiWaitNever, &kwn, sizeof(kwn), 0) ||
+                va_space_rw(vs, KiWaitAlways, &kwa, sizeof(kwa), 0)) {
+            return NULL;
+        }
+
+        printf("[KiWaitNever] = 0x%016lx\n", kwn);
+        printf("[KiWaitAlways] = 0x%016lx\n", kwa);
+
+        /*
+         * If KDBG header can be decoded, KDBG size is available
+         * and entire KDBG can be decoded.
+         */
+        printf("Decoding KDBG header...\n");
+        kdbg_decode((uint64_t *)&kdbg_hdr, (uint64_t *)&kdbg_hdr,
+                sizeof(kdbg_hdr), kwn, kwa, KdpDataBlockEncoded);
+
+        printf("Owner tag is \'%.4s\'\n", (char *)&kdbg_hdr.OwnerTag);
+        if (memcmp(&kdbg_hdr.OwnerTag, OwnerTag, sizeof(OwnerTag))) {
+            eprintf("Failed to decode KDBG header\n");
+            return NULL;
+        }
+    }
+
+    kdbg = malloc(kdbg_hdr.Size);
+    if (!kdbg) {
+        return NULL;
+    }
+
+    if (va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) {
+        eprintf("Failed to extract entire KDBG\n");
+        return NULL;
+    }
+
+    if (!decode) {
+        return kdbg;
+    }
+
+    printf("Decoding KdDebuggerDataBlock...\n");
+    kdbg_decode((uint64_t *)kdbg, (uint64_t *)kdbg, kdbg_hdr.Size,
+                kwn, kwa, KdpDataBlockEncoded);
+
+    va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 1);
+
+    return kdbg;
+}
+
+static void win_context_init_from_qemu_cpu_state(WinContext *ctx,
+        QEMUCPUState *s)
+{
+    WinContext win_ctx = (WinContext){
+        .ContextFlags = WIN_CTX_X64 | WIN_CTX_INT | WIN_CTX_SEG | WIN_CTX_CTL,
+        .MxCsr = INITIAL_MXCSR,
+
+        .SegCs = s->cs.selector,
+        .SegSs = s->ss.selector,
+        .SegDs = s->ds.selector,
+        .SegEs = s->es.selector,
+        .SegFs = s->fs.selector,
+        .SegGs = s->gs.selector,
+        .EFlags = (uint32_t)s->rflags,
+
+        .Rax = s->rax,
+        .Rbx = s->rbx,
+        .Rcx = s->rcx,
+        .Rdx = s->rdx,
+        .Rsp = s->rsp,
+        .Rbp = s->rbp,
+        .Rsi = s->rsi,
+        .Rdi = s->rdi,
+        .R8  = s->r8,
+        .R9  = s->r9,
+        .R10 = s->r10,
+        .R11 = s->r11,
+        .R12 = s->r12,
+        .R13 = s->r13,
+        .R14 = s->r14,
+        .R15 = s->r15,
+
+        .Rip = s->rip,
+        .FltSave = {
+            .MxCsr = INITIAL_MXCSR,
+        },
+    };
+
+    *ctx = win_ctx;
+}
+
+/*
+ * Finds paging-structure hierarchy base,
+ * if previously set doesn't give access to kernel structures
+ */
+static int fix_dtb(struct va_space *vs, QEMU_Elf *qe)
+{
+    /*
+     * Firstly, test previously set DTB.
+     */
+    if (va_space_resolve(vs, SharedUserData)) {
+        return 0;
+    }
+
+    /*
+     * Secondly, find CPU which run system task.
+     */
+    size_t i;
+    for (i = 0; i < qe->state_nr; i++) {
+        QEMUCPUState *s = qe->state[i];
+
+        if (is_system(s)) {
+            va_space_set_dtb(vs, s->cr[3]);
+            printf("DTB 0x%016lx has been found from CPU #%zu"
+                    " as system task CR3\n", vs->dtb, i);
+            return !(va_space_resolve(vs, SharedUserData));
+        }
+    }
+
+    /*
+     * Thirdly, use KERNEL_GS_BASE from CPU #0 as PRCB address and
+     * CR3 as [Prcb+0x7000]
+     */
+    if (qe->has_kernel_gs_base) {
+        QEMUCPUState *s = qe->state[0];
+        uint64_t Prcb = s->kernel_gs_base;
+        uint64_t *cr3 = va_space_resolve(vs, Prcb + 0x7000);
+
+        if (!cr3) {
+            return 1;
+        }
+
+        va_space_set_dtb(vs, *cr3);
+        printf("DirectoryTableBase = 0x%016lx has been found from CPU #0"
+                " as interrupt handling CR3\n", vs->dtb);
+        return !(va_space_resolve(vs, SharedUserData));
+    }
+
+    return 1;
+}
+
+static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps,
+        struct va_space *vs, uint64_t KdDebuggerDataBlock,
+        KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock, int nr_cpus)
+{
+    uint32_t *suite_mask = va_space_resolve(vs, SharedUserData +
+            KUSD_OFFSET_SUITE_MASK);
+    int32_t *product_type = va_space_resolve(vs, SharedUserData +
+            KUSD_OFFSET_PRODUCT_TYPE);
+    DBGKD_GET_VERSION64 kvb;
+    WinDumpHeader64 h;
+    size_t i;
+
+    QEMU_BUILD_BUG_ON(KUSD_OFFSET_SUITE_MASK >= PAGE_SIZE);
+    QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= PAGE_SIZE);
+
+    if (!suite_mask || !product_type) {
+        return 1;
+    }
+
+    if (va_space_rw(vs, KdVersionBlock, &kvb, sizeof(kvb), 0)) {
+        eprintf("Failed to extract KdVersionBlock\n");
+        return 1;
+    }
+
+    h = (WinDumpHeader64) {
+        .Signature = "PAGE",
+        .ValidDump = "DU64",
+        .MajorVersion = kvb.MajorVersion,
+        .MinorVersion = kvb.MinorVersion,
+        .DirectoryTableBase = vs->dtb,
+        .PfnDatabase = kdbg->MmPfnDatabase,
+        .PsLoadedModuleList = kdbg->PsLoadedModuleList,
+        .PsActiveProcessHead = kdbg->PsActiveProcessHead,
+        .MachineImageType = kvb.MachineType,
+        .NumberProcessors = nr_cpus,
+        .BugcheckCode = LIVE_SYSTEM_DUMP,
+        .KdDebuggerDataBlock = KdDebuggerDataBlock,
+        .DumpType = 1,
+        .Comment = "Hello from elf2dmp!",
+        .SuiteMask = *suite_mask,
+        .ProductType = *product_type,
+        .SecondaryDataState = kvb.KdSecondaryVersion,
+        .PhysicalMemoryBlock = (WinDumpPhyMemDesc64) {
+            .NumberOfRuns = ps->block_nr,
+        },
+        .RequiredDumpSpace = sizeof(h),
+    };
+
+    for (i = 0; i < ps->block_nr; i++) {
+        h.PhysicalMemoryBlock.NumberOfPages += ps->block[i].size / PAGE_SIZE;
+        h.PhysicalMemoryBlock.Run[i] = (WinDumpPhyMemRun64) {
+            .BasePage = ps->block[i].paddr / PAGE_SIZE,
+            .PageCount = ps->block[i].size / PAGE_SIZE,
+        };
+    }
+
+    h.RequiredDumpSpace += h.PhysicalMemoryBlock.NumberOfPages << PAGE_BITS;
+
+    *hdr = h;
+
+    return 0;
+}
+
+static int fill_context(KDDEBUGGER_DATA64 *kdbg,
+        struct va_space *vs, QEMU_Elf *qe)
+{
+	int i;
+    for (i = 0; i < qe->state_nr; i++) {
+        uint64_t Prcb;
+        uint64_t Context;
+        WinContext ctx;
+        QEMUCPUState *s = qe->state[i];
+
+        if (va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i,
+                    &Prcb, sizeof(Prcb), 0)) {
+            eprintf("Failed to read CPU #%d PRCB location\n", i);
+            return 1;
+        }
+
+        if (va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext,
+                    &Context, sizeof(Context), 0)) {
+            eprintf("Failed to read CPU #%d ContextFrame location\n", i);
+            return 1;
+        }
+
+        printf("Filling context for CPU #%d...\n", i);
+        win_context_init_from_qemu_cpu_state(&ctx, s);
+
+        if (va_space_rw(vs, Context, &ctx, sizeof(ctx), 1)) {
+            eprintf("Failed to fill CPU #%d context\n", i);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static int write_dump(struct pa_space *ps,
+        WinDumpHeader64 *hdr, const char *name)
+{
+    FILE *dmp_file = fopen(name, "wb");
+    size_t i;
+
+    if (!dmp_file) {
+        eprintf("Failed to open output file \'%s\'\n", name);
+        return 1;
+    }
+
+    printf("Writing header to file...\n");
+
+    if (fwrite(hdr, sizeof(*hdr), 1, dmp_file) != 1) {
+        eprintf("Failed to write dump header\n");
+        fclose(dmp_file);
+        return 1;
+    }
+
+    for (i = 0; i < ps->block_nr; i++) {
+        struct pa_block *b = &ps->block[i];
+
+        printf("Writing block #%zu/%zu to file...\n", i, ps->block_nr);
+        if (fwrite(b->addr, b->size, 1, dmp_file) != 1) {
+            eprintf("Failed to write dump header\n");
+            fclose(dmp_file);
+            return 1;
+        }
+    }
+
+    return fclose(dmp_file);
+}
+
+static int pe_get_pdb_symstore_hash(uint64_t base, void *start_addr,
+        char *hash, struct va_space *vs)
+{
+    const char e_magic[2] = "MZ";
+    const char Signature[4] = "PE\0\0";
+    const char sign_rsds[4] = "RSDS";
+    IMAGE_DOS_HEADER *dos_hdr = start_addr;
+    IMAGE_NT_HEADERS64 nt_hdrs;
+    IMAGE_FILE_HEADER *file_hdr = &nt_hdrs.FileHeader;
+    IMAGE_OPTIONAL_HEADER64 *opt_hdr = &nt_hdrs.OptionalHeader;
+    IMAGE_DATA_DIRECTORY *data_dir = nt_hdrs.OptionalHeader.DataDirectory;
+    IMAGE_DEBUG_DIRECTORY debug_dir;
+    OMFSignatureRSDS rsds;
+    char *pdb_name;
+    size_t pdb_name_sz;
+    size_t i;
+
+    QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= PAGE_SIZE);
+
+    if (memcmp(&dos_hdr->e_magic, e_magic, sizeof(e_magic))) {
+        return 1;
+    }
+
+    if (va_space_rw(vs, base + dos_hdr->e_lfanew,
+                &nt_hdrs, sizeof(nt_hdrs), 0)) {
+        return 1;
+    }
+
+    if (memcmp(&nt_hdrs.Signature, Signature, sizeof(Signature)) ||
+            file_hdr->Machine != 0x8664 || opt_hdr->Magic != 0x020b) {
+        return 1;
+    }
+
+    printf("Debug Directory RVA = 0x%016x\n",
+            data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress);
+
+    if (va_space_rw(vs,
+                base + data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress,
+                &debug_dir, sizeof(debug_dir), 0)) {
+        return 1;
+    }
+
+    if (debug_dir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
+        return 1;
+    }
+
+    if (va_space_rw(vs,
+                base + debug_dir.AddressOfRawData,
+                &rsds, sizeof(rsds), 0)) {
+        return 1;
+    }
+
+    printf("CodeView signature is \'%.4s\'\n", rsds.Signature);
+
+    if (memcmp(&rsds.Signature, sign_rsds, sizeof(sign_rsds))) {
+        return 1;
+    }
+
+    pdb_name_sz = debug_dir.SizeOfData - sizeof(rsds);
+    pdb_name = malloc(pdb_name_sz);
+    if (!pdb_name) {
+        return 1;
+    }
+
+    if (va_space_rw(vs, base + debug_dir.AddressOfRawData +
+                offsetof(OMFSignatureRSDS, name), pdb_name, pdb_name_sz, 0)) {
+        free(pdb_name);
+        return 1;
+    }
+
+    printf("PDB name is \'%s\', \'%s\' expected\n", pdb_name, PDB_NAME);
+
+    if (strcmp(pdb_name, PDB_NAME)) {
+        eprintf("Unexpected PDB name, it seems the kernel isn't found\n");
+        free(pdb_name);
+        return 1;
+    }
+
+    free(pdb_name);
+
+    sprintf(hash, "%.08x%.04x%.04x%.02x%.02x", rsds.guid.a, rsds.guid.b,
+            rsds.guid.c, rsds.guid.d[0], rsds.guid.d[1]);
+    hash += 20;
+    for (i = 0; i < 6; i++, hash += 2) {
+        sprintf(hash, "%.02x", rsds.guid.e[i]);
+    }
+
+    sprintf(hash, "%.01x", rsds.age);
+
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    int err = 0;
+    QEMU_Elf qemu_elf;
+    struct pa_space ps;
+    struct va_space vs;
+    QEMUCPUState *state;
+    idt_desc_t first_idt_desc;
+    uint64_t KernBase;
+    void *nt_start_addr = NULL;
+    WinDumpHeader64 header;
+    char pdb_hash[34];
+    char pdb_url[] = SYM_URL_BASE PDB_NAME
+        "/0123456789ABCDEF0123456789ABCDEFx/" PDB_NAME;
+    struct pdb_reader pdb;
+    uint64_t KdDebuggerDataBlock;
+    KDDEBUGGER_DATA64 *kdbg;
+    uint64_t KdVersionBlock;
+
+    if (argc != 3) {
+        eprintf("usage:\n\t%s elf_file dmp_file\n", argv[0]);
+        return 1;
+    }
+
+    if (QEMU_Elf_init(&qemu_elf, argv[1])) {
+        eprintf("Failed to initialize QEMU ELF dump\n");
+        return 1;
+    }
+
+    if (pa_space_create(&ps, &qemu_elf)) {
+        eprintf("Failed to initialize physical address space\n");
+        err = 1;
+        goto out_elf;
+    }
+
+    state = qemu_elf.state[0];
+    printf("CPU #0 CR3 is 0x%016lx\n", state->cr[3]);
+
+    va_space_create(&vs, &ps, state->cr[3]);
+    if (fix_dtb(&vs, &qemu_elf)) {
+        eprintf("Failed to find paging base\n");
+        err = 1;
+        goto out_elf;
+    }
+
+    printf("CPU #0 IDT is at 0x%016lx\n", state->idt.base);
+
+    if (va_space_rw(&vs, state->idt.base,
+                &first_idt_desc, sizeof(first_idt_desc), 0)) {
+        eprintf("Failed to get CPU #0 IDT[0]\n");
+        err = 1;
+        goto out_ps;
+    }
+    printf("CPU #0 IDT[0] -> 0x%016lx\n", idt_desc_addr(first_idt_desc));
+
+    KernBase = idt_desc_addr(first_idt_desc) & ~(PAGE_SIZE - 1);
+    printf("Searching kernel downwards from 0x%16lx...\n", KernBase);
+
+    for (; KernBase >= 0xfffff78000000000; KernBase -= PAGE_SIZE) {
+        nt_start_addr = va_space_resolve(&vs, KernBase);
+        if (!nt_start_addr) {
+            continue;
+        }
+
+        if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */
+            break;
+        }
+    }
+
+    printf("KernBase = 0x%16lx, signature is \'%.2s\'\n", KernBase,
+            (char *)nt_start_addr);
+
+    if (pe_get_pdb_symstore_hash(KernBase, nt_start_addr, pdb_hash, &vs)) {
+        eprintf("Failed to get PDB symbol store hash\n");
+        err = 1;
+        goto out_ps;
+    }
+
+    sprintf(pdb_url, "%s%s/%s/%s", SYM_URL_BASE, PDB_NAME, pdb_hash, PDB_NAME);
+    printf("PDB URL is %s\n", pdb_url);
+
+    if (download_url(PDB_NAME, pdb_url)) {
+        eprintf("Failed to download PDB file\n");
+        err = 1;
+        goto out_ps;
+    }
+
+    if (pdb_init_from_file(PDB_NAME, &pdb)) {
+        eprintf("Failed to initialize PDB reader\n");
+        err = 1;
+        goto out_pdb_file;
+    }
+
+    if (!SYM_RESOLVE(KernBase, &pdb, KdDebuggerDataBlock) ||
+            !SYM_RESOLVE(KernBase, &pdb, KdVersionBlock)) {
+        err = 1;
+        goto out_pdb;
+    }
+
+    kdbg = get_kdbg(KernBase, &pdb, &vs, KdDebuggerDataBlock);
+    if (!kdbg) {
+        err = 1;
+        goto out_pdb;
+    }
+
+    if (fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg,
+            KdVersionBlock, qemu_elf.state_nr)) {
+        err = 1;
+        goto out_pdb;
+    }
+
+    if (fill_context(kdbg, &vs, &qemu_elf)) {
+        err = 1;
+        goto out_pdb;
+    }
+
+    if (write_dump(&ps, &header, argv[2])) {
+        eprintf("Failed to save dump\n");
+        err = 1;
+        goto out_kdbg;
+    }
+
+out_kdbg:
+    free(kdbg);
+out_pdb:
+    pdb_exit(&pdb);
+out_pdb_file:
+    unlink(PDB_NAME);
+out_ps:
+    pa_space_destroy(&ps);
+out_elf:
+    QEMU_Elf_exit(&qemu_elf);
+
+    return err;
+}
diff --git a/contrib/elf2dmp/pdb.c b/contrib/elf2dmp/pdb.c
new file mode 100644
index 0000000000..bcb01b414f
--- /dev/null
+++ b/contrib/elf2dmp/pdb.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2018 Virtuozzo International GmbH
+ *
+ * Based on source of Wine project
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "qemu/osdep.h"
+#include "pdb.h"
+#include "err.h"
+
+static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
+{
+    return r->ds.toc->file_size[idx];
+}
+
+static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
+{
+    size_t i = 0;
+    char *ptr;
+
+    for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
+        i++;
+        ptr += 8;
+        if (i == n) {
+            break;
+        }
+        ptr += sizeof(pdb_seg);
+    }
+
+    return (pdb_seg *)ptr;
+}
+
+uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
+{
+    size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
+    int length;
+    const union codeview_symbol *sym;
+    const uint8_t *root = r->modimage;
+    size_t i;
+
+    for (i = 0; i < size; i += length) {
+        sym = (const void *)(root + i);
+        length = sym->generic.len + 2;
+
+        if (!sym->generic.id || length < 4) {
+            break;
+        }
+
+        if (sym->generic.id == S_PUB_V3 &&
+                !strcmp(name, sym->public_v3.name)) {
+            pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
+            uint32_t sect_rva = segment->dword[1];
+            uint64_t rva = sect_rva + sym->public_v3.offset;
+
+            printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09lx\n", name,
+                    sect_rva, sym->public_v3.segment,
+                    ((char *)segment - 8), sym->public_v3.offset, rva);
+            return rva;
+        }
+    }
+
+    return 0;
+}
+
+uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
+{
+    uint64_t rva = pdb_find_public_v3_symbol(r, name);
+
+    if (!rva) {
+        return 0;
+    }
+
+    return img_base + rva;
+}
+
+static void pdb_reader_ds_exit(struct pdb_reader *r)
+{
+    free(r->ds.toc);
+}
+
+static void pdb_exit_symbols(struct pdb_reader *r)
+{
+    free(r->modimage);
+    free(r->symbols);
+}
+
+static void pdb_exit_segments(struct pdb_reader *r)
+{
+    free(r->segs);
+}
+
+static void *pdb_ds_read(const PDB_DS_HEADER *header,
+        const uint32_t *block_list, int size)
+{
+    int i, nBlocks;
+    uint8_t *buffer;
+
+    if (!size) {
+        return NULL;
+    }
+
+    nBlocks = (size + header->block_size - 1) / header->block_size;
+
+    buffer = malloc(nBlocks * header->block_size);
+    if (!buffer) {
+        return NULL;
+    }
+
+    for (i = 0; i < nBlocks; i++) {
+        memcpy(buffer + i * header->block_size, (const char *)header +
+                block_list[i] * header->block_size, header->block_size);
+    }
+
+    return buffer;
+}
+
+static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
+{
+    const uint32_t *block_list;
+    uint32_t block_size;
+    const uint32_t *file_size;
+    size_t i;
+
+    if (!r->ds.toc || file_number >= r->ds.toc->num_files) {
+        return NULL;
+    }
+
+    file_size = r->ds.toc->file_size;
+    r->file_used[file_number / 32] |= 1 << (file_number % 32);
+
+    if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) {
+        return NULL;
+    }
+
+    block_list = file_size + r->ds.toc->num_files;
+    block_size = r->ds.header->block_size;
+
+    for (i = 0; i < file_number; i++) {
+        block_list += (file_size[i] + block_size - 1) / block_size;
+    }
+
+    return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
+}
+
+static int pdb_init_segments(struct pdb_reader *r)
+{
+    char *segs;
+    unsigned stream_idx = r->sidx.segments;
+
+    segs = pdb_ds_read_file(r, stream_idx);
+    if (!segs) {
+        return 1;
+    }
+
+    r->segs = segs;
+    r->segs_size = pdb_get_file_size(r, stream_idx);
+
+    return 0;
+}
+
+static int pdb_init_symbols(struct pdb_reader *r)
+{
+    int err = 0;
+    PDB_SYMBOLS *symbols;
+    PDB_STREAM_INDEXES *sidx = &r->sidx;
+
+    memset(sidx, -1, sizeof(*sidx));
+
+    symbols = pdb_ds_read_file(r, 3);
+    if (!symbols) {
+        return 1;
+    }
+
+    r->symbols = symbols;
+
+    if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) {
+        err = 1;
+        goto out_symbols;
+    }
+
+    memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) +
+            symbols->module_size + symbols->offset_size +
+            symbols->hash_size + symbols->srcmodule_size +
+            symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx));
+
+    /* Read global symbol table */
+    r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
+    if (!r->modimage) {
+        err = 1;
+        goto out_symbols;
+    }
+
+    return 0;
+
+out_symbols:
+    free(symbols);
+
+    return err;
+}
+
+static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
+{
+    memset(r->file_used, 0, sizeof(r->file_used));
+    r->ds.header = hdr;
+    r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
+                hdr->toc_page * hdr->block_size), hdr->toc_size);
+
+    if (!r->ds.toc) {
+        return 1;
+    }
+
+    return 0;
+}
+
+static int pdb_reader_init(struct pdb_reader *r, void *data)
+{
+    int err = 0;
+    const char pdb7[] = "Microsoft C/C++ MSF 7.00";
+
+    if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
+        return 1;
+    }
+
+    if (pdb_reader_ds_init(r, data)) {
+        return 1;
+    }
+
+    r->ds.root = pdb_ds_read_file(r, 1);
+    if (!r->ds.root) {
+        err = 1;
+        goto out_ds;
+    }
+
+    if (pdb_init_symbols(r)) {
+        err = 1;
+        goto out_root;
+    }
+
+    if (pdb_init_segments(r)) {
+        err = 1;
+        goto out_sym;
+    }
+
+    return 0;
+
+out_sym:
+    pdb_exit_symbols(r);
+out_root:
+    free(r->ds.root);
+out_ds:
+    pdb_reader_ds_exit(r);
+
+    return err;
+}
+
+static void pdb_reader_exit(struct pdb_reader *r)
+{
+    pdb_exit_segments(r);
+    pdb_exit_symbols(r);
+    free(r->ds.root);
+    pdb_reader_ds_exit(r);
+}
+
+int pdb_init_from_file(const char *name, struct pdb_reader *reader)
+{
+    int err = 0;
+    int fd;
+    void *map;
+    struct stat st;
+
+    fd = open(name, O_RDONLY, 0);
+    if (fd == -1) {
+        eprintf("Failed to open PDB file \'%s\'\n", name);
+        return 1;
+    }
+    reader->fd = fd;
+
+    fstat(fd, &st);
+    reader->file_size = st.st_size;
+
+    map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if (map == MAP_FAILED) {
+        eprintf("Failed to map PDB file\n");
+        err = 1;
+        goto out_fd;
+    }
+
+    if (pdb_reader_init(reader, map)) {
+        err = 1;
+        goto out_unmap;
+    }
+
+    return 0;
+
+out_unmap:
+    munmap(map, st.st_size);
+out_fd:
+    close(fd);
+
+    return err;
+}
+
+void pdb_exit(struct pdb_reader *reader)
+{
+    munmap(reader->ds.header, reader->file_size);
+    close(reader->fd);
+    pdb_reader_exit(reader);
+}
diff --git a/contrib/elf2dmp/pdb.h b/contrib/elf2dmp/pdb.h
new file mode 100644
index 0000000000..4351a2dd61
--- /dev/null
+++ b/contrib/elf2dmp/pdb.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2018 Virtuozzo International GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ *
+ */
+
+#ifndef PDB_H
+#define PDB_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct GUID {
+    unsigned int Data1;
+    unsigned short Data2;
+    unsigned short Data3;
+    unsigned char Data4[8];
+} GUID;
+
+struct PDB_FILE {
+    uint32_t size;
+    uint32_t unknown;
+};
+
+typedef struct PDB_DS_HEADER {
+    char signature[32];
+    uint32_t block_size;
+    uint32_t unknown1;
+    uint32_t num_pages;
+    uint32_t toc_size;
+    uint32_t unknown2;
+    uint32_t toc_page;
+} PDB_DS_HEADER;
+
+typedef struct PDB_DS_TOC {
+    uint32_t num_files;
+    uint32_t file_size[1];
+} PDB_DS_TOC;
+
+typedef struct PDB_DS_ROOT {
+    uint32_t Version;
+    uint32_t TimeDateStamp;
+    uint32_t Age;
+    GUID guid;
+    uint32_t cbNames;
+    char names[1];
+} PDB_DS_ROOT;
+
+typedef struct PDB_TYPES_OLD {
+    uint32_t version;
+    uint16_t first_index;
+    uint16_t last_index;
+    uint32_t type_size;
+    uint16_t file;
+    uint16_t pad;
+} PDB_TYPES_OLD;
+
+typedef struct PDB_TYPES {
+    uint32_t version;
+    uint32_t type_offset;
+    uint32_t first_index;
+    uint32_t last_index;
+    uint32_t type_size;
+    uint16_t file;
+    uint16_t pad;
+    uint32_t hash_size;
+    uint32_t hash_base;
+    uint32_t hash_offset;
+    uint32_t hash_len;
+    uint32_t search_offset;
+    uint32_t search_len;
+    uint32_t unknown_offset;
+    uint32_t unknown_len;
+} PDB_TYPES;
+
+typedef struct PDB_SYMBOL_RANGE {
+    uint16_t segment;
+    uint16_t pad1;
+    uint32_t offset;
+    uint32_t size;
+    uint32_t characteristics;
+    uint16_t index;
+    uint16_t pad2;
+} PDB_SYMBOL_RANGE;
+
+typedef struct PDB_SYMBOL_RANGE_EX {
+    uint16_t segment;
+    uint16_t pad1;
+    uint32_t offset;
+    uint32_t size;
+    uint32_t characteristics;
+    uint16_t index;
+    uint16_t pad2;
+    uint32_t timestamp;
+    uint32_t unknown;
+} PDB_SYMBOL_RANGE_EX;
+
+typedef struct PDB_SYMBOL_FILE {
+    uint32_t unknown1;
+    PDB_SYMBOL_RANGE range;
+    uint16_t flag;
+    uint16_t file;
+    uint32_t symbol_size;
+    uint32_t lineno_size;
+    uint32_t unknown2;
+    uint32_t nSrcFiles;
+    uint32_t attribute;
+    char filename[1];
+} PDB_SYMBOL_FILE;
+
+typedef struct PDB_SYMBOL_FILE_EX {
+    uint32_t unknown1;
+    PDB_SYMBOL_RANGE_EX range;
+    uint16_t flag;
+    uint16_t file;
+    uint32_t symbol_size;
+    uint32_t lineno_size;
+    uint32_t unknown2;
+    uint32_t nSrcFiles;
+    uint32_t attribute;
+    uint32_t reserved[2];
+    char filename[1];
+} PDB_SYMBOL_FILE_EX;
+
+typedef struct PDB_SYMBOL_SOURCE {
+    uint16_t nModules;
+    uint16_t nSrcFiles;
+    uint16_t table[1];
+} PDB_SYMBOL_SOURCE;
+
+typedef struct PDB_SYMBOL_IMPORT {
+    uint32_t unknown1;
+    uint32_t unknown2;
+    uint32_t TimeDateStamp;
+    uint32_t Age;
+    char filename[1];
+} PDB_SYMBOL_IMPORT;
+
+typedef struct PDB_SYMBOLS_OLD {
+    uint16_t hash1_file;
+    uint16_t hash2_file;
+    uint16_t gsym_file;
+    uint16_t pad;
+    uint32_t module_size;
+    uint32_t offset_size;
+    uint32_t hash_size;
+    uint32_t srcmodule_size;
+} PDB_SYMBOLS_OLD;
+
+typedef struct PDB_SYMBOLS {
+    uint32_t signature;
+    uint32_t version;
+    uint32_t unknown;
+    uint32_t hash1_file;
+    uint32_t hash2_file;
+    uint16_t gsym_file;
+    uint16_t unknown1;
+    uint32_t module_size;
+    uint32_t offset_size;
+    uint32_t hash_size;
+    uint32_t srcmodule_size;
+    uint32_t pdbimport_size;
+    uint32_t resvd0;
+    uint32_t stream_index_size;
+    uint32_t unknown2_size;
+    uint16_t resvd3;
+    uint16_t machine;
+    uint32_t resvd4;
+} PDB_SYMBOLS;
+
+typedef struct {
+    uint16_t FPO;
+    uint16_t unk0;
+    uint16_t unk1;
+    uint16_t unk2;
+    uint16_t unk3;
+    uint16_t segments;
+} PDB_STREAM_INDEXES_OLD;
+
+typedef struct {
+    uint16_t FPO;
+    uint16_t unk0;
+    uint16_t unk1;
+    uint16_t unk2;
+    uint16_t unk3;
+    uint16_t segments;
+    uint16_t unk4;
+    uint16_t unk5;
+    uint16_t unk6;
+    uint16_t FPO_EXT;
+    uint16_t unk7;
+} PDB_STREAM_INDEXES;
+
+union codeview_symbol {
+    struct {
+        int16_t len;
+        int16_t id;
+    } generic;
+
+    struct {
+        int16_t len;
+        int16_t id;
+        uint32_t symtype;
+        uint32_t offset;
+        uint16_t segment;
+        char name[1];
+    } public_v3;
+};
+
+#define S_PUB_V3        0x110E
+
+typedef struct pdb_seg {
+    uint32_t dword[8];
+} __attribute__ ((packed)) pdb_seg;
+
+#define IMAGE_FILE_MACHINE_I386 0x014c
+#define IMAGE_FILE_MACHINE_AMD64 0x8664
+
+struct pdb_reader {
+    int fd;
+    size_t file_size;
+    struct {
+        PDB_DS_HEADER *header;
+        PDB_DS_TOC *toc;
+        PDB_DS_ROOT *root;
+    } ds;
+    uint32_t file_used[1024];
+    PDB_SYMBOLS *symbols;
+    PDB_STREAM_INDEXES sidx;
+    uint8_t *modimage;
+    char *segs;
+    size_t segs_size;
+};
+
+int pdb_init_from_file(const char *name, struct pdb_reader *reader);
+void pdb_exit(struct pdb_reader *reader);
+uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name);
+uint64_t pdb_find_public_v3_symbol(struct pdb_reader *reader, const char *name);
+
+#endif /* PDB_H */
diff --git a/contrib/elf2dmp/pe.h b/contrib/elf2dmp/pe.h
new file mode 100644
index 0000000000..374e06a9c5
--- /dev/null
+++ b/contrib/elf2dmp/pe.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2018 Virtuozzo International GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ *
+ */
+
+#ifndef PE_H
+#define PE_H
+
+#include <stdint.h>
+
+typedef struct IMAGE_DOS_HEADER {
+    uint16_t  e_magic;      /* 0x00: MZ Header signature */
+    uint16_t  e_cblp;       /* 0x02: Bytes on last page of file */
+    uint16_t  e_cp;         /* 0x04: Pages in file */
+    uint16_t  e_crlc;       /* 0x06: Relocations */
+    uint16_t  e_cparhdr;    /* 0x08: Size of header in paragraphs */
+    uint16_t  e_minalloc;   /* 0x0a: Minimum extra paragraphs needed */
+    uint16_t  e_maxalloc;   /* 0x0c: Maximum extra paragraphs needed */
+    uint16_t  e_ss;         /* 0x0e: Initial (relative) SS value */
+    uint16_t  e_sp;         /* 0x10: Initial SP value */
+    uint16_t  e_csum;       /* 0x12: Checksum */
+    uint16_t  e_ip;         /* 0x14: Initial IP value */
+    uint16_t  e_cs;         /* 0x16: Initial (relative) CS value */
+    uint16_t  e_lfarlc;     /* 0x18: File address of relocation table */
+    uint16_t  e_ovno;       /* 0x1a: Overlay number */
+    uint16_t  e_res[4];     /* 0x1c: Reserved words */
+    uint16_t  e_oemid;      /* 0x24: OEM identifier (for e_oeminfo) */
+    uint16_t  e_oeminfo;    /* 0x26: OEM information; e_oemid specific */
+    uint16_t  e_res2[10];   /* 0x28: Reserved words */
+    uint32_t  e_lfanew;     /* 0x3c: Offset to extended header */
+} __attribute__ ((packed)) IMAGE_DOS_HEADER;
+
+typedef struct IMAGE_FILE_HEADER {
+  uint16_t  Machine;
+  uint16_t  NumberOfSections;
+  uint32_t  TimeDateStamp;
+  uint32_t  PointerToSymbolTable;
+  uint32_t  NumberOfSymbols;
+  uint16_t  SizeOfOptionalHeader;
+  uint16_t  Characteristics;
+} __attribute__ ((packed)) IMAGE_FILE_HEADER;
+
+typedef struct IMAGE_DATA_DIRECTORY {
+  uint32_t VirtualAddress;
+  uint32_t Size;
+} __attribute__ ((packed)) IMAGE_DATA_DIRECTORY;
+
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
+
+typedef struct IMAGE_OPTIONAL_HEADER64 {
+  uint16_t  Magic; /* 0x20b */
+  uint8_t   MajorLinkerVersion;
+  uint8_t   MinorLinkerVersion;
+  uint32_t  SizeOfCode;
+  uint32_t  SizeOfInitializedData;
+  uint32_t  SizeOfUninitializedData;
+  uint32_t  AddressOfEntryPoint;
+  uint32_t  BaseOfCode;
+  uint64_t  ImageBase;
+  uint32_t  SectionAlignment;
+  uint32_t  FileAlignment;
+  uint16_t  MajorOperatingSystemVersion;
+  uint16_t  MinorOperatingSystemVersion;
+  uint16_t  MajorImageVersion;
+  uint16_t  MinorImageVersion;
+  uint16_t  MajorSubsystemVersion;
+  uint16_t  MinorSubsystemVersion;
+  uint32_t  Win32VersionValue;
+  uint32_t  SizeOfImage;
+  uint32_t  SizeOfHeaders;
+  uint32_t  CheckSum;
+  uint16_t  Subsystem;
+  uint16_t  DllCharacteristics;
+  uint64_t  SizeOfStackReserve;
+  uint64_t  SizeOfStackCommit;
+  uint64_t  SizeOfHeapReserve;
+  uint64_t  SizeOfHeapCommit;
+  uint32_t  LoaderFlags;
+  uint32_t  NumberOfRvaAndSizes;
+  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} __attribute__ ((packed)) IMAGE_OPTIONAL_HEADER64;
+
+typedef struct IMAGE_NT_HEADERS64 {
+  uint32_t Signature;
+  IMAGE_FILE_HEADER FileHeader;
+  IMAGE_OPTIONAL_HEADER64 OptionalHeader;
+} __attribute__ ((packed)) IMAGE_NT_HEADERS64;
+
+#define IMAGE_FILE_DEBUG_DIRECTORY  6
+
+typedef struct IMAGE_DEBUG_DIRECTORY {
+  uint32_t Characteristics;
+  uint32_t TimeDateStamp;
+  uint16_t MajorVersion;
+  uint16_t MinorVersion;
+  uint32_t Type;
+  uint32_t SizeOfData;
+  uint32_t AddressOfRawData;
+  uint32_t PointerToRawData;
+} __attribute__ ((packed)) IMAGE_DEBUG_DIRECTORY;
+
+#define IMAGE_DEBUG_TYPE_CODEVIEW   2
+
+typedef struct guid_t {
+    uint32_t a;
+    uint16_t b;
+    uint16_t c;
+    uint8_t d[2];
+    uint8_t  e[6];
+} __attribute__ ((packed)) guid_t;
+
+typedef struct OMFSignatureRSDS {
+    char        Signature[4];
+    guid_t      guid;
+    uint32_t    age;
+    char        name[];
+} __attribute__ ((packed)) OMFSignatureRSDS;
+
+#endif /* PE_H */
diff --git a/contrib/elf2dmp/qemu_elf.c b/contrib/elf2dmp/qemu_elf.c
new file mode 100644
index 0000000000..e9c0d2534a
--- /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);
+}
diff --git a/contrib/elf2dmp/qemu_elf.h b/contrib/elf2dmp/qemu_elf.h
new file mode 100644
index 0000000000..d85d6558fa
--- /dev/null
+++ b/contrib/elf2dmp/qemu_elf.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018 Virtuozzo International GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ *
+ */
+
+#ifndef QEMU_ELF_H
+#define QEMU_ELF_H
+
+#include <stdint.h>
+#include <elf.h>
+
+typedef struct QEMUCPUSegment {
+    uint32_t selector;
+    uint32_t limit;
+    uint32_t flags;
+    uint32_t pad;
+    uint64_t base;
+} QEMUCPUSegment;
+
+typedef struct QEMUCPUState {
+    uint32_t version;
+    uint32_t size;
+    uint64_t rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp;
+    uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
+    uint64_t rip, rflags;
+    QEMUCPUSegment cs, ds, es, fs, gs, ss;
+    QEMUCPUSegment ldt, tr, gdt, idt;
+    uint64_t cr[5];
+    uint64_t kernel_gs_base;
+} QEMUCPUState;
+
+int is_system(QEMUCPUState *s);
+
+typedef struct QEMU_Elf {
+    int fd;
+    size_t size;
+    void *map;
+    QEMUCPUState **state;
+    size_t state_nr;
+    int has_kernel_gs_base;
+} QEMU_Elf;
+
+int QEMU_Elf_init(QEMU_Elf *qe, const char *filename);
+void QEMU_Elf_exit(QEMU_Elf *qe);
+
+Elf64_Phdr *elf64_getphdr(void *map);
+Elf64_Half elf_getphdrnum(void *map);
+
+#endif /* QEMU_ELF_H */