Search code examples
gdbqemuuefigrub2

Debug GRUB2 EFI image running on QEMU


What I want to achieve

I want to customize a GRUB EFI image, and debug it while running on QEMU.

So I'm trying to debug a vanilla GRUB image before customizing it.

What I have done so far

I downloaded GRUB2 from http://git.savannah.gnu.org and compiled it:

./autogen.sh
./configure --prefix=`pwd`/local --with-platform=efi --target=i386 CFLAGS=-g
make
make install

Then, generated a trivial EFI image with:

./local/bin/grub-mkstandalone -O i386-efi -o bootIA32.efi

And put it in a disk image file:

qemu-img create -f raw hda.img 1G
mkfs.fat hda.img
sudo mount -o uid=$UID hda.img /mnt
mkdir -p /mnt/efi/boot/
mv bootIA32.efi /mnt/efi/boot/
sudo umount /mnt

In order to boot it, I compiled an IA32 OVMF.fd to use it with QEMU:

qemu-system-i386 -bios $UDK_PATH/Build/OvmfIa32/RELEASE_GCC48/FV/OVMF.fd \
                 -hda hda.img

It boots correctly, giving me a grub shell.


Where I got stuck

Now, I want to debug GRUB. So I called QEMU with additional parameters:

qemu-system-i386 -bios $UDK_PATH/Build/OvmfIa32/RELEASE_GCC48/FV/OVMF.fd \
                 -hda hda.img \
                 -s -S

And attached gdb to QEMU:

cd grub-core/
gdb -x gdb_grub

However, seems that debug symbols are missing:

GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1
(...)
For help, type "help".
Type "apropos word" to search for commands related to "word".
0x0000fff0 in grub_disk_cache_table ()
Breakpoint 1 at 0x49b1: file kern/dl.c, line 53.
(gdb) n
Single stepping until exit from function grub_disk_cache_table,
which has no line number information.
0xffffff75 in ?? ()
(gdb)

What am I doing wrong?


After adding symbols

@unixsmurf it seems to be loading the debug symbols when I use symbol-file command. Indeed, gdb says

(gdb) symbol-file ../local/lib/grub/i386-efi/kernel.exec
Reading symbols from ../local/lib/grub/i386-efi/kernel.exec...done.

However, I still cannot step with next command, which returns

(gdb) n
Single stepping until exit from function grub_disk_cache_table,
which has no line number information.
0xffffff75 in ?? ()

I'd like to, for instance, set a breakpoint in grub_core/kern/main.c:grub_main function and run it step by step.

But although the breakpoint is set, when I continue the execution, GRUB reaches the shell without stopping on the breakpoint:

(gdb) b main.c:grub_main
Note: breakpoint 2 also set at pc 0x6082.
Breakpoint 3 at 0x6082: file kern/main.c, line 266.
(gdb) c
Continuing.

Solution

  • There are no debug symbols included in your bootIA32.efi image. The gdb_grub script attempts to do this, but since it was designed for BIOS (not UEFI), and appears to basically be included and generated mainly by accident, this does not really function anymore - since the EFI version of GRUB is dynamically to an address decided at runtime.

    Now, with a bit of trickery (and an OVMF_CODE.fd built with -D DEBUG_ON_SERIAL_PORT), I can see that as long as I don't run any other commands before entering GRUB, I always see:

    Loading driver at 0x0003DDE9000 EntryPoint=0x0003DDE9400
    

    So with a horrible hack to gdb_grub, changing the line near the end:

    file kernel.exec
    

    to

    add-symbol-file kernel.exec 0x0003DDE9400
    

    I end up with a situation instead of

    add symbol table from file "kernel.exec" at
    .text_addr = 0x3dde9400
    0x0000fff0 in ?? ()
    Breakpoint 1 at 0x3ddedddb: file kern/dl.c, line 53.
    (gdb)
    

    After this point. And if I then continue, the module symbol loading now works as the script intended:

    (gdb) cont
    Continuing.
    add symbol table from file "memdisk.module" at
        .text_addr = 0x3bf75cb0
        .rodata.str1.1_addr = 0x3bf75e77
        .data_addr = 0x3bf75ee0
        .module_license_addr = 0x3bf75f00
        .bss_addr = 0x3bf75f10
    add symbol table from file "archelp.module" at
        .text_addr = 0x3b885ef0
        .rodata.str1.1_addr = 0x3b8864d6
        .module_license_addr = 0x3b88653c
    

    Not exactly production-ready, but workable.