xref: /qemu/scripts/dump-guest-memory.py (revision 423edd9a)
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