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