Tags: linux binfmt
Categories: None

Продолжаю тему расковыривания различных подсистем ядра Linux, начатую с поста про Device Mapper. Иногда может понадобиться запускать не родные Linux’у бинарники как нативные, без указания интерпретатора или эмулятора. Мне это понадобилось когда я захотел поработать на своем ноутбуке с корневой файловой системой от Raspberry Pi, которая естесственно состояит из бинарников под ARM. В других случаях, кому-то хочется запускать виндовые бинарники не указывая явно эмулятор, типа wine ./notepad.exe.

Для решения этой задачи в ядре Linux есть подсистема binfmt. Через эту подсистему мы можем зарегистрировать определенный файловый формат и интерпретатор или эмулятор, которым надо запускать файлы этого формата. Например мы говорим, что файлы с расширением class надо запускать интерпретатором java (есть возможность матчиться и по magic number файла).

Для начала возьмем простую задачу, запускать файлы с расширением py интерпретатором python.

Готовим полигон

Заведем чистую виртуалку под эксперименты:

vagrant init ubuntu/trusty64
vagrant up
vagrant ssh

Пробуем запустить python код без интерпретатора:

echo 'print("Hello, Python!")' > hello.py
chmod +x ./hello.py

vagrant@vagrant-ubuntu-trusty-32:~$ ./hello.py
./hello.py: line 1: syntax error near unexpec
./hello.py: line 1: `print("Hello, Python!")'

Bash конечно же думает, что это его код :)

Заводим binfmt для python

Все операции из юзерспейса с binfmt происходят через файловую систему, находящуюся в proc, но для начала ее надо туда смонтировать:

mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc

Регистрация файловых форматов происходит путем записи конфигурации в файл /proc/sys/fs/binfmt_misc/register.
Запись в нашем случае будет выглядеть так:

echo ':Python:E::py::/usr/bin/python:' > /proc/sys/fs/binfmt_misc/register
# После чего появился файл c кофигурацией для Python
cat /proc/sys/fs/binfmt_misc/register/Python

Пробуем запусть снова:

vagrant@vagrant-ubuntu-trusty-32:~$ ./hello.py
Hello, Python!

Теперь все ок. Суть в общем ясна – мы сказали ядру, что при загрузке файла с расширением py запускай его с помощью интерпретатора Python.
Теперь разберем формат строки конфигурации.

Формат конфигурации

Формат следующий: :name:type:offset:magic:mask:interpreter:.

  • name - название формата
  • type - пишем E если хотим идентифицировать файл по расширению и M если по magic number. В случае M нам надо указать magic и опционально offset и mask
  • offset - Смещение до magic number внутри файла (опционально)
  • magic - Magic number - байтовая последовательность в шестнадцатиричном виде, по которой будет идентифицироватья формат
  • mask - Маска нужна, если надо пропустить некоторые биты в magic, binfmt тут делает AND с magic
  • interpreter - интерпретатор

Автоматизируем

Сейчас все операции мы делали вручную: монтирование файловой системы, регистрация форматов и т.д. Для автоматизации всего этого существует пакет binfmt-support. Поставим его:

apt-get install binfmt-support

После установки видим, что автоматически смотрировалась ФС и зарегистрировались форматы для 2-й и 3-й версии Python:

vagrant@vagrant-ubuntu-trusty-32:~$ ls /proc/sys/fs/binfmt_misc/
python2.7 python3.4 register status

В пакет входит утилита для работы с binfmt - upfate-binfmts. С ее помощью можно просматривать список зарегистрированных форматов, регистрировать, удалять и т.д.

# Вот так мы могли бы зарегистрировать формат для Python
update-binfmts --package Python --install Python /usr/bin/python --extension "py"
# Так можно посмотреть список
update-binfmts --display

По остальным операциям смотрим man update-binfmts.

С основами разобрались. Рассмотрим теперь задачу по сложнее – работу с ARM файловой системой на x86.

ARM rootfs на x86

Задача такова. Например надо внести изменения в корневую ФС от Raspberry Pi. Можно конечно запустить все внутри полноценной циртуалки Qemu, но это слишком громоздко для нашей задачи. Хотелось бы просто сделать chroot внутрь ФС и начать работать. Для этого существует статическая сборка qemu-user-static, с помощью которой можно запускать бинарники разных платформ. Но chroot в этом случае не знает, что надо использовать эмулятор. Вот в этом случае и спасает binfmt.

Попробуем все это провернуть.

Поставим qemu-user-static:

apt-get install qemu-user-static

После установки qemu-arm-static автоматически зарегистрировались в binfmt форматы для эмуляции ARM. Можно убедиться:

vagrant@vagrant-ubuntu-trusty-32:~$ cat /proc/sys/fs/binfmt_misc/qemu-arm
enabled
interpreter /usr/bin/qemu-arm-static
flags: OC
offset 0
magic 7f454c4601010100000000000000000002002800
mask ffffffffffffff00fffffffffffffffffeffffff

Тут мы видим magic number, по которому определяется, что это ARM бинарник и путь до эмулятора /usr/bin/qemu-arm-static.

Загрузим корневую файловую систему от Debian Jessie под ARM:

sudo apt-get install debootstrap
sudo debootstrap --no-check-gpg --arch=armhf jessie arm-rootfs ftp://ftp.debian.org/debian/

Теперь все что надо есть, попробуем сделать chroot:

vagrant@vagrant-ubuntu-trusty-32:~$ sudo chroot arm-rootfs /bin/bash
chroot: failed to run command ‘/bin/bash’: No such file or directory

Пичаль. Все дело в том, что внутри rootfs нет эмулятора qemu-arm-static и его надо туда положить.

sudo cp /usr/bin/qemu-arm-static arm-rootfs/usr/bin/
sudo chroot arm-rootfs /bin/bash
$ uname -a
Linux vagrant-ubuntu-trusty-32 3.13.0-74-generic #118-Ubuntu SMP Thu Dec 17 22:52:02 UTC 2015 armv7l GNU/Linux

Теперь все работает, попали внуть rootfs, бинарники эмулируются прозрачно – красота!

comments powered by Disqus