]> git.proxmox.com Git - mirror_edk2.git/commitdiff
BaseTools: Scripts/efi_lldb.py: Add lldb EFI commands and pretty Print
authorRebecca Cran <quic_rcran@quicinc.com>
Mon, 21 Mar 2022 20:20:48 +0000 (04:20 +0800)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Sat, 9 Apr 2022 05:13:56 +0000 (05:13 +0000)
https://bugzilla.tianocore.org/show_bug.cgi?id=3500

Use efi_debugging.py Python Classes to implement EFI gdb commands:
efi_symbols, guid, table, hob, and devicepath

You can attach to any standard gdb or kdp remote server and get EFI
symbols. No modifications of EFI are required.

Example usage:
OvmfPkg/build.sh qemu -gdb tcp::9000
lldb -o "gdb-remote localhost:9000" -o "command script import efi_lldb.py"
Note you may also have to teach lldb about QEMU:
-o "settings set plugin.process.gdb-remote.target-definition-file
 x86_64_target_definition.py"

Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Yuwei Chen <yuwei.chen@intel.com>
Signed-off-by: Rebecca Cran <quic_rcran@quicinc.com>
Reviewed-by: Bob Feng <bob.c.feng@intel.com>
Acked-by: Liming Gao <gaoliming@byosoft.com.cn>
BaseTools/Scripts/efi_lldb.py [new file with mode: 0755]

