Nix хотел $ORIGIN прямо в ядре — получил замену загрузчика через eBPF

На TacoSprint 2026 Фарид Закария попросил список рассылки ядра Linux научить ядро распознавать $ORIGIN — токен, который уже позволяет бинарникам Nix находить собственную папку. Мейнтейнер VFS Кристиан Браунер ответил, что нужные детали уже существуют: связка binfmt_misc с eBPF-программой, и у него был готов рабочий пример.

Вкратце

$ORIGIN — это токен, который говорит загрузчику Linux искать зависимости относительно собственной папки; ld.so поддерживает его уже много лет. Nix хотел, чтобы то же самое умело ядро, — чтобы пакеты перестали жёстко зашивать пути /nix/store. В итоге получилась замена загрузчика через binfmt_misc и eBPF, переопределяющая только PT_INTERP, так что /proc/self/exe по-прежнему указывает на настоящий бинарник.

Быстро всплыла загвоздка: для этого нужны root-права, поскольку в непривилегированном пространстве имён это пока не работает. И один из комментаторов уже собрал похожий трюк в userspace вручную ещё до того, как это стало функцией ядра.

Баг упаковки, который свёлся к токену из шести букв: $ORIGIN

Каждый пакет Nix живёт по фиксированному, адресуемому по содержимому пути вроде /nix/store/xyz-glibc/lib, зашитому в каждый зависящий от него бинарник. Перенеси хранилище — или попробуй запустить бинарник в другом месте — и всё ломается. Фарид Закария, работающий над перемещаемыми бинарниками для Nix, описал решение на основе того, что загрузчик уже почти умеет: $ORIGIN — токен, который ld.so распознаёт годами и который означает «папка, содержащая сам исполняемый файл». Если бы бинарник мог находить свои зависимости относительно того места, где он сейчас лежит, хранилище можно было бы переносить без поломок.

Загвоздка в том, что $ORIGIN работает только после того, как динамический загрузчик уже запущен. А ядро выбирает, какой именно загрузчик запускать, через PT_INTERP — фиксированный абсолютный путь, зашитый в каждый ELF-бинарник при линковке, ещё до того, как логика $ORIGIN получает шанс применяться. Поэтому Закария вынес запрос в список рассылки ядра Linux на TacoSprint 2026: научить ядро распознавать $ORIGIN самостоятельно, ещё до передачи управления интерпретатору.

Что на самом деле выкатил Кристиан Браунер

Ядро не получило $ORIGIN. Ответ мейнтейнера VFS Кристиана Браунера был ближе к «у тебя уже есть все детали»: связать binfmt_misc (механизм, позволяющий ядру передавать файл определённого типа на обработку кастомному обработчику) с eBPF-программой, вычисляющей нужный путь к загрузчику в момент выполнения, исходя из того, где бинарник реально лежит на диске. Никакого нового системного вызова, никакого нового поля ELF — просто небольшая, верифицируемая, JIT-компилируемая программа, зарегистрированная в слоте, который у ядра уже есть.

Настоящей итерации потребовала идентичность. Стандартная передача через binfmt_misc заменяет запущенную программу интерпретатором: argv[0], /proc/self/cmdline, /proc/self/exe — всё в итоге указывает на интерпретатор, а не на настоящий бинарник, что как раз ломает трюк самоопределения местоположения, на который опираются перемещаемые бинарники. В итоговом наборе патчей появился второй режим диспетчеризации, который запускает настоящий бинарник напрямую и переопределяет только PT_INTERP, так что перемещённый бинарник Nix по-прежнему может прочитать собственный путь из /proc/self/exe и найти зависимости рядом с собой. Закария говорит, что переписка в рассылке была необычно доброжелательной для списка с репутацией скандального: инженеры дорабатывали идею вместе с ним, а не отвергали её с ходу.

Первый ход треда: а ядро вообще нужно?

Тред из 72 комментариев начался с возражения ещё до того, как кто-либо добрался до механизма, — от комментатора с долгой историей на сайте.

