Blame SOURCES/kvm-scripts-dump-guest-memory.py-Introduce-multi-arch-su.patch

5d360b
From 0867b6efa038add3ce58fcec50b27cad8718d43a Mon Sep 17 00:00:00 2001
5d360b
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
5d360b
Date: Wed, 13 Dec 2017 13:39:08 +0100
5d360b
Subject: [PATCH 37/41] scripts/dump-guest-memory.py: Introduce multi-arch
5d360b
 support
5d360b
MIME-Version: 1.0
5d360b
Content-Type: text/plain; charset=UTF-8
5d360b
Content-Transfer-Encoding: 8bit
5d360b
5d360b
RH-Author: Marc-André Lureau <marcandre.lureau@redhat.com>
5d360b
Message-id: <20171213133912.26176-38-marcandre.lureau@redhat.com>
5d360b
Patchwork-id: 78386
5d360b
O-Subject: [RHEL-7.5 qemu-kvm PATCH v3 37/41] scripts/dump-guest-memory.py: Introduce multi-arch support
5d360b
Bugzilla: 1411490
5d360b
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
5d360b
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
5d360b
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
5d360b
5d360b
From: Janosch Frank <frankja@linux.vnet.ibm.com>
5d360b
5d360b
By modelling the ELF with ctypes we not only gain full python 3
5d360b
support but can also create dumps for different architectures more easily.
5d360b
5d360b
Tested-by: Andrew Jones <drjones@redhat.com>
5d360b
Acked-by: Laszlo Ersek <lersek@redhat.com>
5d360b
Signed-off-by: Janosch Frank <frankja@linux.vnet.ibm.com>
5d360b
Message-Id: <1453464520-3882-6-git-send-email-frankja@linux.vnet.ibm.com>
5d360b
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
5d360b
5d360b
(cherry picked from commit 368e3adc8928b2786939a25a336527f83f18e926)
5d360b
5d360b
RHEL: Change it to be x86-64 only, to keep compatibility and limit the
5d360b
      feature exposure.
5d360b
5d360b
      Backport it because ctypes have various benefits simplifying the
5d360b
      following patch.
5d360b
5d360b
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
5d360b
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
5d360b
---
5d360b
 scripts/dump-guest-memory.py | 488 ++++++++++++++++++++++++++++---------------
5d360b
 1 file changed, 323 insertions(+), 165 deletions(-)
5d360b
5d360b
diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py
5d360b
index 3d54d05..308cfca 100644
5d360b
--- a/scripts/dump-guest-memory.py
5d360b
+++ b/scripts/dump-guest-memory.py
5d360b
@@ -6,6 +6,7 @@
5d360b
 #
5d360b
 # Authors:
5d360b
 #   Laszlo Ersek <lersek@redhat.com>
5d360b
+#   Janosch Frank <frankja@linux.vnet.ibm.com>
5d360b
 #
5d360b
 # This work is licensed under the terms of the GNU GPL, version 2 or later. See
5d360b
 # the COPYING file in the top-level directory.
5d360b
@@ -15,58 +16,303 @@
5d360b
 # "help data" summary), and it should match how other help texts look in
5d360b
 # gdb.
5d360b
 
5d360b
-import struct
5d360b
+import ctypes
5d360b
 
5d360b
 UINTPTR_T = gdb.lookup_type("uintptr_t")
5d360b
 
5d360b
 TARGET_PAGE_SIZE = 0x1000
5d360b
 TARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000
5d360b
 
5d360b
-# Various ELF constants
5d360b
-EM_X86_64   = 62        # AMD x86-64 target machine
5d360b
-ELFDATA2LSB = 1         # little endian
5d360b
-ELFCLASS64  = 2
5d360b
-ELFMAG      = "\x7FELF"
5d360b
-EV_CURRENT  = 1
5d360b
-ET_CORE     = 4
5d360b
-PT_LOAD     = 1
5d360b
-PT_NOTE     = 4
5d360b
-
5d360b
 # Special value for e_phnum. This indicates that the real number of
