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,尚未随任何正式版本发布。

提案到补丁:一条时间线 2026-06 提议 VFS 原生支持 转向 eBPF binfmt_misc 可编程钩子 2026-07 补丁提交 拟入 -next 未定 正式发布后 NixOS 模块

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 用户短期基本无感,除非应用和发行版主动接入这套机制。