Skip to content

Commit

Permalink
Merge pull request #408 from no92/vmutil-improvements
Browse files Browse the repository at this point in the history
`vm-util.py` improvements
  • Loading branch information
avdgrinten authored Nov 19, 2024
2 parents 9bbda40 + b40813a commit 132b2c6
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 36 deletions.
122 changes: 122 additions & 0 deletions scripts/gdb-commands.py
Original file line number Diff line number Diff line change
@@ -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 <image base address> <PE executable> [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()
64 changes: 28 additions & 36 deletions scripts/vm-util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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",
Expand All @@ -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",
]
Expand All @@ -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")
Expand Down

0 comments on commit 132b2c6

Please sign in to comment.