diff --git a/BaseTools/Scripts/efi_lldb.py b/BaseTools/Scripts/efi_lldb.py
new file mode 100755 (executable)
index 0000000..6487e41
--- /dev/null
@@ -0,0 +1,1044 @@
+#!/usr/bin/python3\r
+'''\r
+Copyright (c) Apple Inc. 2021\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+Example usage:\r
+OvmfPkg/build.sh qemu -gdb tcp::9000\r
+lldb -o "gdb-remote localhost:9000" -o "command script import efi_lldb.py"\r
+'''\r
+\r
+import optparse\r
+import shlex\r
+import subprocess\r
+import uuid\r
+import sys\r
+import os\r
+from pathlib import Path\r
+from efi_debugging import EfiDevicePath, EfiConfigurationTable, EfiTpl\r
+from efi_debugging import EfiHob, GuidNames, EfiStatusClass, EfiBootMode\r
+from efi_debugging import PeTeImage, patch_ctypes\r
+\r
+try:\r
+    # Just try for LLDB in case PYTHONPATH is already correctly setup\r
+    import lldb\r
+except ImportError:\r
+    try:\r
+        env = os.environ.copy()\r
+        env['LLDB_DEFAULT_PYTHON_VERSION'] = str(sys.version_info.major)\r
+        lldb_python_path = subprocess.check_output(\r
+            ["xcrun", "lldb", "-P"], env=env).decode("utf-8").strip()\r
+        sys.path.append(lldb_python_path)\r
+        import lldb\r
+    except ValueError:\r
+        print("Couldn't find LLDB.framework from lldb -P")\r
+        print("PYTHONPATH should match the currently selected lldb")\r
+        sys.exit(-1)\r
+\r
+\r
+class LldbFileObject(object):\r
+    '''\r
+    Class that fakes out file object to abstract lldb from the generic code.\r
+    For lldb this is memory so we don't have a concept of the end of the file.\r
+    '''\r
+\r
+    def __init__(self, process):\r
+        # _exe_ctx is lldb.SBExecutionContext\r
+        self._process = process\r
+        self._offset = 0\r
+        self._SBError = lldb.SBError()\r
+\r
+    def tell(self):\r
+        return self._offset\r
+\r
+    def read(self, size=-1):\r
+        if size == -1:\r
+            # arbitrary default size\r
+            size = 0x1000000\r
+\r
+        data = self._process.ReadMemory(self._offset, size, self._SBError)\r
+        if self._SBError.fail:\r
+            raise MemoryError(\r
+                f'lldb could not read memory 0x{size:x} '\r
+                f' bytes from 0x{self._offset:08x}')\r
+        else:\r
+            return data\r
+\r
+    def readable(self):\r
+        return True\r
+\r
+    def seek(self, offset, whence=0):\r
+        if whence == 0:\r
+            self._offset = offset\r
+        elif whence == 1:\r
+            self._offset += offset\r
+        else:\r
+            # whence == 2 is seek from end\r
+            raise NotImplementedError\r
+\r
+    def seekable(self):\r
+        return True\r
+\r
+    def write(self, data):\r
+        result = self._process.WriteMemory(self._offset, data, self._SBError)\r
+        if self._SBError.fail:\r
+            raise MemoryError(\r
+                f'lldb could not write memory to 0x{self._offset:08x}')\r
+        return result\r
+\r
+    def writable(self):\r
+        return True\r
+\r
+    def truncate(self, size=None):\r
+        raise NotImplementedError\r
+\r
+    def flush(self):\r
+        raise NotImplementedError\r
+\r
+    def fileno(self):\r
+        raise NotImplementedError\r
+\r
+\r
+class EfiSymbols:\r
+    """\r
+    Class to manage EFI Symbols\r
+    You need to pass file, and exe_ctx to load symbols.\r
+    You can print(EfiSymbols()) to see the currently loaded symbols\r
+    """\r
+\r
+    loaded = {}\r
+    stride = None\r
+    range = None\r
+    verbose = False\r
+\r
+    def __init__(self, target=None):\r
+        if target:\r
+            EfiSymbols.target = target\r
+            EfiSymbols._file = LldbFileObject(target.process)\r
+\r
+    @ classmethod\r
+    def __str__(cls):\r
+        return ''.join(f'{pecoff}\n' for (pecoff, _) in cls.loaded.values())\r
+\r
+    @ classmethod\r
+    def configure_search(cls, stride, range, verbose=False):\r
+        cls.stride = stride\r
+        cls.range = range\r
+        cls.verbose = verbose\r
+\r
+    @ classmethod\r
+    def clear(cls):\r
+        cls.loaded = {}\r
+\r
+    @ classmethod\r
+    def add_symbols_for_pecoff(cls, pecoff):\r
+        '''Tell lldb the location of the .text and .data sections.'''\r
+\r
+        if pecoff.LoadAddress in cls.loaded:\r
+            return 'Already Loaded: '\r
+\r
+        module = cls.target.AddModule(None, None, str(pecoff.CodeViewUuid))\r
+        if not module:\r
+            module = cls.target.AddModule(pecoff.CodeViewPdb,\r
+                                          None,\r
+                                          str(pecoff.CodeViewUuid))\r
+        if module.IsValid():\r
+            SBError = cls.target.SetModuleLoadAddress(\r
+                module, pecoff.LoadAddress + pecoff.TeAdjust)\r
+            if SBError.success:\r
+                cls.loaded[pecoff.LoadAddress] = (pecoff, module)\r
+                return ''\r
+\r
+        return 'Symbols NOT FOUND: '\r
+\r
+    @ classmethod\r
+    def address_to_symbols(cls, address, reprobe=False):\r
+        '''\r
+        Given an address search backwards for a PE/COFF (or TE) header\r
+        and load symbols. Return a status string.\r
+        '''\r
+        if not isinstance(address, int):\r
+            address = int(address)\r
+\r
+        pecoff, _ = cls.address_in_loaded_pecoff(address)\r
+        if not reprobe and pecoff is not None:\r
+            # skip the probe of the remote\r
+            return f'{pecoff} is already loaded'\r
+\r
+        pecoff = PeTeImage(cls._file, None)\r
+        if pecoff.pcToPeCoff(address, cls.stride, cls.range):\r
+            res = cls.add_symbols_for_pecoff(pecoff)\r
+            return f'{res}{pecoff}'\r
+        else:\r
+            return f'0x{address:08x} not in a PE/COFF (or TE) image'\r
+\r
+    @ classmethod\r
+    def address_in_loaded_pecoff(cls, address):\r
+        if not isinstance(address, int):\r
+            address = int(address)\r
+\r
+        for (pecoff, module) in cls.loaded.values():\r
+            if (address >= pecoff.LoadAddress and\r
+                    address <= pecoff.EndLoadAddress):\r
+\r
+                return pecoff, module\r
+\r
+        return None, None\r
+\r
+    @ classmethod\r
+    def unload_symbols(cls, address):\r
+        pecoff, module = cls.address_in_loaded_pecoff(address)\r
+        if module:\r
+            name = str(module)\r
+            cls.target.ClearModuleLoadAddress(module)\r
+            cls.target.RemoveModule(module)\r
+            del cls.loaded[pecoff.LoadAddress]\r
+            return f'{name:s} was unloaded'\r
+        return f'0x{address:x} was not in a loaded image'\r
+\r
+\r
+def arg_to_address(frame, arg):\r
+    ''' convert an lldb command arg into a memory address (addr_t)'''\r
+\r
+    if arg is None:\r
+        return None\r
+\r
+    arg_str = arg if isinstance(arg, str) else str(arg)\r
+    SBValue = frame.EvaluateExpression(arg_str)\r
+    if SBValue.error.fail:\r
+        return arg\r
+\r
+    if (SBValue.TypeIsPointerType() or\r
+            SBValue.value_type == lldb.eValueTypeRegister or\r
+            SBValue.value_type == lldb.eValueTypeRegisterSet or\r
+            SBValue.value_type == lldb.eValueTypeConstResult):\r
+        try:\r
+            addr = SBValue.GetValueAsAddress()\r
+        except ValueError:\r
+            addr = SBValue.unsigned\r
+    else:\r
+        try:\r
+            addr = SBValue.address_of.GetValueAsAddress()\r
+        except ValueError:\r
+            addr = SBValue.address_of.unsigned\r
+\r
+    return addr\r
+\r
+\r
+def arg_to_data(frame, arg):\r
+    ''' convert an lldb command arg into a data vale (uint32_t/uint64_t)'''\r
+    if not isinstance(arg, str):\r
+        arg_str = str(str)\r
+\r
+    SBValue = frame.EvaluateExpression(arg_str)\r
+    return SBValue.unsigned\r
+\r
+\r
+class EfiDevicePathCommand:\r
+\r
+    def create_options(self):\r
+        ''' standard lldb command help/options parser'''\r
+        usage = "usage: %prog [options]"\r
+        description = '''Command that can EFI Config Tables\r
+'''\r
+\r
+        # Pass add_help_option = False, since this keeps the command in line\r
+        # with lldb commands, and we wire up "help command" to work by\r
+        # providing the long & short help methods below.\r
+        self.parser = optparse.OptionParser(\r
+            description=description,\r
+            prog='devicepath',\r
+            usage=usage,\r
+            add_help_option=False)\r
+\r
+        self.parser.add_option(\r
+            '-v',\r
+            '--verbose',\r
+            action='store_true',\r
+            dest='verbose',\r
+            help='hex dump extra data',\r
+            default=False)\r
+\r
+        self.parser.add_option(\r
+            '-n',\r
+            '--node',\r
+            action='store_true',\r
+            dest='node',\r
+            help='dump a single device path node',\r
+            default=False)\r
+\r
+        self.parser.add_option(\r
+            '-h',\r
+            '--help',\r
+            action='store_true',\r
+            dest='help',\r
+            help='Show help for the command',\r
+            default=False)\r
+\r
+    def get_short_help(self):\r
+        '''standard lldb function method'''\r
+        return "Display EFI Tables"\r
+\r
+    def get_long_help(self):\r
+        '''standard lldb function method'''\r
+        return self.help_string\r
+\r
+    def __init__(self, debugger, internal_dict):\r
+        '''standard lldb function method'''\r
+        self.create_options()\r
+        self.help_string = self.parser.format_help()\r
+\r
+    def __call__(self, debugger, command, exe_ctx, result):\r
+        '''standard lldb function method'''\r
+        # Use the Shell Lexer to properly parse up command options just like a\r
+        # shell would\r
+        command_args = shlex.split(command)\r
+\r
+        try:\r
+            (options, args) = self.parser.parse_args(command_args)\r
+            dev_list = []\r
+            for arg in args:\r
+                dev_list.append(arg_to_address(exe_ctx.frame, arg))\r
+        except ValueError:\r
+            # if you don't handle exceptions, passing an incorrect argument\r
+            # to the OptionParser will cause LLDB to exit (courtesy of\r
+            # OptParse dealing with argument errors by throwing SystemExit)\r
+            result.SetError("option parsing failed")\r
+            return\r
+\r
+        if options.help:\r
+            self.parser.print_help()\r
+            return\r
+\r
+        file = LldbFileObject(exe_ctx.process)\r
+\r
+        for dev_addr in dev_list:\r
+            if options.node:\r
+                print(EfiDevicePath(file).device_path_node_str(\r
+                    dev_addr, options.verbose))\r
+            else:\r
+                device_path = EfiDevicePath(file, dev_addr, options.verbose)\r
+                if device_path.valid():\r
+                    print(device_path)\r
+\r
+\r
+class EfiHobCommand:\r
+    def create_options(self):\r
+        ''' standard lldb command help/options parser'''\r
+        usage = "usage: %prog [options]"\r
+        description = '''Command that can EFI dump EFI HOBs'''\r
+\r
+        # Pass add_help_option = False, since this keeps the command in line\r
+        # with lldb commands, and we wire up "help command" to work by\r
+        # providing the long & short help methods below.\r
+        self.parser = optparse.OptionParser(\r
+            description=description,\r
+            prog='table',\r
+            usage=usage,\r
+            add_help_option=False)\r
+\r
+        self.parser.add_option(\r
+            '-a',\r
+            '--address',\r
+            type="int",\r
+            dest='address',\r
+            help='Parse HOBs from address',\r
+            default=None)\r
+\r
+        self.parser.add_option(\r
+            '-t',\r
+            '--type',\r
+            type="int",\r
+            dest='type',\r
+            help='Only dump HOBS of his type',\r
+            default=None)\r
+\r
+        self.parser.add_option(\r
+            '-v',\r
+            '--verbose',\r
+            action='store_true',\r
+            dest='verbose',\r
+            help='hex dump extra data',\r
+            default=False)\r
+\r
+        self.parser.add_option(\r
+            '-h',\r
+            '--help',\r
+            action='store_true',\r
+            dest='help',\r
+            help='Show help for the command',\r
+            default=False)\r
+\r
+    def get_short_help(self):\r
+        '''standard lldb function method'''\r
+        return "Display EFI Hobs"\r
+\r
+    def get_long_help(self):\r
+        '''standard lldb function method'''\r
+        return self.help_string\r
+\r
+    def __init__(self, debugger, internal_dict):\r
+        '''standard lldb function method'''\r
+        self.create_options()\r
+        self.help_string = self.parser.format_help()\r
+\r
+    def __call__(self, debugger, command, exe_ctx, result):\r
+        '''standard lldb function method'''\r
+        # Use the Shell Lexer to properly parse up command options just like a\r
+        # shell would\r
+        command_args = shlex.split(command)\r
+\r
+        try:\r
+            (options, _) = self.parser.parse_args(command_args)\r
+        except ValueError:\r
+            # if you don't handle exceptions, passing an incorrect argument\r
+            # to the OptionParser will cause LLDB to exit (courtesy of\r
+            # OptParse dealing with argument errors by throwing SystemExit)\r
+            result.SetError("option parsing failed")\r
+            return\r
+\r
+        if options.help:\r
+            self.parser.print_help()\r
+            return\r
+\r
+        address = arg_to_address(exe_ctx.frame, options.address)\r
+\r
+        file = LldbFileObject(exe_ctx.process)\r
+        hob = EfiHob(file, address, options.verbose).get_hob_by_type(\r
+            options.type)\r
+        print(hob)\r
+\r
+\r
+class EfiTableCommand:\r
+\r
+    def create_options(self):\r
+        ''' standard lldb command help/options parser'''\r
+        usage = "usage: %prog [options]"\r
+        description = '''Command that can display EFI Config Tables\r
+'''\r
+\r
+        # Pass add_help_option = False, since this keeps the command in line\r
+        # with lldb commands, and we wire up "help command" to work by\r
+        # providing the long & short help methods below.\r
+        self.parser = optparse.OptionParser(\r
+            description=description,\r
+            prog='table',\r
+            usage=usage,\r
+            add_help_option=False)\r
+\r
+        self.parser.add_option(\r
+            '-h',\r
+            '--help',\r
+            action='store_true',\r
+            dest='help',\r
+            help='Show help for the command',\r
+            default=False)\r
+\r
+    def get_short_help(self):\r
+        '''standard lldb function method'''\r
+        return "Display EFI Tables"\r
+\r
+    def get_long_help(self):\r
+        '''standard lldb function method'''\r
+        return self.help_string\r
+\r
+    def __init__(self, debugger, internal_dict):\r
+        '''standard lldb function method'''\r
+        self.create_options()\r
+        self.help_string = self.parser.format_help()\r
+\r
+    def __call__(self, debugger, command, exe_ctx, result):\r
+        '''standard lldb function method'''\r
+        # Use the Shell Lexer to properly parse up command options just like a\r
+        # shell would\r
+        command_args = shlex.split(command)\r
+\r
+        try:\r
+            (options, _) = self.parser.parse_args(command_args)\r
+        except ValueError:\r
+            # if you don't handle exceptions, passing an incorrect argument\r
+            # to the OptionParser will cause LLDB to exit (courtesy of\r
+            # OptParse dealing with argument errors by throwing SystemExit)\r
+            result.SetError("option parsing failed")\r
+            return\r
+\r
+        if options.help:\r
+            self.parser.print_help()\r
+            return\r
+\r
+        gST = exe_ctx.target.FindFirstGlobalVariable('gST')\r
+        if gST.error.fail:\r
+            print('Error: This command requires symbols for gST to be loaded')\r
+            return\r
+\r
+        file = LldbFileObject(exe_ctx.process)\r
+        table = EfiConfigurationTable(file, gST.unsigned)\r
+        if table:\r
+            print(table, '\n')\r
+\r
+\r
+class EfiGuidCommand:\r
+\r
+    def create_options(self):\r
+        ''' standard lldb command help/options parser'''\r
+        usage = "usage: %prog [options]"\r
+        description = '''\r
+            Command that can display all EFI GUID's or give info on a\r
+            specific GUID's\r
+            '''\r
+        self.parser = optparse.OptionParser(\r
+            description=description,\r
+            prog='guid',\r
+            usage=usage,\r
+            add_help_option=False)\r
+\r
+        self.parser.add_option(\r
+            '-n',\r
+            '--new',\r
+            action='store_true',\r
+            dest='new',\r
+            help='Generate a new GUID',\r
+            default=False)\r
+\r
+        self.parser.add_option(\r
+            '-v',\r
+            '--verbose',\r
+            action='store_true',\r
+            dest='verbose',\r
+            help='Also display GUID C structure values',\r
+            default=False)\r
+\r
+        self.parser.add_option(\r
+            '-h',\r
+            '--help',\r
+            action='store_true',\r
+            dest='help',\r
+            help='Show help for the command',\r
+            default=False)\r
+\r
+    def get_short_help(self):\r
+        '''standard lldb function method'''\r
+        return "Display EFI GUID's"\r
+\r
+    def get_long_help(self):\r
+        '''standard lldb function method'''\r
+        return self.help_string\r
+\r
+    def __init__(self, debugger, internal_dict):\r
+        '''standard lldb function method'''\r
+        self.create_options()\r
+        self.help_string = self.parser.format_help()\r
+\r
+    def __call__(self, debugger, command, exe_ctx, result):\r
+        '''standard lldb function method'''\r
+        # Use the Shell Lexer to properly parse up command options just like a\r
+        # shell would\r
+        command_args = shlex.split(command)\r
+\r
+        try:\r
+            (options, args) = self.parser.parse_args(command_args)\r
+            if len(args) >= 1:\r
+                # guid { 0x414e6bdd, 0xe47b, 0x47cc,\r
+                #      { 0xb2, 0x44, 0xbb, 0x61, 0x02, 0x0c,0xf5, 0x16 }}\r
+                # this generates multiple args\r
+                arg = ' '.join(args)\r
+        except ValueError:\r
+            # if you don't handle exceptions, passing an incorrect argument\r
+            # to the OptionParser will cause LLDB to exit (courtesy of\r
+            # OptParse dealing with argument errors by throwing SystemExit)\r
+            result.SetError("option parsing failed")\r
+            return\r
+\r
+        if options.help:\r
+            self.parser.print_help()\r
+            return\r
+\r
+        if options.new:\r
+            guid = uuid.uuid4()\r
+            print(str(guid).upper())\r
+            print(GuidNames.to_c_guid(guid))\r
+            return\r
+\r
+        if len(args) > 0:\r
+            if GuidNames.is_guid_str(arg):\r
+                # guid 05AD34BA-6F02-4214-952E-4DA0398E2BB9\r
+                key = arg.lower()\r
+                name = GuidNames.to_name(key)\r
+            elif GuidNames.is_c_guid(arg):\r
+                # guid { 0x414e6bdd, 0xe47b, 0x47cc,\r
+                #      { 0xb2, 0x44, 0xbb, 0x61, 0x02, 0x0c,0xf5, 0x16 }}\r
+                key = GuidNames.from_c_guid(arg)\r
+                name = GuidNames.to_name(key)\r
+            else:\r
+                # guid gEfiDxeServicesTableGuid\r
+                name = arg\r
+                try:\r
+                    key = GuidNames.to_guid(name)\r
+                    name = GuidNames.to_name(key)\r
+                except ValueError:\r
+                    return\r
+\r
+            extra = f'{GuidNames.to_c_guid(key)}: ' if options.verbose else ''\r
+            print(f'{key}: {extra}{name}')\r
+\r
+        else:\r
+            for key, value in GuidNames._dict_.items():\r
+                if options.verbose:\r
+                    extra = f'{GuidNames.to_c_guid(key)}: '\r
+                else:\r
+                    extra = ''\r
+                print(f'{key}: {extra}{value}')\r
+\r
+\r
+class EfiSymbolicateCommand(object):\r
+    '''Class to abstract an lldb command'''\r
+\r
+    def create_options(self):\r
+        ''' standard lldb command help/options parser'''\r
+        usage = "usage: %prog [options]"\r
+        description = '''Command that can load EFI PE/COFF and TE image\r
+        symbols. If you are having trouble in PEI try adding --pei.\r
+        '''\r
+\r
+        # Pass add_help_option = False, since this keeps the command in line\r
+        # with lldb commands, and we wire up "help command" to work by\r
+        # providing the long & short help methods below.\r
+        self.parser = optparse.OptionParser(\r
+            description=description,\r
+            prog='efi_symbols',\r
+            usage=usage,\r
+            add_help_option=False)\r
+\r
+        self.parser.add_option(\r
+            '-a',\r
+            '--address',\r
+            type="int",\r
+            dest='address',\r
+            help='Load symbols for image at address',\r
+            default=None)\r
+\r
+        self.parser.add_option(\r
+            '-f',\r
+            '--frame',\r
+            action='store_true',\r
+            dest='frame',\r
+            help='Load symbols for current stack frame',\r
+            default=False)\r
+\r
+        self.parser.add_option(\r
+            '-p',\r
+            '--pc',\r
+            action='store_true',\r
+            dest='pc',\r
+            help='Load symbols for pc',\r
+            default=False)\r
+\r
+        self.parser.add_option(\r
+            '--pei',\r
+            action='store_true',\r
+            dest='pei',\r
+            help='Load symbols for PEI (searches every 4 bytes)',\r
+            default=False)\r
+\r
+        self.parser.add_option(\r
+            '-e',\r
+            '--extended',\r
+            action='store_true',\r
+            dest='extended',\r
+            help='Try to load all symbols based on config tables.',\r
+            default=False)\r
+\r
+        self.parser.add_option(\r
+            '-r',\r
+            '--range',\r
+            type="long",\r
+            dest='range',\r
+            help='How far to search backward for start of PE/COFF Image',\r
+            default=None)\r
+\r
+        self.parser.add_option(\r
+            '-s',\r
+            '--stride',\r
+            type="long",\r
+            dest='stride',\r
+            help='Boundary to search for PE/COFF header',\r
+            default=None)\r
+\r
+        self.parser.add_option(\r
+            '-t',\r
+            '--thread',\r
+            action='store_true',\r
+            dest='thread',\r
+            help='Load symbols for the frames of all threads',\r
+            default=False)\r
+\r
+        self.parser.add_option(\r
+            '-h',\r
+            '--help',\r
+            action='store_true',\r
+            dest='help',\r
+            help='Show help for the command',\r
+            default=False)\r
+\r
+    def get_short_help(self):\r
+        '''standard lldb function method'''\r
+        return (\r
+            "Load symbols based on an address that is part of"\r
+            " a PE/COFF EFI image.")\r
+\r
+    def get_long_help(self):\r
+        '''standard lldb function method'''\r
+        return self.help_string\r
+\r
+    def __init__(self, debugger, unused):\r
+        '''standard lldb function method'''\r
+        self.create_options()\r
+        self.help_string = self.parser.format_help()\r
+\r
+    def lldb_print(self, lldb_str):\r
+        # capture command out like an lldb command\r
+        self.result.PutCString(lldb_str)\r
+        # flush the output right away\r
+        self.result.SetImmediateOutputFile(\r
+            self.exe_ctx.target.debugger.GetOutputFile())\r
+\r
+    def __call__(self, debugger, command, exe_ctx, result):\r
+        '''standard lldb function method'''\r
+        # Use the Shell Lexer to properly parse up command options just like a\r
+        # shell would\r
+        command_args = shlex.split(command)\r
+\r
+        try:\r
+            (options, _) = self.parser.parse_args(command_args)\r
+        except ValueError:\r
+            # if you don't handle exceptions, passing an incorrect argument\r
+            # to the OptionParser will cause LLDB to exit (courtesy of\r
+            # OptParse dealing with argument errors by throwing SystemExit)\r
+            result.SetError("option parsing failed")\r
+            return\r
+\r
+        if options.help:\r
+            self.parser.print_help()\r
+            return\r
+\r
+        file = LldbFileObject(exe_ctx.process)\r
+        efi_symbols = EfiSymbols(exe_ctx.target)\r
+        self.result = result\r
+        self.exe_ctx = exe_ctx\r
+\r
+        if options.pei:\r
+            # XIP code ends up on a 4 byte boundary.\r
+            options.stride = 4\r
+            options.range = 0x100000\r
+        efi_symbols.configure_search(options.stride, options.range)\r
+\r
+        if not options.pc and options.address is None:\r
+            # default to\r
+            options.frame = True\r
+\r
+        if options.frame:\r
+            if not exe_ctx.frame.IsValid():\r
+                result.SetError("invalid frame")\r
+                return\r
+\r
+            threads = exe_ctx.process.threads if options.thread else [\r
+                exe_ctx.thread]\r
+\r
+            for thread in threads:\r
+                for frame in thread:\r
+                    res = efi_symbols.address_to_symbols(frame.pc)\r
+                    self.lldb_print(res)\r
+\r
+        else:\r
+            if options.address is not None:\r
+                address = options.address\r
+            elif options.pc:\r
+                try:\r
+                    address = exe_ctx.thread.GetSelectedFrame().pc\r
+                except ValueError:\r
+                    result.SetError("invalid pc")\r
+                    return\r
+            else:\r
+                address = 0\r
+\r
+            res = efi_symbols.address_to_symbols(address.pc)\r
+            print(res)\r
+\r
+        if options.extended:\r
+\r
+            gST = exe_ctx.target.FindFirstGlobalVariable('gST')\r
+            if gST.error.fail:\r
+                print('Error: This command requires symbols to be loaded')\r
+            else:\r
+                table = EfiConfigurationTable(file, gST.unsigned)\r
+                for address, _ in table.DebugImageInfo():\r
+                    res = efi_symbols.address_to_symbols(address)\r
+                    self.lldb_print(res)\r
+\r
+        # keep trying module file names until we find a GUID xref file\r
+        for m in exe_ctx.target.modules:\r
+            if GuidNames.add_build_guid_file(str(m.file)):\r
+                break\r
+\r
+\r
+def CHAR16_TypeSummary(valobj, internal_dict):\r
+    '''\r
+    Display CHAR16 as a String in the debugger.\r
+    Note: utf-8 is returned as that is the value for the debugger.\r
+    '''\r
+    SBError = lldb.SBError()\r
+    Str = ''\r
+    if valobj.TypeIsPointerType():\r
+        if valobj.GetValueAsUnsigned() == 0:\r
+            return "NULL"\r
+\r
+        # CHAR16 *   max string size 1024\r
+        for i in range(1024):\r
+            Char = valobj.GetPointeeData(i, 1).GetUnsignedInt16(SBError, 0)\r
+            if SBError.fail or Char == 0:\r
+                break\r
+            Str += chr(Char)\r
+        return 'L"' + Str + '"'\r
+\r
+    if valobj.num_children == 0:\r
+        # CHAR16\r
+        return "L'" + chr(valobj.unsigned) + "'"\r
+\r
+    else:\r
+        # CHAR16 []\r
+        for i in range(valobj.num_children):\r
+            Char = valobj.GetChildAtIndex(i).data.GetUnsignedInt16(SBError, 0)\r
+            if Char == 0:\r
+                break\r
+            Str += chr(Char)\r
+        return 'L"' + Str + '"'\r
+\r
+    return Str\r
+\r
+\r
+def CHAR8_TypeSummary(valobj, internal_dict):\r
+    '''\r
+    Display CHAR8 as a String in the debugger.\r
+    Note: utf-8 is returned as that is the value for the debugger.\r
+    '''\r
+    SBError = lldb.SBError()\r
+    Str = ''\r
+    if valobj.TypeIsPointerType():\r
+        if valobj.GetValueAsUnsigned() == 0:\r
+            return "NULL"\r
+\r
+        # CHAR8 *   max string size 1024\r
+        for i in range(1024):\r
+            Char = valobj.GetPointeeData(i, 1).GetUnsignedInt8(SBError, 0)\r
+            if SBError.fail or Char == 0:\r
+                break\r
+            Str += chr(Char)\r
+        Str = '"' + Str + '"'\r
+        return Str\r
+\r
+    if valobj.num_children == 0:\r
+        # CHAR8\r
+        return "'" + chr(valobj.unsigned) + "'"\r
+    else:\r
+        # CHAR8 []\r
+        for i in range(valobj.num_children):\r
+            Char = valobj.GetChildAtIndex(i).data.GetUnsignedInt8(SBError, 0)\r
+            if SBError.fail or Char == 0:\r
+                break\r
+            Str += chr(Char)\r
+        return '"' + Str + '"'\r
+\r
+    return Str\r
+\r
+\r
+def EFI_STATUS_TypeSummary(valobj, internal_dict):\r
+    if valobj.TypeIsPointerType():\r
+        return ''\r
+    return str(EfiStatusClass(valobj.unsigned))\r
+\r
+\r
+def EFI_TPL_TypeSummary(valobj, internal_dict):\r
+    if valobj.TypeIsPointerType():\r
+        return ''\r
+    return str(EfiTpl(valobj.unsigned))\r
+\r
+\r
+def EFI_GUID_TypeSummary(valobj, internal_dict):\r
+    if valobj.TypeIsPointerType():\r
+        return ''\r
+    return str(GuidNames(bytes(valobj.data.uint8)))\r
+\r
+\r
+def EFI_BOOT_MODE_TypeSummary(valobj, internal_dict):\r
+    if valobj.TypeIsPointerType():\r
+        return ''\r
+    '''Return #define name for EFI_BOOT_MODE'''\r
+    return str(EfiBootMode(valobj.unsigned))\r
+\r
+\r
+def lldb_type_formaters(debugger, mod_name):\r
+    '''Teach lldb about EFI types'''\r
+\r
+    category = debugger.GetDefaultCategory()\r
+    FormatBool = lldb.SBTypeFormat(lldb.eFormatBoolean)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("BOOLEAN"), FormatBool)\r
+\r
+    FormatHex = lldb.SBTypeFormat(lldb.eFormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("UINT64"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("INT64"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("UINT32"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("INT32"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("UINT16"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("INT16"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("UINT8"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("INT8"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("UINTN"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("INTN"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("CHAR8"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("CHAR16"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier(\r
+        "EFI_PHYSICAL_ADDRESS"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier(\r
+        "PHYSICAL_ADDRESS"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier("EFI_LBA"), FormatHex)\r
+    category.AddTypeFormat(\r
+        lldb.SBTypeNameSpecifier("EFI_BOOT_MODE"), FormatHex)\r
+    category.AddTypeFormat(lldb.SBTypeNameSpecifier(\r
+        "EFI_FV_FILETYPE"), FormatHex)\r
+\r
+    #\r
+    # Smart type printing for EFI\r
+    #\r
+\r
+    debugger.HandleCommand(\r
+        f'type summary add GUID - -python-function '\r
+        f'{mod_name}.EFI_GUID_TypeSummary')\r
+    debugger.HandleCommand(\r
+        f'type summary add EFI_GUID --python-function '\r
+        f'{mod_name}.EFI_GUID_TypeSummary')\r
+    debugger.HandleCommand(\r
+        f'type summary add EFI_STATUS --python-function '\r
+        f'{mod_name}.EFI_STATUS_TypeSummary')\r
+    debugger.HandleCommand(\r
+        f'type summary add EFI_TPL - -python-function '\r
+        f'{mod_name}.EFI_TPL_TypeSummary')\r
+    debugger.HandleCommand(\r
+        f'type summary add EFI_BOOT_MODE --python-function '\r
+        f'{mod_name}.EFI_BOOT_MODE_TypeSummary')\r
+\r
+    debugger.HandleCommand(\r
+        f'type summary add CHAR16 --python-function '\r
+        f'{mod_name}.CHAR16_TypeSummary')\r
+\r
+    # W605 this is the correct escape sequence for the lldb command\r
+    debugger.HandleCommand(\r
+        f'type summary add --regex "CHAR16 \[[0-9]+\]" '  # noqa: W605\r
+        f'--python-function {mod_name}.CHAR16_TypeSummary')\r
+\r
+    debugger.HandleCommand(\r
+        f'type summary add CHAR8 --python-function '\r
+        f'{mod_name}.CHAR8_TypeSummary')\r
+\r
+    # W605 this is the correct escape sequence for the lldb command\r
+    debugger.HandleCommand(\r
+        f'type summary add --regex "CHAR8 \[[0-9]+\]"  '  # noqa: W605\r
+        f'--python-function {mod_name}.CHAR8_TypeSummary')\r
+\r
+\r
+class LldbWorkaround:\r
+    needed = True\r
+\r
+    @classmethod\r
+    def activate(cls):\r
+        if cls.needed:\r
+            lldb.debugger.HandleCommand("process handle SIGALRM -n false")\r
+            cls.needed = False\r
+\r
+\r
+def LoadEmulatorEfiSymbols(frame, bp_loc, internal_dict):\r
+    #\r
+    # This is an lldb breakpoint script, and assumes the breakpoint is on a\r
+    # function with the same prototype as SecGdbScriptBreak(). The\r
+    # argument names are important as lldb looks them up.\r
+    #\r
+    # VOID\r
+    # SecGdbScriptBreak (\r
+    #   char                *FileName,\r
+    #   int                 FileNameLength,\r
+    #   long unsigned int   LoadAddress,\r
+    #   int                 AddSymbolFlag\r
+    #   )\r
+    # {\r
+    #   return;\r
+    # }\r
+    #\r
+    # When the emulator loads a PE/COFF image, it calls the stub function with\r
+    # the filename of the symbol file, the length of the FileName, the\r
+    # load address and a flag to indicate if this is a load or unload operation\r
+    #\r
+    LldbWorkaround().activate()\r
+\r
+    symbols = EfiSymbols(frame.thread.process.target)\r
+    LoadAddress = frame.FindVariable("LoadAddress").unsigned\r
+    if frame.FindVariable("AddSymbolFlag").unsigned == 1:\r
+        res = symbols.address_to_symbols(LoadAddress)\r
+    else:\r
+        res = symbols.unload_symbols(LoadAddress)\r
+    print(res)\r
+\r
+    # make breakpoint command continue\r
+    return False\r
+\r
+\r
+def __lldb_init_module(debugger, internal_dict):\r
+    '''\r
+    This initializer is being run from LLDB in the embedded command interpreter\r
+    '''\r
+\r
+    mod_name = Path(__file__).stem\r
+    lldb_type_formaters(debugger, mod_name)\r
+\r
+    # Add any commands contained in this module to LLDB\r
+    debugger.HandleCommand(\r
+        f'command script add -c {mod_name}.EfiSymbolicateCommand efi_symbols')\r
+    debugger.HandleCommand(\r
+        f'command script add -c {mod_name}.EfiGuidCommand guid')\r
+    debugger.HandleCommand(\r
+        f'command script add -c {mod_name}.EfiTableCommand table')\r
+    debugger.HandleCommand(\r
+        f'command script add -c {mod_name}.EfiHobCommand hob')\r
+    debugger.HandleCommand(\r
+        f'command script add -c {mod_name}.EfiDevicePathCommand devicepath')\r
+\r
+    print('EFI specific commands have been installed.')\r
+\r
+    # patch the ctypes c_void_p values if the debuggers OS and EFI have\r
+    # different ideas on the size of the debug.\r
+    try:\r
+        patch_ctypes(debugger.GetSelectedTarget().addr_size)\r
+    except ValueError:\r
+        # incase the script is imported and the debugger has not target\r
+        # defaults to sizeof(UINTN) == sizeof(UINT64)\r
+        patch_ctypes()\r
+\r
+    try:\r
+        target = debugger.GetSelectedTarget()\r
+        if target.FindFunctions('SecGdbScriptBreak').symbols:\r
+            breakpoint = target.BreakpointCreateByName('SecGdbScriptBreak')\r
+            # Set the emulator breakpoints, if we are in the emulator\r
+            cmd = 'breakpoint command add -s python -F '\r
+            cmd += f'efi_lldb.LoadEmulatorEfiSymbols {breakpoint.GetID()}'\r
+            debugger.HandleCommand(cmd)\r
+            print('Type r to run emulator.')\r
+        else:\r
+            raise ValueError("No Emulator Symbols")\r
+\r
+    except ValueError:\r
+        # default action when the script is imported\r
+        debugger.HandleCommand("efi_symbols --frame --extended")\r
+        debugger.HandleCommand("register read")\r
+        debugger.HandleCommand("bt all")\r
+\r
+\r
+if __name__ == '__main__':\r
+    pass\r