From 129c582687484ac3f6aa2d5eeb6e517c053337eb Mon Sep 17 00:00:00 2001
From: Gary Ching-Pang Lin <glin@suse.com>
Date: Tue, 24 Jun 2014 11:57:32 +0800
Subject: [PATCH 1/3] Add DebugPkg

---
 DebugPkg/DebugPkg.dec        |  34 ++++
 DebugPkg/GdbSyms/GdbSyms.c   |  70 +++++++
 DebugPkg/GdbSyms/GdbSyms.inf |  57 ++++++
 DebugPkg/Scripts/gdb_uefi.py | 348 +++++++++++++++++++++++++++++++++++
 4 files changed, 509 insertions(+)
 create mode 100644 DebugPkg/DebugPkg.dec
 create mode 100644 DebugPkg/GdbSyms/GdbSyms.c
 create mode 100644 DebugPkg/GdbSyms/GdbSyms.inf
 create mode 100644 DebugPkg/Scripts/gdb_uefi.py

diff --git a/DebugPkg/DebugPkg.dec b/DebugPkg/DebugPkg.dec
new file mode 100644
index 000000000000..e12401de3e4a
--- /dev/null
+++ b/DebugPkg/DebugPkg.dec
@@ -0,0 +1,34 @@
+## @file
+#  Debug package - various useful stuff for debugging.
+#
+#  Copyright (c) 2006 - 2011, Andrei Warkentin <andreiw@motorola.com>
+#
+#  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]
+  DEC_VERSION                    = 0x00010005
+  PACKAGE_NAME                   = DebugPkg
+  PACKAGE_GUID                   = 2d234f34-50e5-4b9d-b8e3-5562334d87e5
+  PACKAGE_VERSION                = 0.1
+
+[Includes]
+  Include
+
+[Guids]
+
+[Protocols]
+
+[PcdsFixedAtBuild]
+
+[PcdsDynamic]
+
+[LibraryClasses]
+
diff --git a/DebugPkg/GdbSyms/GdbSyms.c b/DebugPkg/GdbSyms/GdbSyms.c
new file mode 100644
index 000000000000..2551dfab90b6
--- /dev/null
+++ b/DebugPkg/GdbSyms/GdbSyms.c
@@ -0,0 +1,70 @@
+/** @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 <andreiw@motorola.com>
+
+  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 <Library/UefiLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PcdLib.h>
+#include <Guid/DebugImageInfoTable.h>
+
+/**
+  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_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) &EIDDE  |
+    (UINTN) &EIDCNE |
+    (UINTN) &EIDCRE |
+    (UINTN) &EIDCME |
+     1
+    ;
+  return !!Dummy & EFI_SUCCESS;
+}
+
+
diff --git a/DebugPkg/GdbSyms/GdbSyms.inf b/DebugPkg/GdbSyms/GdbSyms.inf
new file mode 100644
index 000000000000..afb78871b818
--- /dev/null
+++ b/DebugPkg/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 <andreiw@motorola.com>
+#
+#  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]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  DxeServicesTableLib
+  HobLib
+  MemoryAllocationLib
+  PcdLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Guids]
+
+[Protocols]
+
+[Depex]
+  TRUE
+
diff --git a/DebugPkg/Scripts/gdb_uefi.py b/DebugPkg/Scripts/gdb_uefi.py
new file mode 100644
index 000000000000..dac215c53893
--- /dev/null
+++ b/DebugPkg/Scripts/gdb_uefi.py
@@ -0,0 +1,348 @@
+"""
+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
+(gdb) taget remote ....
+(gdb) source Scripts/gdb_uefi.py
+(gdb) reload-uefi -o /path/to/GdbSyms.dll
+
+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
+
+__license__ = "BSD"
+__version = "1.0.0"
+__maintainer__ = "Andrei Warkentin"
+__email__ = "andrey.warkentin@gmail.com"
+__status__ = "Works"
+
+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:
+            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']
+
+            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 a UTF16 string corresponding to a (CHAR16 *) value in EFI.
+    #
+
+    def parse_utf16 (self, value):
+        index = 0
+        data = array.array ('H')
+        while value[index] != 0:
+            data.append (value[index])
+            index = index + 1
+        return data.tostring ().decode ('utf-16')
+
+    #
+    # 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)
+
+    #
+    # 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 (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)
+
+    #
+    # Parses an EFI_LOADED_IMAGE_PROTOCOL, figuring out the symbol file name.
+    # This file name is then appended to list of loaded symbols.
+    #
+    # TBD: Support TE images.
+    #
+
+    def parse_image (self, image, syms):
+        base = image['ImageBase']
+        pe = self.pe_headers (base)
+        opt = self.pe_optional (pe)
+        sym_name = self.pe_parse_debug (pe)
+
+        # 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 ()
+            syms.append ("add-symbol-file %s 0x%x" % \
+                             (sym_name,
+                              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:
+            print sym
+            gdb.execute (sym)
+
+    #
+    # 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)" % \
+            (self.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, err:
+            self.usage ()
+            return
+        for opt, arg in opts:
+            if opt == "-o":
+                self.offset_by_headers = True
+
+        if len(args) < 1:
+            self.usage ()
+            return
+
+        gdb.execute ("symbol-file")
+        gdb.execute ("symbol-file %s" % args[0])
+        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)
+
+ReloadUefi ()
+
+
-- 
2.23.0


From 84c13bdbdc050c12c55de76ff62ed3f1b89c8f63 Mon Sep 17 00:00:00 2001
From: Gary Ching-Pang Lin <glin@suse.com>
Date: Tue, 24 Jun 2014 11:59:02 +0800
Subject: [PATCH 2/3] Compile DebugPkg and load .debug files

---
 DebugPkg/Scripts/gdb_uefi.py | 2 ++
 OvmfPkg/OvmfPkgX64.dsc       | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/DebugPkg/Scripts/gdb_uefi.py b/DebugPkg/Scripts/gdb_uefi.py
index dac215c53893..3db87a4de430 100644
--- a/DebugPkg/Scripts/gdb_uefi.py
+++ b/DebugPkg/Scripts/gdb_uefi.py
@@ -18,6 +18,7 @@ images were converted from MACH-O or ELF binaries.
 import array
 import getopt
 import binascii
+import re
 
 __license__ = "BSD"
 __version = "1.0.0"
@@ -243,6 +244,7 @@ class ReloadUefi (gdb.Command):
             base = base + opt['SizeOfHeaders']
         if sym_name != self.EINVAL:
             sym_name = sym_name.cast (self.ptype('CHAR8')).string ()
+            sym_name = re.sub(r"\.dll$", ".debug", sym_name)
             syms.append ("add-symbol-file %s 0x%x" % \
                              (sym_name,
                               long (base)))
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index ba7a75884490..c35371447a75 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -923,3 +923,5 @@ [Components]
       NULL|SecurityPkg/Library/HashInstanceLibSm3/HashInstanceLibSm3.inf
   }
 !endif
+
+  DebugPkg/GdbSyms/GdbSyms.inf
-- 
2.23.0


From dcbdaa6cccce44133f8cb6a1a78a0cebd10ac172 Mon Sep 17 00:00:00 2001
From: Gary Lin <glin@suse.com>
Date: Mon, 16 Oct 2017 16:32:27 +0800
Subject: [PATCH 3/3] DebugPkg: Print the local variables

With the "--whole-archive" linker option, several symbols in GdbSyms.dll
such as EFI_SYSTEM_TABLE_POINTER would be dropped, and the gdb_uefi
script could not find those symbols anymore. This commit add a few
useless DEBUG statements to keep those symbols.
---
 DebugPkg/GdbSyms/GdbSyms.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/DebugPkg/GdbSyms/GdbSyms.c b/DebugPkg/GdbSyms/GdbSyms.c
index 2551dfab90b6..0d591e5a8f22 100644
--- a/DebugPkg/GdbSyms/GdbSyms.c
+++ b/DebugPkg/GdbSyms/GdbSyms.c
@@ -64,6 +64,14 @@ Initialize (
     (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__, &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;
 }
 
-- 
2.23.0

