This work is licensed under the terms of the GNU GPL, version 2 or later. See
the COPYING file in the top-level directory.
"""
+from __future__ import print_function
import ctypes
+import struct
-UINTPTR_T = gdb.lookup_type("uintptr_t")
+try:
+ UINTPTR_T = gdb.lookup_type("uintptr_t")
+except Exception as inst:
+ raise gdb.GdbError("Symbols must be loaded prior to sourcing dump-guest-memory.\n"
+ "Symbols may be loaded by 'attach'ing a QEMU process id or by "
+ "'load'ing a QEMU binary.")
TARGET_PAGE_SIZE = 0x1000
TARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000
EM_AARCH = 183
EM_X86_64 = 62
+VMCOREINFO_FORMAT_ELF = 1
+
+def le16_to_cpu(val):
+ return struct.unpack("<H", struct.pack("=H", val))[0]
+
+def le32_to_cpu(val):
+ return struct.unpack("<I", struct.pack("=I", val))[0]
+
+def le64_to_cpu(val):
+ return struct.unpack("<Q", struct.pack("=Q", val))[0]
+
class ELF(object):
"""Representation of a ELF file."""
self.notes = []
self.segments = []
self.notes_size = 0
- self.endianess = None
+ self.endianness = None
self.elfclass = ELFCLASS64
if arch == 'aarch64-le':
- self.endianess = ELFDATA2LSB
+ self.endianness = ELFDATA2LSB
self.elfclass = ELFCLASS64
- self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+ self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
self.ehdr.e_machine = EM_AARCH
elif arch == 'aarch64-be':
- self.endianess = ELFDATA2MSB
- self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+ self.endianness = ELFDATA2MSB
+ self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
self.ehdr.e_machine = EM_AARCH
elif arch == 'X86_64':
- self.endianess = ELFDATA2LSB
- self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+ self.endianness = ELFDATA2LSB
+ self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
self.ehdr.e_machine = EM_X86_64
elif arch == '386':
- self.endianess = ELFDATA2LSB
+ self.endianness = ELFDATA2LSB
self.elfclass = ELFCLASS32
- self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+ self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
self.ehdr.e_machine = EM_386
elif arch == 's390':
- self.endianess = ELFDATA2MSB
- self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+ self.endianness = ELFDATA2MSB
+ self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
self.ehdr.e_machine = EM_S390
elif arch == 'ppc64-le':
- self.endianess = ELFDATA2LSB
- self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+ self.endianness = ELFDATA2LSB
+ self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
self.ehdr.e_machine = EM_PPC64
elif arch == 'ppc64-be':
- self.endianess = ELFDATA2MSB
- self.ehdr = get_arch_ehdr(self.endianess, self.elfclass)
+ self.endianness = ELFDATA2MSB
+ self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
self.ehdr.e_machine = EM_PPC64
else:
def add_note(self, n_name, n_desc, n_type):
"""Adds a note to the ELF."""
- note = get_arch_note(self.endianess, len(n_name), len(n_desc))
+ note = get_arch_note(self.endianness, len(n_name), len(n_desc))
note.n_namesz = len(n_name) + 1
note.n_descsz = len(n_desc)
note.n_name = n_name.encode()
self.segments[0].p_filesz += ctypes.sizeof(note)
self.segments[0].p_memsz += ctypes.sizeof(note)
+
+ def add_vmcoreinfo_note(self, vmcoreinfo):
+ """Adds a vmcoreinfo note to the ELF dump."""
+ # compute the header size, and copy that many bytes from the note
+ header = get_arch_note(self.endianness, 0, 0)
+ ctypes.memmove(ctypes.pointer(header),
+ vmcoreinfo, ctypes.sizeof(header))
+ if header.n_descsz > 1 << 20:
+ print('warning: invalid vmcoreinfo size')
+ return
+ # now get the full note
+ note = get_arch_note(self.endianness,
+ header.n_namesz - 1, header.n_descsz)
+ ctypes.memmove(ctypes.pointer(note), vmcoreinfo, ctypes.sizeof(note))
+
+ self.notes.append(note)
+ self.segments[0].p_filesz += ctypes.sizeof(note)
+ self.segments[0].p_memsz += ctypes.sizeof(note)
+
def add_segment(self, p_type, p_paddr, p_size):
"""Adds a segment to the elf."""
- phdr = get_arch_phdr(self.endianess, self.elfclass)
+ phdr = get_arch_phdr(self.endianness, self.elfclass)
phdr.p_type = p_type
phdr.p_paddr = p_paddr
+ phdr.p_vaddr = p_paddr
phdr.p_filesz = p_size
phdr.p_memsz = p_size
self.segments.append(phdr)
self.ehdr.e_phnum += 1
def to_file(self, elf_file):
- """Writes all ELF structures to the the passed file.
+ """Writes all ELF structures to the passed file.
Structure:
Ehdr
elf_file.write(note)
-def get_arch_note(endianess, len_name, len_desc):
- """Returns a Note class with the specified endianess."""
+def get_arch_note(endianness, len_name, len_desc):
+ """Returns a Note class with the specified endianness."""
- if endianess == ELFDATA2LSB:
+ if endianness == ELFDATA2LSB:
superclass = ctypes.LittleEndianStructure
else:
superclass = ctypes.BigEndianStructure
('ei_abiversion', ctypes.c_ubyte),
('ei_pad', ctypes.c_ubyte * 7)]
- def __init__(self, endianess, elfclass):
+ def __init__(self, endianness, elfclass):
self.ei_mag0 = 0x7F
self.ei_mag1 = ord('E')
self.ei_mag2 = ord('L')
self.ei_mag3 = ord('F')
self.ei_class = elfclass
- self.ei_data = endianess
+ self.ei_data = endianness
self.ei_version = EV_CURRENT
-def get_arch_ehdr(endianess, elfclass):
- """Returns a EHDR64 class with the specified endianess."""
+def get_arch_ehdr(endianness, elfclass):
+ """Returns a EHDR64 class with the specified endianness."""
- if endianess == ELFDATA2LSB:
+ if endianness == ELFDATA2LSB:
superclass = ctypes.LittleEndianStructure
else:
superclass = ctypes.BigEndianStructure
def __init__(self):
super(superclass, self).__init__()
- self.e_ident = Ident(endianess, elfclass)
+ self.e_ident = Ident(endianness, elfclass)
self.e_type = ET_CORE
self.e_version = EV_CURRENT
self.e_ehsize = ctypes.sizeof(self)
self.e_phoff = ctypes.sizeof(self)
- self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass))
+ self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianness, elfclass))
self.e_phnum = 0
def __init__(self):
super(superclass, self).__init__()
- self.e_ident = Ident(endianess, elfclass)
+ self.e_ident = Ident(endianness, elfclass)
self.e_type = ET_CORE
self.e_version = EV_CURRENT
self.e_ehsize = ctypes.sizeof(self)
self.e_phoff = ctypes.sizeof(self)
- self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass))
+ self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianness, elfclass))
self.e_phnum = 0
# End get_arch_ehdr
return EHDR32()
-def get_arch_phdr(endianess, elfclass):
- """Returns a 32 or 64 bit PHDR class with the specified endianess."""
+def get_arch_phdr(endianness, elfclass):
+ """Returns a 32 or 64 bit PHDR class with the specified endianness."""
- if endianess == ELFDATA2LSB:
+ if endianness == ELFDATA2LSB:
superclass = ctypes.LittleEndianStructure
else:
superclass = ctypes.BigEndianStructure
def int128_get64(val):
"""Returns low 64bit part of Int128 struct."""
- assert val["hi"] == 0
- return val["lo"]
+ try:
+ assert val["hi"] == 0
+ return val["lo"]
+ except gdb.error:
+ u64t = gdb.lookup_type('uint64_t').array(2)
+ u64 = val.cast(u64t)
+ if sys.byteorder == 'little':
+ assert u64[1] == 0
+ return u64[0]
+ else:
+ assert u64[0] == 0
+ return u64[1]
def qlist_foreach(head, field_str):
yield var
-def qemu_get_ram_block(ram_addr):
- """Returns the RAMBlock struct to which the given address belongs."""
-
- ram_blocks = gdb.parse_and_eval("ram_list.blocks")
-
- for block in qlist_foreach(ram_blocks, "next"):
- if (ram_addr - block["offset"]) < block["used_length"]:
- return block
-
- raise gdb.GdbError("Bad ram offset %x" % ram_addr)
-
-
-def qemu_get_ram_ptr(ram_addr):
+def qemu_map_ram_ptr(block, offset):
"""Returns qemu vaddr for given guest physical address."""
- block = qemu_get_ram_block(ram_addr)
- return block["host"] + (ram_addr - block["offset"])
+ return block["host"] + offset
def memory_region_get_ram_ptr(memory_region):
return (memory_region_get_ram_ptr(memory_region["alias"].dereference())
+ memory_region["alias_offset"])
- return qemu_get_ram_ptr(memory_region["ram_block"]["offset"])
+ return qemu_map_ram_ptr(memory_region["ram_block"], 0)
def get_guest_phys_blocks():
memory_region = flat_range["mr"].dereference()
# we only care about RAM
- if not memory_region["ram"]:
+ if (not memory_region["ram"] or
+ memory_region["ram_device"] or
+ memory_region["nonvolatile"]):
continue
section_size = int128_get64(flat_range["addr"]["size"])
cur += chunk_size
left -= chunk_size
+ def phys_memory_read(self, addr, size):
+ qemu_core = gdb.inferiors()[0]
+ for block in self.guest_phys_blocks:
+ if block["target_start"] <= addr \
+ and addr + size <= block["target_end"]:
+ haddr = block["host_addr"] + (addr - block["target_start"])
+ return qemu_core.read_memory(haddr, size)
+ return None
+
+ def add_vmcoreinfo(self):
+ if gdb.lookup_symbol("vmcoreinfo_realize")[0] is None:
+ return
+ vmci = 'vmcoreinfo_realize::vmcoreinfo_state'
+ if not gdb.parse_and_eval("%s" % vmci) \
+ or not gdb.parse_and_eval("(%s)->has_vmcoreinfo" % vmci):
+ return
+
+ fmt = gdb.parse_and_eval("(%s)->vmcoreinfo.guest_format" % vmci)
+ addr = gdb.parse_and_eval("(%s)->vmcoreinfo.paddr" % vmci)
+ size = gdb.parse_and_eval("(%s)->vmcoreinfo.size" % vmci)
+
+ fmt = le16_to_cpu(fmt)
+ addr = le64_to_cpu(addr)
+ size = le32_to_cpu(size)
+
+ if fmt != VMCOREINFO_FORMAT_ELF:
+ return
+
+ vmcoreinfo = self.phys_memory_read(addr, size)
+ if vmcoreinfo:
+ self.elf.add_vmcoreinfo_note(bytes(vmcoreinfo))
+
def invoke(self, args, from_tty):
"""Handles command invocation from gdb."""
self.elf = ELF(argv[1])
self.guest_phys_blocks = get_guest_phys_blocks()
+ self.add_vmcoreinfo()
with open(argv[0], "wb") as vmcore:
self.dump_init(vmcore)