Linux Kernel Gets $ORIGIN Support via eBPF

Farid Zakaria, during TacoSprint 2026, set out to make Nix binaries relocatable. The result: a patch series from VFS maintainer Christian Brauner that adds eBPF-based dynamic interpreter selection to binfmt_misc. This enables $ORIGIN resolution in PT_INTERP and shebangs, with a new L dispatch mode that cleanly substitutes the loader.

The Problem: Fixed Interpreters

Dynamic binaries specify their interpreter (e.g., ld.so) via the PT_INTERP ELF segment. This path is absolute. Tools like Nix, Bazel, or Buck that use content-addressed stores want relocation: the interpreter should be relative to the binary's location ($ORIGIN). The kernel currently doesn't support that.

The Solution: eBPF in binfmt_misc

Brauner proposed extending binfmt_misc with eBPF programs. Instead of a fixed interpreter or a static match rule, the kernel invokes a BPF program that decides the interpreter at runtime. Zakaria provided an example:

SEC("struct_ops.s/match")
bool BPF_PROG(nix_match, struct linux_binprm *bprm)
{
    return !bpf_strncmp(bprm->buf, 4, "\x7f" "ELF");
}

SEC("struct_ops.s/load")
int BPF_PROG(nix_load, struct linux_binprm *bprm)
{
    char path[256];
    long n;
    n = bpf_path_d_path(&bprm->file->f_path, path, sizeof(path));
    if (n < 0)
        return n;
    /* derive the loader location from the binary's path */
    return bpf_binprm_set_interp(bprm, path, sizeof(path));
}

SEC(".struct_ops.link")
struct binfmt_misc_ops nix = {
    .match = (void *)nix_match,
    .load = (void *)nix_load,
    .name = "nix",
};

Once loaded and registered via:

bpftool struct_ops register nix_origin.bpf.o /sys/fs/bpf
echo ':origin:B::::nix:' > /proc/sys/fs/binfmt_misc/register

Every ELF binary triggers the BPF program, which computes the interpreter path relative to the binary. The kernel then uses that interpreter.

The 'L' Dispatch Mode

Traditional binfmt_misc hands off execution to the registered interpreter, making it the main process. That breaks /proc/self/exe and argv[0]. Brauner's patch series adds a new L flag (loader substitution). With L, the kernel executes the matched binary natively and merely substitutes the interpreter. The binary retains its identity, so /proc/self/exe points to the binary, not the loader.

What This Means for Nix and Others

For Nix, this enables truly relocatable binaries. Zakaria plans to upstream a NixOS module that registers the BPF program at boot, gated on a new PT_INTERP_NIX ELF segment. Only binaries that opt-in get the dynamic behavior; existing binaries work unchanged.

Beyond $ORIGIN

Since the BPF program has full access to the binary's contents, possibilities expand: shebang $ORIGIN support, per-binary QEMU emulator selection, or custom security policies. The kernel now allows programmable interpreter selection without patching the VFS.

Next Steps

The patch series is heading to the -next branch for integration into a future Linux release. Developers can track progress and experiment with the eBPF approach today.