Andrew Fish


Caveat Emptor as Xcode clang is my daily driver, so I don’t really use GCC.

As Ard mentioned the ELF image is converted to a PE/COFF executable for EFI, and the symbols are stripped so the build ID is not going to help you. In PE/COFF there is something called a Debug Directory Entry[1]. It can point to a .debug section, or info in any section. The Debug Directory Entry starts with a 4 byte structure that defines its layout [1].

The 2nd issue you will have is the PE/COFF images are relocatable executables so they are not loaded at their linked address. Thus to load symbols you need to know the slide (delta of load address to link address) which you can derive from the load address of the image, and a path to the external symbols file. PE/COFF has the property that all the PE/COFF headers are loaded into memory when a PE/COFF image is loaded, this will be important later.

When you get past early PEI[2] and code is no longer XIP (eXecute In Place) you need to know the load address of the code to load symbols. So you end up with these options:
1) Load symbols for PC (This works for XIP too).
2) Load symbols for frame. Really just 1) for each frame PC.
3) Load symbols based on some table that has all the load info. This is the example from the EFI Spec [3].

To implement 1) you start with the PC and walk backwards in memory looking for the PE/COFF (or DOS) header. That header is the load address of the module. From the headers you can find the debug directory entry. The Debug Directory entry can then be decoded and that gives you (maybe) a unique identifier and the file name of the symbols the debugger needs to load. At that point you can tell the debugger this debug info matches the module loaded at this address. If your debugger knows PE/COFF you just have to point at the DOS or PE/COFF header.

So given I use lldb and the macOS flavor of lldb does not parse PE/COFF we had to write lldb Python to do all of the above and some more. So above is the how to do everything yourself, or theory of operation.

There is also the primitive look at the serial logs and load symbols by hand using debugger commands.

In terms of `git grep` in the edk2 look for EFI_IMAGE_DEBUG_DIRECTORY_ENTRY. There is a lib function called PeCoffLoaderGetPdbPointer() that returns a pointer to the PE/COFF Debug Directory Entry, it has the name pdb as that is the name of the windows symbols file that the Debug Directory Entry generally points to for VC++. For Xcode/clang the Debug Directory Entry entry has the UUID of mach-O executable (that got converted into a PE/COFF) and lldb can look up the symbols that way, other toolchains need the path to the debug file to load. So for example the OVMF exception handler [4] will dump out info about the faulting PC.

Sorry for the very generic answer, but I though it might be helpful for other folks too. You might want to ask a question about your target architecture, toolchain, and debugger on the mailing to find out what of the above is available for your toolchain with the edk2 for free. If you are interested in adding more features above is an outline of how it works and you can always ask more detailed questions on the mailing list.


[2] The build kicks out an <FvName> file that contains the info you need to load symbols.
For example: Build/OvmfX64/DEBUG_XCODE5/FV/

PeiCore (Fixed Flash Address, BaseAddress=0x0000820120, EntryPoint=0x000082b012)
(GUID=52C05B14-0B98-496C-BC3B-04B50211D680 .textbaseaddress=0x0000820360 .databaseaddress=0x000082e420)

PcdPeim (Fixed Flash Address, BaseAddress=0x000082ed00, EntryPoint=0x0000831a62)
(GUID=9B3ADA4F-AE56-4C24-8DEA-F03B7558AE50 .textbaseaddress=0x000082ef40 .databaseaddress=0x0000834ac0)


[4] UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c:130: PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *) Pe32Data);


Andrew Fish

On Mar 24, 2021, at 8:25 AM, Ard Biesheuvel <> wrote:

On Wed, 24 Mar 2021 at 16:18, Laszlo Ersek < <>> wrote:


On 03/24/21 02:49, Hernandez Miramontes, Jose Miguel wrote:

Is anyone familiar with this file? (Edk2\BaseTools\Scripts\
I would like to understand more what it does and why it is needed.

When looking at the .efi output of genfw, it seems the .build-id section generated by gcc is discarded.
git-blame is your friend. It leads to commit 7fd5d619806d ("BaseTools
GCC: drop GNU notes section from EFI image", 2016-08-02).
The build-id is used by Linux distros when they ship debug symbols
with production shared libraries. The build-id permits the loader to
look up the correct file, and confirm that the versions match.

In EDK2, the ELF binary is only an intermediate artifact, and so I
fail to see why we would need build IDs here. If the ELF binary was
built with debug symbols, they will be in the binary itself, not in a
side loaded symbol file.

Join to automatically receive all group messages.