nanemu — минималистичная обёртка для упрощения запуска ELF-бинарников на ядре Linux в QEMU.
Проект предназначен для:
- ⚙️ Запуска кода на ядре Linux в ОС linux/macos/windows
- 🔬 Тестирования кода, использующего низкоуровневые механизмы ОС — eBPF, системные вызовы и т.п.
- 🧪 Отладки поведения кода, зависящего от архитектуры и версии ядра Linux
⚡ Субсекундное время запуска на хост-системах с аппаратной поддержкой виртуализации и минималистичным ядром Linux.
- Быстрый старт
- Передача пути к ядру Linux
- Поддерживаемые форматы образов файловой системы
- Примеры использования
- Параметры
- Переменные окружения
- Особенности
- Пример: запуск собственной программы
- Расширения (extensions)
Для Alpine / Debian / Ubuntu:
sudo apt install qemu-system qemu-user-staticОфициальная документация: QEMU installation guide
GOBIN=$GOPATH/bin go install github.com/ebirukov/nanemu/cmd/nanemu@latest# Сборка:
GOOS=linux GOARCH=amd64 go build -o example/build/hello-amd64 ./cmd/hello
# Запуск:
nanemu -initrd example/build/hello-$(go env GOARCH)Локальный файл, предварительно скачанный, например, с dl-cdn.alpinelinux.org:
nanemu -kernel /путь/к/ядру example/build/hello-amd64URL-адрес файла:
nanemu -kernel https://dl-cdn.alpinelinux.org/alpine/edge/releases/x86_64/netboot-3.9.0/vmlinuz-vanilla \
-initrd example/build/hello-amd641. Локальный путь:
nanemu example/build/hello-amd642. Содержимое Docker-образа:
-
Формат OCI-совместимого репозитория
oci://[image-registry]/repository/image:tagnanemu -initrd oci://quay.io/lifebitai/ubuntu:18.10 bash
-
Формат для репозитория docker.com
docker://[repository]/image:tagnanemu -initrd docker:///alpine
nanemu rootfs/initnanemu -kernel kernel/arm64/linux-5.10.0-32-arm64 -arch arm64 rootfs/initQEMU_BIN=/usr/bin/qemu-system-amd64 \
KERNEL_ARGS="initrd=/mybin cma=0 audit=0 nowatchdog nosmp maxcpus=1 ipv6.disable=1 net.ifnames=0 lsm= acpi=off ima_appraise=off" \
nanemu \
-kernel kernel/amd64/linux-6.1.0-35-amd64 \
-arch arm64 \
-timeout 10s \
build/amd64/initramfs| Флаг | Тип | Описание |
|---|---|---|
-kernel |
string |
Путь к образу ядра Linux (обязательный, либо через переменную KERNEL_PATH) |
-arch |
string |
Целевая архитектура (по умолчанию: GOARCH) |
-timeout |
duration |
Максимальное время выполнения QEMU (по умолчанию: 0 — без ограничения) |
-loglevel |
string |
Уровень логирования при загрузке ядра (по умолчанию: 3) |
-initrd |
bool |
Создать образ диска инициализации initramfs (cpio) |
-memory |
string |
Размер памяти для QEMU |
-smp |
string |
Количество ядер процессора для QEMU |
-s |
string |
Серийный порт (по умолчанию: mon:stdio) |
-t |
bool |
Псевдотерминал |
-i |
bool |
Интерактивный режим (по умолчанию: false на Windows) |
-e |
string |
Пары переменных окружения KEY=VALUE (флаг можно указывать несколько раз) |
-fail-on-panic |
bool |
Завершать выполнение при панике ядра (по умолчанию: true) |
-keep-disk |
bool |
Сохранить временный диск после завершения |
| Переменная | Описание |
|---|---|
QEMU_BIN |
Путь к исполняемому файлу QEMU (по умолчанию: qemu-system-$ARCH) |
QEMU_ARGS |
Дополнительные аргументы, передаваемые напрямую в QEMU |
KERNEL_ARGS |
Дополнительные параметры загрузки ядра |
- 🚀 Автоматическое использование аппаратной поддержки виртуализации
- 🏗 Автоматическое создание временного образа диска:
ext4(при наличии модуля ядраVIRTIO_BLK) либоinitramfsв форматеcpio - 🧼 Автоматическая очистка временных файлов после завершения
- 🛠 Поддержка ОС Linux / macOS / Windows и архитектур
amd64,arm64
package main
import (
"fmt"
"os"
"syscall"
"unsafe"
)
func main() {
var uts syscall.Utsname
syscall.Uname(&uts)
osVersion := unsafe.String((*byte)(unsafe.Pointer(&uts.Release[0])), len(uts.Release))
fmt.Printf(
"Hello from process %d on linux kernel version: %s\n", os.Getpid(), osVersion)
syscall.Reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
}GOOS=linux GOARCH=arm64 go build -o build/hello-arm64 ./cmd/hellonanemu \
-kernel https://dl-cdn.alpinelinux.org/alpine/edge/releases/aarch64/netboot/vmlinuz-virt \
-arch arm64 \
-loglevel 6 \
-initrd \
example/build/hello-arm642026/01/26 12:49:47 executing: /usr/bin/qemu-system-arm64 -serial mon:stdio -machine virt -cpu cortex-a53 -nographic -no-reboot -append rdinit=/hello-arm64 console=ttyAMA0 loglevel=6 -kernel kernel/vmlinuz-5.4.43-1-arm64 -initrd ./initramfs.cpio718173793
2026/01/26 12:49:47 process /usr/bin/qemu-system-arm64 started with pid: 2177874
[ 0.000000] Linux version 6.18.7-0-virt (buildozer@build-edge-aarch64) (cc (Alpine 15.2.0) 15.2.0, GNU ld (GNU Binutils) 2.45.1) #1-Alpine SMP PREEMPT_DYNAMIC 2026-01-26 12:49:47
[ 0.000000] Kernel command line: rdinit=/hello-arm64 console=ttyAMA0 loglevel=6
Hello from process 1 on linux kernel version: 6.18.7-0-virt
[ 0.558718] reboot: Power down
2026/02/22 14:15:02 exit with code: 0
Механизм extensions позволяет динамически расширять параметры запуска nanemu, добавляя собственные опции QEMU без изменения исходного кода.
Каждое расширение — это отдельный файл в каталоге:
$HOME/.nanemu/extension/
Имя файла определяет флаг запуска, а содержимое файла — шаблон аргументов, добавляемых при выполнении QEMU.
При старте nanemu сканирует папку $HOME/.nanemu/extension/. Для каждого найденного файла создаётся дополнительный CLI-флаг. При указании этого флага при запуске nanemu содержимое файла автоматически подставляется в параметры QEMU.
Пример: файл console-unix создаёт флаг -console-unix. Если при запуске nanemu указать этот флаг, в QEMU будут добавлены аргументы из содержимого файла.
1. Создайте каталог для расширений (если его ещё нет):
mkdir -p $HOME/.nanemu/extension2. Добавьте файл console-unix со следующим содержимым:
# Подключение UNIX-сокета для консоли
-chardev socket,path=%s,server=on,wait=off,id=unix0
-device virtio-serial-pci
-device virtserialport,chardev=unix0,name=unix.sock3. Запустите nanemu, указав новый флаг:
GOOS=linux GOARCH=amd64 go build -o example/build/hello-amd64 ./cmd/hello
nanemu -console-unix /tmp/nanemu.sock example/build/hello-amd64👉 В результате nanemu автоматически добавит указанные строки в параметры запуска QEMU.
Каждый файл может содержать одну или несколько строк — шаблонов аргументов QEMU. В шаблоне можно использовать подстановку %s — она будет заменена на значение, переданное через одноимённый CLI-флаг.
Пример: файл $HOME/.nanemu/extension/port-fwd
# Проброс TCP-порта
-netdev user,id=net0,hostfwd=%s
-device virtio-net-pci,netdev=net0Запуск:
nanemu -kernel vmlinuz -rootfs build -port-fwd tcp::8080-:22Результат — в QEMU автоматически подставятся аргументы:
-netdev user,id=net0,hostfwd=tcp::8080-:22
-device virtio-net-pci,netdev=net0
-
Расширения можно создавать под разные архитектуры, добавив суффикс в имени файла, например:
usernet-amd64 usernet-arm64В этом случае флаг
-usernetбудет активен только при запуске на соответствующей архитектуре. -
Комментарии внутри файла (
#или//) автоматически попадают в описание флага и отображаются при-h.
| Имя файла | Назначение |
|---|---|
net |
Конфигурация встроенной NAT-сети QEMU (user mode) |