Search code examples
cuefi

Why won't my hello world EFI program build correctly?


I can't seem to make progress; my program won't run and the error message is nonseniscal. A hexdump shows a bad PE binary for no apparent reason:

I started from this tutorial

My Makefile:

ARCH=x86_64

OBJS=hello.o
TARGET=hello.efi

EFIINC=/usr/include/efi
EFIINCS=-I$(EFIINC) -I$(EIFFINC)/$(ARCH) -I$(EFIINC)/protocol
LIB=/usr/lib64
EFILIB=/usr/lib
EFI_CRT_OJBS=$(EFILIB)/crt0-efi-$(ARCH).o
EFI_LDS=$(EFILIB)/elf_$(ARCH)_efi.lds

CFLAGS=$(EFIINCS) -fno-stack-protector -fpic -fshort-wchar -mno-red-zone -Wall -DEFI_FUNCION_WRAPPER

LDFLAGS =-nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L $(EFILIB) -L $(LIB) $(EFI_CRT_OBJS)

all: $(TARGET)

hello.so: $(OBJS)
    ld $(LDFLAGS) $(OBJS) -o $@ -lefi -lgnuefi

%.efi: %.so
    objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j rel -j rela -j reloc --target=efi-app-$(ARCH) $^ $@

My hello.c:

#include <efi.h>
#include <efilib.h>

EFI_STATUS EFIAPI efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
    InitializeLib(ImageHandle, SystemTable);
    Print(L"Hello, World\n");
    return EFI_SUCCESS;
}

I build, copy hello.efi onto disk image, and start qemu with:

qemu-system-x86_64 -enable-kvm -bios /usr/share/qemu/OVMF.fd -nodefaults -display gtk -vga std -hda target

But on trying to run it:

FS0:\> dir
Directory of: FS0:\
12/31/2023    48,181  exit.efi
12/31/2023    41,494  hello.efi
FS0:\> hello.efi
Command Error Status: Unsupported

exit.efi is a stock demo application from efi library; it launches. I'm not sure if it's supposed to hang up qemu or not, but it does something. My application is hello.efi. It does not start.

Running objdump to make sure the file format is right:

$ objdump -f -h hello.efi 

hello.efi:     file format pei-x86-64
architecture: i386:x86-64, flags 0x00000030:
HAS_SYMS, HAS_LOCALS
start address 0x0000000000000000

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00005bf0  0000000000003000  0000000000003000  00000138  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00001d00  0000000000009000  0000000000009000  00005d38  2**5
                  CONTENTS, ALLOC, LOAD, DATA
  2 .dynamic      00000110  000000000000b000  000000000000b000  00007b38  2**3
                  CONTENTS, ALLOC, LOAD, DATA
  3 .dynsym       000000d8  000000000000d000  000000000000d000  00007d38  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

Build and host environment is debian 12.4 x86_64.

Something is very wrong with the compilation process. A hexdump of the top of the binary gives:

$ hexdump -C hello.efi | head -n 16
00000000  4d 5a 90 00 03 00 00 00  04 00 00 00 ff ff 00 00  |MZ..........ÿÿ..|
00000010  b8 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00  |¸.......@.......|
00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 80 00 00 00  |................|
00000040  0e 1f ba 0e 00 b4 09 cd  21 b8 01 4c cd 21 54 68  |..º..´.Í!¸.LÍ!Th|
00000050  69 73 20 70 72 6f 67 72  61 6d 20 63 61 6e 6e 6f  |is program canno|
00000060  74 20 62 65 20 72 75 6e  20 69 6e 20 44 4f 53 20  |t be run in DOS |
00000070  6d 6f 64 65 2e 0d 0d 0a  24 00 00 00 00 00 00 00  |mode....$.......|
00000080  50 45 00 00 64 86 04 00  00 00 00 00 38 7f 00 00  |PE..d.......8...|
00000090  12 01 00 00 00 00 05 02  2e 74 65 78 74 00 00 00  |.........text...|
000000a0  f0 5b 00 00 00 30 00 00  00 5c 00 00 38 01 00 00  |ð[...0...\..8...|
000000b0  00 00 00 00 00 00 00 00  00 00 00 00 20 00 50 60  |............ .P`|
000000c0  2e 64 61 74 61 00 00 00  00 1d 00 00 00 90 00 00  |.data...........|
000000d0  00 1e 00 00 38 5d 00 00  00 00 00 00 00 00 00 00  |....8]..........|
000000e0  00 00 00 00 40 00 60 c0  2e 64 79 6e 61 6d 69 63  |....@.`À.dynamic|
000000f0  10 01 00 00 00 b0 00 00  00 02 00 00 38 7b 00 00  |.....°......8{..|

The section headers start too close to the PE signature. That binary will be unloadable.


Solution

  • You have some typos in your Makefile:

    • EFIINCS=-I$(EFIINC) -I$(EIFFINC)/$(ARCH) -I$(EFIINC)/protocol

    • EFI_CRT_OJBS=$(EFILIB)/crt0-efi-$(ARCH).o

    • objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j rel -j rela -j reloc --target=efi-app-$(ARCH) $^ $@

    I got your code running with this Makefile:

    ARCH=x86_64
    
    OBJS=hello.o
    TARGET=hello.efi
    
    EFIINC=/usr/include/efi
    EFIINCS=-I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/protocol
    LIB=/usr/lib64
    EFILIB=/usr/lib
    EFI_CRT_OBJS=$(EFILIB)/crt0-efi-$(ARCH).o
    EFI_LDS=$(EFILIB)/elf_$(ARCH)_efi.lds
    
    CFLAGS=$(EFIINCS) -fpic -ffreestanding -fno-stack-protector -fno-stack-check -fshort-wchar -mno-red-zone -maccumulate-outgoing-args -Wall -DEFI_FUNCION_WRAPPER
    
    LDFLAGS=-shared -Bsymbolic -T $(EFI_LDS) -L $(EFILIB) -L $(LIB) $(EFI_CRT_OBJS)
    
    all: $(TARGET)
    
    hello.so: $(OBJS)
        ld $(LDFLAGS) $(OBJS) -o $@ -lgnuefi -lefi
    
    %.efi: %.so
        objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel -j .rela -j .rel.* -j .rela.* -j .reloc --target=efi-app-$(ARCH) --subsystem=10 $^ $@