aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/elf2dmp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/elf2dmp')
-rw-r--r--contrib/elf2dmp/addrspace.c233
-rw-r--r--contrib/elf2dmp/addrspace.h44
-rw-r--r--contrib/elf2dmp/download.c45
-rw-r--r--contrib/elf2dmp/download.h13
-rw-r--r--contrib/elf2dmp/err.h13
-rw-r--r--contrib/elf2dmp/kdbg.h198
-rw-r--r--contrib/elf2dmp/main.c597
-rw-r--r--contrib/elf2dmp/meson.build5
-rw-r--r--contrib/elf2dmp/pdb.c315
-rw-r--r--contrib/elf2dmp/pdb.h241
-rw-r--r--contrib/elf2dmp/pe.h122
-rw-r--r--contrib/elf2dmp/qemu_elf.c154
-rw-r--r--contrib/elf2dmp/qemu_elf.h49
13 files changed, 2029 insertions, 0 deletions
diff --git a/contrib/elf2dmp/addrspace.c b/contrib/elf2dmp/addrspace.c
new file mode 100644
index 000000000..53ded1706
--- /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 & ELF2DMP_PFN_MASK;
+ size_t s = (page + ELF2DMP_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 000000000..00b44c121
--- /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 ELF2DMP_PAGE_BITS 12
+#define ELF2DMP_PAGE_SIZE (1ULL << ELF2DMP_PAGE_BITS)
+#define ELF2DMP_PFN_MASK (~(ELF2DMP_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 000000000..bd7650a7a
--- /dev/null
+++ b/contrib/elf2dmp/download.c
@@ -0,0 +1,45 @@
+/*
+ * 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;
+ }
+
+ if (curl_easy_setopt(curl, CURLOPT_URL, url) != CURLE_OK
+ || curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL) != CURLE_OK
+ || curl_easy_setopt(curl, CURLOPT_WRITEDATA, file) != CURLE_OK
+ || curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1) != CURLE_OK
+ || curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0) != CURLE_OK
+ || curl_easy_perform(curl) != CURLE_OK) {
+ unlink(name);
+ fclose(file);
+ err = 1;
+ } else {
+ 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 000000000..5c274925f
--- /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 000000000..5456bd5a3
--- /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 000000000..002e3d0cd
--- /dev/null
+++ b/contrib/elf2dmp/kdbg.h
@@ -0,0 +1,198 @@
+/*
+ * 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;
+
+#ifndef _WIN32
+typedef struct LIST_ENTRY64 {
+ struct LIST_ENTRY64 *Flink;
+ struct LIST_ENTRY64 *Blink;
+} LIST_ENTRY64;
+#endif
+
+typedef struct DBGKD_DEBUG_DATA_HEADER64 {
+ LIST_ENTRY64 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 000000000..20b477d58
--- /dev/null
+++ b/contrib/elf2dmp/main.c
@@ -0,0 +1,597 @@
+/*
+ * 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%016"PRIx64"\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%016"PRIx64"\n", kwn);
+ printf("[KiWaitAlways] = 0x%016"PRIx64"\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%016"PRIx64" 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%016"PRIx64" 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 >= ELF2DMP_PAGE_SIZE);
+ QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= ELF2DMP_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 / ELF2DMP_PAGE_SIZE;
+ h.PhysicalMemoryBlock.Run[i] = (WinDumpPhyMemRun64) {
+ .BasePage = ps->block[i].paddr / ELF2DMP_PAGE_SIZE,
+ .PageCount = ps->block[i].size / ELF2DMP_PAGE_SIZE,
+ };
+ }
+
+ h.RequiredDumpSpace += h.PhysicalMemoryBlock.NumberOfPages << ELF2DMP_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) >= ELF2DMP_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%08"PRIx32"\n",
+ (uint32_t)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%016"PRIx64"\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%016"PRIx64"\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%016"PRIx64"\n", idt_desc_addr(first_idt_desc));
+
+ KernBase = idt_desc_addr(first_idt_desc) & ~(ELF2DMP_PAGE_SIZE - 1);
+ printf("Searching kernel downwards from 0x%016"PRIx64"...\n", KernBase);
+
+ for (; KernBase >= 0xfffff78000000000; KernBase -= ELF2DMP_PAGE_SIZE) {
+ nt_start_addr = va_space_resolve(&vs, KernBase);
+ if (!nt_start_addr) {
+ continue;
+ }
+
+ if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */
+ break;
+ }
+ }
+
+ if (!nt_start_addr) {
+ eprintf("Failed to find NT kernel image\n");
+ err = 1;
+ goto out_ps;
+ }
+
+ printf("KernBase = 0x%016"PRIx64", 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_kdbg;
+ }
+
+ if (fill_context(kdbg, &vs, &qemu_elf)) {
+ err = 1;
+ goto out_kdbg;
+ }
+
+ 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/meson.build b/contrib/elf2dmp/meson.build
new file mode 100644
index 000000000..4d86cb390
--- /dev/null
+++ b/contrib/elf2dmp/meson.build
@@ -0,0 +1,5 @@
+if curl.found()
+ executable('elf2dmp', files('main.c', 'addrspace.c', 'download.c', 'pdb.c', 'qemu_elf.c'),
+ dependencies: [glib, curl],
+ install: true)
+endif
diff --git a/contrib/elf2dmp/pdb.c b/contrib/elf2dmp/pdb.c
new file mode 100644
index 000000000..adcfa7e15
--- /dev/null
+++ b/contrib/elf2dmp/pdb.c
@@ -0,0 +1,315 @@
+/*
+ * 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%09"PRIx64"\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)
+{
+ if (hdr->block_size == 0) {
+ return 1;
+ }
+
+ 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)
+{
+ GError *gerr = NULL;
+ int err = 0;
+ void *map;
+
+ reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
+ if (gerr) {
+ eprintf("Failed to map PDB file \'%s\'\n", name);
+ g_error_free(gerr);
+ return 1;
+ }
+
+ reader->file_size = g_mapped_file_get_length(reader->gmf);
+ map = g_mapped_file_get_contents(reader->gmf);
+ if (pdb_reader_init(reader, map)) {
+ err = 1;
+ goto out_unmap;
+ }
+
+ return 0;
+
+out_unmap:
+ g_mapped_file_unref(reader->gmf);
+
+ return err;
+}
+
+void pdb_exit(struct pdb_reader *reader)
+{
+ g_mapped_file_unref(reader->gmf);
+ pdb_reader_exit(reader);
+}
diff --git a/contrib/elf2dmp/pdb.h b/contrib/elf2dmp/pdb.h
new file mode 100644
index 000000000..4ea8925ee
--- /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
+
+
+#ifndef _WIN32
+typedef struct GUID {
+ unsigned int Data1;
+ unsigned short Data2;
+ unsigned short Data3;
+ unsigned char Data4[8];
+} GUID;
+#endif
+
+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 {
+ GMappedFile *gmf;
+ 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 000000000..c2a4a6ba7
--- /dev/null
+++ b/contrib/elf2dmp/pe.h
@@ -0,0 +1,122 @@
+/*
+ * 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
+
+
+#ifndef _WIN32
+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;
+
+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
+#endif
+
+#define IMAGE_FILE_DEBUG_DIRECTORY 6
+
+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 000000000..b601b6d7b
--- /dev/null
+++ b/contrib/elf2dmp/qemu_elf.c
@@ -0,0 +1,154 @@
+/*
+ * 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)
+{
+ GError *gerr = NULL;
+ int err = 0;
+
+ qe->gmf = g_mapped_file_new(filename, TRUE, &gerr);
+ if (gerr) {
+ eprintf("Failed to map ELF dump file \'%s\'\n", filename);
+ g_error_free(gerr);
+ return 1;
+ }
+
+ qe->map = g_mapped_file_get_contents(qe->gmf);
+ qe->size = g_mapped_file_get_length(qe->gmf);
+
+ if (init_states(qe)) {
+ eprintf("Failed to extract QEMU CPU states\n");
+ err = 1;
+ goto out_unmap;
+ }
+
+ return 0;
+
+out_unmap:
+ g_mapped_file_unref(qe->gmf);
+
+ return err;
+}
+
+void QEMU_Elf_exit(QEMU_Elf *qe)
+{
+ exit_states(qe);
+ g_mapped_file_unref(qe->gmf);
+}
diff --git a/contrib/elf2dmp/qemu_elf.h b/contrib/elf2dmp/qemu_elf.h
new file mode 100644
index 000000000..b2f0d9cbc
--- /dev/null
+++ b/contrib/elf2dmp/qemu_elf.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018 Virtuozzo International GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ */
+
+#ifndef ELF2DMP_QEMU_ELF_H
+#define ELF2DMP_QEMU_ELF_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 {
+ GMappedFile *gmf;
+ 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 /* ELF2DMP_QEMU_ELF_H */