blob: 09bec92b5087225572c1d778e46a7f403ac2c623 [file] [log] [blame]
Janosch Frank28fbf8f2016-01-22 13:08:40 +01001"""
2This python script adds a new gdb command, "dump-guest-memory". It
3should be loaded with "source dump-guest-memory.py" at the (gdb)
4prompt.
5
6Copyright (C) 2013, Red Hat, Inc.
7
8Authors:
9 Laszlo Ersek <lersek@redhat.com>
10 Janosch Frank <frankja@linux.vnet.ibm.com>
11
12This work is licensed under the terms of the GNU GPL, version 2 or later. See
13the COPYING file in the top-level directory.
14"""
Laszlo Ersek3e16d142013-12-17 01:37:06 +010015
Janosch Frank368e3ad2016-01-22 13:08:39 +010016import ctypes
Marc-André Lureaud23bfa92017-09-11 18:59:28 +020017import struct
Laszlo Ersek3e16d142013-12-17 01:37:06 +010018
Janosch Frank47890202016-01-22 13:08:36 +010019UINTPTR_T = gdb.lookup_type("uintptr_t")
20
Janosch Frankca81ce72016-01-22 13:08:35 +010021TARGET_PAGE_SIZE = 0x1000
22TARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000
23
Janosch Frankca81ce72016-01-22 13:08:35 +010024# Special value for e_phnum. This indicates that the real number of
25# program headers is too large to fit into e_phnum. Instead the real
26# value is in the field sh_info of section 0.
27PN_XNUM = 0xFFFF
28
Janosch Frank368e3ad2016-01-22 13:08:39 +010029EV_CURRENT = 1
30
31ELFCLASS32 = 1
32ELFCLASS64 = 2
33
34ELFDATA2LSB = 1
35ELFDATA2MSB = 2
36
37ET_CORE = 4
38
39PT_LOAD = 1
40PT_NOTE = 4
41
42EM_386 = 3
43EM_PPC = 20
44EM_PPC64 = 21
45EM_S390 = 22
46EM_AARCH = 183
47EM_X86_64 = 62
48
Marc-André Lureaud23bfa92017-09-11 18:59:28 +020049VMCOREINFO_FORMAT_ELF = 1
50
51def le16_to_cpu(val):
52 return struct.unpack("<H", struct.pack("=H", val))[0]
53
54def le32_to_cpu(val):
55 return struct.unpack("<I", struct.pack("=I", val))[0]
56
57def le64_to_cpu(val):
58 return struct.unpack("<Q", struct.pack("=Q", val))[0]
59
Janosch Frank368e3ad2016-01-22 13:08:39 +010060class ELF(object):
61 """Representation of a ELF file."""
62
63 def __init__(self, arch):
64 self.ehdr = None
65 self.notes = []
66 self.segments = []
67 self.notes_size = 0
Stefan Weil1d817db2016-03-21 19:21:26 +010068 self.endianness = None
Janosch Frank368e3ad2016-01-22 13:08:39 +010069 self.elfclass = ELFCLASS64
70
71 if arch == 'aarch64-le':
Stefan Weil1d817db2016-03-21 19:21:26 +010072 self.endianness = ELFDATA2LSB
Janosch Frank368e3ad2016-01-22 13:08:39 +010073 self.elfclass = ELFCLASS64
Stefan Weil1d817db2016-03-21 19:21:26 +010074 self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
Janosch Frank368e3ad2016-01-22 13:08:39 +010075 self.ehdr.e_machine = EM_AARCH
76
77 elif arch == 'aarch64-be':
Stefan Weil1d817db2016-03-21 19:21:26 +010078 self.endianness = ELFDATA2MSB
79 self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
Janosch Frank368e3ad2016-01-22 13:08:39 +010080 self.ehdr.e_machine = EM_AARCH
81
82 elif arch == 'X86_64':
Stefan Weil1d817db2016-03-21 19:21:26 +010083 self.endianness = ELFDATA2LSB
84 self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
Janosch Frank368e3ad2016-01-22 13:08:39 +010085 self.ehdr.e_machine = EM_X86_64
86
87 elif arch == '386':
Stefan Weil1d817db2016-03-21 19:21:26 +010088 self.endianness = ELFDATA2LSB
Janosch Frank368e3ad2016-01-22 13:08:39 +010089 self.elfclass = ELFCLASS32
Stefan Weil1d817db2016-03-21 19:21:26 +010090 self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
Janosch Frank368e3ad2016-01-22 13:08:39 +010091 self.ehdr.e_machine = EM_386
92
93 elif arch == 's390':
Stefan Weil1d817db2016-03-21 19:21:26 +010094 self.endianness = ELFDATA2MSB
95 self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
Janosch Frank368e3ad2016-01-22 13:08:39 +010096 self.ehdr.e_machine = EM_S390
97
98 elif arch == 'ppc64-le':
Stefan Weil1d817db2016-03-21 19:21:26 +010099 self.endianness = ELFDATA2LSB
100 self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
Janosch Frank368e3ad2016-01-22 13:08:39 +0100101 self.ehdr.e_machine = EM_PPC64
102
103 elif arch == 'ppc64-be':
Stefan Weil1d817db2016-03-21 19:21:26 +0100104 self.endianness = ELFDATA2MSB
105 self.ehdr = get_arch_ehdr(self.endianness, self.elfclass)
Janosch Frank368e3ad2016-01-22 13:08:39 +0100106 self.ehdr.e_machine = EM_PPC64
107
108 else:
109 raise gdb.GdbError("No valid arch type specified.\n"
110 "Currently supported types:\n"
111 "aarch64-be, aarch64-le, X86_64, 386, s390, "
112 "ppc64-be, ppc64-le")
113
114 self.add_segment(PT_NOTE, 0, 0)
115
116 def add_note(self, n_name, n_desc, n_type):
117 """Adds a note to the ELF."""
118
Stefan Weil1d817db2016-03-21 19:21:26 +0100119 note = get_arch_note(self.endianness, len(n_name), len(n_desc))
Janosch Frank368e3ad2016-01-22 13:08:39 +0100120 note.n_namesz = len(n_name) + 1
121 note.n_descsz = len(n_desc)
122 note.n_name = n_name.encode()
123 note.n_type = n_type
124
125 # Desc needs to be 4 byte aligned (although the 64bit spec
126 # specifies 8 byte). When defining n_desc as uint32 it will be
127 # automatically aligned but we need the memmove to copy the
128 # string into it.
129 ctypes.memmove(note.n_desc, n_desc.encode(), len(n_desc))
130
131 self.notes.append(note)
132 self.segments[0].p_filesz += ctypes.sizeof(note)
133 self.segments[0].p_memsz += ctypes.sizeof(note)
134
Marc-André Lureaud23bfa92017-09-11 18:59:28 +0200135
136 def add_vmcoreinfo_note(self, vmcoreinfo):
137 """Adds a vmcoreinfo note to the ELF dump."""
138 # compute the header size, and copy that many bytes from the note
139 header = get_arch_note(self.endianness, 0, 0)
140 ctypes.memmove(ctypes.pointer(header),
141 vmcoreinfo, ctypes.sizeof(header))
142 if header.n_descsz > 1 << 20:
143 print('warning: invalid vmcoreinfo size')
144 return
145 # now get the full note
146 note = get_arch_note(self.endianness,
147 header.n_namesz - 1, header.n_descsz)
148 ctypes.memmove(ctypes.pointer(note), vmcoreinfo, ctypes.sizeof(note))
149
150 self.notes.append(note)
151 self.segments[0].p_filesz += ctypes.sizeof(note)
152 self.segments[0].p_memsz += ctypes.sizeof(note)
153
Janosch Frank368e3ad2016-01-22 13:08:39 +0100154 def add_segment(self, p_type, p_paddr, p_size):
155 """Adds a segment to the elf."""
156
Stefan Weil1d817db2016-03-21 19:21:26 +0100157 phdr = get_arch_phdr(self.endianness, self.elfclass)
Janosch Frank368e3ad2016-01-22 13:08:39 +0100158 phdr.p_type = p_type
159 phdr.p_paddr = p_paddr
160 phdr.p_filesz = p_size
161 phdr.p_memsz = p_size
162 self.segments.append(phdr)
163 self.ehdr.e_phnum += 1
164
165 def to_file(self, elf_file):
166 """Writes all ELF structures to the the passed file.
167
168 Structure:
169 Ehdr
170 Segment 0:PT_NOTE
171 Segment 1:PT_LOAD
172 Segment N:PT_LOAD
173 Note 0..N
174 Dump contents
175 """
176 elf_file.write(self.ehdr)
177 off = ctypes.sizeof(self.ehdr) + \
178 len(self.segments) * ctypes.sizeof(self.segments[0])
179
180 for phdr in self.segments:
181 phdr.p_offset = off
182 elf_file.write(phdr)
183 off += phdr.p_filesz
184
185 for note in self.notes:
186 elf_file.write(note)
187
188
Stefan Weil1d817db2016-03-21 19:21:26 +0100189def get_arch_note(endianness, len_name, len_desc):
190 """Returns a Note class with the specified endianness."""
Janosch Frank368e3ad2016-01-22 13:08:39 +0100191
Stefan Weil1d817db2016-03-21 19:21:26 +0100192 if endianness == ELFDATA2LSB:
Janosch Frank368e3ad2016-01-22 13:08:39 +0100193 superclass = ctypes.LittleEndianStructure
194 else:
195 superclass = ctypes.BigEndianStructure
196
197 len_name = len_name + 1
198
199 class Note(superclass):
200 """Represents an ELF note, includes the content."""
201
202 _fields_ = [("n_namesz", ctypes.c_uint32),
203 ("n_descsz", ctypes.c_uint32),
204 ("n_type", ctypes.c_uint32),
205 ("n_name", ctypes.c_char * len_name),
206 ("n_desc", ctypes.c_uint32 * ((len_desc + 3) // 4))]
207 return Note()
208
209
210class Ident(ctypes.Structure):
211 """Represents the ELF ident array in the ehdr structure."""
212
213 _fields_ = [('ei_mag0', ctypes.c_ubyte),
214 ('ei_mag1', ctypes.c_ubyte),
215 ('ei_mag2', ctypes.c_ubyte),
216 ('ei_mag3', ctypes.c_ubyte),
217 ('ei_class', ctypes.c_ubyte),
218 ('ei_data', ctypes.c_ubyte),
219 ('ei_version', ctypes.c_ubyte),
220 ('ei_osabi', ctypes.c_ubyte),
221 ('ei_abiversion', ctypes.c_ubyte),
222 ('ei_pad', ctypes.c_ubyte * 7)]
223
Stefan Weil1d817db2016-03-21 19:21:26 +0100224 def __init__(self, endianness, elfclass):
Janosch Frank368e3ad2016-01-22 13:08:39 +0100225 self.ei_mag0 = 0x7F
226 self.ei_mag1 = ord('E')
227 self.ei_mag2 = ord('L')
228 self.ei_mag3 = ord('F')
229 self.ei_class = elfclass
Stefan Weil1d817db2016-03-21 19:21:26 +0100230 self.ei_data = endianness
Janosch Frank368e3ad2016-01-22 13:08:39 +0100231 self.ei_version = EV_CURRENT
232
233
Stefan Weil1d817db2016-03-21 19:21:26 +0100234def get_arch_ehdr(endianness, elfclass):
235 """Returns a EHDR64 class with the specified endianness."""
Janosch Frank368e3ad2016-01-22 13:08:39 +0100236
Stefan Weil1d817db2016-03-21 19:21:26 +0100237 if endianness == ELFDATA2LSB:
Janosch Frank368e3ad2016-01-22 13:08:39 +0100238 superclass = ctypes.LittleEndianStructure
239 else:
240 superclass = ctypes.BigEndianStructure
241
242 class EHDR64(superclass):
243 """Represents the 64 bit ELF header struct."""
244
245 _fields_ = [('e_ident', Ident),
246 ('e_type', ctypes.c_uint16),
247 ('e_machine', ctypes.c_uint16),
248 ('e_version', ctypes.c_uint32),
249 ('e_entry', ctypes.c_uint64),
250 ('e_phoff', ctypes.c_uint64),
251 ('e_shoff', ctypes.c_uint64),
252 ('e_flags', ctypes.c_uint32),
253 ('e_ehsize', ctypes.c_uint16),
254 ('e_phentsize', ctypes.c_uint16),
255 ('e_phnum', ctypes.c_uint16),
256 ('e_shentsize', ctypes.c_uint16),
257 ('e_shnum', ctypes.c_uint16),
258 ('e_shstrndx', ctypes.c_uint16)]
259
260 def __init__(self):
261 super(superclass, self).__init__()
Stefan Weil1d817db2016-03-21 19:21:26 +0100262 self.e_ident = Ident(endianness, elfclass)
Janosch Frank368e3ad2016-01-22 13:08:39 +0100263 self.e_type = ET_CORE
264 self.e_version = EV_CURRENT
265 self.e_ehsize = ctypes.sizeof(self)
266 self.e_phoff = ctypes.sizeof(self)
Stefan Weil1d817db2016-03-21 19:21:26 +0100267 self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianness, elfclass))
Janosch Frank368e3ad2016-01-22 13:08:39 +0100268 self.e_phnum = 0
269
270
271 class EHDR32(superclass):
272 """Represents the 32 bit ELF header struct."""
273
274 _fields_ = [('e_ident', Ident),
275 ('e_type', ctypes.c_uint16),
276 ('e_machine', ctypes.c_uint16),
277 ('e_version', ctypes.c_uint32),
278 ('e_entry', ctypes.c_uint32),
279 ('e_phoff', ctypes.c_uint32),
280 ('e_shoff', ctypes.c_uint32),
281 ('e_flags', ctypes.c_uint32),
282 ('e_ehsize', ctypes.c_uint16),
283 ('e_phentsize', ctypes.c_uint16),
284 ('e_phnum', ctypes.c_uint16),
285 ('e_shentsize', ctypes.c_uint16),
286 ('e_shnum', ctypes.c_uint16),
287 ('e_shstrndx', ctypes.c_uint16)]
288
289 def __init__(self):
290 super(superclass, self).__init__()
Stefan Weil1d817db2016-03-21 19:21:26 +0100291 self.e_ident = Ident(endianness, elfclass)
Janosch Frank368e3ad2016-01-22 13:08:39 +0100292 self.e_type = ET_CORE
293 self.e_version = EV_CURRENT
294 self.e_ehsize = ctypes.sizeof(self)
295 self.e_phoff = ctypes.sizeof(self)
Stefan Weil1d817db2016-03-21 19:21:26 +0100296 self.e_phentsize = ctypes.sizeof(get_arch_phdr(endianness, elfclass))
Janosch Frank368e3ad2016-01-22 13:08:39 +0100297 self.e_phnum = 0
298
299 # End get_arch_ehdr
300 if elfclass == ELFCLASS64:
301 return EHDR64()
302 else:
303 return EHDR32()
304
305
Stefan Weil1d817db2016-03-21 19:21:26 +0100306def get_arch_phdr(endianness, elfclass):
307 """Returns a 32 or 64 bit PHDR class with the specified endianness."""
Janosch Frank368e3ad2016-01-22 13:08:39 +0100308
Stefan Weil1d817db2016-03-21 19:21:26 +0100309 if endianness == ELFDATA2LSB:
Janosch Frank368e3ad2016-01-22 13:08:39 +0100310 superclass = ctypes.LittleEndianStructure
311 else:
312 superclass = ctypes.BigEndianStructure
313
314 class PHDR64(superclass):
315 """Represents the 64 bit ELF program header struct."""
316
317 _fields_ = [('p_type', ctypes.c_uint32),
318 ('p_flags', ctypes.c_uint32),
319 ('p_offset', ctypes.c_uint64),
320 ('p_vaddr', ctypes.c_uint64),
321 ('p_paddr', ctypes.c_uint64),
322 ('p_filesz', ctypes.c_uint64),
323 ('p_memsz', ctypes.c_uint64),
324 ('p_align', ctypes.c_uint64)]
325
326 class PHDR32(superclass):
327 """Represents the 32 bit ELF program header struct."""
328
329 _fields_ = [('p_type', ctypes.c_uint32),
330 ('p_offset', ctypes.c_uint32),
331 ('p_vaddr', ctypes.c_uint32),
332 ('p_paddr', ctypes.c_uint32),
333 ('p_filesz', ctypes.c_uint32),
334 ('p_memsz', ctypes.c_uint32),
335 ('p_flags', ctypes.c_uint32),
336 ('p_align', ctypes.c_uint32)]
337
338 # End get_arch_phdr
339 if elfclass == ELFCLASS64:
340 return PHDR64()
341 else:
342 return PHDR32()
343
Janosch Frankca81ce72016-01-22 13:08:35 +0100344
Janosch Frank47890202016-01-22 13:08:36 +0100345def int128_get64(val):
Janosch Frank6782c0e2016-01-22 13:08:38 +0100346 """Returns low 64bit part of Int128 struct."""
347
Marc-André Lureau9b4b1572017-03-10 15:28:19 +0400348 try:
349 assert val["hi"] == 0
350 return val["lo"]
351 except gdb.error:
352 u64t = gdb.lookup_type('uint64_t').array(2)
353 u64 = val.cast(u64t)
354 if sys.byteorder == 'little':
355 assert u64[1] == 0
356 return u64[0]
357 else:
358 assert u64[0] == 0
359 return u64[1]
Janosch Frank47890202016-01-22 13:08:36 +0100360
Janosch Frank6782c0e2016-01-22 13:08:38 +0100361
Janosch Frank47890202016-01-22 13:08:36 +0100362def qlist_foreach(head, field_str):
Janosch Frank6782c0e2016-01-22 13:08:38 +0100363 """Generator for qlists."""
364
Janosch Frank47890202016-01-22 13:08:36 +0100365 var_p = head["lh_first"]
Janosch Frank6782c0e2016-01-22 13:08:38 +0100366 while var_p != 0:
Janosch Frank47890202016-01-22 13:08:36 +0100367 var = var_p.dereference()
Janosch Frank47890202016-01-22 13:08:36 +0100368 var_p = var[field_str]["le_next"]
Janosch Frank6782c0e2016-01-22 13:08:38 +0100369 yield var
370
Janosch Frank47890202016-01-22 13:08:36 +0100371
Paolo Bonzini0878d0e2016-02-22 11:02:12 +0100372def qemu_map_ram_ptr(block, offset):
Janosch Frank6782c0e2016-01-22 13:08:38 +0100373 """Returns qemu vaddr for given guest physical address."""
374
Paolo Bonzini0878d0e2016-02-22 11:02:12 +0100375 return block["host"] + offset
Janosch Frank47890202016-01-22 13:08:36 +0100376
Janosch Frank6782c0e2016-01-22 13:08:38 +0100377
378def memory_region_get_ram_ptr(memory_region):
379 if memory_region["alias"] != 0:
380 return (memory_region_get_ram_ptr(memory_region["alias"].dereference())
381 + memory_region["alias_offset"])
382
Paolo Bonzini0878d0e2016-02-22 11:02:12 +0100383 return qemu_map_ram_ptr(memory_region["ram_block"], 0)
Janosch Frank6782c0e2016-01-22 13:08:38 +0100384
Janosch Frank47890202016-01-22 13:08:36 +0100385
386def get_guest_phys_blocks():
Janosch Frank6782c0e2016-01-22 13:08:38 +0100387 """Returns a list of ram blocks.
388
389 Each block entry contains:
390 'target_start': guest block phys start address
391 'target_end': guest block phys end address
392 'host_addr': qemu vaddr of the block's start
393 """
394
Janosch Frank47890202016-01-22 13:08:36 +0100395 guest_phys_blocks = []
Janosch Frank6782c0e2016-01-22 13:08:38 +0100396
Janosch Frank7cb10892016-01-22 13:08:37 +0100397 print("guest RAM blocks:")
398 print("target_start target_end host_addr message "
399 "count")
400 print("---------------- ---------------- ---------------- ------- "
401 "-----")
Janosch Frank47890202016-01-22 13:08:36 +0100402
403 current_map_p = gdb.parse_and_eval("address_space_memory.current_map")
404 current_map = current_map_p.dereference()
Janosch Frank7cb10892016-01-22 13:08:37 +0100405
406 # Conversion to int is needed for python 3
407 # compatibility. Otherwise range doesn't cast the value itself and
408 # breaks.
409 for cur in range(int(current_map["nr"])):
Janosch Frank6782c0e2016-01-22 13:08:38 +0100410 flat_range = (current_map["ranges"] + cur).dereference()
411 memory_region = flat_range["mr"].dereference()
Janosch Frank47890202016-01-22 13:08:36 +0100412
413 # we only care about RAM
Janosch Frank6782c0e2016-01-22 13:08:38 +0100414 if not memory_region["ram"]:
Janosch Frank47890202016-01-22 13:08:36 +0100415 continue
416
417 section_size = int128_get64(flat_range["addr"]["size"])
418 target_start = int128_get64(flat_range["addr"]["start"])
Janosch Frank6782c0e2016-01-22 13:08:38 +0100419 target_end = target_start + section_size
420 host_addr = (memory_region_get_ram_ptr(memory_region)
421 + flat_range["offset_in_region"])
Janosch Frank47890202016-01-22 13:08:36 +0100422 predecessor = None
423
424 # find continuity in guest physical address space
Janosch Frank6782c0e2016-01-22 13:08:38 +0100425 if len(guest_phys_blocks) > 0:
Janosch Frank47890202016-01-22 13:08:36 +0100426 predecessor = guest_phys_blocks[-1]
427 predecessor_size = (predecessor["target_end"] -
428 predecessor["target_start"])
429
430 # the memory API guarantees monotonically increasing
431 # traversal
Janosch Frank6782c0e2016-01-22 13:08:38 +0100432 assert predecessor["target_end"] <= target_start
Janosch Frank47890202016-01-22 13:08:36 +0100433
434 # we want continuity in both guest-physical and
435 # host-virtual memory
436 if (predecessor["target_end"] < target_start or
437 predecessor["host_addr"] + predecessor_size != host_addr):
438 predecessor = None
439
Janosch Frank6782c0e2016-01-22 13:08:38 +0100440 if predecessor is None:
Janosch Frank47890202016-01-22 13:08:36 +0100441 # isolated mapping, add it to the list
442 guest_phys_blocks.append({"target_start": target_start,
Janosch Frank6782c0e2016-01-22 13:08:38 +0100443 "target_end": target_end,
444 "host_addr": host_addr})
Janosch Frank47890202016-01-22 13:08:36 +0100445 message = "added"
446 else:
447 # expand predecessor until @target_end; predecessor's
448 # start doesn't change
449 predecessor["target_end"] = target_end
450 message = "joined"
451
Janosch Frank7cb10892016-01-22 13:08:37 +0100452 print("%016x %016x %016x %-7s %5u" %
453 (target_start, target_end, host_addr.cast(UINTPTR_T),
454 message, len(guest_phys_blocks)))
Janosch Frank47890202016-01-22 13:08:36 +0100455
456 return guest_phys_blocks
457
458
Janosch Frank28fbf8f2016-01-22 13:08:40 +0100459# The leading docstring doesn't have idiomatic Python formatting. It is
460# printed by gdb's "help" command (the first line is printed in the
461# "help data" summary), and it should match how other help texts look in
462# gdb.
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100463class DumpGuestMemory(gdb.Command):
464 """Extract guest vmcore from qemu process coredump.
465
Janosch Frank368e3ad2016-01-22 13:08:39 +0100466The two required arguments are FILE and ARCH:
467FILE identifies the target file to write the guest vmcore to.
468ARCH specifies the architecture for which the core will be generated.
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100469
470This GDB command reimplements the dump-guest-memory QMP command in
471python, using the representation of guest memory as captured in the qemu
472coredump. The qemu process that has been dumped must have had the
Janosch Frank368e3ad2016-01-22 13:08:39 +0100473command line option "-machine dump-guest-core=on" which is the default.
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100474
475For simplicity, the "paging", "begin" and "end" parameters of the QMP
476command are not supported -- no attempt is made to get the guest's
477internal paging structures (ie. paging=false is hard-wired), and guest
478memory is always fully dumped.
479
Janosch Frank368e3ad2016-01-22 13:08:39 +0100480Currently aarch64-be, aarch64-le, X86_64, 386, s390, ppc64-be,
481ppc64-le guests are supported.
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100482
483The CORE/NT_PRSTATUS and QEMU notes (that is, the VCPUs' statuses) are
484not written to the vmcore. Preparing these would require context that is
485only present in the KVM host kernel module when the guest is alive. A
486fake ELF note is written instead, only to keep the ELF parser of "crash"
487happy.
488
489Dependent on how busted the qemu process was at the time of the
490coredump, this command might produce unpredictable results. If qemu
491deliberately called abort(), or it was dumped in response to a signal at
492a halfway fortunate point, then its coredump should be in reasonable
493shape and this command should mostly work."""
494
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100495 def __init__(self):
496 super(DumpGuestMemory, self).__init__("dump-guest-memory",
497 gdb.COMMAND_DATA,
498 gdb.COMPLETE_FILENAME)
Janosch Frank368e3ad2016-01-22 13:08:39 +0100499 self.elf = None
Janosch Frank47890202016-01-22 13:08:36 +0100500 self.guest_phys_blocks = None
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100501
Janosch Frank368e3ad2016-01-22 13:08:39 +0100502 def dump_init(self, vmcore):
503 """Prepares and writes ELF structures to core file."""
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100504
Janosch Frank368e3ad2016-01-22 13:08:39 +0100505 # Needed to make crash happy, data for more useful notes is
506 # not available in a qemu core.
507 self.elf.add_note("NONE", "EMPTY", 0)
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100508
Janosch Frank368e3ad2016-01-22 13:08:39 +0100509 # We should never reach PN_XNUM for paging=false dumps,
510 # there's just a handful of discontiguous ranges after
511 # merging.
512 # The constant is needed to account for the PT_NOTE segment.
513 phdr_num = len(self.guest_phys_blocks) + 1
514 assert phdr_num < PN_XNUM
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100515
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100516 for block in self.guest_phys_blocks:
Janosch Frank368e3ad2016-01-22 13:08:39 +0100517 block_size = block["target_end"] - block["target_start"]
518 self.elf.add_segment(PT_LOAD, block["target_start"], block_size)
519
520 self.elf.to_file(vmcore)
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100521
522 def dump_iterate(self, vmcore):
Janosch Frank368e3ad2016-01-22 13:08:39 +0100523 """Writes guest core to file."""
524
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100525 qemu_core = gdb.inferiors()[0]
526 for block in self.guest_phys_blocks:
Janosch Frank6782c0e2016-01-22 13:08:38 +0100527 cur = block["host_addr"]
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100528 left = block["target_end"] - block["target_start"]
Janosch Frank7cb10892016-01-22 13:08:37 +0100529 print("dumping range at %016x for length %016x" %
530 (cur.cast(UINTPTR_T), left))
Janosch Frank368e3ad2016-01-22 13:08:39 +0100531
Janosch Frank6782c0e2016-01-22 13:08:38 +0100532 while left > 0:
Janosch Frankca81ce72016-01-22 13:08:35 +0100533 chunk_size = min(TARGET_PAGE_SIZE, left)
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100534 chunk = qemu_core.read_memory(cur, chunk_size)
535 vmcore.write(chunk)
Janosch Frank6782c0e2016-01-22 13:08:38 +0100536 cur += chunk_size
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100537 left -= chunk_size
538
Marc-André Lureaud23bfa92017-09-11 18:59:28 +0200539 def phys_memory_read(self, addr, size):
540 qemu_core = gdb.inferiors()[0]
541 for block in self.guest_phys_blocks:
542 if block["target_start"] <= addr \
543 and addr + size <= block["target_end"]:
544 haddr = block["host_addr"] + (addr - block["target_start"])
545 return qemu_core.read_memory(haddr, size)
546 return None
547
548 def add_vmcoreinfo(self):
Marc-André Lureauc3b16422017-12-12 17:27:58 +0100549 vmci = 'vmcoreinfo_realize::vmcoreinfo_state'
Marc-André Lureaud36d0a92017-12-01 12:37:44 +0100550 if not gdb.parse_and_eval("%s" % vmci) \
551 or not gdb.parse_and_eval("(%s)->has_vmcoreinfo" % vmci):
Marc-André Lureaud23bfa92017-09-11 18:59:28 +0200552 return
553
Marc-André Lureaud36d0a92017-12-01 12:37:44 +0100554 fmt = gdb.parse_and_eval("(%s)->vmcoreinfo.guest_format" % vmci)
555 addr = gdb.parse_and_eval("(%s)->vmcoreinfo.paddr" % vmci)
556 size = gdb.parse_and_eval("(%s)->vmcoreinfo.size" % vmci)
Marc-André Lureaud23bfa92017-09-11 18:59:28 +0200557
558 fmt = le16_to_cpu(fmt)
559 addr = le64_to_cpu(addr)
560 size = le32_to_cpu(size)
561
562 if fmt != VMCOREINFO_FORMAT_ELF:
563 return
564
565 vmcoreinfo = self.phys_memory_read(addr, size)
566 if vmcoreinfo:
567 self.elf.add_vmcoreinfo_note(vmcoreinfo.tobytes())
568
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100569 def invoke(self, args, from_tty):
Janosch Frank368e3ad2016-01-22 13:08:39 +0100570 """Handles command invocation from gdb."""
571
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100572 # Unwittingly pressing the Enter key after the command should
573 # not dump the same multi-gig coredump to the same file.
574 self.dont_repeat()
575
576 argv = gdb.string_to_argv(args)
Janosch Frank368e3ad2016-01-22 13:08:39 +0100577 if len(argv) != 2:
578 raise gdb.GdbError("usage: dump-guest-memory FILE ARCH")
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100579
Janosch Frank368e3ad2016-01-22 13:08:39 +0100580 self.elf = ELF(argv[1])
581 self.guest_phys_blocks = get_guest_phys_blocks()
Marc-André Lureaud23bfa92017-09-11 18:59:28 +0200582 self.add_vmcoreinfo()
Janosch Frank368e3ad2016-01-22 13:08:39 +0100583
584 with open(argv[0], "wb") as vmcore:
585 self.dump_init(vmcore)
586 self.dump_iterate(vmcore)
Laszlo Ersek3e16d142013-12-17 01:37:06 +0100587
588DumpGuestMemory()