This is a longish and quite raw email, but (for me at least) a big
improvement, so I'll share my findings here.

(1) Clone Andrei Warkentin's repo
<https://github.com/andreiw/andreiw-wip.git>. The commit I'm testing at
is 1ab3fa97.

  git clone https://github.com/andreiw/andreiw-wip.git
  cd andreiw-wip
  git checkout 1ab3fa97

(2) From the root of your clone copy the subdirectory "uefi/DebugPkg" to
your edk2 workspace:

  cp -av uefi/DebugPkg $WORKSPACE

(3) Apply the following patch to OvmfPkg/OvmfPkgX64.dsc:

-----------------
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 97fdedb..794521b 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -592,3 +592,4 @@
 !endif

   OvmfPkg/PlatformDxe/Platform.inf
+  DebugPkg/GdbSyms/GdbSyms.inf
-----------------

Note that we don't modify the FDF file.

(4) Build OVMF (X64) as usual. Don't clean the build directory.

(5) Start OVMF in qemu. Pass the "-s" option to qemu. (Shorthand for
"-gdb tcp::1234").

(6) Start gdb in another terminal, and issue the following commands:

  (gdb) set architecture i386:x86-64:intel
  (gdb) target remote localhost:1234
  (gdb) source [WORKSPACE]/DebugPkg/Scripts/gdb_uefi.py
  (gdb) reload-uefi -o 
[WORKSPACE]/Build/OvmfX64/DEBUG_GCC48/X64/DebugPkg/GdbSyms/GdbSyms/DEBUG/GdbSyms.dll

(7) Issue "bt" at the gdb prompt:

  (gdb) bt

... And then you'll get a stack trace, but the local variables cannot be
displayed. Each will be reported as:

<error reading variable: can't compute CFA for this frame>

In order to fix this, we must understand the above procedure, so let's
see the explanation (sometimes quite the hand-waving, sorry).

(a) Andrei's DebugPkg/GdbSyms module does nothing at all. We don't even
include it in the final firmware image (see the note about the FDF in
(3)).

The reason we need it because it uses the following *types*:

  EFI_SYSTEM_TABLE_POINTER
  EFI_DEBUG_IMAGE_INFO_TABLE_HEADER
  EFI_IMAGE_DOS_HEADER
  EFI_IMAGE_OPTIONAL_HEADER_UNION
  EFI_IMAGE_DEBUG_DIRECTORY_ENTRY
  EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY
  EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY
  EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY

When we build the tree in (4), the layout of the above structures
(names, fields, offsets -- basically the type definitions) will be
generated into the GdbSyms.dll file, as debug symbols.

(b) The "DebugPkg/Scripts/gdb_uefi.py" script is a gdb extension (a new
command), written in Python. It is run "under" gdb, and can communicate
with gdb in a programmatic fashion. It can peek and poke at memory, cast
pointers, ie. whatever you can do at the gdb prompt, but it can do it
algorithmically.

The script defines a new command, "reload-uefi". "reload-uefi" does the
following:

(b1) it loads the *type information* for the structures listed under
(a), from the GdbSyms.dll file. GdbSyms.dll is used for nothing else.

(b2) It locates the EFI system table in (guest) memory, looking at each
multiple of 4MB, checking for the signature and verifying the CRC.

(b3) When the system table is found, it follows a long chain of pointers
and structures (relying heavily on the struct types from (a)) until it
lands on the list of loaded EFI images.

(b4) It iterates over the list of loaded EFI images. For each image:
- it reads the header that identifies the "debug symbols file" for the
  image,
- it makes gdb load that symbol file,
- and it asks gdb to "foist" that symbol file on the base address of
  the loaded image in question.

The "debug symbols file" is just the dll file in the Build directory.
Eg. for "QemuVideoDxe.efi", it is "QemuVideoDxe.dll" (with full,
absolute path under $WORKSPACE of course).

(c) The "bt" command in (7) should just work, but it doesn't really.


The problem is that for local variables to be displayed, the .eh_frame
section should be present in the DLL files (eg. QemuVideoDxe.dll), but
it is absent.

https://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html

It is absent for a reason: GenFw cannot cope with (relocation entries
for) .eh_frame sections. We've discussed this earlier on the list, but
for illustration it is simplest to look at the build log. I'm removing
the longest common prefix from pathnames, for readability:

> "ld" -o QemuVideoDxe.dll ... \
>     --script=.../BaseTools/Scripts/gcc4.4-ld-script ...

In this step, QemuVideoDxe.dll is created. The referenced linker script
contains

  .eh_frame ALIGN(0x20) :
  {
    KEEP (*(.eh_frame))
  }

so the DLL does contain an eh_frame section at this point.

> "objcopy"  QemuVideoDxe.dll

Not sure why this is useful, I think this command does nothing.

> cp -f QemuVideoDxe.dll QemuVideoDxe.debug

Aha! The DLL, with the .eh_frame section, is saved as
"QemuVideoDxe.debug".

> objcopy --strip-unneeded -R .eh_frame QemuVideoDxe.dll

The .eh_frame section is hereby removed from the DLL.

> objcopy --add-gnu-debuglink=QemuVideoDxe.debug QemuVideoDxe.dll

This adds a .gnu_debuglink section to the DLL, pointing at the
"QemuVideoDxe.debug" file. (Only the last pathname component, ie. the
filename, is saved, in that section.)

This might be helpful for other debugging solutions, but it doesn't seem
to help with qemu+gdb at all.

> "GenFw" -e UEFI_DRIVER -o QemuVideoDxe.efi QemuVideoDxe.dll

This is where GenFw converts the DLL to an EFI executable.

*If* we had the .eh_frame section still present in the DLL, this command
would fail, because GenFw doesn't know how to handle .eh_frame.

(More precisely: it doesn't know how to handle relocations for
.eh_frame, ie. '.rela.eh_frame'. See the discussion at
<http://thread.gmane.org/gmane.comp.bios.tianocore.devel/3796/focus=3800>.)

Because the build process has removed the .eh_frame (and the referrer
.rela.eh_frame) sections, GenFw succeeds. But, as a consequence, the DLL
file will *also* be unusable for printing local variables, because it
has no (or insufficient) call frame information.

The solution (or at least "one" solution) is to update Andrei's gdb
script. Rather than loading the DLL file for the EFI file (see (b4)),
let's load the DEBUG file immediately. The DEBUG file is the original
version of the DLL file, ie. from before the stripping.

Hence, after step (2), before step (3), the following patch should be
applied:

-----------------
--- DebugPkg/Scripts/gdb_uefi.py.orig   2014-06-18 22:36:42.131171623 +0200
+++ DebugPkg/Scripts/gdb_uefi.py        2014-06-18 23:24:56.086566783 +0200
@@ -18,6 +18,7 @@
 import array
 import getopt
 import binascii
+import re

 __license__ = "BSD"
 __version = "1.0.0"
@@ -243,6 +244,7 @@
             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)))
-----------------

And then local variables are displayed too.

Thanks,
Laszlo

------------------------------------------------------------------------------
HPCC Systems Open Source Big Data Platform from LexisNexis Risk Solutions
Find What Matters Most in Your Big Data with HPCC Systems
Open Source. Fast. Scalable. Simple. Ideal for Dirty Data.
Leverages Graph Analysis for Fast Processing & Easy Data Exploration
http://p.sf.net/sfu/hpccsystems
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-devel

Reply via email to