public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Andrew Fish" <afish@apple.com>
To: devel@edk2.groups.io
Cc: Andrew Fish <afish@apple.com>, Leif Lindholm <leif@nuviainc.com>,
	Michael D Kinney <michael.d.kinney@intel.com>,
	Hao A Wu <hao.a.wu@intel.com>
Subject: [PATCH 2/3] efi_gdb.py: - Add gdb EFI commands and pretty Print
Date: Sun, 08 Aug 2021 14:46:33 -0700	[thread overview]
Message-ID: <2eccea8497c920699476736910a82ecd5efd884b.1628458551.git.afish@apple.com> (raw)
In-Reply-To: <cover.1628458551.git.afish@apple.com>

https://bugzilla.tianocore.org/show_bug.cgi?id=3500

Use efi_debugging.py Python Classes to implement EFI gdb commands:
(gdb) help efi
Commands for debugging EFI. efi <cmd>

List of efi subcommands:

efi devicepath -- Display an EFI device path.
efi guid -- Display info about EFI GUID's.
efi hob -- Dump EFI HOBs. Type 'hob -h' for more info.
efi symbols -- Load Symbols for EFI. Type 'efi_symbols -h' for more info.
efi table -- Dump EFI System Tables. Type 'table -h' for more info.

This module is coded against a generic gdb remote serial stub. It should
work with QEMU, JTAG debugger, or a generic EFI gdb remote serial stub.
No modifications of EFI is required to load symbols.

Example usage:
OvmfPkg/build.sh qemu -gdb tcp::9000
gdb -ex "target remote localhost:9000" -ex "source efi_gdb.py"

Cc: Leif Lindholm <leif@nuviainc.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Signed-off-by: Andrew Fish <afish@apple.com>
---
 efi_gdb.py | 918 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 918 insertions(+)
 create mode 100755 efi_gdb.py

