Linux kernel will support $ORIGIN, sort of | Farid Zakaria’s Blog
Linux kernel will support $ORIGIN, sort of
Published 2026-07-20 on<br>Farid Zakaria's Blog
For some reason, during TacoSprint 2026 I decided to see if we could tackle relocatable binaries in Nix.
I enjoy these lofty goals to push Nix and the surrounding ecosystem forward. I am bold if not stupid.
I left the last earlier post with one potential idea of how to get there:
We could patch the Linux kernel so that $ORIGIN is supported in PT_INTERP and the shebang.
I waded through the complexity of sending patches over email (turns out I actually enjoy this workflow!), and sent a proposal to the Linux kernel mailing list.
My first attempt here proposed simply adding direct support for $ORIGIN in the Virtual File System (VFS) subsystem.
I waited nervously. I was expecting the result from what I had come to read about online; someone non-politely telling me to F$#CK OFF because there is something I missed, misunderstood or did not consider. 🤬
The result was completely different. 😲
Christian Brauner, the maintainer for VFS responded to me in good faith, asking for the rationale for the change and eventually proposing some ways in which such a support could make it into the subsystem.
Note<br>It definitely helped having someone like John Ericson chime in and advocate why having a non-fixed interpreter (PT_INTERP) is useful to Nix and other use-cases (i.e. Buck & Bazel).
He offered that potentially we could leverage eBPF as a programmable way to select an interpreter through binfmt_misc.
Whoa! 🤯
I wanted to merely allow $ORIGIN but a programmable selection could let us do anything!
The idea must have really intrigued him because soon-after, on his vacation, Christian offered the first draft of such a solution. We went back and forth a little over the mailing list and the end result is a patch series that will make its way into -next branch in the near future.
If you don’t know what eBPF is or binfmt_misc, WTF did we just collaborate on?
Let’s take a look!
I won’t do eBPF justice, and there are plenty of articles online about it as it’s quite in-vogue at the moment.
tl;dr; You can write programs in a C subset that gets compiled to an instruction set whose virtual machine is running within the kernel . Shouldn’t the kernel be super fast? Yes, the programs are jitted to their native CPU architecture and the programs have a fixed-time slice. Isn’t this some crazy vulnerability for the kernel? Before any code is loaded it is “verified” to be safe. Checkout this guide for more info.
We can now support $ORIGIN with a relatively simple eBPF program:
SEC("struct_ops.s/match")<br>bool BPF_PROG(nix_match, struct linux_binprm *bprm)<br>return !bpf_strncmp(bprm->buf, 4, "\x7f" "ELF");
SEC("struct_ops.s/load")<br>int BPF_PROG(nix_load, struct linux_binprm *bprm)<br>char path[256];<br>long n;
n = bpf_path_d_path(&bprm->file->f_path, path, sizeof(path));<br>if (n 0)<br>return n;
/* derive the loader location from the binary's path */
return bpf_binprm_set_interp(bprm, path, sizeof(path));
SEC(".struct_ops.link")<br>struct binfmt_misc_ops nix = {<br>.match = (void *)nix_match,<br>.load = (void *)nix_load,<br>.name = "nix",<br>};
Once the above program is loaded and registered into the kernel, we then ask the binfmt_misc subsystem to trigger it. Checkout this thread if you want to see the complete example.
> bpftool struct_ops register nix_origin.bpf.o /sys/fs/bpf<br>> echo ':origin:B::::nix:' > /proc/sys/fs/binfmt_misc/register
What does that mean?
It means that every binary now triggers the nix_match function above, in this case any ELF file, but it could be executables with a new segment like PT_INTERP_NIX, and the kernel will ask nix_load to determine the interpreter to use dynamically.
Our special BPF program has support for $ORIGIN 💥
What else could you do?
Well we can now even completely replace the traditional QEMU binfmt_misc registration script with a BPF program now like this one.
What else can we do?
Since we can now programmatically select our interpreter based on anything in the file, we can do quite a lot. I’m keen to hear your suggestions and ideas 💡.
Some of the smaller items are that we can even support $ORIGIN in the shebangs (#!$ORIGIN/bin/ld.so) very easily as seen here: we simply look at the first 256 bytes of the file and look for $ORIGIN to trigger.
One downside or side-effect of the traditional binfmt_misc hand-off was that the way in which the desired final binary was invoked was non-transparent.
The registered interpreter becomes the process. It owns the entire process identity, and the binary you actually asked to run gets demoted to an argument. For wine or qemu that’s acceptable as they are emulators but for a per-binary BPF loader that might pick a traditional ld.so it does not make much sense.
This leaks in a few painful ways but the simplest are :
argv[0] and /proc//cmdline show the interpreter invocation, not what you...