https://retrage.github.io/2019/12/05/debugging-ovmf-en.html Building EDK2 Build EDK2 using gcc as usual. $ git clone git@github.com:tianocore/edk2.git $ cd edk2 $ git submodule update --init --recursive $ source edksetup.sh $ make -C BaseTools $ build -p OvmfPkg/OvmfPkgX64.dsc -b DEBUG -a X64 -t GCC5 To make debugging easy, create a Makefile as follow. Note that we have to connect debugcon at 0x402 to dump debug information (debug.log) from OVMF[4]. #!/usr/bin/env make SHELL=/bin/bash LOG=debug.log OVMFBASE=edk2/Build/OvmfX64/DEBUG_GCC5/ OVMFCODE=$(OVMFBASE)/FV/OVMF_CODE.fd OVMFVARS=$(OVMFBASE)/FV/OVMF_VARS.fd QEMU=qemu-system-x86_64 QEMUFLAGS=-drive format=raw,file=fat:rw:image \ -drive if=pflash,format=raw,readonly,file=$(OVMFCODE) \ -drive if=pflash,format=raw,file=$(OVMFVARS) \ -debugcon file:$(LOG) -global isa-debugcon.iobase=0x402 \ -serial stdio \ -nographic \ -nodefaults run: $(QEMU) $(QEMUFLAGS) debug: $(QEMU) $(QEMUFLAGS) -s -S .PHONY: run debug Before debugging, run the firmware to get debug.log. It may be better to provide startup.nsh script. $ make run Now, we have debug.log. It includes the addresses of loaded UEFI images like this: Loading PEIM at 0x00007EA8000 EntryPoint=0x00007EAB0BC DxeCore.efi Next, extract text section (.text) RVA from *.efi PE binaries. This can be done by readelf if it is ELF, but the images are PE format. Here we use retrage/peinfo[3]. $ git clone git@github.com:retrage/peinfo.git $ cd peinfo $ make peinfo extracts section information from a binary. This time we want to know VirtualAddress in RVA. Name: .text VirtualSize: 0x000204c0 VirtualAddress: 0x00000240 SizeOfRawData: 0x000204c0 PointerToRawData: 0x00000240 PointerToRelocations: 0x00000000 PointerToLinenumbers: 0x00000000 NumberOfRelocations: 0x0000 NumberOfLinenumbers: 0x0000 Characteristics: 0x60000020 Run following bash script with debug.log and peinfo. This outputs a snippet of GDB script that adds symbol information (add-symbol-file). It calculates the address of UEFI image text section from base address and VirtualAddress. #!/bin/bash LOG="debug.log" BUILD="edk2/Build/OvmfX64/DEBUG_GCC5/X64" PEINFO="peinfo/peinfo" cat ${LOG} | grep Loading | grep -i efi | while read LINE; do BASE="`echo ${LINE} | cut -d " " -f4`" NAME="`echo ${LINE} | cut -d " " -f6 | tr -d "[:cntrl:]"`" ADDR="`${PEINFO} ${BUILD}/${NAME} \ | grep -A 5 text | grep VirtualAddress | cut -d " " -f2`" TEXT="`python -c "print(hex(${BASE} + ${ADDR}))"`" SYMS="`echo ${NAME} | sed -e "s/\.efi/\.debug/g"`" echo "add-symbol-file ${BUILD}/${SYMS} ${TEXT}" done $ bash gen_symbol_offsets.sh > gdbscript cat gdb The generated GDB script is like this: add-symbol-file edk2/Build/OvmfX64/DEBUG_GCC5/X64/PcdPeim.debug 0x82c380 add-symbol-file edk2/Build/OvmfX64/DEBUG_GCC5/X64/ReportStatusCodeRouterPei.debug 0x831080 add-symbol-file edk2/Build/OvmfX64/DEBUG_GCC5/X64/StatusCodeHandlerPei.debug 0x833100 add-symbol-file edk2/Build/OvmfX64/DEBUG_GCC5/X64/PlatformPei.debug 0x835100 add-symbol-file edk2/Build/OvmfX64/DEBUG_GCC5/X64/PeiCore.debug 0x7ee8240 add-symbol-file edk2/Build/OvmfX64/DEBUG_GCC5/X64/DxeIpl.debug 0x7ee3240 add-symbol-file edk2/Build/OvmfX64/DEBUG_GCC5/X64/S3Resume2Pei.debug 0x7edf240 add-symbol-file edk2/Build/OvmfX64/DEBUG_GCC5/X64/CpuMpPei.debug 0x7ed6240 add-symbol-file edk2/Build/OvmfX64/DEBUG_GCC5/X64/DxeCore.debug 0x7ea8240 add-symbol-file edk2/Build/OvmfX64/DEBUG_GCC5/X64/DevicePathDxe.debug 0x7b8f240 Now we are ready. $ less debug.log ... The 0th FV start address is 0x0000082000 ... Loading PEIM at 0x0000082BFC0 Entry Point = 0x0000082F40A PcdPeim.efi ... $ make debug Let’s place a breakpoint at BootServices->HandleProtocol(). (gdb) source gdbscript . . . add symbol table from file "/home/koenigr/Memtest/git/edk2/Build/OvmfX64/DEBUG_GCC5/X64/UsbBusDxe.debug" at .text_addr = 0x6c85240 add symbol table from file "/home/koenigr/Memtest/git/edk2/Build/OvmfX64/DEBUG_GCC5/X64/UsbKbDxe.debug" at .text_addr = 0x6cb3240 add symbol table from file "/home/koenigr/Memtest/git/edk2/Build/OvmfX64/DEBUG_GCC5/X64/UsbMassStorageDxe.debug" at .text_addr = 0x6c6d240 add symbol table from file "/home/koenigr/Memtest/git/edk2/Build/OvmfX64/DEBUG_GCC5/X64/QemuVideoDxe.debug" at .text_addr = 0x6c66240 add symbol table from file "/home/koenigr/Memtest/git/edk2/Build/OvmfX64/DEBUG_GCC5/X64/VirtioGpuDxe.debug" at .text_addr = 0x6c60240 add symbol table from file "/home/koenigr/Memtest/git/edk2/Build/OvmfX64/DEBUG_GCC5/X64/Shell.debug" at .text_addr = 0x64f5240 (gdb) info functions CoreHandleProtocol All functions matching regular expression "CoreHandleProtocol": File /.../edk2/MdeModulePkg/Core/Dxe/Hand/Handle.c: EFI_STATUS CoreHandleProtocol(EFI_HANDLE, EFI_GUID *, void **); (gdb) info address CoreHandleProtocol Symbol "CoreHandleProtocol" is a function at address 0x7ea4aa9. (gdb) b CoreHandleProtocol (gdb) info symbol 0x82F40A _ModuleEntryPoint in section .text of /home/koenigr/Memtest/git/edk2/Build/OvmfX64/DEBUG_GCC5/X64/PcdPeim.debug (gdb) b *0x82F40A Breakpoint 2 at 0x82f40a: file /home/koenigr/Memtest/git/edk2/MdePkg/Library/PeimEntryPoint/PeimEntryPoint.c, line 33. (gdb) target remote localhost:1234 Remote debugging using localhost:1234 warning: No executable has been specified and target does not support determining executable automatically. Try using the "file" command. 0x000000000000fff0 in ?? () (gdb) c The debugger stops, and we can do source code level debug. !!!!!!!!!!!!!!!!!!!!!!!!!!! DOES NOT WORK !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ┌──/home/akira/src/ovmf-debug/edk2/MdeModulePkg/Core/Dxe/Hand/Handle.c──────┐ │933 CoreHandleProtocol ( │ │934 IN EFI_HANDLE UserHandle, │ │935 IN EFI_GUID *Protocol, │ │936 OUT VOID **Interface │ │937 ) │ B+>│938 { │ │939 return CoreOpenProtocol ( │ │940 UserHandle, │ │941 Protocol, │ │942 Interface, │ │943 gDxeCoreImageHandle, │ │944 NULL, │ │945 EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL │ └───────────────────────────────────────────────────────────────────────────┘ remote Thread 1 In: CoreHandleProtocol L938 PC: 0x7eb6ad4 (gdb)