5d360b
 # program headers is too large to fit into e_phnum. Instead the real
5d360b
 # value is in the field sh_info of section 0.
5d360b
 PN_XNUM = 0xFFFF
5d360b
 
5d360b
-# Format strings for packing and header size calculation.
5d360b
-ELF64_EHDR = ("4s" # e_ident/magic
5d360b
-              "B"  # e_ident/class
5d360b
-              "B"  # e_ident/data
5d360b
-              "B"  # e_ident/version
5d360b
-              "B"  # e_ident/osabi
5d360b
-              "8s" # e_ident/pad
5d360b
-              "H"  # e_type
5d360b
-              "H"  # e_machine
5d360b
-              "I"  # e_version
5d360b
-              "Q"  # e_entry
5d360b
-              "Q"  # e_phoff
5d360b
-              "Q"  # e_shoff
5d360b
-              "I"  # e_flags
5d360b
-              "H"  # e_ehsize
5d360b
-              "H"  # e_phentsize
5d360b
-              "H"  # e_phnum
5d360b
-              "H"  # e_shentsize
5d360b
-              "H"  # e_shnum
5d360b
-              "H"  # e_shstrndx
5d360b
-          )
5d360b
-ELF64_PHDR = ("I"  # p_type
5d360b
-              "I"  # p_flags
5d360b
-              "Q"  # p_offset
5d360b
-              "Q"  # p_vaddr
5d360b
-              "Q"  # p_paddr
5d360b
-              "Q"  # p_filesz
5d360b
-              "Q"  # p_memsz
5d360b
-              "Q"  # p_align
5d360b
-          )
5d360b
+EV_CURRENT = 1
5d360b
+
5d360b
+ELFCLASS32 = 1
5d360b
+ELFCLASS64 = 2
5d360b
+
5d360b
+ELFDATA2LSB = 1
5d360b
+ELFDATA2MSB = 2
5d360b
+
5d360b
+ET_CORE = 4
5d360b
+
5d360b
+PT_LOAD = 1
5d360b
+PT_NOTE = 4
5d360b
+
5d360b
+EM_386 = 3
5d360b
+EM_PPC = 20
5d360b
+EM_PPC64 = 21
5d360b
+EM_S390 = 22
5d360b
+EM_AARCH = 183
5d360b
+EM_X86_64 = 62
5d360b
+
5d360b
+class ELF(object):
5d360b
+    """Representation of a ELF file."""
5d360b
+
5d360b
+    def __init__(self, arch):
5d360b
+        self.ehdr = None
5d360b
+        self.notes = []
5d360b
+        self.segments = []
5d360b
+        self.notes_size = 0
5d360b
+        self.endianess = None
5d360b
+        self.elfclass = ELFCLASS64
5d360b
+
5d360b
+        if arch == 'aarch64-le':
5d360b
+            self.endianess = ELFDATA2LSB
5d360b
+            self.elfclass = ELFCLASS64
5d360b
+            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
5d360b
+            self.ehdr.e_machine = EM_AARCH
5d360b
+
5d360b
+        elif arch == 'aarch64-be':
5d360b
+            self.endianess = ELFDATA2MSB
5d360b
+            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
5d360b
+            self.ehdr.e_machine = EM_AARCH
5d360b
+
5d360b
+        elif arch == 'X86_64':
5d360b
+            self.endianess = ELFDATA2LSB
5d360b
+            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
5d360b
+            self.ehdr.e_machine = EM_X86_64
5d360b
+
5d360b
+        elif arch == '386':
5d360b
+            self.endianess = ELFDATA2LSB
5d360b
+            self.elfclass = ELFCLASS32
5d360b
+            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
5d360b
+            self.ehdr.e_machine = EM_386
5d360b
+
5d360b
+        elif arch == 's390':
5d360b
+            self.endianess = ELFDATA2MSB
5d360b
+            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
5d360b
+            self.ehdr.e_machine = EM_S390
5d360b
+
5d360b
+        elif arch == 'ppc64-le':
5d360b
+            self.endianess = ELFDATA2LSB
5d360b
+            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
5d360b
+            self.ehdr.e_machine = EM_PPC64
5d360b
+
5d360b
+        elif arch == 'ppc64-be':
5d360b
+            self.endianess = ELFDATA2MSB
5d360b
+            self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
5d360b
+            self.ehdr.e_machine = EM_PPC64
5d360b
+
5d360b
+        else:
5d360b
+            raise gdb.GdbError("No valid arch type specified.\n"
5d360b
+                               "Currently supported types:\n"
5d360b
+                               "aarch64-be, aarch64-le, X86_64, 386, s390, "
5d360b
+                               "ppc64-be, ppc64-le")
5d360b
+
5d360b
+        self.add_segment(PT_NOTE, 0, 0)
5d360b
+
5d360b
+    def add_note(self, n_name, n_desc, n_type):
5d360b
+        """Adds a note to the ELF."""
5d360b
+
5d360b
+        note = get_arch_note(self.endianess, len(n_name), len(n_desc))
5d360b
+        note.n_namesz = len(n_name) + 1
5d360b
+        note.n_descsz = len(n_desc)
5d360b
+        note.n_name = n_name.encode()
5d360b
+        note.n_type = n_type
5d360b
+
5d360b
+        # Desc needs to be 4 byte aligned (although the 64bit spec
5d360b
+        # specifies 8 byte). When defining n_desc as uint32 it will be
5d360b
+        # automatically aligned but we need the memmove to copy the
5d360b
+        # string into it.
5d360b
+        ctypes.memmove(note.n_desc, n_desc.encode(), len(n_desc))
5d360b
+
5d360b
+        self.notes.append(note)
5d360b
+        self.segments[0].p_filesz += ctypes.sizeof(note)
5d360b
+        self.segments[0].p_memsz += ctypes.sizeof(note)
5d360b
+
5d360b
+    def add_segment(self, p_type, p_paddr, p_size):
5d360b
+        """Adds a segment to the elf."""
5d360b
+
5d360b
+        phdr = get_arch_phdr(self.endianess, self.elfclass)
5d360b
+        phdr.p_type = p_type
5d360b
+        phdr.p_paddr = p_paddr
5d360b
+        phdr.p_filesz = p_size
5d360b
+        phdr.p_memsz = p_size
5d360b
+        self.segments.append(phdr)
5d360b
+        self.ehdr.e_phnum += 1
5d360b
+
5d360b
+    def to_file(self, elf_file):
5d360b
+        """Writes all ELF structures to the the passed file.
5d360b
+
5d360b
+        Structure:
5d360b
+        Ehdr
5d360b
+        Segment 0:PT_NOTE
5d360b
+        Segment 1:PT_LOAD
5d360b
+        Segment N:PT_LOAD
5d360b
+        Note    0..N
5d360b
+        Dump contents
5d360b
+        """
5d360b
+        elf_file.write(self.ehdr)
5d360b
+        off = ctypes.sizeof(self.ehdr) + \
5d360b
+              len(self.segments) * ctypes.sizeof(self.segments[0])
5d360b
+
5d360b
+        for phdr in self.segments:
5d360b
+            phdr.p_offset = off
5d360b
+            elf_file.write(phdr)
5d360b
+            off += phdr.p_filesz
5d360b
+
5d360b
+        for note in self.notes:
5d360b
+            elf_file.write(note)
5d360b
+
5d360b
+
5d360b
+def get_arch_note(endianess, len_name, len_desc):
5d360b
+    """Returns a Note class with the specified endianess."""
5d360b
+
5d360b
+    if endianess == ELFDATA2LSB:
5d360b
+        superclass = ctypes.LittleEndianStructure
5d360b
+    else:
5d360b
+        superclass = ctypes.BigEndianStructure
5d360b
+
5d360b
+    len_name = len_name + 1
5d360b
+
5d360b
+    class Note(superclass):
5d360b
+        """Represents an ELF note, includes the content."""
5d360b
+
5d360b
+        _fields_ = [("n_namesz", ctypes.c_uint32),
5d360b
+                    ("n_descsz", ctypes.c_uint32),
5d360b
+                    ("n_type", ctypes.c_uint32),
5d360b
+                    ("n_name", ctypes.c_char * len_name),
5d360b
+                    ("n_desc", ctypes.c_uint32 * ((len_desc + 3) // 4))]
5d360b
+    return Note()
5d360b
+
5d360b
+
5d360b
+class Ident(ctypes.Structure):
5d360b
+    """Represents the ELF ident array in the ehdr structure."""
5d360b
+
5d360b
+    _fields_ = [('ei_mag0', ctypes.c_ubyte),
5d360b
+                ('ei_mag1', ctypes.c_ubyte),
5d360b
+                ('ei_mag2', ctypes.c_ubyte),
5d360b
+                ('ei_mag3', ctypes.c_ubyte),
5d360b
+                ('ei_class', ctypes.c_ubyte),
5d360b
+                ('ei_data', ctypes.c_ubyte),
5d360b
+                ('ei_version', ctypes.c_ubyte),
5d360b
+                ('ei_osabi', ctypes.c_ubyte),
5d360b
+                ('ei_abiversion', ctypes.c_ubyte),
5d360b
+                ('ei_pad', ctypes.c_ubyte * 7)]
5d360b
+
5d360b
+    def __init__(self, endianess, elfclass):
5d360b
+        self.ei_mag0 = 0x7F
5d360b
+        self.ei_mag1 = ord('E')
5d360b
+        self.ei_mag2 = ord('L')
5d360b
+        self.ei_mag3 = ord('F')
5d360b
+        self.ei_class = elfclass
5d360b
+        self.ei_data = endianess
5d360b
+        self.ei_version = EV_CURRENT
5d360b
+
5d360b
+
5d360b
+def get_arch_ehdr(endianess, elfclass):
5d360b
+    """Returns a EHDR64 class with the specified endianess."""
5d360b
+
5d360b
+    if endianess == ELFDATA2LSB:
5d360b
+        superclass = ctypes.LittleEndianStructure
5d360b
+    else:
5d360b
+        superclass = ctypes.BigEndianStructure
5d360b
+
5d360b
+    class EHDR64(superclass):
5d360b
+        """Represents the 64 bit ELF header struct."""
5d360b
+
5d360b
+        _fields_ = [('e_ident', Ident),
5d360b
+                    ('e_type', ctypes.c_uint16),
5d360b
+                    ('e_machine', ctypes.c_uint16),
5d360b
+                    ('e_version', ctypes.c_uint32),
5d360b
+                    ('e_entry', ctypes.c_uint64),
5d360b
+                    ('e_phoff', ctypes.c_uint64),
5d360b
+                    ('e_shoff', ctypes.c_uint64),
5d360b
+                    ('e_flags', ctypes.c_uint32),
5d360b
+                    ('e_ehsize', ctypes.c_uint16),
5d360b
+                    ('e_phentsize', ctypes.c_uint16),
5d360b
+                    ('e_phnum', ctypes.c_uint16),
5d360b
+                    ('e_shentsize', ctypes.c_uint16),
5d360b
+                    ('e_shnum', ctypes.c_uint16),
5d360b
+                    ('e_shstrndx', ctypes.c_uint16)]
5d360b
+
5d360b
+        def __init__(self):
5d360b
+            super(superclass, self).__init__()
5d360b
+            self.e_ident = Ident(endianess, elfclass)
5d360b
+            self.e_type = ET_CORE
5d360b
+            self.e_version = EV_CURRENT
5d360b
+            self.e_ehsize = ctypes.sizeof(self)
5d360b
+            self.e_phoff = ctypes.sizeof(self)
5d360b
+            self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass))
5d360b
+            self.e_phnum = 0
5d360b
+
5d360b
+
5d360b
+    class EHDR32(superclass):
5d360b
+        """Represents the 32 bit ELF header struct."""
5d360b
+
5d360b
+        _fields_ = [('e_ident', Ident),
5d360b
+                    ('e_type', ctypes.c_uint16),
5d360b
+                    ('e_machine', ctypes.c_uint16),
5d360b
+                    ('e_version', ctypes.c_uint32),
5d360b
+                    ('e_entry', ctypes.c_uint32),
5d360b
+                    ('e_phoff', ctypes.c_uint32),
5d360b
+                    ('e_shoff', ctypes.c_uint32),
5d360b
+                    ('e_flags', ctypes.c_uint32),
5d360b
+                    ('e_ehsize', ctypes.c_uint16),
5d360b
+                    ('e_phentsize', ctypes.c_uint16),
5d360b
+                    ('e_phnum', ctypes.c_uint16),
5d360b
+                    ('e_shentsize', ctypes.c_uint16),
5d360b
+                    ('e_shnum', ctypes.c_uint16),
5d360b
+                    ('e_shstrndx', ctypes.c_uint16)]
5d360b
+
5d360b
+        def __init__(self):
5d360b
+            super(superclass, self).__init__()
5d360b
+            self.e_ident = Ident(endianess, elfclass)
5d360b
+            self.e_type = ET_CORE
5d360b
+            self.e_version = EV_CURRENT
5d360b
+            self.e_ehsize = ctypes.sizeof(self)
5d360b
+            self.e_phoff = ctypes.sizeof(self)
5d360b
+            self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass))
5d360b
+            self.e_phnum = 0
5d360b
+
5d360b
+    # End get_arch_ehdr
5d360b
+    if elfclass == ELFCLASS64:
5d360b
+        return EHDR64()
5d360b
+    else:
5d360b
+        return EHDR32()
5d360b
+
5d360b
+
5d360b
+def get_arch_phdr(endianess, elfclass):
5d360b
+    """Returns a 32 or 64 bit PHDR class with the specified endianess."""
5d360b
+
5d360b
+    if endianess == ELFDATA2LSB:
5d360b
+        superclass = ctypes.LittleEndianStructure
5d360b
+    else:
5d360b
+        superclass = ctypes.BigEndianStructure
5d360b
+
5d360b
+    class PHDR64(superclass):
5d360b
+        """Represents the 64 bit ELF program header struct."""
5d360b
+
5d360b
+        _fields_ = [('p_type', ctypes.c_uint32),
5d360b
+                    ('p_flags', ctypes.c_uint32),
5d360b
+                    ('p_offset', ctypes.c_uint64),
5d360b
+                    ('p_vaddr', ctypes.c_uint64),
5d360b
+                    ('p_paddr', ctypes.c_uint64),
5d360b
+                    ('p_filesz', ctypes.c_uint64),
5d360b
+                    ('p_memsz', ctypes.c_uint64),
5d360b
+                    ('p_align', ctypes.c_uint64)]
5d360b
+
5d360b
+    class PHDR32(superclass):
5d360b
+        """Represents the 32 bit ELF program header struct."""
5d360b
+
5d360b
+        _fields_ = [('p_type', ctypes.c_uint32),
5d360b
+                    ('p_offset', ctypes.c_uint32),
5d360b
+                    ('p_vaddr', ctypes.c_uint32),
5d360b
+                    ('p_paddr', ctypes.c_uint32),
5d360b
+                    ('p_filesz', ctypes.c_uint32),
5d360b
+                    ('p_memsz', ctypes.c_uint32),
5d360b
+                    ('p_flags', ctypes.c_uint32),
5d360b
+                    ('p_align', ctypes.c_uint32)]
5d360b
+
5d360b
+    # End get_arch_phdr
5d360b
+    if elfclass == ELFCLASS64:
5d360b
+        return PHDR64()
5d360b
+    else:
5d360b
+        return PHDR32()
5d360b
+
5d360b
 
