mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
548 lines
20 KiB
Python
548 lines
20 KiB
Python
# pylint: disable=no-self-use,undefined-variable,wildcard-import,too-many-statements,too-many-branches
|
|
'''
|
|
Port of gdb_uefi.py to LLDB.
|
|
Refer to gdb_uefi.py for more details.
|
|
|
|
'''
|
|
|
|
import array
|
|
import binascii
|
|
import getopt
|
|
import os
|
|
import re
|
|
import shlex
|
|
|
|
from collections import OrderedDict
|
|
from common_uefi import *
|
|
import lldb
|
|
|
|
|
|
class ReloadUefi:
|
|
'''Reload UEFI symbols'''
|
|
|
|
#
|
|
# Various constants.
|
|
#
|
|
|
|
EINVAL = 0xffffffff
|
|
CV_NB10 = 0x3031424E
|
|
CV_RSDS = 0x53445352
|
|
CV_MTOC = 0x434F544D
|
|
DOS_MAGIC = 0x5A4D
|
|
PE32PLUS_MAGIC = 0x20b
|
|
EST_SIGNATURE = 0x5453595320494249
|
|
DEBUG_GUID = [0x49152E77, 0x1ADA, 0x4764,
|
|
[0xB7, 0xA2, 0x7A, 0xFE,
|
|
0xFE, 0xD9, 0x5E, 0x8B]]
|
|
DEBUG_IS_UPDATING = 0x1
|
|
|
|
#
|
|
# If the images were built as ELF/MACH-O and then converted to PE,
|
|
# then the base address needs to be offset by PE headers.
|
|
#
|
|
|
|
offset_by_headers = False
|
|
|
|
def __init__(self, debugger, session_dict):
|
|
self.debugger = debugger
|
|
self.session_dict = session_dict
|
|
self.typetarget = None
|
|
self.activetarget = None
|
|
|
|
#
|
|
# Returns SBType for a type.
|
|
#
|
|
|
|
def type(self, typename):
|
|
return self.typetarget.FindFirstType(typename)
|
|
|
|
#
|
|
# Returns SBType for a pointer to a type.
|
|
#
|
|
|
|
def ptype(self, typename):
|
|
return self.typetarget.FindFirstType(typename).GetPointerType()
|
|
|
|
#
|
|
# Returns typed SBValue for an address.
|
|
#
|
|
def typed_ptr(self, typename, address):
|
|
target = self.activetarget
|
|
sbdata = lldb.SBData.CreateDataFromInt(address, size=8) ##< size=8 required on 32-bit as well as 64-bit, or resultant pointer cannot be dereferenced.
|
|
return target.CreateValueFromData('ptr', sbdata, typename)
|
|
|
|
#
|
|
# Cast pointer in way which works round fragilities of 32-bit handling (simple ptr.Cast(type) works in 64-bit).
|
|
#
|
|
def cast_ptr(self, type, ptr):
|
|
return self.typed_ptr(type, ptr.GetValueAsUnsigned())
|
|
|
|
#
|
|
# Computes CRC32 on an array of data.
|
|
#
|
|
|
|
def crc32(self, data):
|
|
return binascii.crc32(data) & 0xFFFFFFFF
|
|
|
|
#
|
|
# GetChildMemberWithName working round fragilities of 32-bit handling.
|
|
#
|
|
|
|
def get_child_member_with_name(self, value, field_name):
|
|
member = value.GetChildMemberWithName(field_name)
|
|
if member.TypeIsPointerType():
|
|
member = self.cast_ptr(member.GetType(), member)
|
|
return member
|
|
|
|
#
|
|
# Gets a field from struct as an unsigned value.
|
|
#
|
|
|
|
def get_field(self, value, field_name=None, force_bytes=False, force_int=False, single_entry=False):
|
|
if field_name is not None:
|
|
member = self.get_child_member_with_name(value, field_name)
|
|
else:
|
|
member = value
|
|
|
|
if (not force_int and member.GetByteSize() > self.typetarget.GetAddressByteSize()) or force_bytes:
|
|
sbdata = member.GetData()
|
|
byte_size = sbdata.GetByteSize()
|
|
data = array.array('B')
|
|
error = lldb.SBError()
|
|
for i in range(0, byte_size):
|
|
data.append(sbdata.GetUnsignedInt8(error, i))
|
|
return data
|
|
|
|
if member.TypeIsPointerType():
|
|
if single_entry:
|
|
return member.Dereference().GetValueAsUnsigned()
|
|
|
|
# Unfortunately LLDB is unaware of underlying types like CHAR8 when working in PDB mode,
|
|
# so we only have size to trust. DWARF mode is ok, but it is best to stay most compatible.
|
|
member_unsigned = member.GetValueAsUnsigned()
|
|
char_type = member.GetType().GetPointeeType()
|
|
if char_type.GetByteSize() == 1:
|
|
char_size = 1
|
|
char_data = array.array('B')
|
|
elif char_type.GetByteSize() == 2:
|
|
char_size = 2
|
|
char_data = array.array('H')
|
|
else:
|
|
return member_unsigned
|
|
|
|
i = 0
|
|
while i < 0x1000:
|
|
c = member.CreateValueFromAddress('ptr', member_unsigned + i * char_size, char_type).GetValueAsUnsigned()
|
|
char_data.append(c)
|
|
if c == 0:
|
|
break
|
|
i += 1
|
|
|
|
return char_data
|
|
|
|
return member.GetValueAsUnsigned()
|
|
|
|
#
|
|
# Sets a field in a struct to a value, i.e.
|
|
# value->field_name = data.
|
|
#
|
|
|
|
def set_field(self, value, field_name, data):
|
|
member = self.get_child_member_with_name(value, field_name)
|
|
data = lldb.SBData.CreateDataFromInt(data, size=member.GetByteSize())
|
|
error = lldb.SBError()
|
|
member.SetData(data, error)
|
|
|
|
#
|
|
# Locates the EFI_SYSTEM_TABLE as per UEFI spec 17.4.
|
|
# Returns base address or -1.
|
|
#
|
|
|
|
def search_est(self):
|
|
address = 0
|
|
estp_t = self.ptype('EFI_SYSTEM_TABLE_POINTER')
|
|
while True:
|
|
estp = self.typed_ptr(estp_t, address)
|
|
if self.get_field(estp, 'Signature', force_int=True) == self.EST_SIGNATURE:
|
|
oldcrc = self.get_field(estp, 'Crc32')
|
|
self.set_field(estp, 'Crc32', 0)
|
|
newcrc = self.crc32(self.get_field(estp.Dereference()))
|
|
self.set_field(estp, 'Crc32', oldcrc)
|
|
if newcrc == oldcrc:
|
|
print(f'EFI_SYSTEM_TABLE_POINTER @ 0x{address:x}')
|
|
return self.get_child_member_with_name(estp, 'EfiSystemTableBase')
|
|
|
|
address += 4 * 2**20
|
|
if address >= 2**32:
|
|
return self.EINVAL
|
|
|
|
#
|
|
# Searches for a vendor-specific configuration table (in EST),
|
|
# given a vendor-specific table GUID. GUID is a list like -
|
|
# [32-bit, 16-bit, 16-bit, [8 bytes]]
|
|
#
|
|
|
|
def search_config(self, cfg_table, count, guid):
|
|
index = 0
|
|
while index != count:
|
|
# GetChildAtIndex accesses inner structure fields, so we have to use the fugly way.
|
|
cfg_entry = cfg_table.GetValueForExpressionPath(f'[{index}]')
|
|
cfg_guid = self.get_child_member_with_name(cfg_entry, 'VendorGuid')
|
|
if self.get_field(cfg_guid, 'Data1') == guid[0] and \
|
|
self.get_field(cfg_guid, 'Data2') == guid[1] and \
|
|
self.get_field(cfg_guid, 'Data3') == guid[2] and \
|
|
self.get_field(cfg_guid, 'Data4', True).tolist() == guid[3]:
|
|
return self.get_child_member_with_name(cfg_entry, 'VendorTable')
|
|
index += 1
|
|
return self.EINVAL
|
|
|
|
#
|
|
# Returns offset of a field within structure. Useful
|
|
# for getting container of a structure.
|
|
#
|
|
|
|
def offsetof(self, typename, field):
|
|
t = self.ptype(typename)
|
|
for index in range(0, t.GetNumberOfFields()):
|
|
f = t.GetFieldAtIndex(index)
|
|
if f.GetName() == field:
|
|
return f.GetOffsetInBytes()
|
|
raise RuntimeError(f'Cannot find {field} in {typename} to get offset')
|
|
|
|
#
|
|
# Returns sizeof of a type.
|
|
#
|
|
|
|
def sizeof(self, typename):
|
|
return self.type(typename).GetByteSize()
|
|
|
|
#
|
|
# Returns the EFI_IMAGE_NT_HEADERS32 pointer, given
|
|
# an ImageBase address as a SBValue.
|
|
#
|
|
|
|
def pe_headers(self, imagebase):
|
|
dosh_t = self.ptype('EFI_IMAGE_DOS_HEADER')
|
|
head_t = self.ptype('EFI_IMAGE_OPTIONAL_HEADER_UNION')
|
|
dosh = self.typed_ptr(dosh_t, imagebase)
|
|
h_addr = imagebase
|
|
if self.get_field(dosh, 'e_magic') == self.DOS_MAGIC:
|
|
h_addr = h_addr + self.get_field(dosh, 'e_lfanew')
|
|
return self.typed_ptr(head_t, h_addr)
|
|
|
|
#
|
|
# Returns a dictionary with PE sections.
|
|
#
|
|
|
|
def pe_sections(self, combined, file, _):
|
|
sect_t = self.ptype('EFI_IMAGE_SECTION_HEADER')
|
|
common = self.get_child_member_with_name(combined, 'CommonHeader')
|
|
sections_addr = common.GetLoadAddress() + common.GetByteSize() + self.get_field(file, 'SizeOfOptionalHeader')
|
|
sections = self.typed_ptr(sect_t, sections_addr)
|
|
sects = OrderedDict()
|
|
for i in range(self.get_field(file, 'NumberOfSections')):
|
|
section = sections.GetValueForExpressionPath(f'[{i}]')
|
|
name = self.get_field(section, 'Name', force_bytes=True)
|
|
name = UefiMisc.parse_utf8(name)
|
|
addr = self.get_field(section, 'VirtualAddress')
|
|
if name != '':
|
|
sects[name] = addr
|
|
return sects
|
|
|
|
#
|
|
# Returns True if pe_headers refer to a PE32+ image.
|
|
#
|
|
|
|
def pe_is_64(self, pe_headers):
|
|
magic = pe_headers.GetValueForExpressionPath('.Pe32.Magic').GetValueAsUnsigned()
|
|
return magic == self.PE32PLUS_MAGIC
|
|
|
|
#
|
|
# Returns the PE fileheader.
|
|
#
|
|
|
|
def pe_file(self, pe):
|
|
if self.pe_is_64(pe):
|
|
obj = self.get_child_member_with_name(pe, 'Pe32Plus')
|
|
else:
|
|
obj = self.get_child_member_with_name(pe, 'Pe32')
|
|
return self.get_child_member_with_name(obj, 'FileHeader')
|
|
|
|
#
|
|
# Returns the PE (not so) optional header.
|
|
#
|
|
|
|
def pe_combined(self, pe):
|
|
if self.pe_is_64(pe):
|
|
return self.get_child_member_with_name(pe, 'Pe32Plus')
|
|
else:
|
|
return self.get_child_member_with_name(pe, 'Pe32')
|
|
|
|
#
|
|
# Returns the symbol file name for a PE image.
|
|
#
|
|
|
|
def pe_parse_debug(self, base):
|
|
pe = self.pe_headers(base)
|
|
combined = self.pe_combined(pe)
|
|
debug_dir_entry = combined.GetValueForExpressionPath('.DataDirectory[6]')
|
|
dep = self.get_field(debug_dir_entry, 'VirtualAddress') + base
|
|
dep = self.typed_ptr(self.ptype('EFI_IMAGE_DEBUG_DIRECTORY_ENTRY'), dep)
|
|
cvp = self.get_field(dep, 'RVA') + base
|
|
# FIXME: UINT32 should be used here instead of unsigned, but LLDB+PDB type system is broken.
|
|
cvv = self.typed_ptr(self.ptype('unsigned'), cvp).Dereference().GetValueAsUnsigned()
|
|
if cvv == self.CV_NB10:
|
|
return cvp + self.sizeof('EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY')
|
|
if cvv == self.CV_RSDS:
|
|
return cvp + self.sizeof('EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY')
|
|
if cvv == self.CV_MTOC:
|
|
return cvp + self.sizeof('EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY')
|
|
if cvv == 0:
|
|
return 0
|
|
|
|
return self.EINVAL
|
|
|
|
#
|
|
# Prepares lldb symbol load command.
|
|
# Currently supports Mach-O and single-section files.
|
|
#
|
|
def get_sym_cmd(self, filename, orgbase):
|
|
if filename.endswith('.pdb'):
|
|
dll_file = filename.replace('.pdb', '.dll')
|
|
module_cmd = f'target modules add -s {filename} {dll_file}'
|
|
else:
|
|
dll_file = filename
|
|
module_cmd = f'target modules add {filename}'
|
|
map_cmd = f'target modules load -f {dll_file} -s 0x{orgbase:X}'
|
|
return(module_cmd, map_cmd)
|
|
|
|
#
|
|
# Parses an EFI_LOADED_IMAGE_PROTOCOL, figuring out the symbol file name.
|
|
# This file name is then appended to list of loaded symbols.
|
|
#
|
|
# TODO: Support TE images.
|
|
#
|
|
|
|
def parse_image(self, image, syms):
|
|
base = self.get_field(image, 'ImageBase')
|
|
pe = self.pe_headers(base)
|
|
combined = self.pe_combined(pe)
|
|
sym_address = self.pe_parse_debug(base)
|
|
|
|
if sym_address == 0:
|
|
# llvm-objcopy --add-gnu-debuglink=a/x.debug a/x.dll does not update
|
|
# DataDirectory with any data, instead it creates a new section
|
|
# with a /\d+ name containing:
|
|
# - ASCII debug file name (x.debug) padded by 4 bytes
|
|
# - CRC32
|
|
file = self.pe_file(pe)
|
|
sections = self.pe_sections(combined, file, base)
|
|
last_section = next(reversed(sections))
|
|
if re.match(r'^/\d+$', last_section):
|
|
sym_address = sections[last_section]
|
|
sym_ptr = self.typed_ptr(self.ptype('char'), sym_address + base)
|
|
sym_name = UefiMisc.parse_utf8(self.get_field(sym_ptr))
|
|
if not sym_name.endswith('.debug'):
|
|
sym_name = self.EINVAL
|
|
elif sym_address != self.EINVAL:
|
|
sym_ptr = self.typed_ptr(self.ptype('char'), sym_address)
|
|
sym_name = UefiMisc.parse_utf8(self.get_field(sym_ptr))
|
|
else:
|
|
sym_name = self.EINVAL
|
|
|
|
# For ELF and Mach-O-derived images...
|
|
if self.offset_by_headers:
|
|
base = base + self.get_field(combined, 'SizeOfHeaders')
|
|
if sym_name != self.EINVAL:
|
|
self.add_sym(sym_name, base, syms)
|
|
|
|
#
|
|
# Add symbol load command with additional processing for correct file location.
|
|
#
|
|
|
|
def add_sym(self, sym_name, base, syms):
|
|
macho = os.path.isdir(sym_name + '.dSYM')
|
|
if macho:
|
|
real_sym = sym_name
|
|
else:
|
|
sym_name_dbg = re.sub(r'\.dll$', '.debug', sym_name)
|
|
if sym_name_dbg != sym_name and os.path.exists(sym_name_dbg):
|
|
real_sym = sym_name_dbg
|
|
else:
|
|
real_sym = None
|
|
paths = os.getenv('EFI_SYMBOL_PATH', '').split(':')
|
|
for path in paths:
|
|
if os.path.exists(os.path.join(path, sym_name)):
|
|
real_sym = os.path.join(path, sym_name)
|
|
break
|
|
|
|
if real_sym:
|
|
syms.append(self.get_sym_cmd(real_sym, base))
|
|
else:
|
|
print(f'No symbol file {sym_name}')
|
|
|
|
#
|
|
# Use debug info from new image loader.
|
|
#
|
|
|
|
def use_new_debug_info(self, entry, syms):
|
|
sym_address = self.get_child_member_with_name(entry, 'PdbPath')
|
|
if sym_address:
|
|
sym_ptr = self.cast_ptr(self.ptype('char'), sym_address)
|
|
sym_name = UefiMisc.parse_utf8(self.get_field(sym_ptr))
|
|
debug_base = self.get_field(entry, 'DebugBase')
|
|
self.add_sym(sym_name, debug_base, syms)
|
|
else:
|
|
print('No symbol file')
|
|
|
|
#
|
|
# Parses table EFI_DEBUG_IMAGE_INFO structures, builds
|
|
# a list of add-symbol-file commands, and reloads debugger
|
|
# symbols.
|
|
#
|
|
|
|
def parse_edii(self, edii, count):
|
|
index = 0
|
|
syms = []
|
|
while index != count:
|
|
# GetChildAtIndex accesses inner structure fields, so we have to use the fugly way again.
|
|
entry = edii.GetValueForExpressionPath(f'[{index}]')
|
|
image_type = self.get_field(entry, 'ImageInfoType', single_entry=True)
|
|
if image_type == 1:
|
|
entry = self.get_child_member_with_name(entry, 'NormalImage')
|
|
self.parse_image(self.get_child_member_with_name(entry, 'LoadedImageProtocolInstance'), syms)
|
|
elif image_type == 2:
|
|
entry = self.get_child_member_with_name(entry, 'NormalImage2')
|
|
self.use_new_debug_info(entry, syms)
|
|
else:
|
|
print(f'Skipping unknown EFI_DEBUG_IMAGE_INFO (ImageInfoType {image_type})')
|
|
index = index + 1
|
|
print('Loading new symbols...')
|
|
for sym in syms:
|
|
print(sym[0])
|
|
self.debugger.HandleCommand(sym[0])
|
|
print(sym[1])
|
|
self.debugger.HandleCommand(sym[1])
|
|
|
|
#
|
|
# Parses EFI_DEBUG_IMAGE_INFO_TABLE_HEADER, in order to load
|
|
# image symbols.
|
|
#
|
|
|
|
def parse_dh(self, dh):
|
|
dh_t = self.ptype('EFI_DEBUG_IMAGE_INFO_TABLE_HEADER')
|
|
dh = self.cast_ptr(dh_t, dh)
|
|
print(f"DebugImageInfoTable @ 0x{self.get_field(dh, 'EfiDebugImageInfoTable'):x}, 0x{self.get_field(dh, 'TableSize'):x} entries")
|
|
if self.get_field(dh, 'UpdateStatus') & self.DEBUG_IS_UPDATING:
|
|
print('EfiDebugImageInfoTable update in progress, retry later')
|
|
return
|
|
self.parse_edii(self.get_child_member_with_name(dh, 'EfiDebugImageInfoTable'), self.get_field(dh, 'TableSize'))
|
|
|
|
#
|
|
# Parses EFI_SYSTEM_TABLE, in order to load image symbols.
|
|
#
|
|
|
|
def parse_est(self, est):
|
|
est_t = self.ptype('EFI_SYSTEM_TABLE')
|
|
est = self.cast_ptr(est_t, est)
|
|
print(f"Connected to {UefiMisc.parse_utf16(self.get_field(est, 'FirmwareVendor'))}(Rev. 0x{self.get_field(est, 'FirmwareRevision'):x})")
|
|
print(f"ConfigurationTable @ 0x{self.get_field(est, 'ConfigurationTable'):x}, 0x{self.get_field(est, 'NumberOfTableEntries'):x} entries")
|
|
dh = self.search_config(self.get_child_member_with_name(est, 'ConfigurationTable'), self.get_field(est, 'NumberOfTableEntries'), self.DEBUG_GUID)
|
|
if dh == self.EINVAL:
|
|
print('No EFI_DEBUG_IMAGE_INFO_TABLE_HEADER')
|
|
return
|
|
self.parse_dh(dh)
|
|
|
|
#
|
|
# Usage information.
|
|
#
|
|
|
|
def get_short_help(self):
|
|
return 'Usage: reload-uefi [-o] [/path/to/GdbSyms.dll]'
|
|
|
|
def get_long_help(self):
|
|
return 'Usage: reload-uefi [-o] [/path/to/GdbSyms.dll]'
|
|
|
|
def usage(self):
|
|
print(self.get_short_help())
|
|
|
|
#
|
|
# Handler for reload-uefi.
|
|
#
|
|
|
|
def __call__(self, debugger, command, exe_ctx, result):
|
|
self.debugger = debugger
|
|
|
|
category = debugger.GetDefaultCategory()
|
|
format_bool = lldb.SBTypeFormat(lldb.eFormatBoolean)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('BOOLEAN'), format_bool)
|
|
|
|
format_hex = lldb.SBTypeFormat(lldb.eFormatHex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('UINT64'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('INT64'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('UINT32'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('INT32'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('UINT16'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('INT16'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('UINT8'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('INT8'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('UINTN'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('INTN'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('CHAR8'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('CHAR16'), format_hex)
|
|
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('EFI_PHYSICAL_ADDRESS'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('PHYSICAL_ADDRESS'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('EFI_STATUS'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('EFI_TPL'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('EFI_LBA'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('EFI_BOOT_MODE'), format_hex)
|
|
category.AddTypeFormat(lldb.SBTypeNameSpecifier('EFI_FV_FILETYPE'), format_hex)
|
|
|
|
args = shlex.split(command)
|
|
try:
|
|
opts, args = getopt.getopt(args, 'o', ['offset-by-headers'])
|
|
except(getopt.GetoptError) as _:
|
|
self.usage()
|
|
return
|
|
for opt, _ in opts:
|
|
if opt == '-o':
|
|
self.offset_by_headers = True
|
|
|
|
self.typetarget = None
|
|
self.activetarget = None
|
|
|
|
# FIXME: Use ReadCStringFromMemory.
|
|
# FIXME: Support executing code.
|
|
if len(args) >= 1 and args[0] != '':
|
|
gdb.execute('symbol-file')
|
|
gdb.execute(f'symbol-file {args[0]}')
|
|
else:
|
|
for i in range(0, self.debugger.GetNumTargets()):
|
|
target = self.debugger.GetTargetAtIndex(i)
|
|
target_name = str(target)
|
|
print(f"Target {i} is '{target_name}'")
|
|
if target_name.find('GdbSyms') >= 0:
|
|
self.typetarget = target
|
|
elif target_name.find('No executable module.') >= 0:
|
|
self.activetarget = target
|
|
|
|
if not self.typetarget:
|
|
print('Cannot find GdbSyms target!')
|
|
return
|
|
|
|
if not self.activetarget:
|
|
print('Cannot find target with full memory access!')
|
|
return
|
|
|
|
# Force into full memory target.
|
|
self.debugger.SetSelectedTarget(self.activetarget)
|
|
|
|
est = self.search_est()
|
|
if est == self.EINVAL:
|
|
print('No EFI_SYSTEM_TABLE...')
|
|
return
|
|
|
|
print(f'EFI_SYSTEM_TABLE @ 0x{est.GetValueAsUnsigned():x}')
|
|
self.parse_est(est)
|