提交 a1a8bd6c 编写于 作者: M Mike Beaton

Debug: Add support for UE image format

上级 88ada4d9
...@@ -8,6 +8,7 @@ OpenCore Changelog ...@@ -8,6 +8,7 @@ OpenCore Changelog
- Updated `AppleEfiSignTool` to work with new PE COFF loader - Updated `AppleEfiSignTool` to work with new PE COFF loader
- Fixed recovery failing to boot on some systems - Fixed recovery failing to boot on some systems
- Updated `ProvideCurrentCpuInfo` quirk to support CPUID leaf 0x2 cache size reporting on Mac OS X 10.5 and 10.6 - Updated `ProvideCurrentCpuInfo` quirk to support CPUID leaf 0x2 cache size reporting on Mac OS X 10.5 and 10.6
- Updated `efidebug.tool` to support new standard image format
#### v0.9.6 #### v0.9.6
- Updated builtin firmware versions for SMBIOS and the rest - Updated builtin firmware versions for SMBIOS and the rest
......
...@@ -193,41 +193,19 @@ class ReloadUefi(gdb.Command): ...@@ -193,41 +193,19 @@ class ReloadUefi(gdb.Command):
h_addr = h_addr + int(dosh['e_lfanew']) h_addr = h_addr + int(dosh['e_lfanew'])
return gdb.Value(h_addr).cast(head_t) return gdb.Value(h_addr).cast(head_t)
#
# Returns a dictionary with PE sections.
#
def pe_sections(self, opt, file, _):
sect_t = self.ptype('EFI_IMAGE_SECTION_HEADER')
sections = (opt.address + 1).cast(sect_t)
sects = {}
for i in range(file['NumberOfSections']):
name = UefiMisc.parse_utf8(sections[i]['Name'])
addr = int(sections[i]['VirtualAddress'])
if name != '':
sects[name] = addr
return sects
# #
# Returns True if pe_headers refer to a PE32+ image. # Returns True if pe_headers refer to a PE32+ image.
# #
def pe_is_64(self, pe_headers): def pe_is_64(self, pe_headers):
return pe_headers['Pe32']['OptionalHeader']['Magic'] == self.PE32PLUS_MAGIC return pe_headers['Pe32']['Magic'] == self.PE32PLUS_MAGIC
#
# Returns the PE fileheader.
#
def pe_file(self, pe):
return pe['Pe32Plus']['FileHeader'] if self.pe_is_64(pe) else pe['Pe32']['FileHeader']
# #
# Returns the PE(not so) optional header. # Returns the PE combined common and (not so) optional header.
# #
def pe_optional(self, pe): def pe_combined(self, pe):
return pe['Pe32Plus']['OptionalHeader'] if self.pe_is_64(pe) else pe['Pe32']['OptionalHeader'] return pe['Pe32Plus'] if self.pe_is_64(pe) else pe['Pe32']
# #
# Returns the symbol file name for a PE image. # Returns the symbol file name for a PE image.
...@@ -235,8 +213,8 @@ class ReloadUefi(gdb.Command): ...@@ -235,8 +213,8 @@ class ReloadUefi(gdb.Command):
def pe_parse_debug(self, base): def pe_parse_debug(self, base):
pe = self.pe_headers(base) pe = self.pe_headers(base)
opt = self.pe_optional(pe) combined = self.pe_combined(pe)
debug_dir_entry = opt['DataDirectory'][6] debug_dir_entry = combined['DataDirectory'][6]
dep = debug_dir_entry['VirtualAddress'] + int(base) dep = debug_dir_entry['VirtualAddress'] + int(base)
dep = dep.cast(self.ptype('EFI_IMAGE_DEBUG_DIRECTORY_ENTRY')) dep = dep.cast(self.ptype('EFI_IMAGE_DEBUG_DIRECTORY_ENTRY'))
cvp = dep.dereference()['RVA'] + int(base) cvp = dep.dereference()['RVA'] + int(base)
...@@ -250,9 +228,9 @@ class ReloadUefi(gdb.Command): ...@@ -250,9 +228,9 @@ class ReloadUefi(gdb.Command):
return gdb.Value(self.EINVAL) return gdb.Value(self.EINVAL)
# #
# Prepares gdb symbol load command with proper section information. # Prepares gdb symbol load command.
# #
def get_sym_cmd(self, file, base, sections, macho): def get_sym_cmd(self, file, base):
return f'add-symbol-file {file} -o 0x{base:x}' return f'add-symbol-file {file} -o 0x{base:x}'
# #
...@@ -265,25 +243,42 @@ class ReloadUefi(gdb.Command): ...@@ -265,25 +243,42 @@ class ReloadUefi(gdb.Command):
def parse_image(self, image, syms): def parse_image(self, image, syms):
base = image['ImageBase'] base = image['ImageBase']
pe = self.pe_headers(base) pe = self.pe_headers(base)
opt = self.pe_optional(pe) combined = self.pe_combined(pe)
file = self.pe_file(pe)
sym_name = self.pe_parse_debug(base) sym_name = self.pe_parse_debug(base)
sections = self.pe_sections(opt, file, base)
# For ELF and Mach-O-derived images... # For ELF and Mach-O-derived images...
if self.offset_by_headers: if self.offset_by_headers:
base = base + opt['SizeOfHeaders'] base = base + combined['SizeOfHeaders']
if sym_name != self.EINVAL: if sym_name != self.EINVAL:
sym_name = sym_name.cast(self.ptype('CHAR8')).string() self.add_sym(sym_name, base, syms)
sym_name_dbg = re.sub(r'\.dll$', '.debug', sym_name)
macho = False #
if os.path.isdir(sym_name + '.dSYM'): # Add symbol load command with additional processing for correct file location.
sym_name += '.dSYM/Contents/Resources/DWARF/' + os.path.basename(sym_name) #
macho = True
elif sym_name_dbg != sym_name and os.path.exists(sym_name_dbg): def add_sym(self, sym_name, base, syms):
# TODO: implement .elf handling. sym_name = sym_name.cast(self.ptype('CHAR8')).string()
sym_name = sym_name_dbg sym_name_dbg = re.sub(r'\.dll$', '.debug', sym_name)
syms.append(self.get_sym_cmd(sym_name, int(base), sections, macho)) # macho = False
if os.path.isdir(sym_name + '.dSYM'):
sym_name += '.dSYM/Contents/Resources/DWARF/' + os.path.basename(sym_name)
# macho = True
elif sym_name_dbg != sym_name and os.path.exists(sym_name_dbg):
# TODO: implement .elf handling.
sym_name = sym_name_dbg
syms.append(self.get_sym_cmd(sym_name, int(base)))
#
# Use debug info from new image loader.
#
def use_new_debug_info(self, entry, syms):
pdb_path = entry['PdbPath']
if pdb_path:
debug_base = entry['DebugBase']
self.add_sym(pdb_path, debug_base, syms)
else:
print('No symbol file')
# #
# Parses table EFI_DEBUG_IMAGE_INFO structures, builds # Parses table EFI_DEBUG_IMAGE_INFO structures, builds
...@@ -297,9 +292,13 @@ class ReloadUefi(gdb.Command): ...@@ -297,9 +292,13 @@ class ReloadUefi(gdb.Command):
print(f'Found {count} images...') print(f'Found {count} images...')
while index != count: while index != count:
entry = edii[index] entry = edii[index]
if entry['ImageInfoType'].dereference() == 1: image_type = entry['ImageInfoType'].dereference()
if image_type == 1:
entry = entry['NormalImage'] entry = entry['NormalImage']
self.parse_image(entry['LoadedImageProtocolInstance'], syms) self.parse_image(entry['LoadedImageProtocolInstance'], syms)
elif image_type == 2:
entry = entry['NormalImage2']
self.use_new_debug_info(entry, syms)
else: else:
print(f"Skipping unknown EFI_DEBUG_IMAGE_INFO(Type {str(entry['ImageInfoType'].dereference())})") print(f"Skipping unknown EFI_DEBUG_IMAGE_INFO(Type {str(entry['ImageInfoType'].dereference())})")
index += 1 index += 1
......
...@@ -234,9 +234,10 @@ class ReloadUefi: ...@@ -234,9 +234,10 @@ class ReloadUefi:
# Returns a dictionary with PE sections. # Returns a dictionary with PE sections.
# #
def pe_sections(self, opt, file, _): def pe_sections(self, combined, file, _):
sect_t = self.ptype('EFI_IMAGE_SECTION_HEADER') sect_t = self.ptype('EFI_IMAGE_SECTION_HEADER')
sections_addr = opt.GetLoadAddress() + opt.GetByteSize() 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) sections = self.typed_ptr(sect_t, sections_addr)
sects = OrderedDict() sects = OrderedDict()
for i in range(self.get_field(file, 'NumberOfSections')): for i in range(self.get_field(file, 'NumberOfSections')):
...@@ -253,7 +254,7 @@ class ReloadUefi: ...@@ -253,7 +254,7 @@ class ReloadUefi:
# #
def pe_is_64(self, pe_headers): def pe_is_64(self, pe_headers):
magic = pe_headers.GetValueForExpressionPath('.Pe32.OptionalHeader.Magic').GetValueAsUnsigned() magic = pe_headers.GetValueForExpressionPath('.Pe32.Magic').GetValueAsUnsigned()
return magic == self.PE32PLUS_MAGIC return magic == self.PE32PLUS_MAGIC
# #
...@@ -271,12 +272,11 @@ class ReloadUefi: ...@@ -271,12 +272,11 @@ class ReloadUefi:
# Returns the PE (not so) optional header. # Returns the PE (not so) optional header.
# #
def pe_optional(self, pe): def pe_combined(self, pe):
if self.pe_is_64(pe): if self.pe_is_64(pe):
obj = self.get_child_member_with_name(pe, 'Pe32Plus') return self.get_child_member_with_name(pe, 'Pe32Plus')
else: else:
obj = self.get_child_member_with_name(pe, 'Pe32') return self.get_child_member_with_name(pe, 'Pe32')
return self.get_child_member_with_name(obj, 'OptionalHeader')
# #
# Returns the symbol file name for a PE image. # Returns the symbol file name for a PE image.
...@@ -284,8 +284,8 @@ class ReloadUefi: ...@@ -284,8 +284,8 @@ class ReloadUefi:
def pe_parse_debug(self, base): def pe_parse_debug(self, base):
pe = self.pe_headers(base) pe = self.pe_headers(base)
opt = self.pe_optional(pe) combined = self.pe_combined(pe)
debug_dir_entry = opt.GetValueForExpressionPath('.DataDirectory[6]') debug_dir_entry = combined.GetValueForExpressionPath('.DataDirectory[6]')
dep = self.get_field(debug_dir_entry, 'VirtualAddress') + base dep = self.get_field(debug_dir_entry, 'VirtualAddress') + base
dep = self.typed_ptr(self.ptype('EFI_IMAGE_DEBUG_DIRECTORY_ENTRY'), dep) dep = self.typed_ptr(self.ptype('EFI_IMAGE_DEBUG_DIRECTORY_ENTRY'), dep)
cvp = self.get_field(dep, 'RVA') + base cvp = self.get_field(dep, 'RVA') + base
...@@ -303,10 +303,10 @@ class ReloadUefi: ...@@ -303,10 +303,10 @@ class ReloadUefi:
return self.EINVAL return self.EINVAL
# #
# Prepares symbol load command with proper section information. # Prepares lldb symbol load command.
# Currently supports Mach-O and single-section files. # Currently supports Mach-O and single-section files.
# #
def get_sym_cmd(self, filename, orgbase, *_): def get_sym_cmd(self, filename, orgbase):
if filename.endswith('.pdb'): if filename.endswith('.pdb'):
dll_file = filename.replace('.pdb', '.dll') dll_file = filename.replace('.pdb', '.dll')
module_cmd = f'target modules add -s {filename} {dll_file}' module_cmd = f'target modules add -s {filename} {dll_file}'
...@@ -324,12 +324,10 @@ class ReloadUefi: ...@@ -324,12 +324,10 @@ class ReloadUefi:
# #
def parse_image(self, image, syms): def parse_image(self, image, syms):
orgbase = base = self.get_field(image, 'ImageBase') base = self.get_field(image, 'ImageBase')
pe = self.pe_headers(base) pe = self.pe_headers(base)
opt = self.pe_optional(pe) combined = self.pe_combined(pe)
file = self.pe_file(pe)
sym_address = self.pe_parse_debug(base) sym_address = self.pe_parse_debug(base)
sections = self.pe_sections(opt, file, base)
if sym_address == 0: if sym_address == 0:
# llvm-objcopy --add-gnu-debuglink=a/x.debug a/x.dll does not update # llvm-objcopy --add-gnu-debuglink=a/x.debug a/x.dll does not update
...@@ -337,6 +335,8 @@ class ReloadUefi: ...@@ -337,6 +335,8 @@ class ReloadUefi:
# with a /\d+ name containing: # with a /\d+ name containing:
# - ASCII debug file name (x.debug) padded by 4 bytes # - ASCII debug file name (x.debug) padded by 4 bytes
# - CRC32 # - CRC32
file = self.pe_file(pe)
sections = self.pe_sections(combined, file, base)
last_section = next(reversed(sections)) last_section = next(reversed(sections))
if re.match(r'^/\d+$', last_section): if re.match(r'^/\d+$', last_section):
sym_address = sections[last_section] sym_address = sections[last_section]
...@@ -352,8 +352,15 @@ class ReloadUefi: ...@@ -352,8 +352,15 @@ class ReloadUefi:
# For ELF and Mach-O-derived images... # For ELF and Mach-O-derived images...
if self.offset_by_headers: if self.offset_by_headers:
base = base + self.get_field(opt, 'SizeOfHeaders') base = base + self.get_field(combined, 'SizeOfHeaders')
if sym_name != self.EINVAL: 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') macho = os.path.isdir(sym_name + '.dSYM')
if macho: if macho:
real_sym = sym_name real_sym = sym_name
...@@ -370,10 +377,24 @@ class ReloadUefi: ...@@ -370,10 +377,24 @@ class ReloadUefi:
break break
if real_sym: if real_sym:
syms.append(self.get_sym_cmd(real_sym, orgbase, sections, macho, base)) syms.append(self.get_sym_cmd(real_sym, base))
else: else:
print(f'No symbol file {sym_name}') 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 # Parses table EFI_DEBUG_IMAGE_INFO structures, builds
# a list of add-symbol-file commands, and reloads debugger # a list of add-symbol-file commands, and reloads debugger
...@@ -390,6 +411,9 @@ class ReloadUefi: ...@@ -390,6 +411,9 @@ class ReloadUefi:
if image_type == 1: if image_type == 1:
entry = self.get_child_member_with_name(entry, 'NormalImage') entry = self.get_child_member_with_name(entry, 'NormalImage')
self.parse_image(self.get_child_member_with_name(entry, 'LoadedImageProtocolInstance'), syms) 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: else:
print(f'Skipping unknown EFI_DEBUG_IMAGE_INFO (ImageInfoType {image_type})') print(f'Skipping unknown EFI_DEBUG_IMAGE_INFO (ImageInfoType {image_type})')
index = index + 1 index = index + 1
...@@ -399,6 +423,7 @@ class ReloadUefi: ...@@ -399,6 +423,7 @@ class ReloadUefi:
self.debugger.HandleCommand(sym[0]) self.debugger.HandleCommand(sym[0])
print(sym[1]) print(sym[1])
self.debugger.HandleCommand(sym[1]) self.debugger.HandleCommand(sym[1])
# #
# Parses EFI_DEBUG_IMAGE_INFO_TABLE_HEADER, in order to load # Parses EFI_DEBUG_IMAGE_INFO_TABLE_HEADER, in order to load
# image symbols. # image symbols.
...@@ -420,7 +445,7 @@ class ReloadUefi: ...@@ -420,7 +445,7 @@ class ReloadUefi:
def parse_est(self, est): def parse_est(self, est):
est_t = self.ptype('EFI_SYSTEM_TABLE') est_t = self.ptype('EFI_SYSTEM_TABLE')
est = self.cast_ptr(est_t, est) 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"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") 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) dh = self.search_config(self.get_child_member_with_name(est, 'ConfigurationTable'), self.get_field(est, 'NumberOfTableEntries'), self.DEBUG_GUID)
if dh == self.EINVAL: if dh == self.EINVAL:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册