diff --git a/efi_gdb.py b/efi_gdb.py
new file mode 100755
index 000000000000..f3e7fd9d0c28
--- /dev/null
+++ b/efi_gdb.py
@@ -0,0 +1,918 @@
+#!/usr/bin/python3
+'''
+Copyright 2021 (c) Apple Inc. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+EFI gdb commands based on efi_debugging classes.
+
+Example usage:
+OvmfPkg/build.sh qemu -gdb tcp::9000
+gdb -ex "target remote localhost:9000" -ex "source efi_gdb.py"
+
+(gdb) help efi
+Commands for debugging EFI. efi <cmd>
+
+List of efi subcommands:
+
+efi devicepath -- Display an EFI device path.
+efi guid -- Display info about EFI GUID's.
+efi hob -- Dump EFI HOBs. Type 'hob -h' for more info.
+efi symbols -- Load Symbols for EFI. Type 'efi_symbols -h' for more info.
+efi table -- Dump EFI System Tables. Type 'table -h' for more info.
+
+This module is coded against a generic gdb remote serial stub. It should work
+with QEMU, JTAG debugger, or a generic EFI gdb remote serial stub.
+
+If you are debugging with QEMU or a JTAG hardware debugger you can insert
+a CpuDeadLoop(); in your code, attach with gdb, and then `p Index=1` to
+step past. If you have a debug stub in EFI you can use CpuBreakpoint();.
+'''
+
+from gdb.printing import RegexpCollectionPrettyPrinter
+from gdb.printing import register_pretty_printer
+import gdb
+import os
+import sys
+import uuid
+import optparse
+import shlex
+
+# gdb will not import from the same path as this script.
+# so lets fix that for gdb...
+sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+
+from efi_debugging import PeTeImage, patch_ctypes            # noqa: E402
+from efi_debugging import EfiHob, GuidNames, EfiStatusClass  # noqa: E402
+from efi_debugging import EfiBootMode, EfiDevicePath         # noqa: E402
+from efi_debugging import EfiConfigurationTable, EfiTpl      # noqa: E402
+
+
+class GdbFileObject(object):
+    '''Provide a file like object required by efi_debugging'''
+
+    def __init__(self):
+        self.inferior = gdb.selected_inferior()
+        self.offset = 0
+
+    def tell(self):
+        return self.offset
+
+    def read(self, size=-1):
+        if size == -1:
+            # arbitrary default size
+            size = 0x1000000
+
+        try:
+            data = self.inferior.read_memory(self.offset, size)
+        except MemoryError:
+            data = bytearray(size)
+            assert False
+        if len(data) != size:
+            raise MemoryError(
+                f'gdb could not read memory 0x{size:x}'
+                + f' bytes from 0x{self.offset:08x}')
+        else:
+            # convert memoryview object to a bytestring.
+            return data.tobytes()
+
+    def readable(self):
+        return True
+
+    def seek(self, offset, whence=0):
+        if whence == 0:
+            self.offset = offset
+        elif whence == 1:
+            self.offset += offset
+        else:
+            # whence == 2 is seek from end
+            raise NotImplementedError
+
+    def seekable(self):
+        return True
+
+    def write(self, data):
+        self.inferior.write_memory(self.offset, data)
+        return len(data)
+
+    def writable(self):
+        return True
+
+    def truncate(self, size=None):
+        raise NotImplementedError
+
+    def flush(self):
+        raise NotImplementedError
+
+    def fileno(self):
+        raise NotImplementedError
+
+
+class EfiSymbols:
+    """Class to manage EFI Symbols"""
+
+    loaded = {}
+    stride = None
+    range = None
+    verbose = False
+
+    def __init__(self, file=None):
+        EfiSymbols.file = file if file else GdbFileObject()
+
+    @ classmethod
+    def __str__(cls):
+        return ''.join(f'{value}\n' for value in cls.loaded.values())
+
+    @ classmethod
+    def configure_search(cls, stride, range=None, verbose=False):
+        cls.stride = stride
+        cls.range = range
+        cls.verbose = verbose
+
+    @ classmethod
+    def clear(cls):
+        cls.loaded = {}
+
+    @ classmethod
+    def add_symbols_for_pecoff(cls, pecoff):
+        '''Tell lldb the location of the .text and .data sections.'''
+
+        if pecoff.TextAddress in cls.loaded:
+            return 'Already Loaded: '
+        try:
+            res = 'Loading Symbols Failed:'
+            res = gdb.execute('add-symbol-file ' + pecoff.CodeViewPdb +
+                              ' ' + hex(pecoff.TextAddress) +
+                              ' -s .data ' + hex(pecoff.DataAddress),
+                              False, True)
+
+            cls.loaded[pecoff.TextAddress] = pecoff
+            if cls.verbose:
+                print(f'\n{res:s}\n')
+            return ''
+        except gdb.error:
+            return res
+
+    @ classmethod
+    def address_to_symbols(cls, address, reprobe=False):
+        '''
+        Given an address search backwards for a PE/COFF (or TE) header
+        and load symbols. Return a status string.
+        '''
+        if not isinstance(address, int):
+            address = int(address)
+
+        pecoff = cls.address_in_loaded_pecoff(address)
+        if not reprobe and pecoff is not None:
+            # skip the probe of the remote
+            return f'{pecoff} is already loaded'
+
+        pecoff = PeTeImage(cls.file, None)
+        if pecoff.pcToPeCoff(address, cls.stride, cls.range):
+            res = cls.add_symbols_for_pecoff(pecoff)
+            return f'{res}{pecoff}'
+        else:
+            return f'0x{address:08x} not in a PE/COFF (or TE) image'
+
+    @ classmethod
+    def address_in_loaded_pecoff(cls, address):
+        if not isinstance(address, int):
+            address = int(address)
+
+        for value in cls.loaded.values():
+            if (address >= value.LoadAddress and
+                    address <= value.EndLoadAddress):
+                return value
+
+        return None
+
+    @ classmethod
+    def unload_symbols(cls, address):
+        if not isinstance(address, int):
+            address = int(address)
+
+        pecoff = cls.address_in_loaded_pecoff(address)
+        try:
+            res = 'Unloading Symbols Failed:'
+            res = gdb.execute(
+                f'remove-symbol-file -a {hex(pecoff.TextAddress):s}',
+                False, True)
+            del cls.loaded[pecoff.LoadAddress]
+            return res
+        except gdb.error:
+            return res
+
+
+class CHAR16_PrettyPrinter(object):
+
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        if int(self.val) < 0x20:
+            return f"L'\\x{int(self.val):02x}'"
+        else:
+            return f"L'{chr(self.val):s}'"
+
+
+class EFI_TPL_PrettyPrinter(object):
+
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        return str(EfiTpl(int(self.val)))
+
+
+class EFI_STATUS_PrettyPrinter(object):
+
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        status = int(self.val)
+        return f'{str(EfiStatusClass(status)):s} (0x{status:08x})'
+
+
+class EFI_BOOT_MODE_PrettyPrinter(object):
+
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        return str(EfiBootMode(int(self.val)))
+
+
+class EFI_GUID_PrettyPrinter(object):
+    """Print 'EFI_GUID' as 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'"""
+
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        # if we could get a byte like object of *(unsigned char (*)[16])
+        # then we could just use uuid.UUID() to convert
+        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 str(GuidNames(guid))
+
+
+def build_pretty_printer():
+    # Turn off via: disable pretty-printer global EFI
+    pp = RegexpCollectionPrettyPrinter("EFI")
+    # you can also tell gdb `x/sh <address>` to print CHAR16 string
+    pp.add_printer('CHAR16', '^CHAR16$', CHAR16_PrettyPrinter)
+    pp.add_printer('EFI_BOOT_MODE', '^EFI_BOOT_MODE$',
+                   EFI_BOOT_MODE_PrettyPrinter)
+    pp.add_printer('EFI_GUID', '^EFI_GUID$', EFI_GUID_PrettyPrinter)
+    pp.add_printer('EFI_STATUS', '^EFI_STATUS$', EFI_STATUS_PrettyPrinter)
+    pp.add_printer('EFI_TPL', '^EFI_TPL$', EFI_TPL_PrettyPrinter)
+    return pp
+
+
+class EfiDevicePathCmd (gdb.Command):
+    """Display an EFI device path. Type 'efi devicepath -h' for more info"""
+
+    def __init__(self):
+        super(EfiDevicePathCmd, self).__init__(
+            "efi devicepath", gdb.COMMAND_NONE)
+
+        self.file = GdbFileObject()
+
+    def create_options(self, arg, from_tty):
+        usage = "usage: %prog [options] [arg]"
+        description = (
+            "Command that can load EFI PE/COFF and TE image symbols. ")
+
+        self.parser = optparse.OptionParser(
+            description=description,
+            prog='efi devicepath',
+            usage=usage,
+            add_help_option=False)
+
+        self.parser.add_option(
+            '-v',
+            '--verbose',
+            action='store_true',
+            dest='verbose',
+            help='hex dump extra data',
+            default=False)
+
+        self.parser.add_option(
+            '-n',
+            '--node',
+            action='store_true',
+            dest='node',
+            help='dump a single device path node',
+            default=False)
+
+        self.parser.add_option(
+            '-h',
+            '--help',
+            action='store_true',
+            dest='help',
+            help='Show help for the command',
+            default=False)
+
+        return self.parser.parse_args(shlex.split(arg))
+
+    def invoke(self, arg, from_tty):
+        '''gdb command to dump EFI device paths'''
+
+        try:
+            (options, _) = self.create_options(arg, from_tty)
+            if options.help:
+                self.parser.print_help()
+                return
+
+            dev_addr = int(gdb.parse_and_eval(arg))
+        except ValueError:
+            print("Invalid argument!")
+            return
+
+        if options.node:
+            print(EfiDevicePath(
+                self.file).device_path_node_str(dev_addr,
+                                                options.verbose))
+        else:
+            device_path = EfiDevicePath(self.file, dev_addr, options.verbose)
+            if device_path.valid():
+                print(device_path)
+
+
+class EfiGuidCmd (gdb.Command):
+    """Display info about EFI GUID's. Type 'efi guid -h' for more info"""
+
+    def __init__(self):
+        super(EfiGuidCmd, self).__init__("efi guid",
+                                         gdb.COMMAND_NONE,
+                                         gdb.COMPLETE_EXPRESSION)
+        self.file = GdbFileObject()
+
+    def create_options(self, arg, from_tty):
+        usage = "usage: %prog [options] [arg]"
+        description = (
+            "Show EFI_GUID values and the C name of the EFI_GUID variables"
+            "in the C code. If symbols are loaded the Guid.xref file"
+            "can be processed and the complete GUID database can be shown."
+            "This command also suports generating new GUID's, and showing"
+            "the value used to initialize the C variable.")
+
+        self.parser = optparse.OptionParser(
+            description=description,
+            prog='efi guid',
+            usage=usage,
+            add_help_option=False)
+
+        self.parser.add_option(
+            '-n',
+            '--new',
+            action='store_true',
+            dest='new',
+            help='Generate a new GUID',
+            default=False)
+
+        self.parser.add_option(
+            '-v',
+            '--verbose',
+            action='store_true',
+            dest='verbose',
+            help='Also display GUID C structure values',
+            default=False)
+
+        self.parser.add_option(
+            '-h',
+            '--help',
+            action='store_true',
+            dest='help',
+            help='Show help for the command',
+            default=False)
+
+        return self.parser.parse_args(shlex.split(arg))
+
+    def invoke(self, arg, from_tty):
+        '''gdb command to dump EFI System Tables'''
+
+        try:
+            (options, args) = self.create_options(arg, from_tty)
+            if options.help:
+                self.parser.print_help()
+                return
+            if len(args) >= 1:
+                # guid { 0x414e6bdd, 0xe47b, 0x47cc,
+                #        { 0xb2, 0x44, 0xbb, 0x61, 0x02, 0x0c,0xf5, 0x16 }}
+                # this generates multiple args
+                guid = ' '.join(args)
+        except ValueError:
+            print('bad arguments!')
+            return
+
+        if options.new:
+            guid = uuid.uuid4()
+            print(str(guid).upper())
+            print(GuidNames.to_c_guid(guid))
+            return
+
+        if len(args) > 0:
+            if GuidNames.is_guid_str(arg):
+                # guid 05AD34BA-6F02-4214-952E-4DA0398E2BB9
+                key = guid.upper()
+                name = GuidNames.to_name(key)
+            elif GuidNames.is_c_guid(arg):
+                # guid { 0x414e6bdd, 0xe47b, 0x47cc,
+                #        { 0xb2, 0x44, 0xbb, 0x61, 0x02, 0x0c,0xf5, 0x16 }}
+                key = GuidNames.from_c_guid(arg)
+                name = GuidNames.to_name(key)
+            else:
+                # guid gEfiDxeServicesTableGuid
+                name = guid
+                try:
+                    key = GuidNames.to_guid(name)
+                    name = GuidNames.to_name(key)
+                except ValueError:
+                    return
+
+            extra = f'{GuidNames.to_c_guid(key)}: ' if options.verbose else ''
+            print(f'{key}: {extra}{name}')
+
+        else:
+            for key, value in GuidNames._dict_.items():
+                if options.verbose:
+                    extra = f'{GuidNames.to_c_guid(key)}: '
+                else:
+                    extra = ''
+                print(f'{key}: {extra}{value}')
+
+
+class EfiHobCmd (gdb.Command):
+    """Dump EFI HOBs. Type 'hob -h' for more info."""
+
+    def __init__(self):
+        super(EfiHobCmd, self).__init__("efi hob", gdb.COMMAND_NONE)
+        self.file = GdbFileObject()
+
+    def create_options(self, arg, from_tty):
+        usage = "usage: %prog [options] [arg]"
+        description = (
+            "Command that can load EFI PE/COFF and TE image symbols. ")
+
+        self.parser = optparse.OptionParser(
+            description=description,
+            prog='efi hob',
+            usage=usage,
+            add_help_option=False)
+
+        self.parser.add_option(
+            '-a',
+            '--address',
+            type="int",
+            dest='address',
+            help='Parse HOBs from address',
+            default=None)
+
+        self.parser.add_option(
+            '-t',
+            '--type',
+            type="int",
+            dest='type',
+            help='Only dump HOBS of his type',
+            default=None)
+
+        self.parser.add_option(
+            '-v',
+            '--verbose',
+            action='store_true',
+            dest='verbose',
+            help='hex dump extra data',
+            default=False)
+
+        self.parser.add_option(
+            '-h',
+            '--help',
+            action='store_true',
+            dest='help',
+            help='Show help for the command',
+            default=False)
+
+        return self.parser.parse_args(shlex.split(arg))
+
+    def invoke(self, arg, from_tty):
+        '''gdb command to dump EFI System Tables'''
+
+        try:
+            (options, _) = self.create_options(arg, from_tty)
+            if options.help:
+                self.parser.print_help()
+                return
+        except ValueError:
+            print('bad arguments!')
+            return
+
+        if options.address:
+            try:
+                value = gdb.parse_and_eval(options.address)
+                address = int(value)
+            except ValueError:
+                address = None
+        else:
+            address = None
+
+        hob = EfiHob(self.file,
+                     address,
+                     options.verbose).get_hob_by_type(options.type)
+        print(hob)
+
+
+class EfiTablesCmd (gdb.Command):
+    """Dump EFI System Tables. Type 'table -h' for more info."""
+
+    def __init__(self):
+        super(EfiTablesCmd, self).__init__("efi table", gdb.COMMAND_NONE)
+
+        self.file = GdbFileObject()
+
+    def create_options(self, arg, from_tty):
+        usage = "usage: %prog [options] [arg]"
+        description = "Dump EFI System Tables. Requires symbols to be loaded"
+
+        self.parser = optparse.OptionParser(
+            description=description,
+            prog='efi table',
+            usage=usage,
+            add_help_option=False)
+
+        self.parser.add_option(
+            '-h',
+            '--help',
+            action='store_true',
+            dest='help',
+            help='Show help for the command',
+            default=False)
+
+        return self.parser.parse_args(shlex.split(arg))
+
+    def invoke(self, arg, from_tty):
+        '''gdb command to dump EFI System Tables'''
+
+        try:
+            (options, _) = self.create_options(arg, from_tty)
+            if options.help:
+                self.parser.print_help()
+                return
+        except ValueError:
+            print('bad arguments!')
+            return
+
+        gST = gdb.lookup_global_symbol('gST')
+        if gST is None:
+            print('Error: This command requires symbols for gST to be loaded')
+            return
+
+        table = EfiConfigurationTable(
+            self.file, int(gST.value(gdb.selected_frame())))
+        if table:
+            print(table, '\n')
+
+
+class EfiSymbolsCmd (gdb.Command):
+    """Load Symbols for EFI. Type 'efi symbols -h' for more info."""
+
+    def __init__(self):
+        super(EfiSymbolsCmd, self).__init__("efi symbols",
+                                            gdb.COMMAND_NONE,
+                                            gdb.COMPLETE_EXPRESSION)
+        self.file = GdbFileObject()
+        self.gST = None
+        self.efi_symbols = EfiSymbols(self.file)
+
+    def create_options(self, arg, from_tty):
+        usage = "usage: %prog [options]"
+        description = (
+            "Command that can load EFI PE/COFF and TE image symbols. "
+            "If you are having trouble in PEI try adding --pei. "
+            "Given any address search backward for the PE/COFF (or TE header) "
+            "and then parse the PE/COFF image to get debug info. "
+            "The address can come from the current pc, pc values in the "
+            "frame, or an address provided to the command"
+            "")
+
+        self.parser = optparse.OptionParser(
+            description=description,
+            prog='efi symbols',
+            usage=usage,
+            add_help_option=False)
+
+        self.parser.add_option(
+            '-a',
+            '--address',
+            type="str",
+            dest='address',
+            help='Load symbols for image that contains address',
+            default=None)
+
+        self.parser.add_option(
+            '-c',
+            '--clear',
+            action='store_true',
+            dest='clear',
+            help='Clear the cache of loaded images',
+            default=False)
+
+        self.parser.add_option(
+            '-f',
+            '--frame',
+            action='store_true',
+            dest='frame',
+            help='Load symbols for current stack frame',
+            default=False)
+
+        self.parser.add_option(
+            '-p',
+            '--pc',
+            action='store_true',
+            dest='pc',
+            help='Load symbols for pc',
+            default=False)
+
+        self.parser.add_option(
+            '--pei',
+            action='store_true',
+            dest='pei',
+            help='Load symbols for PEI (searches every 4 bytes)',
+            default=False)
+
+        self.parser.add_option(
+            '-e',
+            '--extended',
+            action='store_true',
+            dest='extended',
+            help='Try to load all symbols based on config tables',
+            default=False)
+
+        self.parser.add_option(
+            '-r',
+            '--range',
+            type="long",
+            dest='range',
+            help='How far to search backward for start of PE/COFF Image',
+            default=None)
+
+        self.parser.add_option(
+            '-s',
+            '--stride',
+            type="long",
+            dest='stride',
+            help='Boundary to search for PE/COFF header',
+            default=None)
+
+        self.parser.add_option(
+            '-t',
+            '--thread',
+            action='store_true',
+            dest='thread',
+            help='Load symbols for the frames of all threads',
+            default=False)
+
+        self.parser.add_option(
+            '-v',
+            '--verbose',
+            action='store_true',
+            dest='verbose',
+            help='Show more info on symbols loading in gdb',
+            default=False)
+
+        self.parser.add_option(
+            '-h',
+            '--help',
+            action='store_true',
+            dest='help',
+            help='Show help for the command',
+            default=False)
+
+        return self.parser.parse_args(shlex.split(arg))
+
+    def save_user_state(self):
+        self.pagination = gdb.parameter("pagination")
+        if self.pagination:
+            gdb.execute("set pagination off")
+
+        self.user_selected_thread = gdb.selected_thread()
+        self.user_selected_frame = gdb.selected_frame()
+
+    def restore_user_state(self):
+        self.user_selected_thread.switch()
+        self.user_selected_frame.select()
+
+        if self.pagination:
+            gdb.execute("set pagination on")
+
+    def canonical_address(self, address):
+        '''
+        Scrub out 48-bit non canonical addresses
+        Raw frames in gdb can have some funky values
+        '''
+
+        # Skip lowest 256 bytes to avoid interrupt frames
+        if address > 0xFF and address < 0x00007FFFFFFFFFFF:
+            return True
+        if address >= 0xFFFF800000000000:
+            return True
+
+        return False
+
+    def pc_set_for_frames(self):
+        '''Return a set for the PC's in the current frame'''
+        pc_list = []
+        frame = gdb.newest_frame()
+        while frame:
+            pc = int(frame.read_register('pc'))
+            if self.canonical_address(pc):
+                pc_list.append(pc)
+            frame = frame.older()
+
+        return set(pc_list)
+
+    def invoke(self, arg, from_tty):
+        '''gdb command to symbolicate all the frames from all the threads'''
+
+        try:
+            (options, _) = self.create_options(arg, from_tty)
+            if options.help:
+                self.parser.print_help()
+                return
+        except ValueError:
+            print('bad arguments!')
+            return
+
+        self.dont_repeat()
+
+        self.save_user_state()
+
+        if options.clear:
+            self.efi_symbols.clear()
+            return
+
+        if options.pei:
+            # XIP code can be 4 byte aligned in the FV
+            options.stride = 4
+            options.range = 0x100000
+        self.efi_symbols.configure_search(options.stride,
+                                          options.range,
+                                          options.verbose)
+
+        if options.thread:
+            thread_list = gdb.selected_inferior().threads()
+        else:
+            thread_list = (gdb.selected_thread(),)
+
+        address = None
+        if options.address:
+            value = gdb.parse_and_eval(options.address)
+            address = int(value)
+        elif options.pc:
+            address = gdb.selected_frame().pc()
+
+        if address:
+            res = self.efi_symbols.address_to_symbols(address)
+            print(res)
+        else:
+
+            for thread in thread_list:
+                thread.switch()
+
+                # You can not iterate over frames as you load symbols. Loading
+                # symbols changes the frames gdb can see due to inlining and
+                # boom. So we loop adding symbols for the current frame, and
+                # we test to see if new frames have shown up. If new frames
+                # show up we process those new frames. Thus 1st pass is the
+                # raw frame, and other passes are only new PC values.
+                NewPcSet = self.pc_set_for_frames()
+                while NewPcSet:
+                    PcSet = self.pc_set_for_frames()
+                    for pc in NewPcSet:
+                        res = self.efi_symbols.address_to_symbols(pc)
+                        print(res)
+
+                    NewPcSet = PcSet.symmetric_difference(
+                        self.pc_set_for_frames())
+
+        # find the EFI System tables the 1st time
+        if self.gST is None:
+            gST = gdb.lookup_global_symbol('gST')
+            if gST is not None:
+                self.gST = int(gST.value(gdb.selected_frame()))
+                table = EfiConfigurationTable(self.file, self.gST)
+            else:
+                table = None
+        else:
+            table = EfiConfigurationTable(self.file, self.gST)
+
+        if options.extended and table:
+            # load symbols from EFI System Table entry
+            for address, _ in table.DebugImageInfo():
+                res = self.efi_symbols.address_to_symbols(address)
+                print(res)
+
+        # sync up the GUID database from the build output
+        for m in gdb.objfiles():
+            if GuidNames.add_build_guid_file(str(m.filename)):
+                break
+
+        self.restore_user_state()
+
+
+class EfiCmd (gdb.Command):
+    """Commands for debugging EFI. efi <cmd>"""
+
+    def __init__(self):
+        super(EfiCmd, self).__init__("efi",
+                                     gdb.COMMAND_NONE,
+                                     gdb.COMPLETE_NONE,
+                                     True)
+
+    def invoke(self, arg, from_tty):
+        '''default to loading symbols'''
+        if '-h' in arg or '--help' in arg:
+            gdb.execute('help efi')
+        else:
+            # default to loading all symbols
+            gdb.execute('efi symbols --extended')
+
+
+class LoadEmulatorEfiSymbols(gdb.Breakpoint):
+    '''
+    breakpoint for EmulatorPkg to load symbols
+    Note: make sure SecGdbScriptBreak is not optimized away!
+    Also turn off the dlopen() flow like on macOS.
+    '''
+    def stop(self):
+        symbols = EfiSymbols()
+        # Emulator adds SizeOfHeaders so we need file alignment to search
+        symbols.configure_search(0x20)
+
+        frame = gdb.newest_frame()
+
+        try:
+            # gdb was looking at spill address, pre spill :(
+            LoadAddress = frame.read_register('rdx')
+            AddSymbolFlag = frame.read_register('rcx')
+        except gdb.error:
+            LoadAddress = frame.read_var('LoadAddress')
+            AddSymbolFlag = frame.read_var('AddSymbolFlag')
+
+        if AddSymbolFlag == 1:
+            res = symbols.address_to_symbols(LoadAddress)
+        else:
+            res = symbols.unload_symbols(LoadAddress)
+        print(res)
+
+        # keep running
+        return False
+
+
+# Get python backtraces to debug errors in this script
+gdb.execute("set python print-stack full")
+
+# tell efi_debugging how to walk data structures with pointers
+try:
+    pointer_width = gdb.lookup_type('int').pointer().sizeof
+except ValueError:
+    pointer_width = 8
+patch_ctypes(pointer_width)
+
+register_pretty_printer(None, build_pretty_printer(), replace=True)
+
+# gdb commands that we are adding
+# add `efi` prefix gdb command
+EfiCmd()
+
+# subcommands for `efi`
+EfiSymbolsCmd()
+EfiTablesCmd()
+EfiHobCmd()
+EfiDevicePathCmd()
+EfiGuidCmd()
+
+#
+bp = LoadEmulatorEfiSymbols('SecGdbScriptBreak', internal=True)
+if bp.pending:
+    try:
+        gdb.selected_frame()
+        # Not the emulator so do this when you attach
+        gdb.execute('efi symbols --frame --extended', True)
+        gdb.execute('bt')
+        # If you want to skip the above commands comment them out
+        pass
+    except gdb.error:
+        # If you load the script and there is no target ignore the error.
+        pass
+else:
+    # start the emulator
+    gdb.execute('run')
-- 
2.30.1 (Apple Git-130)


  parent reply	other threads:[~2021-08-08 21:46 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-08 21:46 [PATCH 0/3] Add support for gdb and lldb Andrew Fish
2021-08-08 21:46 ` [PATCH 1/3] efi_debugging.py: - Add debugger agnostic debugging Python Classes Andrew Fish
2021-08-08 21:46 ` Andrew Fish [this message]
2021-08-08 21:46 ` [PATCH 3/3] efi_lldb.py: - Add lldb EFI commands and pretty Print Andrew Fish
2021-08-11 22:11 ` [edk2-devel] [PATCH 0/3] Add support for gdb and lldb Rebecca Cran
2021-08-11 23:22   ` Andrew Fish
2021-08-12  0:30     ` Rebecca Cran
2021-08-12 22:56       ` Andrew Fish
2021-08-13  5:09         ` Rebecca Cran
2021-09-14 23:47 ` Rebecca Cran
2021-09-15  0:47   ` Andrew Fish
2021-10-23  2:07     ` Rebecca Cran
2021-12-13 21:59     ` Rebecca Cran
     [not found]     ` <16C06F6B056C6852.12686@groups.io>
2022-01-07  2:42       ` Rebecca Cran

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2eccea8497c920699476736910a82ecd5efd884b.1628458551.git.afish@apple.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox