mirror of
https://gitlab.com/qemu-project/qemu.git
synced 2024-12-02 18:42:58 +03:00
* configure fix for environment variables (Daniel)
* fix memory leaks (Alex) * x86_64 MTTCG fixes (Emilio) * introduce atomic64 (Emilio) * Fix for virtio hang (Fam, myself) * SH serial port fix (Geert) * Deprecate rotation_rate for scsi-block (Fam) * Extend memory-backend-file availability to all POSIX hosts (Hikaru) * Memory API cleanups and fixes (Igor, Li Qiang, Peter, Philippe) * MSI/IOMMU fix (Jan) * Socket reconnection fixes (Marc-André) * icount fixes (Emilio, myself) * QSP fixes for Coverity (myself) * Some record/replay improovements (Pavel) * Packed struct fixes (Peter) * Windows dump fixes and elf2dmp (Viktor) * kbmclock fix (Yongji) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAABAgAGBQJbs6coAAoJEL/70l94x66DaL0IAISiRZcm7SMFTUafivyzQ9Ao vk2SZ64/BUmDI5q5t30NGiVkMzAc0qDunRSqD4FnIvhGl8phFSSYqaN28JFLe4l1 JhX7FdLQgeevYY35hEPjpCEOAR7WD116/NaZ/UZ+7zZ4Z+CtcCEXZefb4dD9vijj M/rH7vXJsulSb7q2Np3hhbai/GL7ZvNURaHOXZpuPE2aJGAcSXhYtAbGHPJ4NKgn qjP3AGTose8cRD0u5smY0JnyL5vcF606+dupIUsnciDC3wF1SPusTMwLRLoEGNA4 lmxTdGbxvvM1TDu/mY70WYXJ2ujC9Suhj1jkftgDTWRZqwQ9N/B4eB2JcQ9WbhQ= =SzeA -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging * configure fix for environment variables (Daniel) * fix memory leaks (Alex) * x86_64 MTTCG fixes (Emilio) * introduce atomic64 (Emilio) * Fix for virtio hang (Fam, myself) * SH serial port fix (Geert) * Deprecate rotation_rate for scsi-block (Fam) * Extend memory-backend-file availability to all POSIX hosts (Hikaru) * Memory API cleanups and fixes (Igor, Li Qiang, Peter, Philippe) * MSI/IOMMU fix (Jan) * Socket reconnection fixes (Marc-André) * icount fixes (Emilio, myself) * QSP fixes for Coverity (myself) * Some record/replay improovements (Pavel) * Packed struct fixes (Peter) * Windows dump fixes and elf2dmp (Viktor) * kbmclock fix (Yongji) # gpg: Signature made Tue 02 Oct 2018 18:13:12 BST # gpg: using RSA key BFFBD25F78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: (80 commits) hw/scsi/mptendian: Avoid taking address of fields in packed structs cpus: fix TCG kick timer leak docs/devel/memory.txt: Document _with_attrs accessors hw/nvram/fw_cfg: Use memberwise copy of MemoryRegionOps struct memory: Remove old_mmio accessors memory: Fix access_with_adjusted_size(small size) on big-endian memory regions memory: Refactor common shifting code from accessors memory: Use MAKE_64BIT_MASK() virtio: do not take address of packed members replay: replay BH for IDE trim operation hostmem-file: make available memory-backend-file on POSIX-based hosts target/i386: fix translation for icount mode hvf: drop unused variable qom/object: add some interface asserts accel/tcg: Remove dead code lsi53c895a: convert to trace-events scsi-block: Deprecate rotation_rate kvmclock: run KVM_KVMCLOCK_CTRL ioctl in vcpu thread MAINTAINERS: add myself as elf2dmp maintainer contrib: add elf2dmp tool ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
dafd950536
@ -1903,6 +1903,11 @@ S: Maintained
|
||||
F: include/qemu/iova-tree.h
|
||||
F: util/iova-tree.c
|
||||
|
||||
elf2dmp
|
||||
M: Viktor Prutyanov <viktor.prutyanov@phystech.edu>
|
||||
S: Maintained
|
||||
F: contrib/elf2dmp/
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
Overall
|
||||
|
5
Makefile
5
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, $^)
|
||||
|
@ -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/
|
||||
|
@ -2009,15 +2009,6 @@ void tb_invalidate_phys_page_fast(struct page_collection *pages,
|
||||
{
|
||||
PageDesc *p;
|
||||
|
||||
#if 0
|
||||
if (1) {
|
||||
qemu_log("modifying code at 0x%x size=%d EIP=%x PC=%08x\n",
|
||||
cpu_single_env->mem_io_vaddr, len,
|
||||
cpu_single_env->eip,
|
||||
cpu_single_env->eip +
|
||||
(intptr_t)cpu_single_env->segs[R_CS].base);
|
||||
}
|
||||
#endif
|
||||
assert_memory_lock();
|
||||
|
||||
p = page_find(start >> TARGET_PAGE_BITS);
|
||||
|
@ -34,6 +34,8 @@ void translator_loop_temp_check(DisasContextBase *db)
|
||||
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
CPUState *cpu, TranslationBlock *tb)
|
||||
{
|
||||
int bp_insn = 0;
|
||||
|
||||
/* Initialize DisasContext */
|
||||
db->tb = tb;
|
||||
db->pc_first = tb->pc;
|
||||
@ -71,11 +73,13 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
/* Pass breakpoint hits to target for further processing */
|
||||
if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||
if (!db->singlestep_enabled
|
||||
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||
CPUBreakpoint *bp;
|
||||
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
||||
if (bp->pc == db->pc_next) {
|
||||
if (ops->breakpoint_check(db, cpu, bp)) {
|
||||
bp_insn = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -118,7 +122,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
|
||||
/* Emit code to exit the TB, as indicated by db->is_jmp. */
|
||||
ops->tb_stop(db, cpu);
|
||||
gen_tb_end(db->tb, db->num_insns);
|
||||
gen_tb_end(db->tb, db->num_insns - bp_insn);
|
||||
|
||||
/* The disas_log hook may use these values rather than recompute. */
|
||||
db->tb->size = db->pc_next - db->pc_first;
|
||||
|
@ -4,7 +4,7 @@ common-obj-$(CONFIG_POSIX) += rng-random.o
|
||||
common-obj-$(CONFIG_TPM) += tpm.o
|
||||
|
||||
common-obj-y += hostmem.o hostmem-ram.o
|
||||
common-obj-$(CONFIG_LINUX) += hostmem-file.o
|
||||
common-obj-$(CONFIG_POSIX) += hostmem-file.o
|
||||
|
||||
common-obj-y += cryptodev.o
|
||||
common-obj-y += cryptodev-builtin.o
|
||||
|
@ -51,7 +51,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
error_setg(errp, "mem-path property not set");
|
||||
return;
|
||||
}
|
||||
#ifndef CONFIG_LINUX
|
||||
#ifndef CONFIG_POSIX
|
||||
error_setg(errp, "-mem-path not supported on this host");
|
||||
#else
|
||||
if (!host_memory_backend_mr_inited(backend)) {
|
||||
|
@ -140,18 +140,22 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
bc->alloc = memfd_backend_memory_alloc;
|
||||
|
||||
object_class_property_add_bool(oc, "hugetlb",
|
||||
memfd_backend_get_hugetlb,
|
||||
memfd_backend_set_hugetlb,
|
||||
&error_abort);
|
||||
object_class_property_add(oc, "hugetlbsize", "int",
|
||||
memfd_backend_get_hugetlbsize,
|
||||
memfd_backend_set_hugetlbsize,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_add_bool(oc, "seal",
|
||||
memfd_backend_get_seal,
|
||||
memfd_backend_set_seal,
|
||||
&error_abort);
|
||||
if (qemu_memfd_check(MFD_HUGETLB)) {
|
||||
object_class_property_add_bool(oc, "hugetlb",
|
||||
memfd_backend_get_hugetlb,
|
||||
memfd_backend_set_hugetlb,
|
||||
&error_abort);
|
||||
object_class_property_add(oc, "hugetlbsize", "int",
|
||||
memfd_backend_get_hugetlbsize,
|
||||
memfd_backend_set_hugetlbsize,
|
||||
NULL, NULL, &error_abort);
|
||||
}
|
||||
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
|
||||
object_class_property_add_bool(oc, "seal",
|
||||
memfd_backend_get_seal,
|
||||
memfd_backend_set_seal,
|
||||
&error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
static const TypeInfo memfd_backend_info = {
|
||||
@ -164,7 +168,9 @@ static const TypeInfo memfd_backend_info = {
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
type_register_static(&memfd_backend_info);
|
||||
if (qemu_memfd_check(0)) {
|
||||
type_register_static(&memfd_backend_info);
|
||||
}
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
@ -31,10 +31,6 @@
|
||||
|
||||
#include "chardev/char-io.h"
|
||||
|
||||
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
||||
|| defined(__GLIBC__)
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
QIOChannel *ioc;
|
||||
@ -299,5 +295,3 @@ static void register_types(void)
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
#endif
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/qapi-visit-sockets.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
#include "chardev/char-io.h"
|
||||
|
||||
@ -354,6 +353,15 @@ static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond)
|
||||
return qio_channel_create_watch(s->ioc, cond);
|
||||
}
|
||||
|
||||
static void remove_hup_source(SocketChardev *s)
|
||||
{
|
||||
if (s->hup_source != NULL) {
|
||||
g_source_destroy(s->hup_source);
|
||||
g_source_unref(s->hup_source);
|
||||
s->hup_source = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void tcp_chr_free_connection(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
@ -368,11 +376,7 @@ static void tcp_chr_free_connection(Chardev *chr)
|
||||
s->read_msgfds_num = 0;
|
||||
}
|
||||
|
||||
if (s->hup_source != NULL) {
|
||||
g_source_destroy(s->hup_source);
|
||||
g_source_unref(s->hup_source);
|
||||
s->hup_source = NULL;
|
||||
}
|
||||
remove_hup_source(s);
|
||||
|
||||
tcp_set_msgfds(chr, NULL, 0);
|
||||
remove_fd_in_watch(chr);
|
||||
@ -541,6 +545,27 @@ static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len,
|
||||
}
|
||||
}
|
||||
|
||||
static void update_ioc_handlers(SocketChardev *s)
|
||||
{
|
||||
Chardev *chr = CHARDEV(s);
|
||||
|
||||
if (!s->connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||
tcp_chr_read_poll,
|
||||
tcp_chr_read, chr,
|
||||
chr->gcontext);
|
||||
|
||||
remove_hup_source(s);
|
||||
s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP);
|
||||
g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup,
|
||||
chr, NULL);
|
||||
g_source_attach(s->hup_source, chr->gcontext);
|
||||
}
|
||||
|
||||
static void tcp_chr_connect(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
@ -553,16 +578,7 @@ static void tcp_chr_connect(void *opaque)
|
||||
s->is_listen, s->is_telnet);
|
||||
|
||||
s->connected = 1;
|
||||
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||
tcp_chr_read_poll,
|
||||
tcp_chr_read,
|
||||
chr, chr->gcontext);
|
||||
|
||||
s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP);
|
||||
g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup,
|
||||
chr, NULL);
|
||||
g_source_attach(s->hup_source, chr->gcontext);
|
||||
|
||||
update_ioc_handlers(s);
|
||||
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||
}
|
||||
|
||||
@ -593,17 +609,7 @@ static void tcp_chr_update_read_handler(Chardev *chr)
|
||||
tcp_chr_telnet_init(CHARDEV(s));
|
||||
}
|
||||
|
||||
if (!s->connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
if (s->ioc) {
|
||||
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||
tcp_chr_read_poll,
|
||||
tcp_chr_read, chr,
|
||||
chr->gcontext);
|
||||
}
|
||||
update_ioc_handlers(s);
|
||||
}
|
||||
|
||||
static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
|
||||
@ -724,11 +730,6 @@ static void tcp_chr_tls_init(Chardev *chr)
|
||||
Error *err = NULL;
|
||||
gchar *name;
|
||||
|
||||
if (!machine_init_done) {
|
||||
/* This will be postponed to machine_done notifier */
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->is_listen) {
|
||||
tioc = qio_channel_tls_new_server(
|
||||
s->ioc, s->tls_creds,
|
||||
@ -1011,8 +1012,9 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
||||
s->reconnect_time = reconnect;
|
||||
}
|
||||
|
||||
/* If reconnect_time is set, will do that in chr_machine_done. */
|
||||
if (!s->reconnect_time) {
|
||||
if (s->reconnect_time) {
|
||||
tcp_chr_connect_async(chr);
|
||||
} else {
|
||||
if (s->is_listen) {
|
||||
char *name;
|
||||
s->listener = qio_net_listener_new();
|
||||
@ -1161,21 +1163,6 @@ char_socket_get_connected(Object *obj, Error **errp)
|
||||
return s->connected;
|
||||
}
|
||||
|
||||
static int tcp_chr_machine_done_hook(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
|
||||
if (s->reconnect_time) {
|
||||
tcp_chr_connect_async(chr);
|
||||
}
|
||||
|
||||
if (s->ioc && s->tls_creds) {
|
||||
tcp_chr_tls_init(chr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
@ -1191,7 +1178,6 @@ static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||
cc->chr_add_client = tcp_chr_add_client;
|
||||
cc->chr_add_watch = tcp_chr_add_watch;
|
||||
cc->chr_update_read_handler = tcp_chr_update_read_handler;
|
||||
cc->chr_machine_done = tcp_chr_machine_done_hook;
|
||||
|
||||
object_class_property_add(oc, "addr", "SocketAddress",
|
||||
char_socket_get_addr, NULL,
|
||||
|
45
configure
vendored
45
configure
vendored
@ -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
|
||||
@ -7024,12 +7027,14 @@ TARGET_ABI_DIR=""
|
||||
|
||||
case "$target_name" in
|
||||
i386)
|
||||
mttcg="yes"
|
||||
gdb_xml_files="i386-32bit.xml i386-32bit-core.xml i386-32bit-sse.xml"
|
||||
target_compiler=$cross_cc_i386
|
||||
target_compiler_cflags=$cross_cc_ccflags_i386
|
||||
;;
|
||||
x86_64)
|
||||
TARGET_BASE_ARCH=i386
|
||||
mttcg="yes"
|
||||
gdb_xml_files="i386-64bit.xml i386-64bit-core.xml i386-64bit-sse.xml"
|
||||
target_compiler=$cross_cc_x86_64
|
||||
;;
|
||||
@ -7527,6 +7532,46 @@ cat <<EOD >config.status
|
||||
# Compiler output produced by configure, useful for debugging
|
||||
# configure, is in config.log if it exists.
|
||||
EOD
|
||||
|
||||
preserve_env() {
|
||||
envname=$1
|
||||
|
||||
eval envval=\$$envname
|
||||
|
||||
if test -n "$envval"
|
||||
then
|
||||
echo "$envname='$envval'" >> config.status
|
||||
echo "export $envname" >> config.status
|
||||
else
|
||||
echo "unset $envname" >> config.status
|
||||
fi
|
||||
}
|
||||
|
||||
# Preserve various env variables that influence what
|
||||
# features/build target configure will detect
|
||||
preserve_env AR
|
||||
preserve_env AS
|
||||
preserve_env CC
|
||||
preserve_env CPP
|
||||
preserve_env CXX
|
||||
preserve_env INSTALL
|
||||
preserve_env LD
|
||||
preserve_env LD_LIBRARY_PATH
|
||||
preserve_env LIBTOOL
|
||||
preserve_env MAKE
|
||||
preserve_env NM
|
||||
preserve_env OBJCOPY
|
||||
preserve_env PATH
|
||||
preserve_env PKG_CONFIG
|
||||
preserve_env PKG_CONFIG_LIBDIR
|
||||
preserve_env PKG_CONFIG_PATH
|
||||
preserve_env PYTHON
|
||||
preserve_env SDL_CONFIG
|
||||
preserve_env SDL2_CONFIG
|
||||
preserve_env SMBD
|
||||
preserve_env STRIP
|
||||
preserve_env WINDRES
|
||||
|
||||
printf "exec" >>config.status
|
||||
printf " '%s'" "$0" "$@" >>config.status
|
||||
echo ' "$@"' >>config.status
|
||||
|
1
contrib/elf2dmp/Makefile.objs
Normal file
1
contrib/elf2dmp/Makefile.objs
Normal file
@ -0,0 +1 @@
|
||||
elf2dmp-obj-y = main.o addrspace.o download.o pdb.o qemu_elf.o
|
233
contrib/elf2dmp/addrspace.c
Normal file
233
contrib/elf2dmp/addrspace.c
Normal file
@ -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;
|
||||
}
|
44
contrib/elf2dmp/addrspace.h
Normal file
44
contrib/elf2dmp/addrspace.h
Normal file
@ -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 */
|
47
contrib/elf2dmp/download.c
Normal file
47
contrib/elf2dmp/download.c
Normal file
@ -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;
|
||||
}
|
13
contrib/elf2dmp/download.h
Normal file
13
contrib/elf2dmp/download.h
Normal file
@ -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 */
|
13
contrib/elf2dmp/err.h
Normal file
13
contrib/elf2dmp/err.h
Normal file
@ -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 */
|
194
contrib/elf2dmp/kdbg.h
Normal file
194
contrib/elf2dmp/kdbg.h
Normal file
@ -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 */
|
589
contrib/elf2dmp/main.c
Normal file
589
contrib/elf2dmp/main.c
Normal file
@ -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( |