Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vm-util.py improvements #408

Merged
merged 5 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading