13e16d14fSLaszlo Ersek# This python script adds a new gdb command, "dump-guest-memory". It 23e16d14fSLaszlo Ersek# should be loaded with "source dump-guest-memory.py" at the (gdb) 33e16d14fSLaszlo Ersek# prompt. 43e16d14fSLaszlo Ersek# 53e16d14fSLaszlo Ersek# Copyright (C) 2013, Red Hat, Inc. 63e16d14fSLaszlo Ersek# 73e16d14fSLaszlo Ersek# Authors: 83e16d14fSLaszlo Ersek# Laszlo Ersek <lersek@redhat.com> 9*368e3adcSJanosch Frank# Janosch Frank <frankja@linux.vnet.ibm.com> 103e16d14fSLaszlo Ersek# 113e16d14fSLaszlo Ersek# This work is licensed under the terms of the GNU GPL, version 2 or later. See 123e16d14fSLaszlo Ersek# the COPYING file in the top-level directory. 133e16d14fSLaszlo Ersek# 143e16d14fSLaszlo Ersek# The leading docstring doesn't have idiomatic Python formatting. It is 153e16d14fSLaszlo Ersek# printed by gdb's "help" command (the first line is printed in the 163e16d14fSLaszlo Ersek# "help data" summary), and it should match how other help texts look in 173e16d14fSLaszlo Ersek# gdb. 183e16d14fSLaszlo Ersek 19*368e3adcSJanosch Frankimport ctypes 203e16d14fSLaszlo Ersek 2147890203SJanosch FrankUINTPTR_T = gdb.lookup_type("uintptr_t") 2247890203SJanosch Frank 233e16d14fSLaszlo ErsekTARGET_PAGE_SIZE = 0x1000 243e16d14fSLaszlo ErsekTARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000 253e16d14fSLaszlo Ersek 263e16d14fSLaszlo Ersek# Special value for e_phnum. This indicates that the real number of 273e16d14fSLaszlo Ersek# program headers is too large to fit into e_phnum. Instead the real 283e16d14fSLaszlo Ersek# value is in the field sh_info of section 0. 293e16d14fSLaszlo ErsekPN_XNUM = 0xFFFF 303e16d14fSLaszlo Ersek 31*368e3adcSJanosch FrankEV_CURRENT = 1 32*368e3adcSJanosch Frank 33*368e3adcSJanosch FrankELFCLASS32 = 1 34*368e3adcSJanosch FrankELFCLASS64 = 2 35*368e3adcSJanosch Frank 36*368e3adcSJanosch FrankELFDATA2LSB = 1 37*368e3adcSJanosch FrankELFDATA2MSB = 2 38*368e3adcSJanosch Frank 39*368e3adcSJanosch FrankET_CORE = 4 40*368e3adcSJanosch Frank 41*368e3adcSJanosch FrankPT_LOAD = 1 42*368e3adcSJanosch FrankPT_NOTE = 4 43*368e3adcSJanosch Frank 44*368e3adcSJanosch FrankEM_386 = 3 45*368e3adcSJanosch FrankEM_PPC = 20 46*368e3adcSJanosch FrankEM_PPC64 = 21 47*368e3adcSJanosch FrankEM_S390 = 22 48*368e3adcSJanosch FrankEM_AARCH = 183 49*368e3adcSJanosch FrankEM_X86_64 = 62 50*368e3adcSJanosch Frank 51*368e3adcSJanosch Frankclass ELF(object): 52*368e3adcSJanosch Frank """Representation of a ELF file.""" 53*368e3adcSJanosch Frank 54*368e3adcSJanosch Frank def __init__(self, arch): 55*368e3adcSJanosch Frank self.ehdr = None 56*368e3adcSJanosch Frank self.notes = [] 57*368e3adcSJanosch Frank self.segments = [] 58*368e3adcSJanosch Frank self.notes_size = 0 59*368e3adcSJanosch Frank self.endianess = None 60*368e3adcSJanosch Frank self.elfclass = ELFCLASS64 61*368e3adcSJanosch Frank 62*368e3adcSJanosch Frank if arch == 'aarch64-le': 63*368e3adcSJanosch Frank self.endianess = ELFDATA2LSB 64*368e3adcSJanosch Frank self.elfclass = ELFCLASS64 65*368e3adcSJanosch Frank self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) 66*368e3adcSJanosch Frank self.ehdr.e_machine = EM_AARCH 67*368e3adcSJanosch Frank 68*368e3adcSJanosch Frank elif arch == 'aarch64-be': 69*368e3adcSJanosch Frank self.endianess = ELFDATA2MSB 70*368e3adcSJanosch Frank self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) 71*368e3adcSJanosch Frank self.ehdr.e_machine = EM_AARCH 72*368e3adcSJanosch Frank 73*368e3adcSJanosch Frank elif arch == 'X86_64': 74*368e3adcSJanosch Frank self.endianess = ELFDATA2LSB 75*368e3adcSJanosch Frank self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) 76*368e3adcSJanosch Frank self.ehdr.e_machine = EM_X86_64 77*368e3adcSJanosch Frank 78*368e3adcSJanosch Frank elif arch == '386': 79*368e3adcSJanosch Frank self.endianess = ELFDATA2LSB 80*368e3adcSJanosch Frank self.elfclass = ELFCLASS32 81*368e3adcSJanosch Frank self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) 82*368e3adcSJanosch Frank self.ehdr.e_machine = EM_386 83*368e3adcSJanosch Frank 84*368e3adcSJanosch Frank elif arch == 's390': 85*368e3adcSJanosch Frank self.endianess = ELFDATA2MSB 86*368e3adcSJanosch Frank self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) 87*368e3adcSJanosch Frank self.ehdr.e_machine = EM_S390 88*368e3adcSJanosch Frank 89*368e3adcSJanosch Frank elif arch == 'ppc64-le': 90*368e3adcSJanosch Frank self.endianess = ELFDATA2LSB 91*368e3adcSJanosch Frank self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) 92*368e3adcSJanosch Frank self.ehdr.e_machine = EM_PPC64 93*368e3adcSJanosch Frank 94*368e3adcSJanosch Frank elif arch == 'ppc64-be': 95*368e3adcSJanosch Frank self.endianess = ELFDATA2MSB 96*368e3adcSJanosch Frank self.ehdr = get_arch_ehdr(self.endianess, self.elfclass) 97*368e3adcSJanosch Frank self.ehdr.e_machine = EM_PPC64 98*368e3adcSJanosch Frank 99*368e3adcSJanosch Frank else: 100*368e3adcSJanosch Frank raise gdb.GdbError("No valid arch type specified.\n" 101*368e3adcSJanosch Frank "Currently supported types:\n" 102*368e3adcSJanosch Frank "aarch64-be, aarch64-le, X86_64, 386, s390, " 103*368e3adcSJanosch Frank "ppc64-be, ppc64-le") 104*368e3adcSJanosch Frank 105*368e3adcSJanosch Frank self.add_segment(PT_NOTE, 0, 0) 106*368e3adcSJanosch Frank 107*368e3adcSJanosch Frank def add_note(self, n_name, n_desc, n_type): 108*368e3adcSJanosch Frank """Adds a note to the ELF.""" 109*368e3adcSJanosch Frank 110*368e3adcSJanosch Frank note = get_arch_note(self.endianess, len(n_name), len(n_desc)) 111*368e3adcSJanosch Frank note.n_namesz = len(n_name) + 1 112*368e3adcSJanosch Frank note.n_descsz = len(n_desc) 113*368e3adcSJanosch Frank note.n_name = n_name.encode() 114*368e3adcSJanosch Frank note.n_type = n_type 115*368e3adcSJanosch Frank 116*368e3adcSJanosch Frank # Desc needs to be 4 byte aligned (although the 64bit spec 117*368e3adcSJanosch Frank # specifies 8 byte). When defining n_desc as uint32 it will be 118*368e3adcSJanosch Frank # automatically aligned but we need the memmove to copy the 119*368e3adcSJanosch Frank # string into it. 120*368e3adcSJanosch Frank ctypes.memmove(note.n_desc, n_desc.encode(), len(n_desc)) 121*368e3adcSJanosch Frank 122*368e3adcSJanosch Frank self.notes.append(note) 123*368e3adcSJanosch Frank self.segments[0].p_filesz += ctypes.sizeof(note) 124*368e3adcSJanosch Frank self.segments[0].p_memsz += ctypes.sizeof(note) 125*368e3adcSJanosch Frank 126*368e3adcSJanosch Frank def add_segment(self, p_type, p_paddr, p_size): 127*368e3adcSJanosch Frank """Adds a segment to the elf.""" 128*368e3adcSJanosch Frank 129*368e3adcSJanosch Frank phdr = get_arch_phdr(self.endianess, self.elfclass) 130*368e3adcSJanosch Frank phdr.p_type = p_type 131*368e3adcSJanosch Frank phdr.p_paddr = p_paddr 132*368e3adcSJanosch Frank phdr.p_filesz = p_size 133*368e3adcSJanosch Frank phdr.p_memsz = p_size 134*368e3adcSJanosch Frank self.segments.append(phdr) 135*368e3adcSJanosch Frank self.ehdr.e_phnum += 1 136*368e3adcSJanosch Frank 137*368e3adcSJanosch Frank def to_file(self, elf_file): 138*368e3adcSJanosch Frank """Writes all ELF structures to the the passed file. 139*368e3adcSJanosch Frank 140*368e3adcSJanosch Frank Structure: 141*368e3adcSJanosch Frank Ehdr 142*368e3adcSJanosch Frank Segment 0:PT_NOTE 143*368e3adcSJanosch Frank Segment 1:PT_LOAD 144*368e3adcSJanosch Frank Segment N:PT_LOAD 145*368e3adcSJanosch Frank Note 0..N 146*368e3adcSJanosch Frank Dump contents 147*368e3adcSJanosch Frank """ 148*368e3adcSJanosch Frank elf_file.write(self.ehdr) 149*368e3adcSJanosch Frank off = ctypes.sizeof(self.ehdr) + \ 150*368e3adcSJanosch Frank len(self.segments) * ctypes.sizeof(self.segments[0]) 151*368e3adcSJanosch Frank 152*368e3adcSJanosch Frank for phdr in self.segments: 153*368e3adcSJanosch Frank phdr.p_offset = off 154*368e3adcSJanosch Frank elf_file.write(phdr) 155*368e3adcSJanosch Frank off += phdr.p_filesz 156*368e3adcSJanosch Frank 157*368e3adcSJanosch Frank for note in self.notes: 158*368e3adcSJanosch Frank elf_file.write(note) 159*368e3adcSJanosch Frank 160*368e3adcSJanosch Frank 161*368e3adcSJanosch Frankdef get_arch_note(endianess, len_name, len_desc): 162*368e3adcSJanosch Frank """Returns a Note class with the specified endianess.""" 163*368e3adcSJanosch Frank 164*368e3adcSJanosch Frank if endianess == ELFDATA2LSB: 165*368e3adcSJanosch Frank superclass = ctypes.LittleEndianStructure 166*368e3adcSJanosch Frank else: 167*368e3adcSJanosch Frank superclass = ctypes.BigEndianStructure 168*368e3adcSJanosch Frank 169*368e3adcSJanosch Frank len_name = len_name + 1 170*368e3adcSJanosch Frank 171*368e3adcSJanosch Frank class Note(superclass): 172*368e3adcSJanosch Frank """Represents an ELF note, includes the content.""" 173*368e3adcSJanosch Frank 174*368e3adcSJanosch Frank _fields_ = [("n_namesz", ctypes.c_uint32), 175*368e3adcSJanosch Frank ("n_descsz", ctypes.c_uint32), 176*368e3adcSJanosch Frank ("n_type", ctypes.c_uint32), 177*368e3adcSJanosch Frank ("n_name", ctypes.c_char * len_name), 178*368e3adcSJanosch Frank ("n_desc", ctypes.c_uint32 * ((len_desc + 3) // 4))] 179*368e3adcSJanosch Frank return Note() 180*368e3adcSJanosch Frank 181*368e3adcSJanosch Frank 182*368e3adcSJanosch Frankclass Ident(ctypes.Structure): 183*368e3adcSJanosch Frank """Represents the ELF ident array in the ehdr structure.""" 184*368e3adcSJanosch Frank 185*368e3adcSJanosch Frank _fields_ = [('ei_mag0', ctypes.c_ubyte), 186*368e3adcSJanosch Frank ('ei_mag1', ctypes.c_ubyte), 187*368e3adcSJanosch Frank ('ei_mag2', ctypes.c_ubyte), 188*368e3adcSJanosch Frank ('ei_mag3', ctypes.c_ubyte), 189*368e3adcSJanosch Frank ('ei_class', ctypes.c_ubyte), 190*368e3adcSJanosch Frank ('ei_data', ctypes.c_ubyte), 191*368e3adcSJanosch Frank ('ei_version', ctypes.c_ubyte), 192*368e3adcSJanosch Frank ('ei_osabi', ctypes.c_ubyte), 193*368e3adcSJanosch Frank ('ei_abiversion', ctypes.c_ubyte), 194*368e3adcSJanosch Frank ('ei_pad', ctypes.c_ubyte * 7)] 195*368e3adcSJanosch Frank 196*368e3adcSJanosch Frank def __init__(self, endianess, elfclass): 197*368e3adcSJanosch Frank self.ei_mag0 = 0x7F 198*368e3adcSJanosch Frank self.ei_mag1 = ord('E') 199*368e3adcSJanosch Frank self.ei_mag2 = ord('L') 200*368e3adcSJanosch Frank self.ei_mag3 = ord('F') 201*368e3adcSJanosch Frank self.ei_class = elfclass 202*368e3adcSJanosch Frank self.ei_data = endianess 203*368e3adcSJanosch Frank self.ei_version = EV_CURRENT 204*368e3adcSJanosch Frank 205*368e3adcSJanosch Frank 206*368e3adcSJanosch Frankdef get_arch_ehdr(endianess, elfclass): 207*368e3adcSJanosch Frank """Returns a EHDR64 class with the specified endianess.""" 208*368e3adcSJanosch Frank 209*368e3adcSJanosch Frank if endianess == ELFDATA2LSB: 210*368e3adcSJanosch Frank superclass = ctypes.LittleEndianStructure 211*368e3adcSJanosch Frank else: 212*368e3adcSJanosch Frank superclass = ctypes.BigEndianStructure 213*368e3adcSJanosch Frank 214*368e3adcSJanosch Frank class EHDR64(superclass): 215*368e3adcSJanosch Frank """Represents the 64 bit ELF header struct.""" 216*368e3adcSJanosch Frank 217*368e3adcSJanosch Frank _fields_ = [('e_ident', Ident), 218*368e3adcSJanosch Frank ('e_type', ctypes.c_uint16), 219*368e3adcSJanosch Frank ('e_machine', ctypes.c_uint16), 220*368e3adcSJanosch Frank ('e_version', ctypes.c_uint32), 221*368e3adcSJanosch Frank ('e_entry', ctypes.c_uint64), 222*368e3adcSJanosch Frank ('e_phoff', ctypes.c_uint64), 223*368e3adcSJanosch Frank ('e_shoff', ctypes.c_uint64), 224*368e3adcSJanosch Frank ('e_flags', ctypes.c_uint32), 225*368e3adcSJanosch Frank ('e_ehsize', ctypes.c_uint16), 226*368e3adcSJanosch Frank ('e_phentsize', ctypes.c_uint16), 227*368e3adcSJanosch Frank ('e_phnum', ctypes.c_uint16), 228*368e3adcSJanosch Frank ('e_shentsize', ctypes.c_uint16), 229*368e3adcSJanosch Frank ('e_shnum', ctypes.c_uint16), 230*368e3adcSJanosch Frank ('e_shstrndx', ctypes.c_uint16)] 231*368e3adcSJanosch Frank 232*368e3adcSJanosch Frank def __init__(self): 233*368e3adcSJanosch Frank super(superclass, self).__init__() 234*368e3adcSJanosch Frank self.e_ident = Ident(endianess, elfclass) 235*368e3adcSJanosch Frank self.e_type = ET_CORE 236*368e3adcSJanosch Frank self.e_version = EV_CURRENT 237*368e3adcSJanosch Frank self.e_ehsize = ctypes.sizeof(self) 238*368e3adcSJanosch Frank self.e_phoff = ctypes.sizeof(self) 239*368e3adcSJanosch Frank self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass)) 240*368e3adcSJanosch Frank self.e_phnum = 0 241*368e3adcSJanosch Frank 242*368e3adcSJanosch Frank 243*368e3adcSJanosch Frank class EHDR32(superclass): 244*368e3adcSJanosch Frank """Represents the 32 bit ELF header struct.""" 245*368e3adcSJanosch Frank 246*368e3adcSJanosch Frank _fields_ = [('e_ident', Ident), 247*368e3adcSJanosch Frank ('e_type', ctypes.c_uint16), 248*368e3adcSJanosch Frank ('e_machine', ctypes.c_uint16), 249*368e3adcSJanosch Frank ('e_version', ctypes.c_uint32), 250*368e3adcSJanosch Frank ('e_entry', ctypes.c_uint32), 251*368e3adcSJanosch Frank ('e_phoff', ctypes.c_uint32), 252*368e3adcSJanosch Frank ('e_shoff', ctypes.c_uint32), 253*368e3adcSJanosch Frank ('e_flags', ctypes.c_uint32), 254*368e3adcSJanosch Frank ('e_ehsize', ctypes.c_uint16), 255*368e3adcSJanosch Frank ('e_phentsize', ctypes.c_uint16), 256*368e3adcSJanosch Frank ('e_phnum', ctypes.c_uint16), 257*368e3adcSJanosch Frank ('e_shentsize', ctypes.c_uint16), 258*368e3adcSJanosch Frank ('e_shnum', ctypes.c_uint16), 259*368e3adcSJanosch Frank ('e_shstrndx', ctypes.c_uint16)] 260*368e3adcSJanosch Frank 261*368e3adcSJanosch Frank def __init__(self): 262*368e3adcSJanosch Frank super(superclass, self).__init__() 263*368e3adcSJanosch Frank self.e_ident = Ident(endianess, elfclass) 264*368e3adcSJanosch Frank self.e_type = ET_CORE 265*368e3adcSJanosch Frank self.e_version = EV_CURRENT 266*368e3adcSJanosch Frank self.e_ehsize = ctypes.sizeof(self) 267*368e3adcSJanosch Frank self.e_phoff = ctypes.sizeof(self) 268*368e3adcSJanosch Frank self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianess, elfclass)) 269*368e3adcSJanosch Frank self.e_phnum = 0 270*368e3adcSJanosch Frank 271*368e3adcSJanosch Frank # End get_arch_ehdr 272*368e3adcSJanosch Frank if elfclass == ELFCLASS64: 273*368e3adcSJanosch Frank return EHDR64() 274*368e3adcSJanosch Frank else: 275*368e3adcSJanosch Frank return EHDR32() 276*368e3adcSJanosch Frank 277*368e3adcSJanosch Frank 278*368e3adcSJanosch Frankdef get_arch_phdr(endianess, elfclass): 279*368e3adcSJanosch Frank """Returns a 32 or 64 bit PHDR class with the specified endianess.""" 280*368e3adcSJanosch Frank 281*368e3adcSJanosch Frank if endianess == ELFDATA2LSB: 282*368e3adcSJanosch Frank superclass = ctypes.LittleEndianStructure 283*368e3adcSJanosch Frank else: 284*368e3adcSJanosch Frank superclass = ctypes.BigEndianStructure 285*368e3adcSJanosch Frank 286*368e3adcSJanosch Frank class PHDR64(superclass): 287*368e3adcSJanosch Frank """Represents the 64 bit ELF program header struct.""" 288*368e3adcSJanosch Frank 289*368e3adcSJanosch Frank _fields_ = [('p_type', ctypes.c_uint32), 290*368e3adcSJanosch Frank ('p_flags', ctypes.c_uint32), 291*368e3adcSJanosch Frank ('p_offset', ctypes.c_uint64), 292*368e3adcSJanosch Frank ('p_vaddr', ctypes.c_uint64), 293*368e3adcSJanosch Frank ('p_paddr', ctypes.c_uint64), 294*368e3adcSJanosch Frank ('p_filesz', ctypes.c_uint64), 295*368e3adcSJanosch Frank ('p_memsz', ctypes.c_uint64), 296*368e3adcSJanosch Frank ('p_align', ctypes.c_uint64)] 297*368e3adcSJanosch Frank 298*368e3adcSJanosch Frank class PHDR32(superclass): 299*368e3adcSJanosch Frank """Represents the 32 bit ELF program header struct.""" 300*368e3adcSJanosch Frank 301*368e3adcSJanosch Frank _fields_ = [('p_type', ctypes.c_uint32), 302*368e3adcSJanosch Frank ('p_offset', ctypes.c_uint32), 303*368e3adcSJanosch Frank ('p_vaddr', ctypes.c_uint32), 304*368e3adcSJanosch Frank ('p_paddr', ctypes.c_uint32), 305*368e3adcSJanosch Frank ('p_filesz', ctypes.c_uint32), 306*368e3adcSJanosch Frank ('p_memsz', ctypes.c_uint32), 307*368e3adcSJanosch Frank ('p_flags', ctypes.c_uint32), 308*368e3adcSJanosch Frank ('p_align', ctypes.c_uint32)] 309*368e3adcSJanosch Frank 310*368e3adcSJanosch Frank # End get_arch_phdr 311*368e3adcSJanosch Frank if elfclass == ELFCLASS64: 312*368e3adcSJanosch Frank return PHDR64() 313*368e3adcSJanosch Frank else: 314*368e3adcSJanosch Frank return PHDR32() 315*368e3adcSJanosch Frank 3163e16d14fSLaszlo Ersek 31747890203SJanosch Frankdef int128_get64(val): 3186782c0e7SJanosch Frank """Returns low 64bit part of Int128 struct.""" 3196782c0e7SJanosch Frank 3206782c0e7SJanosch Frank assert val["hi"] == 0 32147890203SJanosch Frank return val["lo"] 32247890203SJanosch Frank 3236782c0e7SJanosch Frank 32447890203SJanosch Frankdef qlist_foreach(head, field_str): 3256782c0e7SJanosch Frank """Generator for qlists.""" 3266782c0e7SJanosch Frank 32747890203SJanosch Frank var_p = head["lh_first"] 3286782c0e7SJanosch Frank while var_p != 0: 32947890203SJanosch Frank var = var_p.dereference() 33047890203SJanosch Frank var_p = var[field_str]["le_next"] 3316782c0e7SJanosch Frank yield var 3326782c0e7SJanosch Frank 33347890203SJanosch Frank 33447890203SJanosch Frankdef qemu_get_ram_block(ram_addr): 3356782c0e7SJanosch Frank """Returns the RAMBlock struct to which the given address belongs.""" 3366782c0e7SJanosch Frank 33747890203SJanosch Frank ram_blocks = gdb.parse_and_eval("ram_list.blocks") 3386782c0e7SJanosch Frank 33947890203SJanosch Frank for block in qlist_foreach(ram_blocks, "next"): 3406782c0e7SJanosch Frank if (ram_addr - block["offset"]) < block["used_length"]: 34147890203SJanosch Frank return block 3426782c0e7SJanosch Frank 34347890203SJanosch Frank raise gdb.GdbError("Bad ram offset %x" % ram_addr) 34447890203SJanosch Frank 3456782c0e7SJanosch Frank 34647890203SJanosch Frankdef qemu_get_ram_ptr(ram_addr): 3476782c0e7SJanosch Frank """Returns qemu vaddr for given guest physical address.""" 3486782c0e7SJanosch Frank 34947890203SJanosch Frank block = qemu_get_ram_block(ram_addr) 35047890203SJanosch Frank return block["host"] + (ram_addr - block["offset"]) 35147890203SJanosch Frank 3526782c0e7SJanosch Frank 3536782c0e7SJanosch Frankdef memory_region_get_ram_ptr(memory_region): 3546782c0e7SJanosch Frank if memory_region["alias"] != 0: 3556782c0e7SJanosch Frank return (memory_region_get_ram_ptr(memory_region["alias"].dereference()) 3566782c0e7SJanosch Frank + memory_region["alias_offset"]) 3576782c0e7SJanosch Frank 3586782c0e7SJanosch Frank return qemu_get_ram_ptr(memory_region["ram_addr"] & TARGET_PAGE_MASK) 3596782c0e7SJanosch Frank 36047890203SJanosch Frank 36147890203SJanosch Frankdef get_guest_phys_blocks(): 3626782c0e7SJanosch Frank """Returns a list of ram blocks. 3636782c0e7SJanosch Frank 3646782c0e7SJanosch Frank Each block entry contains: 3656782c0e7SJanosch Frank 'target_start': guest block phys start address 3666782c0e7SJanosch Frank 'target_end': guest block phys end address 3676782c0e7SJanosch Frank 'host_addr': qemu vaddr of the block's start 3686782c0e7SJanosch Frank """ 3696782c0e7SJanosch Frank 37047890203SJanosch Frank guest_phys_blocks = [] 3716782c0e7SJanosch Frank 3727cb1089dSJanosch Frank print("guest RAM blocks:") 37347890203SJanosch Frank print("target_start target_end host_addr message " 37447890203SJanosch Frank "count") 37547890203SJanosch Frank print("---------------- ---------------- ---------------- ------- " 37647890203SJanosch Frank "-----") 37747890203SJanosch Frank 37847890203SJanosch Frank current_map_p = gdb.parse_and_eval("address_space_memory.current_map") 37947890203SJanosch Frank current_map = current_map_p.dereference() 3807cb1089dSJanosch Frank 3817cb1089dSJanosch Frank # Conversion to int is needed for python 3 3827cb1089dSJanosch Frank # compatibility. Otherwise range doesn't cast the value itself and 3837cb1089dSJanosch Frank # breaks. 3847cb1089dSJanosch Frank for cur in range(int(current_map["nr"])): 38547890203SJanosch Frank flat_range = (current_map["ranges"] + cur).dereference() 3866782c0e7SJanosch Frank memory_region = flat_range["mr"].dereference() 38747890203SJanosch Frank 38847890203SJanosch Frank # we only care about RAM 3896782c0e7SJanosch Frank if not memory_region["ram"]: 39047890203SJanosch Frank continue 39147890203SJanosch Frank 39247890203SJanosch Frank section_size = int128_get64(flat_range["addr"]["size"]) 39347890203SJanosch Frank target_start = int128_get64(flat_range["addr"]["start"]) 39447890203SJanosch Frank target_end = target_start + section_size 3956782c0e7SJanosch Frank host_addr = (memory_region_get_ram_ptr(memory_region) 3966782c0e7SJanosch Frank + flat_range["offset_in_region"]) 39747890203SJanosch Frank predecessor = None 39847890203SJanosch Frank 39947890203SJanosch Frank # find continuity in guest physical address space 4006782c0e7SJanosch Frank if len(guest_phys_blocks) > 0: 40147890203SJanosch Frank predecessor = guest_phys_blocks[-1] 40247890203SJanosch Frank predecessor_size = (predecessor["target_end"] - 40347890203SJanosch Frank predecessor["target_start"]) 40447890203SJanosch Frank 40547890203SJanosch Frank # the memory API guarantees monotonically increasing 40647890203SJanosch Frank # traversal 4076782c0e7SJanosch Frank assert predecessor["target_end"] <= target_start 40847890203SJanosch Frank 40947890203SJanosch Frank # we want continuity in both guest-physical and 41047890203SJanosch Frank # host-virtual memory 41147890203SJanosch Frank if (predecessor["target_end"] < target_start or 41247890203SJanosch Frank predecessor["host_addr"] + predecessor_size != host_addr): 41347890203SJanosch Frank predecessor = None 41447890203SJanosch Frank 4156782c0e7SJanosch Frank if predecessor is None: 41647890203SJanosch Frank # isolated mapping, add it to the list 41747890203SJanosch Frank guest_phys_blocks.append({"target_start": target_start, 41847890203SJanosch Frank "target_end": target_end, 41947890203SJanosch Frank "host_addr": host_addr}) 42047890203SJanosch Frank message = "added" 42147890203SJanosch Frank else: 42247890203SJanosch Frank # expand predecessor until @target_end; predecessor's 42347890203SJanosch Frank # start doesn't change 42447890203SJanosch Frank predecessor["target_end"] = target_end 42547890203SJanosch Frank message = "joined" 42647890203SJanosch Frank 42747890203SJanosch Frank print("%016x %016x %016x %-7s %5u" % 42847890203SJanosch Frank (target_start, target_end, host_addr.cast(UINTPTR_T), 42947890203SJanosch Frank message, len(guest_phys_blocks))) 43047890203SJanosch Frank 43147890203SJanosch Frank return guest_phys_blocks 43247890203SJanosch Frank 43347890203SJanosch Frank 434ca81ce72SJanosch Frankclass DumpGuestMemory(gdb.Command): 435ca81ce72SJanosch Frank """Extract guest vmcore from qemu process coredump. 436ca81ce72SJanosch Frank 437*368e3adcSJanosch FrankThe two required arguments are FILE and ARCH: 438*368e3adcSJanosch FrankFILE identifies the target file to write the guest vmcore to. 439*368e3adcSJanosch FrankARCH specifies the architecture for which the core will be generated. 440ca81ce72SJanosch Frank 441ca81ce72SJanosch FrankThis GDB command reimplements the dump-guest-memory QMP command in 442ca81ce72SJanosch Frankpython, using the representation of guest memory as captured in the qemu 443ca81ce72SJanosch Frankcoredump. The qemu process that has been dumped must have had the 444*368e3adcSJanosch Frankcommand line option "-machine dump-guest-core=on" which is the default. 445ca81ce72SJanosch Frank 446ca81ce72SJanosch FrankFor simplicity, the "paging", "begin" and "end" parameters of the QMP 447ca81ce72SJanosch Frankcommand are not supported -- no attempt is made to get the guest's 448ca81ce72SJanosch Frankinternal paging structures (ie. paging=false is hard-wired), and guest 449ca81ce72SJanosch Frankmemory is always fully dumped. 450ca81ce72SJanosch Frank 451*368e3adcSJanosch FrankCurrently aarch64-be, aarch64-le, X86_64, 386, s390, ppc64-be, 452*368e3adcSJanosch Frankppc64-le guests are supported. 453ca81ce72SJanosch Frank 454ca81ce72SJanosch FrankThe CORE/NT_PRSTATUS and QEMU notes (that is, the VCPUs' statuses) are 455ca81ce72SJanosch Franknot written to the vmcore. Preparing these would require context that is 456ca81ce72SJanosch Frankonly present in the KVM host kernel module when the guest is alive. A 457ca81ce72SJanosch Frankfake ELF note is written instead, only to keep the ELF parser of "crash" 458ca81ce72SJanosch Frankhappy. 459ca81ce72SJanosch Frank 460ca81ce72SJanosch FrankDependent on how busted the qemu process was at the time of the 461ca81ce72SJanosch Frankcoredump, this command might produce unpredictable results. If qemu 462ca81ce72SJanosch Frankdeliberately called abort(), or it was dumped in response to a signal at 463ca81ce72SJanosch Franka halfway fortunate point, then its coredump should be in reasonable 464ca81ce72SJanosch Frankshape and this command should mostly work.""" 465ca81ce72SJanosch Frank 4663e16d14fSLaszlo Ersek def __init__(self): 4673e16d14fSLaszlo Ersek super(DumpGuestMemory, self).__init__("dump-guest-memory", 4683e16d14fSLaszlo Ersek gdb.COMMAND_DATA, 4693e16d14fSLaszlo Ersek gdb.COMPLETE_FILENAME) 470*368e3adcSJanosch Frank self.elf = None 47147890203SJanosch Frank self.guest_phys_blocks = None 4723e16d14fSLaszlo Ersek 473*368e3adcSJanosch Frank def dump_init(self, vmcore): 474*368e3adcSJanosch Frank """Prepares and writes ELF structures to core file.""" 4753e16d14fSLaszlo Ersek 476*368e3adcSJanosch Frank # Needed to make crash happy, data for more useful notes is 477*368e3adcSJanosch Frank # not available in a qemu core. 478*368e3adcSJanosch Frank self.elf.add_note("NONE", "EMPTY", 0) 4793e16d14fSLaszlo Ersek 480*368e3adcSJanosch Frank # We should never reach PN_XNUM for paging=false dumps, 481*368e3adcSJanosch Frank # there's just a handful of discontiguous ranges after 482*368e3adcSJanosch Frank # merging. 483*368e3adcSJanosch Frank # The constant is needed to account for the PT_NOTE segment. 484*368e3adcSJanosch Frank phdr_num = len(self.guest_phys_blocks) + 1 485*368e3adcSJanosch Frank assert phdr_num < PN_XNUM 4863e16d14fSLaszlo Ersek 4873e16d14fSLaszlo Ersek for block in self.guest_phys_blocks: 488*368e3adcSJanosch Frank block_size = block["target_end"] - block["target_start"] 489*368e3adcSJanosch Frank self.elf.add_segment(PT_LOAD, block["target_start"], block_size) 490*368e3adcSJanosch Frank 491*368e3adcSJanosch Frank self.elf.to_file(vmcore) 4923e16d14fSLaszlo Ersek 4933e16d14fSLaszlo Ersek def dump_iterate(self, vmcore): 494*368e3adcSJanosch Frank """Writes guest core to file.""" 495*368e3adcSJanosch Frank 4963e16d14fSLaszlo Ersek qemu_core = gdb.inferiors()[0] 4973e16d14fSLaszlo Ersek for block in self.guest_phys_blocks: 4983e16d14fSLaszlo Ersek cur = block["host_addr"] 4993e16d14fSLaszlo Ersek left = block["target_end"] - block["target_start"] 5003e16d14fSLaszlo Ersek print("dumping range at %016x for length %016x" % 50147890203SJanosch Frank (cur.cast(UINTPTR_T), left)) 502*368e3adcSJanosch Frank 5036782c0e7SJanosch Frank while left > 0: 504ca81ce72SJanosch Frank chunk_size = min(TARGET_PAGE_SIZE, left) 5053e16d14fSLaszlo Ersek chunk = qemu_core.read_memory(cur, chunk_size) 5063e16d14fSLaszlo Ersek vmcore.write(chunk) 5073e16d14fSLaszlo Ersek cur += chunk_size 5083e16d14fSLaszlo Ersek left -= chunk_size 5093e16d14fSLaszlo Ersek 5103e16d14fSLaszlo Ersek def invoke(self, args, from_tty): 511*368e3adcSJanosch Frank """Handles command invocation from gdb.""" 512*368e3adcSJanosch Frank 5133e16d14fSLaszlo Ersek # Unwittingly pressing the Enter key after the command should 5143e16d14fSLaszlo Ersek # not dump the same multi-gig coredump to the same file. 5153e16d14fSLaszlo Ersek self.dont_repeat() 5163e16d14fSLaszlo Ersek 5173e16d14fSLaszlo Ersek argv = gdb.string_to_argv(args) 518*368e3adcSJanosch Frank if len(argv) != 2: 519*368e3adcSJanosch Frank raise gdb.GdbError("usage: dump-guest-memory FILE ARCH") 5203e16d14fSLaszlo Ersek 521*368e3adcSJanosch Frank self.elf = ELF(argv[1]) 522*368e3adcSJanosch Frank self.guest_phys_blocks = get_guest_phys_blocks() 523*368e3adcSJanosch Frank 524*368e3adcSJanosch Frank with open(argv[0], "wb") as vmcore: 525*368e3adcSJanosch Frank self.dump_init(vmcore) 526*368e3adcSJanosch Frank self.dump_iterate(vmcore) 5273e16d14fSLaszlo Ersek 5283e16d14fSLaszlo ErsekDumpGuestMemory() 529