5d360b
 def int128_get64(val):
5d360b
     """Returns low 64bit part of Int128 struct."""
5d360b
@@ -186,20 +432,22 @@ def get_guest_phys_blocks():
5d360b
 class DumpGuestMemory(gdb.Command):
5d360b
     """Extract guest vmcore from qemu process coredump.
5d360b
 
5d360b
-The sole argument is FILE, identifying the target file to write the
5d360b
-guest vmcore to.
5d360b
+The two required arguments are FILE and ARCH:
5d360b
+FILE identifies the target file to write the guest vmcore to.
5d360b
+ARCH specifies the architecture for which the core will be generated.
5d360b
 
5d360b
 This GDB command reimplements the dump-guest-memory QMP command in
5d360b
 python, using the representation of guest memory as captured in the qemu
5d360b
 coredump. The qemu process that has been dumped must have had the
5d360b
-command line option "-machine dump-guest-core=on".
5d360b
+command line option "-machine dump-guest-core=on" which is the default.
5d360b
 
5d360b
 For simplicity, the "paging", "begin" and "end" parameters of the QMP
5d360b
 command are not supported -- no attempt is made to get the guest's
5d360b
 internal paging structures (ie. paging=false is hard-wired), and guest
5d360b
 memory is always fully dumped.
5d360b
 
5d360b
-Only x86_64 guests are supported.
5d360b
+Currently aarch64-be, aarch64-le, X86_64, 386, s390, ppc64-be,
5d360b
+ppc64-le guests are supported.
5d360b
 
5d360b
 The CORE/NT_PRSTATUS and QEMU notes (that is, the VCPUs' statuses) are
5d360b
 not written to the vmcore. Preparing these would require context that is
5d360b
@@ -217,129 +465,39 @@ shape and this command should mostly work."""
5d360b
         super(DumpGuestMemory, self).__init__("dump-guest-memory",
5d360b
                                               gdb.COMMAND_DATA,
5d360b
                                               gdb.COMPLETE_FILENAME)
5d360b
-        self.elf64_ehdr_le = struct.Struct("<%s" % ELF64_EHDR)
5d360b
-        self.elf64_phdr_le = struct.Struct("<%s" % ELF64_PHDR)
5d360b
+        self.elf = None
5d360b
         self.guest_phys_blocks = None
5d360b
 
5d360b
-    def cpu_get_dump_info(self):
5d360b
-        # We can't synchronize the registers with KVM post-mortem, and
5d360b
-        # the bits in (first_x86_cpu->env.hflags) seem to be stale; they
5d360b
-        # may not reflect long mode for example. Hence just assume the
5d360b
-        # most common values. This also means that instruction pointer
5d360b
-        # etc. will be bogus in the dump, but at least the RAM contents
5d360b
-        # should be valid.
5d360b
-        self.dump_info = {"d_machine": EM_X86_64,
5d360b
-                          "d_endian" : ELFDATA2LSB,
5d360b
-                          "d_class"  : ELFCLASS64}
5d360b
-
5d360b
-    def encode_elf64_ehdr_le(self):
5d360b
-        return self.elf64_ehdr_le.pack(
5d360b
-                                 ELFMAG,                      # e_ident/magic
5d360b
-                                 self.dump_info["d_class"],   # e_ident/class
5d360b
-                                 self.dump_info["d_endian"],  # e_ident/data
5d360b
-                                 EV_CURRENT,                  # e_ident/version
5d360b
-                                 0,                           # e_ident/osabi
5d360b
-                                 "",                          # e_ident/pad
5d360b
-                                 ET_CORE,                     # e_type
5d360b
-                                 self.dump_info["d_machine"], # e_machine
5d360b
-                                 EV_CURRENT,                  # e_version
5d360b
-                                 0,                           # e_entry
5d360b
-                                 self.elf64_ehdr_le.size,     # e_phoff
5d360b
-                                 0,                           # e_shoff
5d360b
-                                 0,                           # e_flags
5d360b
-                                 self.elf64_ehdr_le.size,     # e_ehsize
5d360b
-                                 self.elf64_phdr_le.size,     # e_phentsize
5d360b
-                                 self.phdr_num,               # e_phnum
5d360b
-                                 0,                           # e_shentsize
5d360b
-                                 0,                           # e_shnum
5d360b
-                                 0                            # e_shstrndx
5d360b
-                                )
5d360b
-
5d360b
-    def encode_elf64_note_le(self):
5d360b
-        return self.elf64_phdr_le.pack(PT_NOTE,              # p_type
5d360b
-                                       0,                    # p_flags
5d360b
-                                       (self.memory_offset -
5d360b
-                                        len(self.note)),     # p_offset
5d360b
-                                       0,                    # p_vaddr
5d360b
-                                       0,                    # p_paddr
5d360b
-                                       len(self.note),       # p_filesz
5d360b
-                                       len(self.note),       # p_memsz
5d360b
-                                       0                     # p_align
5d360b
-                                      )
5d360b
-
5d360b
-    def encode_elf64_load_le(self, offset, start_hwaddr, range_size):
5d360b
-        return self.elf64_phdr_le.pack(PT_LOAD,      # p_type
5d360b
-                                       0,            # p_flags
5d360b
-                                       offset,       # p_offset
5d360b
-                                       0,            # p_vaddr
5d360b
-                                       start_hwaddr, # p_paddr
5d360b
-                                       range_size,   # p_filesz
5d360b
-                                       range_size,   # p_memsz
5d360b
-                                       0             # p_align
5d360b
-                                      )
5d360b
-
5d360b
-    def note_init(self, name, desc, type):
5d360b
-        # name must include a trailing NUL
5d360b
-        namesz = (len(name) + 1 + 3) / 4 * 4
5d360b
-        descsz = (len(desc)     + 3) / 4 * 4
5d360b
-        fmt = ("<"   # little endian
5d360b
-               "I"   # n_namesz
5d360b
-               "I"   # n_descsz
5d360b
-               "I"   # n_type
5d360b
-               "%us" # name
5d360b
-               "%us" # desc
5d360b
-               % (namesz, descsz))
5d360b
-        self.note = struct.pack(fmt,
5d360b
-                                len(name) + 1, len(desc), type, name, desc)
5d360b
-
5d360b
-    def dump_init(self):
5d360b
-        self.guest_phys_blocks = get_guest_phys_blocks()
5d360b
-        self.cpu_get_dump_info()
5d360b
-        # we have no way to retrieve the VCPU status from KVM
5d360b
-        # post-mortem
5d360b
-        self.note_init("NONE", "EMPTY", 0)
5d360b
-
5d360b
-        # Account for PT_NOTE.
5d360b
-        self.phdr_num = 1
5d360b
-
5d360b
-        # We should never reach PN_XNUM for paging=false dumps: there's
5d360b
-        # just a handful of discontiguous ranges after merging.
5d360b
-        self.phdr_num += len(self.guest_phys_blocks)
5d360b
-        assert self.phdr_num < PN_XNUM
5d360b
-
5d360b
-        # Calculate the ELF file offset where the memory dump commences:
5d360b
-        #
5d360b
-        #   ELF header
5d360b
-        #   PT_NOTE
5d360b
-        #   PT_LOAD: 1
5d360b
-        #   PT_LOAD: 2
5d360b
-        #   ...
5d360b
-        #   PT_LOAD: len(self.guest_phys_blocks)
5d360b
-        #   ELF note
5d360b
-        #   memory dump
5d360b
-        self.memory_offset = (self.elf64_ehdr_le.size +
5d360b
-                              self.elf64_phdr_le.size * self.phdr_num +
5d360b
-                              len(self.note))
5d360b
-
5d360b
-    def dump_begin(self, vmcore):
5d360b
-        vmcore.write(self.encode_elf64_ehdr_le())
5d360b
-        vmcore.write(self.encode_elf64_note_le())
5d360b
-        running = self.memory_offset
5d360b
+    def dump_init(self, vmcore):
5d360b
+        """Prepares and writes ELF structures to core file."""
5d360b
+
5d360b
+        # Needed to make crash happy, data for more useful notes is
5d360b
+        # not available in a qemu core.
5d360b
+        self.elf.add_note("NONE", "EMPTY", 0)
5d360b
+
5d360b
+        # We should never reach PN_XNUM for paging=false dumps,
5d360b
+        # there's just a handful of discontiguous ranges after
5d360b
+        # merging.
5d360b
+        # The constant is needed to account for the PT_NOTE segment.
5d360b
+        phdr_num = len(self.guest_phys_blocks) + 1
5d360b
+        assert phdr_num < PN_XNUM
5d360b
+
5d360b
         for block in self.guest_phys_blocks:
