GNU-EFI
Overview
GNU-EFI is a development library and toolchain support package that allows UEFI applications to be written in C using GCC.
It provides:
- UEFI headers
- Type definitions
- Helper macros
- Startup code
- Build support for generating
.efibinaries
This project uses GNU-EFI together with GCC, GNU ld and QEMU/OVMF.
Why GNU-EFI?
UEFI applications require:
- A specific executable format
- UEFI-compatible calling conventions
- Firmware type definitions
- Access to UEFI service interfaces
GNU-EFI provides these components while allowing development with the standard GNU toolchain.
Basic Application Structure
A minimal GNU-EFI application looks like:
#include <efi.h>
#include <efilib.h>
EFI_STATUS
efi_main(EFI_HANDLE ImageHandle,
EFI_SYSTEM_TABLE *SystemTable)
{
InitializeLib(ImageHandle, SystemTable);
Print(L"Hello, UEFI!\n");
return EFI_SUCCESS;
}
The firmware loads the .efi image and calls efi_main().
Key Headers
Common headers used in this project:
| Header | Purpose |
|---|---|
<efi.h> | Core UEFI types and definitions |
<efilib.h> | Helper functions and library utilities |
Building EFI Binaries
GNU-EFI applications are not ordinary Linux executables.
The build process typically involves:
- Compiling C code
- Linking with GNU-EFI libraries
- Producing a PE/COFF EFI image
- Renaming the output to
.efi
The project automates this process through the Makefile.
The UEFI Calling Convention
UEFI uses a firmware-defined calling convention.
Functions exposed by the firmware are declared with the EFIAPI macro.
Example:
EFI_STATUS EFIAPI MyFunction(void);
Using the correct calling convention is essential when interacting with firmware services.
Boot Services Access
GNU-EFI exposes the firmware interfaces through the UEFI System Table.
Example:
SystemTable->BootServices
This gives access to functions such as:
GetMemoryMap()AllocatePool()FreePool()LocateProtocol()
A Subtle ABI Issue
During development of the Memory Map Viewer, direct Boot Services calls caused unexpected failures on x86_64.
Example:
SystemTable->BootServices->GetMemoryMap(...)
Using the GNU-EFI helper:
uefi_call_wrapper(...)
resolved the issue.
This suggests an ABI or calling-convention related interaction between GCC-generated code and firmware service calls.
The exact root cause requires further investigation, but the finding highlights that firmware interfaces are more sensitive to ABI details than ordinary user-space APIs.
Why This Matters
GNU-EFI is intentionally lightweight.
It exposes the UEFI interfaces relatively directly, which makes it well suited for learning:
- Firmware execution environments
- Boot Services
- Protocol-based interfaces
- Memory management
- OS handoff mechanics
This is one of the main reasons it was chosen for this project.