AxlImage — executable-image lifecycle
Backend-neutral wrapper for loading, starting, and unloading
executable images. On UEFI, maps to LoadImage / StartImage
/ UnloadImage; on a future Linux backend the same shape would
map to posix_spawn. Consumers operate on an opaque
AxlImage * handle — the underlying EFI_HANDLE never crosses
the public API.
Sibling to AxlSys — System Utilities (axl_driver_* for DXE-driver lifecycle,
which AxlImage delegates to internally for its load and unload
paths). The one place AxlImage diverges from AxlDriver is
axl_image_start, which captures the image’s exit code —
something axl_driver_start discards because drivers aren’t
expected to exit cleanly.
API Reference
Executable-image lifecycle: load, start, unload.
A backend-neutral abstraction for what UEFI calls LoadImage/StartImage/UnloadImage. On a future Linux backend the same shape would map to posix_spawn or execve-style entry; on coreboot stages, to their loader. Consumer code never references EFI_HANDLE or EFI_LOADED_IMAGE_PROTOCOL directly — the AxlImage handle is opaque.
AxlImage *img;
if (axl_image_load("fs0:\\boot\\hello.efi", &img) == 0) {
int exit_code = 0;
axl_image_start(img, &exit_code);
axl_image_unload(img);
axl_printf("hello.efi exited with %d\n", exit_code);
}
Typedefs
-
typedef struct AxlImage AxlImage
Opaque handle to a loaded executable image.
Created by axl_image_load(); released by axl_image_unload(). The struct is intentionally not defined here — consumers treat it as a pointer-only type.
-
typedef int (*AxlImageIterFn)(const AxlImageInfo *info, void *ctx)
Iterator callback for
axl_image_enumerate.- Return:
0 to continue iteration, non-zero to stop. The non-zero value is returned to the
axl_image_enumeratecaller.
Functions
-
int axl_image_load(const char *path, AxlImage **out)
Load an executable image from a path on a mounted volume.
Path syntax follows the UEFI Shell convention: a volume label, a colon, and a backslash-separated path. Forward slashes are accepted and normalized internally. The image is loaded but not yet started.
- Parameters:
path – image path (e.g. “fs0:\boot\hello.efi”)
out – [out] receives the image handle
- Returns:
AXL_OK on success, AXL_ERR if the file can’t be read or the image format is rejected.
-
int axl_image_set_load_options(AxlImage *img, const void *data, size_t size)
Set load options on a loaded image before starting it.
Mirrors axl_driver_set_load_options for the image-level API: installs
dataas the loaded image’sEFI_LOADED_IMAGE_PROTOCOL.LoadOptionsso the started image sees it via the same surface a shell launch would expose (theargc/argvthe loaded image’smainreceives, or the raw byte buffer via axl_driver_get_load_options_raw).The data is copied internally — caller’s buffer can be freed after. The copy is owned by AXL and released by axl_image_unload (or by a subsequent set on the same handle, which replaces the previous copy). Pass NULL data (or size == 0) to clear load options and free any previous copy. Call between axl_image_load and axl_image_start.
Encoding: pass-through. UEFI shells encode argv as UCS-2 strings; programmatic launchers can pass arbitrary bytes — the started image is responsible for interpreting the buffer.
- Parameters:
img – image handle from axl_image_load
data – option bytes (copied; NULL to clear)
size – option size in bytes
- Returns:
AXL_OK on success, AXL_ERR on bad arguments, alloc failure, or firmware protocol error.
-
int axl_image_start(AxlImage *img, int *exit_code)
Start a loaded image and wait for it to return.
Transfers control to the image’s entry point. Returns when the image calls Exit() or returns from its entry. The handle remains valid after start; the caller still owns it and must axl_image_unload() it.
The image’s exit code (low 32 bits of its EFI_STATUS) is reported in
*exit_code. For an image that callsExit(EFI_SUCCESS, ...)this is 0; for an explicitExit(7, ...)it is 7. UEFI’s Exit() and propagated-error channels share the same encoding, so callers that need to distinguish should treat any non-zero value as “image did not succeed” rather than rely on a specific code.- Parameters:
img – image handle from axl_image_load
exit_code – [out] image’s exit status (NULL allowed)
- Returns:
AXL_OK on successful start (regardless of the image’s exit code), AXL_ERR if the image could not be started at all.
-
int axl_image_unload(AxlImage *img)
Unload an image, releasing its memory.
Safe to call on a never-started image. Frees the handle.
- Parameters:
img – image handle from axl_image_load
- Returns:
AXL_OK on success, AXL_ERR if the firmware refuses (e.g. the image has installed protocols that aren’t released yet).
-
int axl_image_enumerate(AxlImageIterFn cb, void *ctx)
Walk every currently-loaded image, invoking
cbonce per image.Backend-neutral abstraction over UEFI’s
LocateHandleBuffer(EFI_LOADED_IMAGE_PROTOCOL)+ per-handleHandleProtocol. The callback receives a layout-stableAxlImageInfo— consumer code never seesEFI_LOADED_IMAGE_PROTOCOL.info->pathmay be NULL for images whose firmware FilePath couldn’t be decoded (e.g. synthetic loads or in-memory images). Callers that use the path for display should fall back to a placeholder.- Returns:
AXL_OK if the walk completed, the callback’s non-zero value if it stopped early, or AXL_ERR on enumeration failure.
-
int axl_image_self_get_range(void **out_base, size_t *out_size)
Get the base address and size of the currently-running image.
Convenience over
axl_image_enumeratewhen the caller only wants the self image’s range — used for fault attribution and similar “where am I in memory” checks. Equivalent to walkingaxl_image_enumerateand matching the entry whose path equalsaxl_app_image_path(), but cheaper.- Parameters:
out_base – [out] image base load address
out_size – [out] image size in bytes (NULL allowed)
- Returns:
AXL_OK on success (
out_baseandout_sizepopulated); AXL_ERR if firmware doesn’t expose the loaded-image protocol for the current image (extremely unusual).
-
struct AxlImageInfo
- #include <axl-image.h>
Snapshot of a loaded image as visible to the firmware.
Returned by
axl_image_enumerate’s callback andaxl_image_self_get_range. The string fields point into runtime- owned storage that’s valid for the duration of the callback (or until the next call for_self_get_range); copy if you need it longer.
AxlImageVerify — Authenticode signature inspection
PE Authenticode signature inspection without launching the image —
two-axis check (presence + Secure-Boot-db validity) for offline
integrity-check tooling. See include/axl/axl-image-verify.h
for the side-effect contract on the consult_db path.
PE Authenticode signature inspection without launching the image.
axl_image_load runs the firmware’s PE-loader signature checks as a side-effect of loading, and only when Secure Boot is on. Tools that want to ask “is this PE file signed and does its signature
validate against the current Secure Boot db?” without committing to launching the image (incident-response triage, BIOS-update pre-flight, bootable-media verification) reach for axl_image_verify_signature.
The check has two orthogonal axes:
Presence (
has_signature): does the PE file’s Certificate Table data directory (PE/COFF spec §6.4) hold a non-empty WIN_CERTIFICATE blob? Detected by parsing file bytes only — works regardless of Secure Boot state and on any platform.Validity (
signature_valid,consulted_db): if the caller asks for db validation and Secure Boot is enabled, the firmware’s PE loader is asked to dry-run the same signature check it would perform on a real launch (viaLoadImage(SourceBuffer=...)+ immediateUnloadImage). The result isEFI_SECURITY_VIOLATIONfor a signature mismatch,EFI_SUCCESSfor a valid one. When the caller passesconsult_db = false, or Secure Boot is off, or the firmware refuses to load via SourceBuffer,consulted_dbis set false andsignature_validmirrorshas_signature(presence-only).
AxlImageSignatureInfo info = {0};
if (axl_image_verify_signature("fs0:\\boot.efi", true, &info) != 0) {
axl_print("could not read or parse PE\n");
} else if (!info.has_signature) {
axl_print("UNSIGNED\n");
} else if (info.consulted_db && !info.signature_valid) {
axl_print("SIGNATURE INVALID against current Secure Boot db\n");
} else {
axl_print("SIGNED%s\n",
info.consulted_db ? " (db-validated)" : " (presence only)");
}
axl_image_signature_info_free(&info);
Functions
-
int axl_image_verify_signature(const char *path, bool consult_db, AxlImageSignatureInfo *info)
Inspect a PE image’s signature without launching it.
Reads the file, locates the Certificate Table data-directory entry, and (optionally) asks the firmware to dry-run the signature check against the current Secure Boot db. See the file-level overview for the per-field contract.
- Parameters:
path – Image file path (e.g.
"fs0:\\boot.efi").consult_db – When true, ask the firmware to dry-run a full db validation via
LoadImage(SourceBuffer)immediate
UnloadImage. Has no effect beyond presence detection when Secure Boot is off. Side-effect note: the firmware’s PE loader allocates image memory, applies relocations, and invokes any registeredEFI_SECURITY2_ARCH_PROTOCOLhandlers as part of the dry-run. Production firmwares that hook those for audit logging, PCR measurement, ordbxupdate notifications WILL trigger those side effects on everyconsult_db = truecall;UnloadImagereverses the load but not the observability hooks. Passconsult_db = falsewhen those side effects are unacceptable.
info – [out] receives the inspection result. Must be non-NULL. Caller frees via axl_image_signature_info_free. When
infois non-NULL, every bool/pointer field is cleared to false / NULL before any further work — so on a -1 return the struct is in a defined “unknown / nothing detected” state, not arbitrary leftover bytes.
- Returns:
AXL_OK on success (with
infopopulated), AXL_ERR if the file is missing/unreadable, the bytes are not a recognizable PE image, orinfois NULL.
-
void axl_image_signature_info_free(AxlImageSignatureInfo *info)
Release any heap-allocated fields inside
info.Frees AxlImageSignatureInfo::subject_cn and AxlImageSignatureInfo::issuer_cn (each independently — either may be NULL) and clears the struct’s pointer fields back to NULL. NULL-safe on the
infopointer itself.
-
struct AxlImageSignatureInfo
- #include <axl-image-verify.h>
PE Authenticode signature inspection result.
Cleared with axl_image_signature_info_free, which is NULL-safe — callers that don’t pass a non-NULL info pointer to axl_image_verify_signature can skip the free.
Public Members
-
bool has_signature
PE Certificate Table directory entry holds a non-empty WIN_CERTIFICATE blob.
-
bool signature_valid
signature validates (db-validated when consulted_db, presence-only otherwise)
-
bool consulted_db
Secure Boot db was actually consulted (firmware LoadImage dry-run succeeded)
-
char *subject_cn
Subject CommonName from the first certificate in the PKCS#7 SignedData bundle. signtool.exe and most Authenticode signers emit the signer’s certificate first in practice, but the format does not require it — the formal way to identify the signer is via SignerInfo’s IssuerAndSerial. This field is best-effort, suitable for diagnostic output (“Signed by
`<cn>`”) but NOT for security decisions. NULL if has_signature is false, the cert can’t be parsed, no CN attribute is present, or the CN string uses an encoding the walker doesn’t support (T61String, BMPString, IA5String). Heap-allocated UTF-8; caller frees via axl_image_signature_info_free.
-
char *issuer_cn
Issuer CommonName from the same certificate as subject_cn, extracted by the same parser. Same “first cert in the bundle,
best-effort, diagnostic-only” caveats apply.
-
bool has_signature