5d360b
-            range_size = block["target_end"] - block["target_start"]
5d360b
-            vmcore.write(self.encode_elf64_load_le(running,
5d360b
-                                                   block["target_start"],
5d360b
-                                                   range_size))
5d360b
-            running += range_size
5d360b
-        vmcore.write(self.note)
5d360b
+            block_size = block["target_end"] - block["target_start"]
5d360b
+            self.elf.add_segment(PT_LOAD, block["target_start"], block_size)
5d360b
+
5d360b
+        self.elf.to_file(vmcore)
5d360b
 
5d360b
     def dump_iterate(self, vmcore):
5d360b
+        """Writes guest core to file."""
5d360b
+
5d360b
         qemu_core = gdb.inferiors()[0]
5d360b
         for block in self.guest_phys_blocks:
5d360b
             cur = block["host_addr"]
5d360b
             left = block["target_end"] - block["target_start"]
5d360b
             print("dumping range at %016x for length %016x" %
5d360b
                   (cur.cast(UINTPTR_T), left))
5d360b
+
5d360b
             while left > 0:
5d360b
                 chunk_size = min(TARGET_PAGE_SIZE, left)
5d360b
                 chunk = qemu_core.read_memory(cur, chunk_size)
5d360b
@@ -347,13 +505,9 @@ shape and this command should mostly work."""
5d360b
                 cur += chunk_size
5d360b
                 left -= chunk_size
5d360b
 
5d360b
-    def create_vmcore(self, filename):
5d360b
-        vmcore = open(filename, "wb")
5d360b
-        self.dump_begin(vmcore)
5d360b
-        self.dump_iterate(vmcore)
5d360b
-        vmcore.close()
5d360b
-
5d360b
     def invoke(self, args, from_tty):
5d360b
+        """Handles command invocation from gdb."""
5d360b
+
5d360b
         # Unwittingly pressing the Enter key after the command should
5d360b
         # not dump the same multi-gig coredump to the same file.
5d360b
         self.dont_repeat()
5d360b
@@ -362,7 +516,11 @@ shape and this command should mostly work."""
5d360b
         if len(argv) != 1:
5d360b
             raise gdb.GdbError("usage: dump-guest-memory FILE")
5d360b
 
5d360b
-        self.dump_init()
5d360b
-        self.create_vmcore(argv[0])
5d360b
+        self.elf = ELF("X86_64")
5d360b
+        self.guest_phys_blocks = get_guest_phys_blocks()
5d360b
+
5d360b
+        with open(argv[0], "wb") as vmcore:
5d360b
+            self.dump_init(vmcore)
5d360b
+            self.dump_iterate(vmcore)
5d360b
 
5d360b
 DumpGuestMemory()
5d360b
-- 
5d360b
1.8.3.1
5d360b