Вопрос или проблема
Похоже, что когда я запускаю свою программу tools/busybox с qemu-i386, подпроцессы, запущенные с помощью fork(2) + execve(2), выполняются нативно. Я хочу, чтобы qemu-i386 их эмулировал вместо этого.
В настоящее время следующие 2 команды имеют разные результаты (потому что только вторая использует execve(2) и выполняет mychroot/bin/uname нативно, а первая эмулирует mychroot/bin/uname):
$ qemu-i386 mychroot/bin/uname -mio
i686 i686 GNU/Linux
$ qemu-i386 tools/busybox sh -c 'exec mychroot/bin/uname -mio'
x86_64 x86_64 GNU/Linux
Как получить эмуляцию для обоих случаев и затем одинаковый вывод?
Ответ или решение
Для решения проблемы, с которой вы столкнулись при использовании qemu-i386 для эмуляции подпроцессов, запущенных с помощью fork(2) и execve(2), важно понять, как QEMU работает и какие механики доступны для достижения вашей цели эмулирования.
Теория
QEMU — это универсальный эмулятор и виртуализатор, который позволяет запускать код, скомпилированный для одной архитектуры, на другой. В вашем случае qemu-i386 используется для выполнения программ, скомпилированных для 32-битной архитектуры x86, на 64-битной системе. Однако, по умолчанию, qemu-user (включая qemu-i386) исполняет только первый процесс в эмуляции. Любые последующие вызовы execve(2) исполняются нативно, то есть на вашем текущем хосте, если в командной строке уже используется бинари хоста.
Это поведение объясняется особенностями работы qemu-user, которая, в отличие от qemu-system, полагает выполнение на уровне исполняемых файлов, а не всей операционной среды. Поэтому, когда запускается новый процесс, он не проходит через тот же интерпретатор qemu, и исполняется непосредственно на хосте.
Пример
Ваша проблема заключается в том, что вызовы команд, которые используются для запуска, влияют на то, каким образом эмулются подпроцессы. Например, прямой вызов qemu-i386 mychroot/bin/uname -mio
полностью эмулирует процесс, тогда как команда qemu-i386 tools/busybox sh -c 'exec mychroot/bin/uname -mio'
запускает sh как подпроцесс, при этом uname выполняется как нативный процесс хоста.
При попытке выполнить execve
, QEMU в текущей конфигурации исполняет файл напрямую, как оный исполнимый бинарь на хосте, тем самым теряя эмуляцию.
Применение
Для решения этой задачи требуется принудительное использование QEMU в качестве интерпретатора для всех процессов, включая подпроцессы, созданные с помощью execve. Рассмотрим несколько возможных решений:
-
Использование binfmt_misc: Вы можете настроить binfmt_misc для автоматического "перехвата" любых вызовов x86 бинарных файлов и их эмуляции с использованием qemu-i386. Нужно добавить соответствующую запись в /proc/sys/fs/binfmt_misc, чтобы все x86 бинарные файлы запускались через qemu-i386 автоматически. Например:
echo ':i386:M::\x7fELF\x01\x01\x01::/usr/bin/qemu-i386-static:' > /proc/sys/fs/binfmt_misc/register
Это гарантирует, что любые бинарные файлы ELF для i386 будут исполняться с использованием
qemu-i386-static
, что предотвратит их нативное выполнение. -
Перекомпиляция или модификация BusyBox: Еще один способ решения — это конфигурирование вашего executable в BusyBox, который используется для запуска команд, таких как
sh
, для использования qemu-helper и автоматического управления окружением. -
Контейнеризация или Chroot: Если возможно, использование chroot или контейнерной изоляции, где все процессы запускаются с qemu окружением. Создайте chroot окружение с qemu-recompiled binaries, которое будет эмулировать все ботируемые процессы в пределах этого окружения.
-
Скриптовое обертывание подпроцессов: Подумайте о создании обёртки или скрипта, который будет оборачивать каждый вызов, который может порождать подпроцессы, и принудительно запускать их через qemu-i386. Например, каждый вызов exec заменяется на вызов qemu-i386, с соответствующим относительным путем исполняемого файла.
Каждое из этих решений потребует более строгой настройки системы и возможно изменения инфраструктуры вашего приложения, однако каждое из них предоставляет путь к тому, чтобы обеспечить полную эмуляцию с использованием qemu-i386 для всех процессов, включая подпроцессы.
Рекомендуется протестировать каждое решение в контролируемой среде, чтобы убедиться, что оно соответствует вашим ожиданиям относительно производительности и совместимости, а также удовлетворяет требования безопасности вашего окружения.