Nix 二进制有个老毛病:路径是编译时写死的。整个 store 挪个地方,程序就找不到自己了。
7 月,一组补丁在 Linux 内核邮件列表上流转,想解决这个问题。思路和最初设想完全不同:不是让内核直接认识 $ORIGIN,是用 eBPF 给 binfmt_misc 装一个可编程的换解释器开关。这不是内核原生支持 $ORIGIN,补丁目前计划先进 -next 分支,还没跟着正式版本走。
内核给的是钩子,不是翻译器
最早的提案很直接:在 VFS 层给 PT_INTERP 和 shebang 加上 $ORIGIN 解析。VFS 维护者 Christian Brauner 回应后,讨论转向另一条路。
新方案是用 eBPF 写一个 struct_ops 程序,挂到 binfmt_misc 上。匹配 ELF 文件后动态算出解释器路径,再让内核替换掉。有 Nix 贡献者背景的 John Ericson 在讨论里点出,这种需求不止 Nix 一家,Buck、Bazel 一类构建工具也有类似诉求。
$ORIGIN 只是这个钩子上跑的一个具体用例。内核本身不解析这个字符串,也不会主动读懂 PT_INTERP 或 shebang 里的相对路径写法。真正干活的是加载进内核的 eBPF 程序,它拿到二进制路径后自己算解释器该指向哪。
补丁系列于 2026 年 7 月提交,计划先进 -next,尚未随任何正式版本发布。
L 模式治身份错位,但比现有做法多一层机制
传统 binfmt_misc 有个副作用:注册的解释器接管整个进程身份。argv[0]、/proc/self/exe 全部显示成解释器,不是你实际执行的那个二进制。对 wine、qemu 这类模拟器无所谓,Nix 生成的二进制经常靠 /proc/self/exe 反查自己位置,身份被顶替反而找不到自己。
Brauner 最新的补丁加了两个分发模式,作者最看重的是 L 模式(loader substitution):内核仍把原二进制当主映像执行,只替换 PT_INTERP 指向的加载器。没有身份交接,标准动态链接器不用改就能直接用。
这套方案和 Nix 现在用的方式比起来,取舍点不一样:
| 方案 | 谁处理路径 | 进程身份 | 现状 |
|---|---|---|---|
| patchelf 重写(Nix 现用) | 构建后静态改写绝对路径 | 不变 | 已在用,store 一挪就失效 |
| 传统 binfmt_misc | 解释器接管匹配文件 | 变成解释器身份 | 老机制,不适合 $ORIGIN 场景 |
| 新提案 L 模式(eBPF) | 内核态动态算路径 | 保留原二进制身份 | 补丁刚提交,未进正式内核 |
内核给的是通用钩子,不是替 Nix 写好的答案。
要跑起来,得先编译 eBPF 程序,用 bpftool 注册进内核,再往 /proc/sys/fs/binfmt_misc/register 写注册字符串。不是应用程序里写个字面量 $ORIGIN 就能自动生效,还依赖发行版是否放开对应的 eBPF 权限和内核配置。
作者的兼容策略是引入新的 PT_INTERP_NIX 段。只有显式带这个段的二进制才会被 eBPF 接管,现有 Nix 产物不受影响。等补丁进正式内核发布后,他计划推动一个 NixOS 模块开机自动注册——这目前只是个人规划,没被上游确认成标准路径。
谁现在该动,谁可以再等
Nix、NixOS 维护者可以去跟 -next 分支的补丁,评估接入 PT_INTERP_NIX 的成本。生产环境短期还得继续用 patchelf,不用急着切换。
内核和发行版工程师该盯的是 eBPF 权限模型:binfmt_misc 注册会不会被默认打开,这决定了普通用户什么时候能碰到这个机制。
普通 Linux 用户短期基本无感,除非应用和发行版主动接入这套机制。
