diff --git a/Debug/GdbSyms/Bin/X64_XCODE5/GdbSyms.dll b/Debug/GdbSyms/Bin/X64_XCODE5/GdbSyms.dll new file mode 100644 index 00000000..b1e49b6c Binary files /dev/null and b/Debug/GdbSyms/Bin/X64_XCODE5/GdbSyms.dll differ diff --git a/Debug/GdbSyms/GdbSyms.c b/Debug/GdbSyms/GdbSyms.c new file mode 100644 index 00000000..2e23ed94 --- /dev/null +++ b/Debug/GdbSyms/GdbSyms.c @@ -0,0 +1,79 @@ +/** @file + + Bare-minimum GDB symbols needed for reloading symbols. + + This is not a "driver" and should not be placed in a FD. + + Copyright (c) 2011, Andrei Warkentin + + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PiDxe.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Main entry point. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS Successfully initialized. + +**/ +EFI_STATUS +EFIAPI +Initialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_SYSTEM_TABLE_POINTER ESTP; + EFI_DEBUG_IMAGE_INFO_TABLE_HEADER EDIITH; + EFI_IMAGE_DOS_HEADER EIDH; + EFI_IMAGE_OPTIONAL_HEADER_UNION EIOHU; + EFI_IMAGE_SECTION_HEADER EISH; + EFI_IMAGE_DEBUG_DIRECTORY_ENTRY EIDDE; + EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY EIDCNE; + EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY EIDCRE; + EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY EIDCME; + UINTN Dummy = + (UINTN) &ESTP | + (UINTN) &EDIITH | + (UINTN) &EIDH | + (UINTN) &EIOHU | + (UINTN) &EISH | + (UINTN) &EIDDE | + (UINTN) &EIDCNE | + (UINTN) &EIDCRE | + (UINTN) &EIDCME | + 1 + ; + DEBUG ((DEBUG_VERBOSE, "%a: %llx\n", __FUNCTION__, &ESTP)); + DEBUG ((DEBUG_VERBOSE, "%a: %llx\n", __FUNCTION__, &EDIITH)); + DEBUG ((DEBUG_VERBOSE, "%a: %llx\n", __FUNCTION__, &EIDH)); + DEBUG ((DEBUG_VERBOSE, "%a: %llx\n", __FUNCTION__, &EIOHU)); + DEBUG ((DEBUG_VERBOSE, "%a: %llx\n", __FUNCTION__, &EISH)); + DEBUG ((DEBUG_VERBOSE, "%a: %llx\n", __FUNCTION__, &EIDDE)); + DEBUG ((DEBUG_VERBOSE, "%a: %llx\n", __FUNCTION__, &EIDCNE)); + DEBUG ((DEBUG_VERBOSE, "%a: %llx\n", __FUNCTION__, &EIDCRE)); + DEBUG ((DEBUG_VERBOSE, "%a: %llx\n", __FUNCTION__, &EIDCME)); + return !!Dummy & EFI_SUCCESS; +} diff --git a/Debug/GdbSyms/GdbSyms.inf b/Debug/GdbSyms/GdbSyms.inf new file mode 100644 index 00000000..89dfd395 --- /dev/null +++ b/Debug/GdbSyms/GdbSyms.inf @@ -0,0 +1,57 @@ +## @file +# +# Bare-minimum GDB symbols needed for reloading symbols. +# +# This is not a "driver" and should not be placed in a FD. +# +# Copyright (c) 2011, Andrei Warkentin +# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = GdbSyms + FILE_GUID = 22abcb60-fb40-42ac-b01f-3ab1fad9aad8 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = Initialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC ARM +# + +[Sources] + GdbSyms.c + +[Packages] + OcSupportPkg/OcSupportPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + DxeServicesTableLib + HobLib + MemoryAllocationLib + PcdLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + +[Guids] + +[Protocols] + +[Depex] + TRUE diff --git a/Debug/README.md b/Debug/README.md new file mode 100644 index 00000000..d898172b --- /dev/null +++ b/Debug/README.md @@ -0,0 +1,88 @@ +UEFI Debugging with GDB +======================= + +These scripts provide support for easier UEFI code debugging on virtual machines like VMware Fusion +or QEMU. The code is based on [Andrei Warkentin](https://github.com/andreiw)'s +[DebugPkg](https://github.com/andreiw/andreiw-wip/tree/master/uefi/DebugPkg) with improvements +in macOS support, pretty printing, and bug fixing. + +The general approach is as follows: + +1. Build GdbSyms binary with EDK II type info in DWARF +1. Locate `EFI_SYSTEM_TABLE` in memory by its magic +1. Locate `EFI_DEBUG_IMAGE_INFO_TABLE` by its GUID +1. Map relocated images within GDB +1. Provide handy functions and pretty printers + +#### Preparing Source Code + +By default EDK II optimises produced binaries, so to build a "real" debug binary one should target +`NOOPT`. Do be aware that it strongly affects resulting binary size. + +`GdbSyms.dll` is built as a part of OcSupportPkg, yet prebuilt binaries are also available: + +- `GdbSyms/Bin/X64_XCODE5/GdbSyms.dll` is built with UDK2018 and XCODE5 + +To wait for debugger connection on startup `WaitForKeyPress` functions from `OcMiscLib.h` can be +utilised. Do be aware that this function additionally calls `DebugBreak` function, which may +be broken at during GDB init. + +#### VMware Configuration + +VMware Fusion contains a dedicated debugStub, which can be enabled by adding the following +lines to .vmx file. Afterwards vmware-vmx will listen on TCP ports 8832 and 8864 (on the host) +for 32-bit and 64-bit gdb connections respectively, similarly to QEMU: +``` +debugStub.listen.guest32 = "TRUE" +debugStub.listen.guest64 = "TRUE" +``` + +In case the debugging session is remote the following lines should be appended: +``` +debugStub.listen.guest32.remote = "TRUE" +debugStub.listen.guest64.remote = "TRUE" +``` + +To halt the virtual machine upon executing the first instruction the following line code be added. +Note, that it does not seem to work on VMware Fusion 11 and results in freezes: +``` +monitor.debugOnStartGuest32 = "TRUE" +``` + +To force hardware breakpoints (instead of software INT 3 breakpoints) add the following line: +``` +debugStub.hideBreakpoints = "TRUE" +``` + +To stall during POST for 3 seconds add the following line. Pressing any key will boot into firmware +settings: +``` +bios.bootDelay = "3000" +``` + +#### GDB Configuration + +It is a good idea to use GDB Multiarch in case different debugging architectures are planned to be +used. This can be done in several ways: + +- https://www.gnu.org/software/gdb/ — from source +- https://macports.org/ — via MacPorts (`sudo port install gdb +multiarch`) +- Your preferred method here + +Once GDB is installed the process is as simple as running the following set of commands: + +``` +$ ggdb /opt/UDK/Build/OcSupportPkg/NOOPT_XCODE5/X64/OcSupportPkg/Debug/GdbSyms/GdbSyms/DEBUG/GdbSyms.dll.dSYM/Contents/Resources/DWARF/GdbSyms.dll + +target remote localhost:8864 +source /opt/UDK/OcSupportPkg/Debug/Scripts/gdb_uefi.py +set pagination off +reload-uefi +b DebugBreak +``` + +#### References + +1. https://communities.vmware.com/thread/390128 +1. https://wiki.osdev.org/VMware +1. https://github.com/andreiw/andreiw-wip/tree/master/uefi/DebugPkg diff --git a/Debug/Scripts/gdb_uefi.py b/Debug/Scripts/gdb_uefi.py new file mode 100644 index 00000000..4e935b55 --- /dev/null +++ b/Debug/Scripts/gdb_uefi.py @@ -0,0 +1,671 @@ +""" +Allows loading TianoCore symbols into a GDB session attached to EFI +Firmware. + +This is how it works: build GdbSyms - it's a dummy binary that +contains the relevant symbols needed to find and load image symbols. + +$ gdb /path/to/GdbSyms.dll +(gdb) target remote .... +(gdb) source Scripts/gdb_uefi.py +(gdb) reload-uefi -o /path/to/GdbSyms.dll + +N.B: it was noticed that GDB for certain targets behaves strangely +when run without any binary - like assuming a certain physical +address space size and endianness. To avoid this madness and +seing strange bugs, make sure to pass /path/to/GdbSyms.dll +when starting gdb. + +The -o option should be used if you've debugging EFI, where the PE +images were converted from MACH-O or ELF binaries. + +""" + +import array +import getopt +import binascii +import re +import sys +import os +import subprocess + +__license__ = "BSD" +__version = "1.0.0" +__maintainer__ = "Andrei Warkentin" +__email__ = "andrey.warkentin@gmail.com" +__status__ = "Works" + +if sys.version_info > (3,): + long = int + +class UefiMisc(): + # + # Returns string corresponding to type value in specified charset. + # + @classmethod + def parse_string (cls, value, type, charset): + index = 0 + data = array.array (type) + while value[index] != 0: + # TODO: add more ASCII symbols? + v = value[index] + if v == 0x0A: # \n + data.append(0x5C) + data.append(0x6E) + elif v == 0x0D: # \r + data.append(0x5C) + data.append(0x72) + elif v == 0x09: # \t + data.append(0x5C) + data.append(0x74) + elif v == 0x22: # " + data.append(0x5C) + data.append(0x22) + elif v == 0x5C: # \ + data.append(0x5C) + data.append(0x5C) + else: + data.append (v) + index = index + 1 + return data.tostring ().decode (charset) + + # + # Returns a UTF16 string corresponding to a (CHAR16 *) value in EFI. + # + @classmethod + def parse_utf16 (cls, value): + return UefiMisc.parse_string (value, 'H', 'utf-16') + + # + # Returns a UTF8 string corresponding to a (CHAR8 *) value in EFI. + # + @classmethod + def parse_utf8 (cls, value): + return UefiMisc.parse_string (value, 'B', 'utf-8') + + # + # Returns a printable EFI or RETURN status. + # + @classmethod + def parse_status (cls, value, efi): + suffix = '' + err = 0 + val = long(value) + if val & 0x80000000: + err = val & ~0x80000000 + elif val & 0x8000000000000000: + err = val & ~0x8000000000000000 + + if err != 0: + # TODO: make this a collection... + if err == 1: + suffix = 'LOAD_ERROR' + elif err == 2: + suffix = 'INVALID_PARAMETER' + elif err == 3: + suffix = 'UNSUPPORTED' + elif err == 4: + suffix = 'BAD_BUFFER_SIZE' + elif err == 5: + suffix = 'BUFFER_TOO_SMALL' + elif err == 6: + suffix = 'NOT_READY' + elif err == 7: + suffix = 'DEVICE_ERROR' + elif err == 8: + suffix = 'WRITE_PROTECTED' + elif err == 9: + suffix = 'OUT_OF_RESOURCES' + elif err == 10: + suffix = 'VOLUME_CORRUPTED' + elif err == 11: + suffix = 'VOLUME_FULL' + elif err == 12: + suffix = 'NO_MEDIA' + elif err == 13: + suffix = 'MEDIA_CHANGED' + elif err == 14: + suffix = 'NOT_FOUND' + elif err == 15: + suffix = 'ACCESS_DENIED' + elif err == 16: + suffix = 'NO_RESPONSE' + elif err == 17: + suffix = 'NO_MAPPING' + elif err == 18: + suffix = 'TIMEOUT' + elif err == 19: + suffix = 'NOT_STARTED' + elif err == 20: + suffix = 'ALREADY_STARTED' + elif err == 21: + suffix = 'ABORTED' + elif err == 22: + suffix = 'ICMP_ERROR' + elif err == 23: + suffix = 'TFTP_ERROR' + elif err == 24: + suffix = 'PROTOCOL_ERROR' + elif err == 25: + suffix = 'INCOMPATIBLE_VERSION' + elif err == 26: + suffix = 'SECURITY_VIOLATION' + elif err == 27: + suffix = 'CRC_ERROR' + elif err == 28: + suffix = 'END_OF_MEDIA' + elif err == 31: + suffix = 'END_OF_FILE' + elif err == 32: + suffix = 'INVALID_LANGUAGE' + elif err == 33: + suffix = 'COMPROMISED_DATA' + elif err == 35: + suffix = 'HTTP_ERROR' + elif efi and err == 100: + suffix = 'NETWORK_UNREACHABLE' + elif efi and err == 101: + suffix = 'HOST_UNREACHABLE' + elif efi and err == 102: + suffix = 'PROTOCOL_UNREACHABLE' + elif efi and err == 103: + suffix = 'PORT_UNREACHABLE' + elif efi and err == 104: + suffix = 'CONNECTION_FIN' + elif efi and err == 105: + suffix = 'CONNECTION_RESET' + elif efi and err == 106: + suffix = 'CONNECTION_REFUSED' + else: + if val == 0: + suffix = 'SUCCESS' + elif val == 1: + suffix = 'WARN_UNKNOWN_GLYPH' + elif val == 2: + suffix = 'WARN_DELETE_FAILURE' + elif val == 3: + suffix = 'WARN_WRITE_FAILURE' + elif val == 4: + suffix = 'WARN_BUFFER_TOO_SMALL' + elif val == 5: + suffix = 'WARN_STALE_DATA' + elif val == 6: + suffix = 'WARN_FILE_SYSTEM' + if suffix != '': + return ('EFI_' if efi else 'RETURN_') + suffix + return hex(val) + + # + # Returns a UTF16 string corresponding to a (CHAR16 *) value in EFI. + # + @classmethod + def parse_guid (cls, value): + guid = "<%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X>" % ( + value['Data1'], + value['Data2'], + value['Data3'], + value['Data4'][0], + value['Data4'][1], + value['Data4'][2], + value['Data4'][3], + value['Data4'][4], + value['Data4'][5], + value['Data4'][6], + value['Data4'][7]) + return guid + +class ReloadUefi (gdb.Command): + """Reload UEFI symbols""" + + # + # Various constants. + # + + EINVAL = 0xffffffff + CV_NB10 = 0x3031424E + CV_RSDS = 0x53445352 + CV_MTOC = 0x434F544D + DOS_MAGIC = 0x5A4D + PE32PLUS_MAGIC = 0x20b + EST_SIGNATURE = 0x5453595320494249L + 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): + super (ReloadUefi, self).__init__ ("reload-uefi", gdb.COMMAND_OBSCURE) + + # + # Returns gdb.Type for a type. + # + + def type (self, typename): + return gdb.lookup_type (typename) + + # + # Returns gdb.Type for a pointer to a type. + # + + def ptype (self, typename): + return gdb.lookup_type (typename).pointer () + + # + # Computes CRC32 on an array of data. + # + + def crc32 (self, data): + return binascii.crc32 (data) & 0xFFFFFFFF + + # + # Sets a field in a struct to a value, i.e. + # value->field_name = data. + # + # Newer Py bindings to Gdb provide access to the inferior + # memory, but not all, so have to do it this awkward way. + # + + def set_field (self, value, field_name, data): + gdb.execute ("set *(%s *) 0x%x = 0x%x" % \ + (str (value[field_name].type), \ + long (value[field_name].address), data)) + + # + # Returns data backing a gdb.Value as an array. + # Same comment as above regarding newer Py bindings... + # + + def value_data (self, value, bytes=0): + value_address = gdb.Value (value.address) + array_t = self.ptype ('UINT8') + value_array = value_address.cast (array_t) + if bytes == 0: + bytes = value.type.sizeof + data = array.array ('B') + for i in range (0, bytes): + data.append (value_array[i]) + return data + + # + # 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: + try: + estp = gdb.Value(address).cast(estp_t) + if estp['Signature'] == self.EST_SIGNATURE: + oldcrc = long(estp['Crc32']) + self.set_field (estp, 'Crc32', 0) + newcrc = self.crc32 (self.value_data (estp.dereference (), 0)) + self.set_field (estp, 'Crc32', long(oldcrc)) + if newcrc == oldcrc: + return estp['EfiSystemTableBase'] + except gdb.MemoryError: + pass + + address = address + 4*1024*1024 + if long(address) == 0: + return gdb.Value(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: + cfg_entry = cfg_table[index]['VendorGuid'] + if cfg_entry['Data1'] == guid[0] and \ + cfg_entry['Data2'] == guid[1] and \ + cfg_entry['Data3'] == guid[2] and \ + self.value_data (cfg_entry['Data4']).tolist () == guid[3]: + return cfg_table[index]['VendorTable'] + index = index + 1 + return gdb.Value(self.EINVAL) + + # + # Returns offset of a field within structure. Useful + # for getting container of a structure. + # + + def offsetof (self, typename, field): + t = gdb.Value (0).cast (self.ptype (typename)) + return long(t[field].address) + + # + # Returns sizeof of a type. + # + + def sizeof (self, typename): + return self.type (typename).sizeof + + # + # Returns the EFI_IMAGE_NT_HEADERS32 pointer, given + # an ImageBase address as a gdb.Value. + # + + def pe_headers (self, imagebase): + dosh_t = self.ptype ('EFI_IMAGE_DOS_HEADER') + head_t = self.ptype ('EFI_IMAGE_OPTIONAL_HEADER_UNION') + dosh = imagebase.cast (dosh_t) + h_addr = imagebase + if dosh['e_magic'] == self.DOS_MAGIC: + h_addr = h_addr + dosh['e_lfanew'] + return gdb.Value(h_addr).cast (head_t) + + def pe_sections (self, opt, file, imagebase): + sect_t = self.ptype ('EFI_IMAGE_SECTION_HEADER') + sections = (opt.address + 1).cast (sect_t) + sects = {} + for i in xrange (file['NumberOfSections']): + name = UefiMisc.parse_utf8 (sections[i]['Name']) + addr = long(sections[i]['VirtualAddress']) + if name != '': + sects[name] = addr + return sects + + # TODO: implement pe sections + + # + # Returns True if pe_headers refer to a PE32+ image. + # + + def pe_is_64 (self, pe_headers): + if pe_headers['Pe32']['OptionalHeader']['Magic'] == self.PE32PLUS_MAGIC: + return True + return False + + # + # Returns the PE fileheader. + # + + def pe_file (self, pe): + if self.pe_is_64 (pe): + return pe['Pe32Plus']['FileHeader'] + else: + return pe['Pe32']['FileHeader'] + + # + # Returns the PE (not so) optional header. + # + + def pe_optional (self, pe): + if self.pe_is_64 (pe): + return pe['Pe32Plus']['OptionalHeader'] + else: + return pe['Pe32']['OptionalHeader'] + + # + # Returns the symbol file name for a PE image. + # + + def pe_parse_debug (self, pe): + opt = self.pe_optional (pe) + debug_dir_entry = opt['DataDirectory'][6] + dep = debug_dir_entry['VirtualAddress'] + opt['ImageBase'] + dep = dep.cast (self.ptype ('EFI_IMAGE_DEBUG_DIRECTORY_ENTRY')) + cvp = dep.dereference ()['RVA'] + opt['ImageBase'] + cvv = cvp.cast(self.ptype ('UINT32')).dereference () + if cvv == self.CV_NB10: + return cvp + self.sizeof('EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY') + elif cvv == self.CV_RSDS: + return cvp + self.sizeof('EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY') + elif cvv == self.CV_MTOC: + return cvp + self.sizeof('EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY') + return gdb.Value(self.EINVAL) + + # + # Prepares gdb symbol load command with proper section information. + # Currently supports Mach-O and single-section files. + # + # TODO: Proper ELF support. + # + def get_sym_cmd (self, file, orgbase, sections, macho, fallack_base): + cmd = 'add-symbol-file %s' % file + + # Fallback case, no sections, just load .text. + if not sections.get('.text') or not sections.get('.data'): + cmd += ' 0x%x' % (fallack_base) + return cmd + + cmd += ' 0x%x' % (long(orgbase) + sections['.text']) + + if not macho or not os.path.exists(file): + # Another fallback, try to load data at least. + cmd += ' -s .data 0x%x' % (long(orgbase) + sections['.data']) + return cmd + + # 1. Parse Mach-O. + # FIXME: We should not rely on otool really. + commands = subprocess.check_output(['otool', '-l', file]) + in_sect = False + machsections = {} + for line in commands.split('\n'): + line = line.strip() + if line.startswith('Section'): + in_sect = True + sectname = None + segname = None + elif in_sect: + if line.startswith('sectname'): + sectname = line.split()[1] + elif line.startswith('segname'): + segname = line.split()[1] + elif line.startswith('addr'): + machsections[segname + '.' + sectname] = long(line.split()[1], base=16) + in_sect = False + + # 2. Convert section names to gdb sections. + mapping = { + '__TEXT.__cstring': '.cstring', + '__TEXT.__const': '.const', + '__TEXT.__ustring': '__TEXT.__ustring', + '__DATA.__const': '.const_data', + '__DATA.__data': '.data', + '__DATA.__bss': '.bss', + '__DATA.__common': '__DATA.__common', + # FIXME: These should not be loadable, but gdb still loads them :/ + # '__DWARF.__apple_names': '__DWARF.__apple_names', + # '__DWARF.__apple_namespac': '__DWARF.__apple_namespac', + # '__DWARF.__apple_types': '__DWARF.__apple_types', + # '__DWARF.__apple_objc': '__DWARF.__apple_objc', + } + + # 3. Rebase. + for entry in mapping: + if machsections.get(entry): + cmd += ' -s %s 0x%x' % (mapping[entry], long(orgbase) + machsections[entry]) + + return 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): + orgbase = base = image['ImageBase'] + pe = self.pe_headers (base) + opt = self.pe_optional (pe) + file = self.pe_file (pe) + sym_name = self.pe_parse_debug (pe) + sections = self.pe_sections (opt, file, base) + + # For ELF and Mach-O-derived images... + if self.offset_by_headers: + base = base + opt['SizeOfHeaders'] + if sym_name != self.EINVAL: + sym_name = sym_name.cast (self.ptype('CHAR8')).string () + sym_name_dbg = re.sub(r"\.dll$", ".debug", sym_name) + 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, long(orgbase), sections, macho, long(base))) + + # + # 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: + entry = edii[index] + if entry['ImageInfoType'].dereference () == 1: + entry = entry['NormalImage'] + self.parse_image(entry['LoadedImageProtocolInstance'], syms) + else: + print ("Skipping unknown EFI_DEBUG_IMAGE_INFO (Type 0x%x)" % \ + entry['ImageInfoType'].dereference ()) + index = index + 1 + gdb.execute ("symbol-file") + print ("Loading new symbols...") + for sym in syms: + try: + gdb.execute (sym) + except (gdb.error) as err: + print ('Failed: %s' % err) + + # + # 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 = dh.cast (dh_t) + print ("DebugImageInfoTable @ 0x%x, 0x%x entries" % \ + (long (dh['EfiDebugImageInfoTable']), dh['TableSize'])) + if dh['UpdateStatus'] & self.DEBUG_IS_UPDATING: + print ("EfiDebugImageInfoTable update in progress, retry later") + return + self.parse_edii (dh['EfiDebugImageInfoTable'], 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 = est.cast (est_t) + print ("Connected to %s (Rev. 0x%x)" % \ + (UefiMisc.parse_utf16 (est['FirmwareVendor']), \ + long (est['FirmwareRevision']))) + print ("ConfigurationTable @ 0x%x, 0x%x entries" % \ + (long (est['ConfigurationTable']), est['NumberOfTableEntries'])) + + dh = self.search_config(est['ConfigurationTable'], + 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 usage (self): + print ("Usage: reload-uefi [-o] [/path/to/GdbSyms.dll]") + + # + # Handler for reload-uefi. + # + + def invoke (self, arg, from_tty): + args = arg.split(' ') + try: + opts, args = getopt.getopt(args, "o", ["offset-by-headers"]) + except (getopt.GetoptError) as err: + self.usage () + return + for opt, arg in opts: + if opt == "-o": + self.offset_by_headers = True + + if len(args) >= 1 and args[0] != '': + gdb.execute ("symbol-file") + gdb.execute ("symbol-file %s" % args[0]) + else: + # FIXME: gdb.objfiles () loses files after symbol-file execution, + # so we have to extract GdbSymbs.dll manually. + lines = gdb.execute ("info files", to_string=True).split('\n') + for line in lines: + m = re.search("`([^']+)'", line) + if m: + gdb.execute ("symbol-file") + gdb.execute ("symbol-file %s" % m.group(1)) + break + + est = self.search_est () + if est == self.EINVAL: + print ("No EFI_SYSTEM_TABLE...") + return + + print ("EFI_SYSTEM_TABLE @ 0x%x" % est) + self.parse_est (est) + +class UefiStringPrinter: + def __init__(self, val): + self.val = val + + def to_string (self): + if not self.val: + return "NULL" + return 'L"' + UefiMisc.parse_utf16(self.val) + '"' + +class UefiEfiStatusPrinter: + def __init__(self, val): + self.val = val + + def to_string (self): + return UefiMisc.parse_status(self.val, True) + +class UefiReturnStatusPrinter: + def __init__(self, val): + self.val = val + + def to_string (self): + return UefiMisc.parse_status(self.val, False) + +class UefiGuidPrinter: + def __init__(self, val): + self.val = val + + def to_string (self): + return UefiMisc.parse_guid(self.val) + +def lookup_uefi_type (val): + if str(val.type) == 'const CHAR16 *' or str(val.type) == 'CHAR16 *': + return UefiStringPrinter(val) + elif str(val.type) == 'EFI_STATUS': + return UefiEfiStatusPrinter(val) + elif str(val.type) == 'RETURN_STATUS': + return UefiReturnStatusPrinter(val) + elif str(val.type) == 'GUID' or str(val.type) == 'EFI_GUID': + return UefiGuidPrinter(val) + return None + +ReloadUefi () +gdb.pretty_printers.append (lookup_uefi_type) diff --git a/Debug/macgdb.tool b/Debug/macgdb.tool new file mode 100755 index 00000000..69039f5e --- /dev/null +++ b/Debug/macgdb.tool @@ -0,0 +1,28 @@ +#!/bin/bash + +RUNDIR=$(dirname "$0") +pushd "$RUNDIR" >/dev/null +RUNDIR=$(pwd) +popd >/dev/null + +cd "$RUNDIR" + +if [ "$GDB" = "" ]; then + GDB=$(which ggdb) +fi + +if [ "$GDB" = "" ]; then + GDB=$(which gdb) +fi + +if [ "$GDB" = "" ]; then + echo "Failed to find GDB" + exit 1 +fi + +"$GDB" -ex "target remote localhost:8864" \ + -ex "source Scripts/gdb_uefi.py" \ + -ex "set pagination off" \ + -ex "reload-uefi" \ + -ex "b DebugBreak" \ + GdbSyms/Bin/X64_XCODE5/GdbSyms.dll diff --git a/Include/Library/OcMiscLib.h b/Include/Library/OcMiscLib.h index c7b3df77..12c70fc2 100755 --- a/Include/Library/OcMiscLib.h +++ b/Include/Library/OcMiscLib.h @@ -145,4 +145,22 @@ AllocateNullTextOutSystemTable ( EFI_SYSTEM_TABLE *SystemTable ); +/** + Dummy function that debuggers may break on. +**/ +VOID +DebugBreak ( + VOID + ); + +/** + Wait for user input after printing message. + + @param[in] Message Message to print. +**/ +VOID +WaitForKeyPress ( + CONST CHAR16 *Message + ); + #endif // OC_MISC_LIB_H_ diff --git a/Library/OcDevicePropertyLib/OcDevicePropertyLib.c b/Library/OcDevicePropertyLib/OcDevicePropertyLib.c index fe821fca..c50816f4 100755 --- a/Library/OcDevicePropertyLib/OcDevicePropertyLib.c +++ b/Library/OcDevicePropertyLib/OcDevicePropertyLib.c @@ -228,7 +228,7 @@ InternalSyncWithThunderboltDevices ( } } -// DppDbGetPropertyValue +// DppDbGetProperty /** Locates a device property in the database and returns its value into Value. @param[in] This A pointer to the protocol instance. @@ -252,7 +252,7 @@ InternalSyncWithThunderboltDevices ( **/ EFI_STATUS EFIAPI -DppDbGetPropertyValue ( +DppDbGetProperty ( IN EFI_DEVICE_PATH_PROPERTY_DATABASE_PROTOCOL *This, IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN CONST CHAR16 *Name, @@ -784,7 +784,7 @@ InternalReadEfiVariableProperties ( STATIC EFI_DEVICE_PATH_PROPERTY_DATABASE_PROTOCOL DppDbProtocolTemplate = { EFI_DEVICE_PATH_PROPERTY_DATABASE_PROTOCOL_REVISION, - DppDbGetPropertyValue, + DppDbGetProperty, DppDbSetProperty, DppDbRemoveProperty, DppDbGetPropertyBuffer diff --git a/Library/OcMiscLib/DebugHelp.c b/Library/OcMiscLib/DebugHelp.c new file mode 100644 index 00000000..7436c682 --- /dev/null +++ b/Library/OcMiscLib/DebugHelp.c @@ -0,0 +1,63 @@ +/** @file + Copyright (C) 2019, vit9696. All rights reserved. + + All rights reserved. + + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include +#include +#include +#include + +VOID +#ifdef __GNUC__ +__attribute__ ((noinline)) +#endif +DebugBreak ( + VOID + ) +{ + // + // This function has no code, debuggers may break on it. + // +} + +VOID +WaitForKeyPress ( + CONST CHAR16 *Message + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY Key; + volatile BOOLEAN Proceed; + + // + // Print message. + // + gST->ConOut->OutputString (gST->ConOut, (CHAR16 *) Message); + gST->ConOut->OutputString (gST->ConOut, (CHAR16 *) L"\r\n"); + + // + // Skip previously pressed characters. + // + do { + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + } while (!EFI_ERROR (Status)); + + // + // Wait for debugger signal or key press. + // + Proceed = FALSE; + while (EFI_ERROR (Status) && !Proceed) { + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + DebugBreak (); + } +} diff --git a/Library/OcMiscLib/OcMiscLib.inf b/Library/OcMiscLib/OcMiscLib.inf index d479f19d..f2d06e00 100755 --- a/Library/OcMiscLib/OcMiscLib.inf +++ b/Library/OcMiscLib/OcMiscLib.inf @@ -48,6 +48,7 @@ [Sources] Base64Decode.c ConvertDataToString.c + DebugHelp.c LegacyRegionLock.c LegacyRegionUnLock.c LogBootOrder.c diff --git a/OcSupportPkg.dsc b/OcSupportPkg.dsc index 255d5994..7d2654c9 100644 --- a/OcSupportPkg.dsc +++ b/OcSupportPkg.dsc @@ -44,6 +44,8 @@ UefiUsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf PciLib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.inf + DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf + HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf [Components] OcSupportPkg/Library/OcAppleImageVerificationLib/OcAppleImageVerificationLib.inf @@ -66,6 +68,7 @@ OcSupportPkg/Library/OcSerializeLib/OcSerializeLib.inf OcSupportPkg/Library/OcTemplateLib/OcTemplateLib.inf OcSupportPkg/Library/OcXmlLib/OcXmlLib.inf + OcSupportPkg/Debug/GdbSyms/GdbSyms.inf [PcdsFixedAtBuild] gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength|0 @@ -87,11 +90,12 @@ # While there are no PCDs as of now, there at least are some custom macros. DEFINE OCSUPPORTPKG_BUILD_OPTIONS_GEN = -D DISABLE_NEW_DEPRECATED_INTERFACES $(OCSUPPORTPKG_BUILD_OPTIONS) - INTEL:DEBUG_*_*_CC_FLAGS = $(OCSUPPORTPKG_BUILD_OPTIONS_GEN) - INTEL:RELEASE_*_*_CC_FLAGS = $(OCSUPPORTPKG_BUILD_OPTIONS_GEN) GCC:DEBUG_*_*_CC_FLAGS = $(OCSUPPORTPKG_BUILD_OPTIONS_GEN) + GCC:NOOPT_*_*_CC_FLAGS = $(OCSUPPORTPKG_BUILD_OPTIONS_GEN) GCC:RELEASE_*_*_CC_FLAGS = $(OCSUPPORTPKG_BUILD_OPTIONS_GEN) MSFT:DEBUG_*_*_CC_FLAGS = $(OCSUPPORTPKG_BUILD_OPTIONS_GEN) + MSFT:NOOPT_*_*_CC_FLAGS = $(OCSUPPORTPKG_BUILD_OPTIONS_GEN) MSFT:RELEASE_*_*_CC_FLAGS = $(OCSUPPORTPKG_BUILD_OPTIONS_GEN) - XCODE:RELEASE_*_*_CC_FLAGS = -flto $(OCSUPPORTPKG_BUILD_OPTIONS_GEN) XCODE:DEBUG_*_*_CC_FLAGS = $(OCSUPPORTPKG_BUILD_OPTIONS_GEN) + XCODE:NOOPT_*_*_CC_FLAGS = $(OCSUPPORTPKG_BUILD_OPTIONS_GEN) + XCODE:RELEASE_*_*_CC_FLAGS = -flto $(OCSUPPORTPKG_BUILD_OPTIONS_GEN)