«Но если ld.so уже поддерживает $ORIGIN, зачем это нужно ядру? Или, точнее, почему ядро не может опереться на ld.so и сделать всё это целиком в userspace?» — пользователь HN с кармой свыше 6000

Ответ, спрятанный в самом механизме: ld.so не запускается вообще, пока ядро уже не выбрало путь к интерпретатору через PT_INTERP. К тому моменту, когда логика $ORIGIN в userspace могла бы сработать, неправильный загрузчик, возможно, уже не смог загрузиться. Патч ядра решает не сам вопрос разрешения $ORIGIN — он решает то, что происходит на шаг раньше.

Читатель ждал политической баталии, а получил рабочий патч

Формулировка «ядро поддержит X» обычно читается как заявка на фичу, обречённая на привычное противостояние с мейнтейнерами. Один системный программист настроился именно на это.

«Моя первая реакция на заголовок была негативной (политика!), но реальный результат оказался вполне разумным. «Эй, можем мы добавить $ORIGIN в слой VFS для поддержки перемещаемых интерпретаторов?» — «Это уже можно сделать через binfmt_misc и ebpf, вот пример». Потом, чуть позже, пара патчей, чтобы сделать binfmt_misc удобнее. Похоже на хороший результат» — wzdd, системный программист из Сиднея

Он описывает всю историю в миниатюре: запрос тихо отклонили, а то, что выкатили в итоге, оказалось меньше и универсальнее, чем просили.

Загвоздка, о которой молчат в питче: нужен root

Не все реакции касались дизайна. Один ответ зацепился за ограничение на развёртывание, которому автор статьи не уделил внимания.

«Досадно, что тут используется eBPF, а не cBPF. Я бы хотел включить это в пространстве имён через поддержку пространств имён в binfmt-misc, но eBPF требует root» — разработчик, хотевший запустить это внутри непривилегированного пространства имён

Это реальное ограничение на то, кто может этим пользоваться без содействия системного администратора: непривилегированные контейнеры, sandbox-сборки без root и CI-раннеры, не выдающие root, пока не могут зарегистрировать свой собственный eBPF-обработчик binfmt. Удобство, которого хотел Nix на уровне пакетов, всё ещё требует привилегированного оператора на уровне машины.

Кто-то уже наполовину решил это руками

Ещё до того, как всё это появилось, по крайней мере один человек в треде уже собирал такой же результат из подручных средств.

«Я сделал appseed, который использовал zapps и кучу трюков, чтобы в итоге добиться того, в чём может помочь этот патч ядра, и, надеюсь, поможет ещё больше в будущем! Мой проект appseed не заработал для больших проектов вроде OBS, но мне кажется, что подход, реализованный здесь, может привести к массовому распространению, раз это теперь функция ядра» — разработчик, построивший инструмент для переносимых бинарников appseed поверх zapps в userspace ещё до того, как это стало функцией ядра

Не все приняли рамку, в которой Nix выступает проблемой, которую нужно решать. Другой ответ настаивал, что бремя лежит в обратную сторону: потребность Nix в подвижном хранилище продолжает порождать патчи, которые вынуждены нести другие проекты, — от кастомных шебангов до хуков сборки, специфичных для flake, — вместо того чтобы Nix подстраивался под то, как уже работает остальная экосистема.

Прецедент переживёт сам $ORIGIN

Настоящая победа здесь не в том, что бинарники Nix смогут свободнее перемещаться, — хотя и это тоже. Она в том, что «научить ядро новой политике во время выполнения» больше не означает новый системный вызов и многолетнее обязательство по стабильности ABI. Это означает написать небольшую eBPF-программу и зарегистрировать её в слоте, который у ядра уже есть. У всех, кому в следующий раз понадобится узкое, специфичное для конкретного бинарника правило загрузки, теперь есть шаблон, полностью минующий очередь на рассмотрение системных вызовов.

Получайте такие разборы каждый день

Главные истории Hacker News — на email, в Telegram или RSS.

Это последняя новость выпуска 22 июля 2026.

Перейти в архив выпусков