This mega-guide shows how to run a linux kernel vmlinux in qemu from scratch, moving step-by-step from “What even is vmlinux
?” to a full, debuggable guest that boots in under a second on modern hardware. We cover tool-chain setup, kernel compilation, QEMU invocation for x86-64 and arm64, serial-only boots, initrd/rootfs strategies, PVH-enabled ELF loading, and GDB integration—sprinkling lessons learned from real-world bug-hunts along the way. Every major command is backed by upstream documentation or battle-tested field notes so you can reproduce the exact same results on your laptop tonight. Newbies welcome; no prior kernel-hacking experience required.
Part 1 – The anatomy of vmlinux
, bzImage
, and why the keyword matters
Before you can master how to run a linux kernel vmlinux in qemu, you need to know what the file actually is.vmlinux
is the uncompressed, ELF-formatted output of the kernel link step, containing the full symbol table—great for debugging but useless for legacy bootloaders that expect a self-extracting header such as bzImage
or vmlinuz
(superuser.com). QEMU, however, can skip the firmware entirely and load an ELF kernel directly with the -kernel
switch—provided the image contains either a PVH entry on x86-64 or the Flattened Image Tree (FIT) header on arm64 (qemu-project.gitlab.io, cdn.kernel.org).

Why choose vmlinux
over bzImage
?
- Symbol-rich: GDB understands the ELF and can resolve every function/variable without
System.map
(kernel.org). - One-step bisects: There is no decompression step, shaving precious milliseconds from every test boot (ops.tips).
- PVH boots: Since Linux 5.5, setting
CONFIG_PVH=y
embeds the Xen PVH note that QEMU (4.0+) can use for instant entry atstartup_xen64
(superuser.com).
Pro-tip: When someone on Stack Overflow complains that “
qemu-system-arm -kernel vmlinux
gives me a black console,” nine times out of ten they passed the wrong machine type or forgot a matchingconsole=
argument (stackoverflow.com). We’ll solve that later.
Part 2 – Preparing the build & emulation environment
2.1 Host packages
On any modern Debian/Ubuntu host you can grab everything with
sudo apt install build-essential bc flex bison libssl-dev libelf-dev \
qemu-system-x86 qemu-system-aarch64 qemu-efi-aarch64 \
ovmf git make
Ubuntu’s own docs recommend qemu-kvm
plus OVMF
when you plan to use UEFI guests (documentation.ubuntu.com).
2.2 Cloning and configuring the kernel
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
make defconfig
./scripts/config -e CONFIG_GDB_SCRIPTS -e CONFIG_PVH # x86-64 fast-boot helpers
make -j$(nproc)
The result appears at ./vmlinux
(uncompressed ELF) and ./arch/x86/boot/bzImage
(compressed) (nickdesaulniers.github.io).
2.3 Quick-and-dirty initramfs
Many tutorials waste hours on disk images; for smoke tests a BusyBox initramfs is enough. The ops.tips recipe automates this in four commands (ops.tips). When finished, place the archive at initramfs.cpio.gz
.
2.4 QEMU in a nutshell
QEMU acts as both firmware and hypervisor. In Direct Linux Boot mode you only need:
qemu-system-x86_64 -kernel ./vmlinux \
-initrd initramfs.cpio.gz \
-append "console=ttyS0 nokaslr" \
-nographic -m 1G
-nographic
re-maps the serial port and monitor to your terminal for an ultra-lightweight headless session (qemu-project.gitlab.io).
For hardware-assisted speed add -enable-kvm -cpu host
(x86) or -machine virtualization=true
(arm64) (documentation.ubuntu.com, cdn.kernel.org).
Part 3 – Building a reproducible workflow
3.1 Workspace layout
The M47r1x blog suggests keeping kernel, rootfs, and QEMU command scripts under a single $WORKSPACE
folder (m47r1x.github.io). Adopt something like:
~/kdev/
├── build/linux/ # O= build dir
├── build/initramfs/
└── scripts/run-qemu.sh
3.2 Automating rebuild-and-run
Combine make -j$(nproc)
with a post-build hook that restarts QEMU when the image changes. This is the secret to the “edit-compile-boot in <10 s” loop praised by Nick Desaulniers (nickdesaulniers.github.io).
3.3 Serial-only consoles
Why does every tutorial pass console=ttyS0
? Because once you drop VGA, QEMU can start with zero GPU emulation, saving CPU and letting you copy-paste dmesg logs easily (qemu-project.gitlab.io, nickdesaulniers.github.io).
3.4 Troubleshooting early panics
If you see Kernel panic - not syncing: VFS: Unable to mount root fs…
, your initramfs or root=
parameter is wrong. Either embed a tiny BusyBox root or point root=/dev/vda
to a virtio-blk disk; see the ArchWiki and Ubuntu Core examples for common layouts (wiki.archlinux.org, documentation.ubuntu.com).
Part 4 – how to run a linux kernel vmlinux in qemu step-by-step
- Compile with PVH (x86-64 only).
./scripts/config -e CONFIG_PVH make olddefconfig && make -j
QEMU ≥ 4.0 will then “just work” with an ELFvmlinux
(superuser.com). - Launch the guest.
qemu-system-x86_64 -kernel ./vmlinux \ -append "earlyprintk=ttyS0 console=ttyS0 root=/dev/ram rw" \ -initrd initramfs.cpio.gz -m 2048 -enable-kvm \ -cpu host -nographic
The console should stop at theinitramfs
shell prompt within 0.7 s on a modern laptop. - Add storage.
For a writable root:qemu-img create -f qcow2 root.qcow2 8G qemu-system-x86_64 ... -drive file=root.qcow2,format=qcow2,if=virtio \ -append "root=/dev/vda"
QCOW2 overlays let you revert broken boots instantly (en.wikipedia.org). - Cross-arch demo (arm64).
The kernel.org HOWTO boots an arm64 Debian image inside x86-64 QEMU by mixingqemu-system-aarch64
, UEFI firmware, and a custom kernel passed via-kernel Image
(cdn.kernel.org). SwapImage
for your freshly builtvmlinux
(afterobjcopy -O binary
) and keep the same-dtb
&-append
flags. - Persist commands in a script.
Save the full QEMU line inrun-qemu.sh
; future runs are a single keystroke away (ops.tips).
Part 5 – Debugging, profiling, and going further
5.1 GDB stub
Launch QEMU with -S -s
to pause at reset and expose port 1234. Then:
gdb vmlinux
(gdb) target remote :1234
(gdb) hbreak start_kernel
Kernel-provided helper scripts (lx-dmesg
, lx-symbols
, etc.) turn GDB into a live /proc browser (kernel.org).
5.2 Panic-time forensics
If your guest hard-locks, switch to the monitor with Ctrl-a c
, run info registers
, or dump memory. Serial logs remain intact because of the earlier -nographic
choice (nickdesaulniers.github.io).
5.3 Networking tricks
User-mode networking (-net user,hostfwd=tcp::8022-:22
) lets you SSH from host to guest without bridges—used by Ubuntu Core’s reference command lines (documentation.ubuntu.com).
5.4 File-sharing with 9pfs
Add
-virtfs local,path=$PWD/share,mount_tag=host0,security_model=mapped,id=host0
and mount inside the guest with mount -t 9p host0 /mnt
(cdn.kernel.org).
5.5 From sandbox to real hardware
Remember the caveat: “If it boots in QEMU, it’s a good sign—not a guarantee” (nickdesaulniers.github.io). Hardware quirks (ACPI tables, missing drivers) still bite, but QEMU removes the turn-around time barrier so you can iterate hundreds of kernel rebuilds per day.
FAQ (quick answers for frantic Googlers)
Question | Answer |
---|---|
Q1. Why does qemu-system-x86_64 -kernel vmlinux complain about “PVH ELF Note”? | Set CONFIG_PVH=y when you build; without it, QEMU refuses a raw ELF image (superuser.com). |
Q2. My console is blank. What gives? | Pass console=ttyS0 (x86) or console=ttyAMA0 (arm) and add -nographic so serial output lands in your terminal (stackoverflow.com). |
Q3. The kernel panics on VFS: Unable to mount root fs . | Either add an initramfs (-initrd ) or point root= to a virtio disk (-drive … if=virtio ) (m47r1x.github.io). |
Q4. How do I debug early boot code? | Use -S -s and GDB’s hbreak start_kernel , then employ lx-dmesg and friends for live introspection (kernel.org). |