PicoEFI

From OSDev Wiki
Jump to navigation Jump to search

PicoEFI is a minimal build environment for UEFI applications. It was forked from GNU-EFI and stripped down to just the relocation stub, the linker scripts, and the EFI header files, discarding GNU-EFI's convenience library (libefi.a) and its uefi_call_wrapper machinery entirely.

Background

PicoEFI has been through a couple of names:

  • It was forked from gnu-efi as reduced-gnu-efi in 2021.
  • It was later renamed to limine-efi in 2022, and then nyu-efi in 2024.
  • In 2025 it was briefly hosted on Codeberg before returning to GitHub. During that period it was renamed again to PicoEFI.

Why PicoEFI?

Compared to GNU-EFI style setups, MinGW-based approaches, LLVM/lld-link, or EDK2, PicoEFI's maintainers point to a few concrete advantages:

  • Toolchain portability: It builds with any ELF toolchain, be it GCC or Clang, links with GNU ld (bfd), lld, or gold, and supports GNU's and LLVM's versions of binutils, in any combination. There is no requirement to install or (worse) build a MinGW cross-toolchain or use LLVM/lld-link just to get a toolchain that natively understands the Microsoft ABI and PE — any ELF toolchain, which is what practically every Unix-like system already ships as their system toolchain, or something like <architecture>-elf-targeted freestanding GCC toolchains, will work out of the box.
  • No per-toolchain special-casing: Because it does not depend on a PE-native toolchain or other special tooling, there is no need to maintain separate code paths in the build system or elsewhere for MinGW-based GCC builds vs LLVM/lld-link builds vs EDK2 style builds... — the ELF toolchains that PicoEFI relies on are compatible enough that one simple and streamlined build setup covers them all (see the PicoEFI C template).
  • Reaches targets MinGW does not: Building natively from ELF means PicoEFI can produce working UEFI binaries for architectures MinGW has no cross-compiler for at all, such as RISC-V and LoongArch.
  • A correctly zeroed BSS: Rather than a hardcoded one in the binary like GNU-EFI does.
  • No library baggage: No intrusive library beyond pure EFI. No Print(), no InitializeLib(), no uefi_call_wrapper... Pure EFI functions-only are invoked directly from the System Table.
  • No GNU-EFI bugs: PicoEFI has fixed several bugs (including some serious ones) that the portions of GNU-EFI it was forked from had.

What it provides

For each supported architecture, PicoEFI provides:

  • A relocation stub providing the entry point that the firmware calls, which in turn calls your efi_main.
  • The relocation code needed to fix up the position-independent PE image at load time.
  • A linker script for turning the compiler's native output into a PE/COFF image firmware can load.
  • A set of EFI headers (types, protocols, the System Table, etc.), inherited from GNU-EFI's headers, with some additions (e.g. missing EFI protocols/GUIDs).

Supported architectures

Directory Architecture
ia32 x86 (32-bit)
x86_64 x86-64
aarch64 ARM64
riscv64 RISC-V (64-bit)
loongarch64 LoongArch (64-bit)

License

PicoEFI is permissively licensed, but not under a single uniform license: being derived from GNU-EFI (which in turn absorbed code from Intel and Hewlett-Packard's original EFI sample implementations), individual files may carry the Intel Open Source License, a BSD-2-Clause notice, or an HP license notice, alongside a top-level COPYING file and a LICENSES directory holding the individual license texts. Check the header of any file you redistribute rather than assuming a single license applies.

Getting started

The easiest way to start a project against PicoEFI is the official template, which vendors PicoEFI as a dependency and provides a ready-to-use Makefile:

$ git clone https://github.com/PicoEFI/picoefi-c-template.git
$ cd picoefi-c-template
$ ./get-deps  # Obtain the dependencies the project relies on, including PicoEFI itself.
$ make clean all run

Minimal example

Since there is no convenience library, even printing a string means going through the System Table's console-output protocol directly:

#include <efi.h>

EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
    (void)ImageHandle;
    SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello world\r\n");
    for (;;);
}

See also

Articles

External Links