diff --git a/scripts/gdb-commands.py b/scripts/gdb-commands.py new file mode 100644 index 000000000..f0afe48d4 --- /dev/null +++ b/scripts/gdb-commands.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 + +import pefile +import os + +class FindUefiMain(gdb.Command): + def __init__(self): + super(FindUefiMain, self).__init__("find-uefi-main", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + for t in gdb.selected_inferior().threads(): + if not (t.is_running() or t.is_stopped()): + continue + t.switch() + if gdb.newest_frame().function() and gdb.newest_frame().function().name.startswith('eirUefiMain('): + ready_var, _ = gdb.lookup_symbol('eir_gdb_ready') + if not ready_var: + print("warning: could not find 'eir_gdb_ready'") + else: + gdb.execute("set eir_gdb_ready=1") + + print(f"info: UEFI running on thread {t.num}") + return + + print("error: could not find correct thread") + return + +class LoadPE(gdb.Command): + def __init__(self): + super(LoadPE, self).__init__("loadpe", gdb.COMMAND_USER) + + def usage(self): + print("usage: loadpe [ELF executable]") + + def invoke(self, arg, from_tty): + args = arg.split(' ') + + if len(args) not in range(2, 4): + self.usage() + return + + try: + image_base = int(args[0], 16) + except ValueError: + self.usage() + return + + if not os.path.isfile(args[1]): + print(f"file '{args[1]}' does not exist") + return + + pe_path = args[1] + symbol_file_path = pe_path + + if len(args) > 2: + if not os.path.isfile(args[2]): + print(f"file '{args[2]}' does not exist") + return + + symbol_file_path = args[2] + + with open(pe_path, "rb") as f: + data = f.read() + pe = pefile.PE(data=data) + sections = {} + + st_offset = pe.FILE_HEADER.PointerToSymbolTable + (pe.FILE_HEADER.NumberOfSymbols * 18) + + for section in pe.sections: + name = section.Name.strip(b"\x00").decode("utf-8") + + if name.startswith("/"): + offset = int(name[1:]) + new_name = pe.get_string_from_data(st_offset + offset, data).decode("ascii") + name = new_name + + if name == ".reloc": + continue + + if name.startswith("."): + sections[name] = section.VirtualAddress + image_base + + sections_str = " -s ".join(" ".join((f"\"{name}\"", f"{address:#x}")) for name, address in sections.items() if name != ".text") + pe_symbols = f"add-symbol-file {symbol_file_path} {sections['.text']:#x} -s {sections_str}" + + gdb.execute(pe_symbols) + +class EfiGuidPrinter: + _table = { + '5B1B31A1-9562-11D2-8E3F-00A0C969723B': 'EFI_LOADED_IMAGE_PROTOCOL_GUID', + '8868E871-E4F1-11D3-BC22-0080C73C8881': 'EFI_ACPI_20_TABLE_GUID', + '9042A9DE-23DC-4A38-96FB-7ADED080516A': 'EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID', + '964E5B22-6459-11D2-8E39-00A0C969723B': 'EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID', + 'B1B621D5-F19C-41A5-830B-D9152C69AAE0': 'EFI_DTB_TABLE_GUID', + } + + def __init__(self, val): + self.val = val + + def to_string(self): + Data1 = int(self.val['data1']) + Data2 = int(self.val['data2']) + Data3 = int(self.val['data3']) + Data4 = self.val['data4'] + guid = f'{Data1:08X}-{Data2:04X}-' + guid += f'{Data3:04X}-' + guid += f'{int(Data4[0]):02X}{int(Data4[1]):02X}-' + guid += f'{int(Data4[2]):02X}{int(Data4[3]):02X}' + guid += f'{int(Data4[4]):02X}{int(Data4[5]):02X}' + guid += f'{int(Data4[6]):02X}{int(Data4[7]):02X}' + + return self._table.get(guid, guid) + +def lookup_type(val): + if str(val.type) == 'efi_guid': + return EfiGuidPrinter(val) + return None + +gdb.pretty_printers.append(lookup_type) + +FindUefiMain() +LoadPE() diff --git a/scripts/vm-util.py b/scripts/vm-util.py index 1ac170230..b0c161e3e 100755 --- a/scripts/vm-util.py +++ b/scripts/vm-util.py @@ -257,8 +257,9 @@ def do_qemu(args): qemu_args += ["-drive", f"if=pflash,format=raw,file=tools/ovmf/OVMF_CODE_{args.arch}.fd,readonly=on"] qemu_args += ["-drive", f"if=pflash,format=raw,file={tmp_ovmf_vars.name}"] - qemu_args += ["-chardev", "file,id=uefi-load-base,path=uefi-load-base.addr"] - qemu_args += ["-device", "isa-debugcon,iobase=0xCB7,chardev=uefi-load-base"] + if args.arch == "x86_64": + qemu_args += ["-chardev", "file,id=uefi-load-base,path=uefi-load-base.addr"] + qemu_args += ["-device", "isa-debugcon,iobase=0xCB7,chardev=uefi-load-base"] if args.ovmf_logs: if not args.uefi: @@ -477,6 +478,14 @@ def do_qemu(args): def do_gdb(args): gdb_args = ["gdb"] + + src_path = os.path.dirname(os.path.realpath("bootstrap.link")) + with open('.gdbinit', 'w+') as f: + f.write(f"source {src_path}/scripts/gdb-commands.py\n") + f.write(f"set substitute-path ../../../src {src_path}\n") + f.write(f"set substitute-path /var/lib/managarm-buildenv/build/ ./\n") + f.write(f"set sysroot system-root\n") + if args.qemu: gdb_args += [ "--symbols=pkg-builds/managarm-kernel/kernel/thor/thor", @@ -490,51 +499,33 @@ def do_gdb(args): print("error: please install the 'pefile' python module") sys.exit(1) - with open("uefi-load-base.addr", mode="rb") as f: - f.seek(0, os.SEEK_END) - if f.tell() < 8: - print("error: no UEFI image base address found") - sys.exit(1) - f.seek(-8, os.SEEK_END) - image_base = struct.unpack("P", f.read())[0] - - eir_path = "pkg-builds/managarm-kernel-uefi/kernel/eir/protos/uefi/eir-uefi" - with open(eir_path, "rb") as f: - data = f.read() - pe = pefile.PE(data=data) - sections = {} - - st_offset = pe.FILE_HEADER.PointerToSymbolTable + (pe.FILE_HEADER.NumberOfSymbols * 18) - - for section in pe.sections: - name = section.Name.strip(b"\x00").decode("utf-8") - - if name.startswith("/"): - offset = int(name[1:]) - new_name = pe.get_string_from_data(st_offset + offset, data).decode("ascii") - name = new_name + if args.uefi_base: + image_base = int(args.uefi_base, 16) + else: + with open("uefi-load-base.addr", mode="rb") as f: + f.seek(0, os.SEEK_END) + if f.tell() < 8: + print("error: no UEFI image base address found") + sys.exit(1) + f.seek(-8, os.SEEK_END) + image_base = struct.unpack("P", f.read())[0] - if name.startswith("."): - sections[name] = section.VirtualAddress + image_base + eir_path_pe = "pkg-builds/managarm-kernel-uefi/kernel/eir/protos/uefi/eir-uefi" + eir_path_debug = "pkg-builds/managarm-kernel-uefi/kernel/eir/protos/uefi/eir-uefi-elf" - sections_str = " -s ".join(" ".join((f"\"{name}\"", f"{address:#x}")) for name, address in sections.items() if name != ".text") - pe_symbols = f"add-symbol-file {eir_path} {sections['.text']:#x} -s {sections_str}" + if not os.access(eir_path_debug, os.R_OK): + eir_path_debug = eir_path_pe gdb_args += [ + "-ex", f"loadpe {image_base:#x} {eir_path_pe} {eir_path_debug}", "-ex", "target remote tcp:" + args.ip + ":1234", - "-ex", "set confirm off", - "-ex", "set substitute-path ../../../src ../src", - "-ex", pe_symbols, - "-ex", "set confirm on", - "-ex", "set eir_gdb_ready=1" + "-ex", "find-uefi-main" ] elif args.kernel: gdb_args += ["-ex", "target remote tcp:" + args.ip + ":5678"] else: assert args.posix gdb_args += [ - "-ex", - "set sysroot system-root", "-ex", "target remote tcp:" + args.ip + ":5679", ] @@ -545,6 +536,7 @@ def do_gdb(args): gdb_parser = main_subparsers.add_parser("gdb") gdb_parser.set_defaults(_fn=do_gdb) gdb_parser.add_argument("--ip", type=str, default="localhost") +gdb_parser.add_argument("--uefi-base", type=str) gdb_group = gdb_parser.add_mutually_exclusive_group(required=True) gdb_group.add_argument("--qemu", action="store_true")