128fbf8f6SJanosch Frank""" 228fbf8f6SJanosch FrankThis python script adds a new gdb command, "dump-guest-memory". It 328fbf8f6SJanosch Frankshould be loaded with "source dump-guest-memory.py" at the (gdb) 428fbf8f6SJanosch Frankprompt. 528fbf8f6SJanosch Frank 628fbf8f6SJanosch FrankCopyright (C) 2013, Red Hat, Inc. 728fbf8f6SJanosch Frank 828fbf8f6SJanosch FrankAuthors: 928fbf8f6SJanosch Frank Laszlo Ersek <lersek@redhat.com> 1028fbf8f6SJanosch Frank Janosch Frank <frankja@linux.vnet.ibm.com> 1128fbf8f6SJanosch Frank 1228fbf8f6SJanosch FrankThis work is licensed under the terms of the GNU GPL, version 2 or later. See 1328fbf8f6SJanosch Frankthe COPYING file in the top-level directory. 1428fbf8f6SJanosch Frank""" 153e16d14fSLaszlo Ersek 16368e3adcSJanosch Frankimport ctypes 17d23bfa91SMarc-André Lureauimport struct 183e16d14fSLaszlo Ersek 194b17bc93SAndrew Jonestry: 2047890203SJanosch Frank UINTPTR_T = gdb.lookup_type("uintptr_t") 214b17bc93SAndrew Jonesexcept Exception as inst: 224b17bc93SAndrew Jones raise gdb.GdbError("Symbols must be loaded prior to sourcing dump-guest-memory.\n" 234b17bc93SAndrew Jones "Symbols may be loaded by 'attach'ing a QEMU process id or by " 244b17bc93SAndrew Jones "'load'ing a QEMU binary.") 2547890203SJanosch Frank 263e16d14fSLaszlo ErsekTARGET_PAGE_SIZE = 0x1000 273e16d14fSLaszlo ErsekTARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000 283e16d14fSLaszlo Ersek 293e16d14fSLaszlo Ersek# Special value for e_phnum. This indicates that the real number of 303e16d14fSLaszlo Ersek# program headers is too large to fit into e_phnum. Instead the real 313e16d14fSLaszlo Ersek# value is in the field sh_info of section 0. 323e16d14fSLaszlo ErsekPN_XNUM = 0xFFFF 333e16d14fSLaszlo Ersek 34368e3adcSJanosch FrankEV_CURRENT = 1 35368e3adcSJanosch Frank 36368e3adcSJanosch FrankELFCLASS32 = 1 37368e3adcSJanosch FrankELFCLASS64 = 2 38368e3adcSJanosch Frank 39368e3adcSJanosch FrankELFDATA2LSB = 1 40368e3adcSJanosch FrankELFDATA2MSB = 2 41368e3adcSJanosch Frank 42368e3adcSJanosch FrankET_CORE = 4 43368e3adcSJanosch Frank 44368e3adcSJanosch FrankPT_LOAD = 1 45368e3adcSJanosch FrankPT_NOTE = 4 46368e3adcSJanosch Frank 47368e3adcSJanosch FrankEM_386 = 3 48368e3adcSJanosch FrankEM_PPC = 20 49368e3adcSJanosch FrankEM_PPC64 = 21 50368e3adcSJanosch FrankEM_S390 = 22 51368e3adcSJanosch FrankEM_AARCH = 183 52368e3adcSJanosch FrankEM_X86_64 = 62 53368e3adcSJanosch Frank 54d23bfa91SMarc-André LureauVMCOREINFO_FORMAT_ELF = 1 55d23bfa91SMarc-André Lureau 56d23bfa91SMarc-André Lureaudef le16_to_cpu(val): 57d23bfa91SMarc-André Lureau return struct.unpack("<H", struct.pack("=H", val))[0] 58d23bfa91SMarc-André Lureau 59d23bfa91SMarc-André Lureaudef le32_to_cpu(val): 60d23bfa91SMarc-André Lureau return struct.unpack("<I", struct.pack("=I", val))[0] 61d23bfa91SMarc-André Lureau 62d23bfa91SMarc-André Lureaudef le64_to_cpu(val): 63d23bfa91SMarc-André Lureau return struct.unpack("<Q", struct.pack("=Q", val))[0] 64d23bfa91SMarc-André Lureau 65368e3adcSJanosch Frankclass ELF(object): 66368e3adcSJanosch Frank """Representation of a ELF file.""" 67368e3adcSJanosch Frank 68368e3adcSJanosch Frank def __init__(self, arch): 69368e3adcSJanosch Frank self.ehdr = None 70368e3adcSJanosch Frank self.notes = [] 71368e3adcSJanosch Frank self.segments = [] 72368e3adcSJanosch Frank self.notes_size = 0 731d817db3SStefan Weil self.endianness = None 74368e3adcSJanosch Frank self.elfclass = ELFCLASS64 75368e3adcSJanosch Frank 76368e3adcSJanosch Frank if arch == 'aarch64-le': 771d817db3SStefan Weil self.endianness = ELFDATA2LSB 78368e3adcSJanosch Frank self.elfclass = ELFCLASS64 791d817db3SStefan Weil self.ehdr = get_arch_ehdr(self.endianness, self.elfclass) 80368e3adcSJanosch Frank self.ehdr.e_machine = EM_AARCH 81368e3adcSJanosch Frank 82368e3adcSJanosch Frank elif arch == 'aarch64-be': 831d817db3SStefan Weil self.endianness = ELFDATA2MSB 841d817db3SStefan Weil self.ehdr = get_arch_ehdr(self.endianness, self.elfclass) 85368e3adcSJanosch Frank self.ehdr.e_machine = EM_AARCH 86368e3adcSJanosch Frank 87368e3adcSJanosch Frank elif arch == 'X86_64': 881d817db3SStefan Weil self.endianness = ELFDATA2LSB 891d817db3SStefan Weil self.ehdr = get_arch_ehdr(self.endianness, self.elfclass) 90368e3adcSJanosch Frank self.ehdr.e_machine = EM_X86_64 91368e3adcSJanosch Frank 92368e3adcSJanosch Frank elif arch == '386': 931d817db3SStefan Weil self.endianness = ELFDATA2LSB 94368e3adcSJanosch Frank self.elfclass = ELFCLASS32 951d817db3SStefan Weil self.ehdr = get_arch_ehdr(self.endianness, self.elfclass) 96368e3adcSJanosch Frank self.ehdr.e_machine = EM_386 97368e3adcSJanosch Frank 98368e3adcSJanosch Frank elif arch == 's390': 991d817db3SStefan Weil self.endianness = ELFDATA2MSB 1001d817db3SStefan Weil self.ehdr = get_arch_ehdr(self.endianness, self.elfclass) 101368e3adcSJanosch Frank self.ehdr.e_machine = EM_S390 102368e3adcSJanosch Frank 103368e3adcSJanosch Frank elif arch == 'ppc64-le': 1041d817db3SStefan Weil self.endianness = ELFDATA2LSB 1051d817db3SStefan Weil self.ehdr = get_arch_ehdr(self.endianness, self.elfclass) 106368e3adcSJanosch Frank self.ehdr.e_machine = EM_PPC64 107368e3adcSJanosch Frank 108368e3adcSJanosch Frank elif arch == 'ppc64-be': 1091d817db3SStefan Weil self.endianness = ELFDATA2MSB 1101d817db3SStefan Weil self.ehdr = get_arch_ehdr(self.endianness, self.elfclass) 111368e3adcSJanosch Frank self.ehdr.e_machine = EM_PPC64 112368e3adcSJanosch Frank 113368e3adcSJanosch Frank else: 114368e3adcSJanosch Frank raise gdb.GdbError("No valid arch type specified.\n" 115368e3adcSJanosch Frank "Currently supported types:\n" 116368e3adcSJanosch Frank "aarch64-be, aarch64-le, X86_64, 386, s390, " 117368e3adcSJanosch Frank "ppc64-be, ppc64-le") 118368e3adcSJanosch Frank 119368e3adcSJanosch Frank self.add_segment(PT_NOTE, 0, 0) 120368e3adcSJanosch Frank 121368e3adcSJanosch Frank def add_note(self, n_name, n_desc, n_type): 122368e3adcSJanosch Frank """Adds a note to the ELF.""" 123368e3adcSJanosch Frank 1241d817db3SStefan Weil note = get_arch_note(self.endianness, len(n_name), len(n_desc)) 125368e3adcSJanosch Frank note.n_namesz = len(n_name) + 1 126368e3adcSJanosch Frank note.n_descsz = len(n_desc) 127368e3adcSJanosch Frank note.n_name = n_name.encode() 128368e3adcSJanosch Frank note.n_type = n_type 129368e3adcSJanosch Frank 130368e3adcSJanosch Frank # Desc needs to be 4 byte aligned (although the 64bit spec 131368e3adcSJanosch Frank # specifies 8 byte). When defining n_desc as uint32 it will be 132368e3adcSJanosch Frank # automatically aligned but we need the memmove to copy the 133368e3adcSJanosch Frank # string into it. 134368e3adcSJanosch Frank ctypes.memmove(note.n_desc, n_desc.encode(), len(n_desc)) 135368e3adcSJanosch Frank 136368e3adcSJanosch Frank self.notes.append(note) 137368e3adcSJanosch Frank self.segments[0].p_filesz += ctypes.sizeof(note) 138368e3adcSJanosch Frank self.segments[0].p_memsz += ctypes.sizeof(note) 139368e3adcSJanosch Frank 140d23bfa91SMarc-André Lureau 141d23bfa91SMarc-André Lureau def add_vmcoreinfo_note(self, vmcoreinfo): 142d23bfa91SMarc-André Lureau """Adds a vmcoreinfo note to the ELF dump.""" 143d23bfa91SMarc-André Lureau # compute the header size, and copy that many bytes from the note 144d23bfa91SMarc-André Lureau header = get_arch_note(self.endianness, 0, 0) 145d23bfa91SMarc-André Lureau ctypes.memmove(ctypes.pointer(header), 146d23bfa91SMarc-André Lureau vmcoreinfo, ctypes.sizeof(header)) 147d23bfa91SMarc-André Lureau if header.n_descsz > 1 << 20: 148d23bfa91SMarc-André Lureau print('warning: invalid vmcoreinfo size') 149d23bfa91SMarc-André Lureau return 150d23bfa91SMarc-André Lureau # now get the full note 151d23bfa91SMarc-André Lureau note = get_arch_note(self.endianness, 152d23bfa91SMarc-André Lureau header.n_namesz - 1, header.n_descsz) 153d23bfa91SMarc-André Lureau ctypes.memmove(ctypes.pointer(note), vmcoreinfo, ctypes.sizeof(note)) 154d23bfa91SMarc-André Lureau 155d23bfa91SMarc-André Lureau self.notes.append(note) 156d23bfa91SMarc-André Lureau self.segments[0].p_filesz += ctypes.sizeof(note) 157d23bfa91SMarc-André Lureau self.segments[0].p_memsz += ctypes.sizeof(note) 158d23bfa91SMarc-André Lureau 159368e3adcSJanosch Frank def add_segment(self, p_type, p_paddr, p_size): 160368e3adcSJanosch Frank """Adds a segment to the elf.""" 161368e3adcSJanosch Frank 1621d817db3SStefan Weil phdr = get_arch_phdr(self.endianness, self.elfclass) 163368e3adcSJanosch Frank phdr.p_type = p_type 164368e3adcSJanosch Frank phdr.p_paddr = p_paddr 165e17bebd0SJon Doron phdr.p_vaddr = p_paddr 166368e3adcSJanosch Frank phdr.p_filesz = p_size 167368e3adcSJanosch Frank phdr.p_memsz = p_size 168368e3adcSJanosch Frank self.segments.append(phdr) 169368e3adcSJanosch Frank self.ehdr.e_phnum += 1 170368e3adcSJanosch Frank 171368e3adcSJanosch Frank def to_file(self, elf_file): 172*df59feb1SDr. David Alan Gilbert """Writes all ELF structures to the passed file. 173368e3adcSJanosch Frank 174368e3adcSJanosch Frank Structure: 175368e3adcSJanosch Frank Ehdr 176368e3adcSJanosch Frank Segment 0:PT_NOTE 177368e3adcSJanosch Frank Segment 1:PT_LOAD 178368e3adcSJanosch Frank Segment N:PT_LOAD 179368e3adcSJanosch Frank Note 0..N 180368e3adcSJanosch Frank Dump contents 181368e3adcSJanosch Frank """ 182368e3adcSJanosch Frank elf_file.write(self.ehdr) 183368e3adcSJanosch Frank off = ctypes.sizeof(self.ehdr) + \ 184368e3adcSJanosch Frank len(self.segments) * ctypes.sizeof(self.segments[0]) 185368e3adcSJanosch Frank 186368e3adcSJanosch Frank for phdr in self.segments: 187368e3adcSJanosch Frank phdr.p_offset = off 188368e3adcSJanosch Frank elf_file.write(phdr) 189368e3adcSJanosch Frank off += phdr.p_filesz 190368e3adcSJanosch Frank 191368e3adcSJanosch Frank for note in self.notes: 192368e3adcSJanosch Frank elf_file.write(note) 193368e3adcSJanosch Frank 194368e3adcSJanosch Frank 1951d817db3SStefan Weildef get_arch_note(endianness, len_name, len_desc): 1961d817db3SStefan Weil """Returns a Note class with the specified endianness.""" 197368e3adcSJanosch Frank 1981d817db3SStefan Weil if endianness == ELFDATA2LSB: 199368e3adcSJanosch Frank superclass = ctypes.LittleEndianStructure 200368e3adcSJanosch Frank else: 201368e3adcSJanosch Frank superclass = ctypes.BigEndianStructure 202368e3adcSJanosch Frank 203368e3adcSJanosch Frank len_name = len_name + 1 204368e3adcSJanosch Frank 205368e3adcSJanosch Frank class Note(superclass): 206368e3adcSJanosch Frank """Represents an ELF note, includes the content.""" 207368e3adcSJanosch Frank 208368e3adcSJanosch Frank _fields_ = [("n_namesz", ctypes.c_uint32), 209368e3adcSJanosch Frank ("n_descsz", ctypes.c_uint32), 210368e3adcSJanosch Frank ("n_type", ctypes.c_uint32), 211368e3adcSJanosch Frank ("n_name", ctypes.c_char * len_name), 212368e3adcSJanosch Frank ("n_desc", ctypes.c_uint32 * ((len_desc + 3) // 4))] 213368e3adcSJanosch Frank return Note() 214368e3adcSJanosch Frank 215368e3adcSJanosch Frank 216368e3adcSJanosch Frankclass Ident(ctypes.Structure): 217368e3adcSJanosch Frank """Represents the ELF ident array in the ehdr structure.""" 218368e3adcSJanosch Frank 219368e3adcSJanosch Frank _fields_ = [('ei_mag0', ctypes.c_ubyte), 220368e3adcSJanosch Frank ('ei_mag1', ctypes.c_ubyte), 221368e3adcSJanosch Frank ('ei_mag2', ctypes.c_ubyte), 222368e3adcSJanosch Frank ('ei_mag3', ctypes.c_ubyte), 223368e3adcSJanosch Frank ('ei_class', ctypes.c_ubyte), 224368e3adcSJanosch Frank ('ei_data', ctypes.c_ubyte), 225368e3adcSJanosch Frank ('ei_version', ctypes.c_ubyte), 226368e3adcSJanosch Frank ('ei_osabi', ctypes.c_ubyte), 227368e3adcSJanosch Frank ('ei_abiversion', ctypes.c_ubyte), 228368e3adcSJanosch Frank ('ei_pad', ctypes.c_ubyte * 7)] 229368e3adcSJanosch Frank 2301d817db3SStefan Weil def __init__(self, endianness, elfclass): 231368e3adcSJanosch Frank self.ei_mag0 = 0x7F 232368e3adcSJanosch Frank self.ei_mag1 = ord('E') 233368e3adcSJanosch Frank self.ei_mag2 = ord('L') 234368e3adcSJanosch Frank self.ei_mag3 = ord('F') 235368e3adcSJanosch Frank self.ei_class = elfclass 2361d817db3SStefan Weil self.ei_data = endianness 237368e3adcSJanosch Frank self.ei_version = EV_CURRENT 238368e3adcSJanosch Frank 239368e3adcSJanosch Frank 2401d817db3SStefan Weildef get_arch_ehdr(endianness, elfclass): 2411d817db3SStefan Weil """Returns a EHDR64 class with the specified endianness.""" 242368e3adcSJanosch Frank 2431d817db3SStefan Weil if endianness == ELFDATA2LSB: 244368e3adcSJanosch Frank superclass = ctypes.LittleEndianStructure 245368e3adcSJanosch Frank else: 246368e3adcSJanosch Frank superclass = ctypes.BigEndianStructure 247368e3adcSJanosch Frank 248368e3adcSJanosch Frank class EHDR64(superclass): 249368e3adcSJanosch Frank """Represents the 64 bit ELF header struct.""" 250368e3adcSJanosch Frank 251368e3adcSJanosch Frank _fields_ = [('e_ident', Ident), 252368e3adcSJanosch Frank ('e_type', ctypes.c_uint16), 253368e3adcSJanosch Frank ('e_machine', ctypes.c_uint16), 254368e3adcSJanosch Frank ('e_version', ctypes.c_uint32), 255368e3adcSJanosch Frank ('e_entry', ctypes.c_uint64), 256368e3adcSJanosch Frank ('e_phoff', ctypes.c_uint64), 257368e3adcSJanosch Frank ('e_shoff', ctypes.c_uint64), 258368e3adcSJanosch Frank ('e_flags', ctypes.c_uint32), 259368e3adcSJanosch Frank ('e_ehsize', ctypes.c_uint16), 260368e3adcSJanosch Frank ('e_phentsize', ctypes.c_uint16), 261368e3adcSJanosch Frank ('e_phnum', ctypes.c_uint16), 262368e3adcSJanosch Frank ('e_shentsize', ctypes.c_uint16), 263368e3adcSJanosch Frank ('e_shnum', ctypes.c_uint16), 264368e3adcSJanosch Frank ('e_shstrndx', ctypes.c_uint16)] 265368e3adcSJanosch Frank 266368e3adcSJanosch Frank def __init__(self): 267368e3adcSJanosch Frank super(superclass, self).__init__() 2681d817db3SStefan Weil self.e_ident = Ident(endianness, elfclass) 269368e3adcSJanosch Frank self.e_type = ET_CORE 270368e3adcSJanosch Frank self.e_version = EV_CURRENT 271368e3adcSJanosch Frank self.e_ehsize = ctypes.sizeof(self) 272368e3adcSJanosch Frank self.e_phoff = ctypes.sizeof(self) 2731d817db3SStefan Weil self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianness, elfclass)) 274368e3adcSJanosch Frank self.e_phnum = 0 275368e3adcSJanosch Frank 276368e3adcSJanosch Frank 277368e3adcSJanosch Frank class EHDR32(superclass): 278368e3adcSJanosch Frank """Represents the 32 bit ELF header struct.""" 279368e3adcSJanosch Frank 280368e3adcSJanosch Frank _fields_ = [('e_ident', Ident), 281368e3adcSJanosch Frank ('e_type', ctypes.c_uint16), 282368e3adcSJanosch Frank ('e_machine', ctypes.c_uint16), 283368e3adcSJanosch Frank ('e_version', ctypes.c_uint32), 284368e3adcSJanosch Frank ('e_entry', ctypes.c_uint32), 285368e3adcSJanosch Frank ('e_phoff', ctypes.c_uint32), 286368e3adcSJanosch Frank ('e_shoff', ctypes.c_uint32), 287368e3adcSJanosch Frank ('e_flags', ctypes.c_uint32), 288368e3adcSJanosch Frank ('e_ehsize', ctypes.c_uint16), 289368e3adcSJanosch Frank ('e_phentsize', ctypes.c_uint16), 290368e3adcSJanosch Frank ('e_phnum', ctypes.c_uint16), 291368e3adcSJanosch Frank ('e_shentsize', ctypes.c_uint16), 292368e3adcSJanosch Frank ('e_shnum', ctypes.c_uint16), 293368e3adcSJanosch Frank ('e_shstrndx', ctypes.c_uint16)] 294368e3adcSJanosch Frank 295368e3adcSJanosch Frank def __init__(self): 296368e3adcSJanosch Frank super(superclass, self).__init__() 2971d817db3SStefan Weil self.e_ident = Ident(endianness, elfclass) 298368e3adcSJanosch Frank self.e_type = ET_CORE 299368e3adcSJanosch Frank self.e_version = EV_CURRENT 300368e3adcSJanosch Frank self.e_ehsize = ctypes.sizeof(self) 301368e3adcSJanosch Frank self.e_phoff = ctypes.sizeof(self) 3021d817db3SStefan Weil self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianness, elfclass)) 303368e3adcSJanosch Frank self.e_phnum = 0 304368e3adcSJanosch Frank 305368e3adcSJanosch Frank # End get_arch_ehdr 306368e3adcSJanosch Frank if elfclass == ELFCLASS64: 307368e3adcSJanosch Frank return EHDR64() 308368e3adcSJanosch Frank else: 309368e3adcSJanosch Frank return EHDR32() 310368e3adcSJanosch Frank 311368e3adcSJanosch Frank 3121d817db3SStefan Weildef get_arch_phdr(endianness, elfclass): 3131d817db3SStefan Weil """Returns a 32 or 64 bit PHDR class with the specified endianness.""" 314368e3adcSJanosch Frank 3151d817db3SStefan Weil if endianness == ELFDATA2LSB: 316368e3adcSJanosch Frank superclass = ctypes.LittleEndianStructure 317368e3adcSJanosch Frank else: 318368e3adcSJanosch Frank superclass = ctypes.BigEndianStructure 319368e3adcSJanosch Frank 320368e3adcSJanosch Frank class PHDR64(superclass): 321368e3adcSJanosch Frank """Represents the 64 bit ELF program header struct.""" 322368e3adcSJanosch Frank 323368e3adcSJanosch Frank _fields_ = [('p_type', ctypes.c_uint32), 324368e3adcSJanosch Frank ('p_flags', ctypes.c_uint32), 325368e3adcSJanosch Frank ('p_offset', ctypes.c_uint64), 326368e3adcSJanosch Frank ('p_vaddr', ctypes.c_uint64), 327368e3adcSJanosch Frank ('p_paddr', ctypes.c_uint64), 328368e3adcSJanosch Frank ('p_filesz', ctypes.c_uint64), 329368e3adcSJanosch Frank ('p_memsz', ctypes.c_uint64), 330368e3adcSJanosch Frank ('p_align', ctypes.c_uint64)] 331368e3adcSJanosch Frank 332368e3adcSJanosch Frank class PHDR32(superclass): 333368e3adcSJanosch Frank """Represents the 32 bit ELF program header struct.""" 334368e3adcSJanosch Frank 335368e3adcSJanosch Frank _fields_ = [('p_type', ctypes.c_uint32), 336368e3adcSJanosch Frank ('p_offset', ctypes.c_uint32), 337368e3adcSJanosch Frank ('p_vaddr', ctypes.c_uint32), 338368e3adcSJanosch Frank ('p_paddr', ctypes.c_uint32), 339368e3adcSJanosch Frank ('p_filesz', ctypes.c_uint32), 340368e3adcSJanosch Frank ('p_memsz', ctypes.c_uint32), 341368e3adcSJanosch Frank ('p_flags', ctypes.c_uint32), 342368e3adcSJanosch Frank ('p_align', ctypes.c_uint32)] 343368e3adcSJanosch Frank 344368e3adcSJanosch Frank # End get_arch_phdr 345368e3adcSJanosch Frank if elfclass == ELFCLASS64: 346368e3adcSJanosch Frank return PHDR64() 347368e3adcSJanosch Frank else: 348368e3adcSJanosch Frank return PHDR32() 349368e3adcSJanosch Frank 3503e16d14fSLaszlo Ersek 35147890203SJanosch Frankdef int128_get64(val): 3526782c0e7SJanosch Frank """Returns low 64bit part of Int128 struct.""" 3536782c0e7SJanosch Frank 3549b4b157eSMarc-André Lureau try: 3556782c0e7SJanosch Frank assert val["hi"] == 0 35647890203SJanosch Frank return val["lo"] 3579b4b157eSMarc-André Lureau except gdb.error: 3589b4b157eSMarc-André Lureau u64t = gdb.lookup_type('uint64_t').array(2) 3599b4b157eSMarc-André Lureau u64 = val.cast(u64t) 3609b4b157eSMarc-André Lureau if sys.byteorder == 'little': 3619b4b157eSMarc-André Lureau assert u64[1] == 0 3629b4b157eSMarc-André Lureau return u64[0] 3639b4b157eSMarc-André Lureau else: 3649b4b157eSMarc-André Lureau assert u64[0] == 0 3659b4b157eSMarc-André Lureau return u64[1] 36647890203SJanosch Frank 3676782c0e7SJanosch Frank 36847890203SJanosch Frankdef qlist_foreach(head, field_str): 3696782c0e7SJanosch Frank """Generator for qlists.""" 3706782c0e7SJanosch Frank 37147890203SJanosch Frank var_p = head["lh_first"] 3726782c0e7SJanosch Frank while var_p != 0: 37347890203SJanosch Frank var = var_p.dereference() 37447890203SJanosch Frank var_p = var[field_str]["le_next"] 3756782c0e7SJanosch Frank yield var 3766782c0e7SJanosch Frank 37747890203SJanosch Frank 3780878d0e1SPaolo Bonzinidef qemu_map_ram_ptr(block, offset): 3796782c0e7SJanosch Frank """Returns qemu vaddr for given guest physical address.""" 3806782c0e7SJanosch Frank 3810878d0e1SPaolo Bonzini return block["host"] + offset 38247890203SJanosch Frank 3836782c0e7SJanosch Frank 3846782c0e7SJanosch Frankdef memory_region_get_ram_ptr(memory_region): 3856782c0e7SJanosch Frank if memory_region["alias"] != 0: 3866782c0e7SJanosch Frank return (memory_region_get_ram_ptr(memory_region["alias"].dereference()) 3876782c0e7SJanosch Frank + memory_region["alias_offset"]) 3886782c0e7SJanosch Frank 3890878d0e1SPaolo Bonzini return qemu_map_ram_ptr(memory_region["ram_block"], 0) 3906782c0e7SJanosch Frank 39147890203SJanosch Frank 39247890203SJanosch Frankdef get_guest_phys_blocks(): 3936782c0e7SJanosch Frank """Returns a list of ram blocks. 3946782c0e7SJanosch Frank 3956782c0e7SJanosch Frank Each block entry contains: 3966782c0e7SJanosch Frank 'target_start': guest block phys start address 3976782c0e7SJanosch Frank 'target_end': guest block phys end address 3986782c0e7SJanosch Frank 'host_addr': qemu vaddr of the block's start 3996782c0e7SJanosch Frank """ 4006782c0e7SJanosch Frank 40147890203SJanosch Frank guest_phys_blocks = [] 4026782c0e7SJanosch Frank 4037cb1089dSJanosch Frank print("guest RAM blocks:") 40447890203SJanosch Frank print("target_start target_end host_addr message " 40547890203SJanosch Frank "count") 40647890203SJanosch Frank print("---------------- ---------------- ---------------- ------- " 40747890203SJanosch Frank "-----") 40847890203SJanosch Frank 40947890203SJanosch Frank current_map_p = gdb.parse_and_eval("address_space_memory.current_map") 41047890203SJanosch Frank current_map = current_map_p.dereference() 4117cb1089dSJanosch Frank 4127cb1089dSJanosch Frank # Conversion to int is needed for python 3 4137cb1089dSJanosch Frank # compatibility. Otherwise range doesn't cast the value itself and 4147cb1089dSJanosch Frank # breaks. 4157cb1089dSJanosch Frank for cur in range(int(current_map["nr"])): 41647890203SJanosch Frank flat_range = (current_map["ranges"] + cur).dereference() 4176782c0e7SJanosch Frank memory_region = flat_range["mr"].dereference() 41847890203SJanosch Frank 41947890203SJanosch Frank # we only care about RAM 4207f135356SPaolo Bonzini if (not memory_region["ram"] or 4217f135356SPaolo Bonzini memory_region["ram_device"] or 4227f135356SPaolo Bonzini memory_region["nonvolatile"]): 42347890203SJanosch Frank continue 42447890203SJanosch Frank 42547890203SJanosch Frank section_size = int128_get64(flat_range["addr"]["size"]) 42647890203SJanosch Frank target_start = int128_get64(flat_range["addr"]["start"]) 42747890203SJanosch Frank target_end = target_start + section_size 4286782c0e7SJanosch Frank host_addr = (memory_region_get_ram_ptr(memory_region) 4296782c0e7SJanosch Frank + flat_range["offset_in_region"]) 43047890203SJanosch Frank predecessor = None 43147890203SJanosch Frank 43247890203SJanosch Frank # find continuity in guest physical address space 4336782c0e7SJanosch Frank if len(guest_phys_blocks) > 0: 43447890203SJanosch Frank predecessor = guest_phys_blocks[-1] 43547890203SJanosch Frank predecessor_size = (predecessor["target_end"] - 43647890203SJanosch Frank predecessor["target_start"]) 43747890203SJanosch Frank 43847890203SJanosch Frank # the memory API guarantees monotonically increasing 43947890203SJanosch Frank # traversal 4406782c0e7SJanosch Frank assert predecessor["target_end"] <= target_start 44147890203SJanosch Frank 44247890203SJanosch Frank # we want continuity in both guest-physical and 44347890203SJanosch Frank # host-virtual memory 44447890203SJanosch Frank if (predecessor["target_end"] < target_start or 44547890203SJanosch Frank predecessor["host_addr"] + predecessor_size != host_addr): 44647890203SJanosch Frank predecessor = None 44747890203SJanosch Frank 4486782c0e7SJanosch Frank if predecessor is None: 44947890203SJanosch Frank # isolated mapping, add it to the list 45047890203SJanosch Frank guest_phys_blocks.append({"target_start": target_start, 45147890203SJanosch Frank "target_end": target_end, 45247890203SJanosch Frank "host_addr": host_addr}) 45347890203SJanosch Frank message = "added" 45447890203SJanosch Frank else: 45547890203SJanosch Frank # expand predecessor until @target_end; predecessor's 45647890203SJanosch Frank # start doesn't change 45747890203SJanosch Frank predecessor["target_end"] = target_end 45847890203SJanosch Frank message = "joined" 45947890203SJanosch Frank 46047890203SJanosch Frank print("%016x %016x %016x %-7s %5u" % 46147890203SJanosch Frank (target_start, target_end, host_addr.cast(UINTPTR_T), 46247890203SJanosch Frank message, len(guest_phys_blocks))) 46347890203SJanosch Frank 46447890203SJanosch Frank return guest_phys_blocks 46547890203SJanosch Frank 46647890203SJanosch Frank 46728fbf8f6SJanosch Frank# The leading docstring doesn't have idiomatic Python formatting. It is 46828fbf8f6SJanosch Frank# printed by gdb's "help" command (the first line is printed in the 46928fbf8f6SJanosch Frank# "help data" summary), and it should match how other help texts look in 47028fbf8f6SJanosch Frank# gdb. 471ca81ce72SJanosch Frankclass DumpGuestMemory(gdb.Command): 472ca81ce72SJanosch Frank """Extract guest vmcore from qemu process coredump. 473ca81ce72SJanosch Frank 474368e3adcSJanosch FrankThe two required arguments are FILE and ARCH: 475368e3adcSJanosch FrankFILE identifies the target file to write the guest vmcore to. 476368e3adcSJanosch FrankARCH specifies the architecture for which the core will be generated. 477ca81ce72SJanosch Frank 478ca81ce72SJanosch FrankThis GDB command reimplements the dump-guest-memory QMP command in 479ca81ce72SJanosch Frankpython, using the representation of guest memory as captured in the qemu 480ca81ce72SJanosch Frankcoredump. The qemu process that has been dumped must have had the 481368e3adcSJanosch Frankcommand line option "-machine dump-guest-core=on" which is the default. 482ca81ce72SJanosch Frank 483ca81ce72SJanosch FrankFor simplicity, the "paging", "begin" and "end" parameters of the QMP 484ca81ce72SJanosch Frankcommand are not supported -- no attempt is made to get the guest's 485ca81ce72SJanosch Frankinternal paging structures (ie. paging=false is hard-wired), and guest 486ca81ce72SJanosch Frankmemory is always fully dumped. 487ca81ce72SJanosch Frank 488368e3adcSJanosch FrankCurrently aarch64-be, aarch64-le, X86_64, 386, s390, ppc64-be, 489368e3adcSJanosch Frankppc64-le guests are supported. 490ca81ce72SJanosch Frank 491ca81ce72SJanosch FrankThe CORE/NT_PRSTATUS and QEMU notes (that is, the VCPUs' statuses) are 492ca81ce72SJanosch Franknot written to the vmcore. Preparing these would require context that is 493ca81ce72SJanosch Frankonly present in the KVM host kernel module when the guest is alive. A 494ca81ce72SJanosch Frankfake ELF note is written instead, only to keep the ELF parser of "crash" 495ca81ce72SJanosch Frankhappy. 496ca81ce72SJanosch Frank 497ca81ce72SJanosch FrankDependent on how busted the qemu process was at the time of the 498ca81ce72SJanosch Frankcoredump, this command might produce unpredictable results. If qemu 499ca81ce72SJanosch Frankdeliberately called abort(), or it was dumped in response to a signal at 500ca81ce72SJanosch Franka halfway fortunate point, then its coredump should be in reasonable 501ca81ce72SJanosch Frankshape and this command should mostly work.""" 502ca81ce72SJanosch Frank 5033e16d14fSLaszlo Ersek def __init__(self): 5043e16d14fSLaszlo Ersek super(DumpGuestMemory, self).__init__("dump-guest-memory", 5053e16d14fSLaszlo Ersek gdb.COMMAND_DATA, 5063e16d14fSLaszlo Ersek gdb.COMPLETE_FILENAME) 507368e3adcSJanosch Frank self.elf = None 50847890203SJanosch Frank self.guest_phys_blocks = None 5093e16d14fSLaszlo Ersek 510368e3adcSJanosch Frank def dump_init(self, vmcore): 511368e3adcSJanosch Frank """Prepares and writes ELF structures to core file.""" 5123e16d14fSLaszlo Ersek 513368e3adcSJanosch Frank # Needed to make crash happy, data for more useful notes is 514368e3adcSJanosch Frank # not available in a qemu core. 515368e3adcSJanosch Frank self.elf.add_note("NONE", "EMPTY", 0) 5163e16d14fSLaszlo Ersek 517368e3adcSJanosch Frank # We should never reach PN_XNUM for paging=false dumps, 518368e3adcSJanosch Frank # there's just a handful of discontiguous ranges after 519368e3adcSJanosch Frank # merging. 520368e3adcSJanosch Frank # The constant is needed to account for the PT_NOTE segment. 521368e3adcSJanosch Frank phdr_num = len(self.guest_phys_blocks) + 1 522368e3adcSJanosch Frank assert phdr_num < PN_XNUM 5233e16d14fSLaszlo Ersek 5243e16d14fSLaszlo Ersek for block in self.guest_phys_blocks: 525368e3adcSJanosch Frank block_size = block["target_end"] - block["target_start"] 526368e3adcSJanosch Frank self.elf.add_segment(PT_LOAD, block["target_start"], block_size) 527368e3adcSJanosch Frank 528368e3adcSJanosch Frank self.elf.to_file(vmcore) 5293e16d14fSLaszlo Ersek 5303e16d14fSLaszlo Ersek def dump_iterate(self, vmcore): 531368e3adcSJanosch Frank """Writes guest core to file.""" 532368e3adcSJanosch Frank 5333e16d14fSLaszlo Ersek qemu_core = gdb.inferiors()[0] 5343e16d14fSLaszlo Ersek for block in self.guest_phys_blocks: 5353e16d14fSLaszlo Ersek cur = block["host_addr"] 5363e16d14fSLaszlo Ersek left = block["target_end"] - block["target_start"] 5373e16d14fSLaszlo Ersek print("dumping range at %016x for length %016x" % 53847890203SJanosch Frank (cur.cast(UINTPTR_T), left)) 539368e3adcSJanosch Frank 5406782c0e7SJanosch Frank while left > 0: 541ca81ce72SJanosch Frank chunk_size = min(TARGET_PAGE_SIZE, left) 5423e16d14fSLaszlo Ersek chunk = qemu_core.read_memory(cur, chunk_size) 5433e16d14fSLaszlo Ersek vmcore.write(chunk) 5443e16d14fSLaszlo Ersek cur += chunk_size 5453e16d14fSLaszlo Ersek left -= chunk_size 5463e16d14fSLaszlo Ersek 547d23bfa91SMarc-André Lureau def phys_memory_read(self, addr, size): 548d23bfa91SMarc-André Lureau qemu_core = gdb.inferiors()[0] 549d23bfa91SMarc-André Lureau for block in self.guest_phys_blocks: 550d23bfa91SMarc-André Lureau if block["target_start"] <= addr \ 551d23bfa91SMarc-André Lureau and addr + size <= block["target_end"]: 552d23bfa91SMarc-André Lureau haddr = block["host_addr"] + (addr - block["target_start"]) 553d23bfa91SMarc-André Lureau return qemu_core.read_memory(haddr, size) 554d23bfa91SMarc-André Lureau return None 555d23bfa91SMarc-André Lureau 556d23bfa91SMarc-André Lureau def add_vmcoreinfo(self): 557ce6b9e42SMarc-André Lureau if gdb.lookup_symbol("vmcoreinfo_realize")[0] is None: 558ce6b9e42SMarc-André Lureau return 559c3b1642bSMarc-André Lureau vmci = 'vmcoreinfo_realize::vmcoreinfo_state' 560d36d0a9dSMarc-André Lureau if not gdb.parse_and_eval("%s" % vmci) \ 561d36d0a9dSMarc-André Lureau or not gdb.parse_and_eval("(%s)->has_vmcoreinfo" % vmci): 562d23bfa91SMarc-André Lureau return 563d23bfa91SMarc-André Lureau 564d36d0a9dSMarc-André Lureau fmt = gdb.parse_and_eval("(%s)->vmcoreinfo.guest_format" % vmci) 565d36d0a9dSMarc-André Lureau addr = gdb.parse_and_eval("(%s)->vmcoreinfo.paddr" % vmci) 566d36d0a9dSMarc-André Lureau size = gdb.parse_and_eval("(%s)->vmcoreinfo.size" % vmci) 567d23bfa91SMarc-André Lureau 568d23bfa91SMarc-André Lureau fmt = le16_to_cpu(fmt) 569d23bfa91SMarc-André Lureau addr = le64_to_cpu(addr) 570d23bfa91SMarc-André Lureau size = le32_to_cpu(size) 571d23bfa91SMarc-André Lureau 572d23bfa91SMarc-André Lureau if fmt != VMCOREINFO_FORMAT_ELF: 573d23bfa91SMarc-André Lureau return 574d23bfa91SMarc-André Lureau 575d23bfa91SMarc-André Lureau vmcoreinfo = self.phys_memory_read(addr, size) 576d23bfa91SMarc-André Lureau if vmcoreinfo: 5776f49ec40SMarc-André Lureau self.elf.add_vmcoreinfo_note(bytes(vmcoreinfo)) 578d23bfa91SMarc-André Lureau 5793e16d14fSLaszlo Ersek def invoke(self, args, from_tty): 580368e3adcSJanosch Frank """Handles command invocation from gdb.""" 581368e3adcSJanosch Frank 5823e16d14fSLaszlo Ersek # Unwittingly pressing the Enter key after the command should 5833e16d14fSLaszlo Ersek # not dump the same multi-gig coredump to the same file. 5843e16d14fSLaszlo Ersek self.dont_repeat() 5853e16d14fSLaszlo Ersek 5863e16d14fSLaszlo Ersek argv = gdb.string_to_argv(args) 587368e3adcSJanosch Frank if len(argv) != 2: 588368e3adcSJanosch Frank raise gdb.GdbError("usage: dump-guest-memory FILE ARCH") 5893e16d14fSLaszlo Ersek 590368e3adcSJanosch Frank self.elf = ELF(argv[1]) 591368e3adcSJanosch Frank self.guest_phys_blocks = get_guest_phys_blocks() 592d23bfa91SMarc-André Lureau self.add_vmcoreinfo() 593368e3adcSJanosch Frank 594368e3adcSJanosch Frank with open(argv[0], "wb") as vmcore: 595368e3adcSJanosch Frank self.dump_init(vmcore) 596368e3adcSJanosch Frank self.dump_iterate(vmcore) 5973e16d14fSLaszlo Ersek 5983e16d14fSLaszlo ErsekDumpGuestMemory() 599