Compiling QEMU from Source on Linux, My First Big Success
by Mike Levin SEO & Datamaster, 10/04/2012
Okay, I am restarting my effort to get my own distribution of Linux going. It will essentially be Tiny Core Linux, but booting from a tiny QEMU virtual computer from either Linux, Mac or Windows. It’s what I consider the ideal Linux learning platform, and life-long life preserver for your code. My effort has stalled out because I didn’t want to release my last version that used the now obsolete Q version of QEMU for Mac, along with compiled binaries that I got from different sources for PC and Linux, for which I did not really encapsulate the dependencies.
I’m taking a newer, cleaner approach by which I compile all the QEMU binaries myself, and very deliberately “encapsulate” each version to carry around as many of its own dependencies as reasonable, so it truly doesn’t need any installs, but still runs with a double-click from any x86 host machine. This is a sort of universal Linux code execution platform that you can carry with you for life.
I’ve been down this route several times before, and compiling QEMU isn’t as easy as it seems like it should be. There’s the dependencies, the toolchain to do the compile, and the nuances of each platform. Well, I’m starting out with Linux, since the compile toolchain is native to Linux. Or, at least it was mostly native. I had to put it on with the following command:
sudo apt-get install build-essential
The one key dependency for Linux that I didn’t already have on my machine was the shared GNU version of the C standard library. I doubt newbies are going to bother reading this article, but just in case you are, GNU stands for “GNU is not Unix”. It’s a typically nerdy self-referential acronym, and stands for all the parts of Linux that are not the kernel, for Linux as most people think of it is actually GNU/Linux, two separate open source projects that combine together for one complete operating system. Part of that is a shared library that lots of software assumes is there. I had to add it with the following command:
sudo apt-get install glibc
I’m going to have to deal with this, and potentially even much worse dependency issues when I compile for Mac and OS X, because the Linux versions are effectively native, but the Mac and Windows versions will have dependencies that are outside Mac’s and Windows’ normal operation. On Mac, I’ll be using either something called Homebrew, MacPorts or Fink. On Windows, it will almost certainly be the Win32 Cygwin GNU compiler toolchain that uses Red Hat’s newlib. There are further dependencies that are going to be tricky to navigate, but I will cross those bridges when I come to them.
Another thing I learned from experience is that even though there are tons of compile options for QEMU, and you could try to compile in static libraries, leave out SDL graphics, and do all sorts of things to try to optimize the binary for the application, too many things go wrong. It is best to stick close to the road most often traveled. Optimizing the binary will be for the future. For now, the only ./configure option I’m going to do is –target-list=i386-softmmu, which simply keeps it from producing emulation of all the other platforms, like PowerPC, ARM and the like. I only need 32-bit x86 emulation. So after downloading and untarballing the archive, I cd’d into the directory and typed:
I then typed:
…which had the following output:
|install -d -m 0755 “/usr/local/share/qemu” install -d -m 0755 “/usr/local/etc/qemu” install -c -m 0644 /home/username/Desktop/qemu-1.2.0/sysconfigs/target/target-x86_64.conf “/usr/local/etc/qemu” install -c -m 0644 /home/username/Desktop/qemu-1.2.0/sysconfigs/target/cpus-x86_64.conf “/usr/local/share/qemu” install -d -m 0755 “/usr/local/bin” install -c -m 0755 qemu-ga qemu-nbd qemu-img qemu-io “/usr/local/bin” install -d -m 0755 “/usr/local/libexec” install -c -m 0755 qemu-bridge-helper “/usr/local/libexec” set -e; for x in bios.bin sgabios.bin vgabios.bin vgabios-cirrus.bin vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom qemu-icon.bmp bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb multiboot.bin linuxboot.bin kvmvapic.bin s390-zipl.rom spapr-rtas.bin slof.bin palcode-clipper; do \ install -c -m 0644 /home/username/Desktop/qemu-1.2.0/pc-bios/$x “/usr/local/share/qemu”; \ done install -d -m 0755 “/usr/local/share/qemu/keymaps” set -e; for x in da en-gb et fr fr-ch is lt modifiers no pt-br sv ar de en-us fi fr-be hr it lv nl pl ru th common de-ch es fo fr-ca hu ja mk nl-be pt sl tr bepo; do \ install -c -m 0644 /home/username/Desktop/qemu-1.2.0/pc-bios/keymaps/$x “/usr/local/share/qemu/keymaps”; \ done for d in i386-softmmu; do \ make -C $d install||exit 1 ; \ done make: Entering directory `/home/username/Desktop/qemu-1.2.0/i386-softmmu’ install -m 755 qemu-system-i386 “/usr/local/bin” strip “/usr/local/bin/qemu-system-i386” make: Leaving directory `/home/username/Desktop/qemu-1.2.0/i386-softmmu’|
Capturing this output is particularly useful, because it lets you see where the install process put everything, and where the qemu binary is going to expect to find everything when it runs. I’m going to have to figure out how to make it look for these things relative to its own location, rather than from fixed locations on the system. Speaking of which, there’s a command ldd which lists your library dependencies, and running it against /usr/local/bin-qemu-system-i386 produces the following:
ldd qemu-system-i386 linux-gate.so.1 => (0xb779b000) librt.so.1 => /lib/i386-linux-gnu/librt.so.1 (0xb6d3f000) libglib-2.0.so.0 => /lib/i386-linux-gnu/libglib-2.0.so.0 (0xb6c44000) libutil.so.1 => /lib/i386-linux-gnu/libutil.so.1 (0xb6c3f000) libncurses.so.5 => /lib/i386-linux-gnu/libncurses.so.5 (0xb6c1d000) libtinfo.so.5 => /lib/i386-linux-gnu/libtinfo.so.5 (0xb6bfe000) libpng12.so.0 => /lib/i386-linux-gnu/libpng12.so.0 (0xb6bd5000) libSDL-1.2.so.0 => /usr/lib/i386-linux-gnu/libSDL-1.2.so.0 (0xb6b3c000) libX11.so.6 => /usr/lib/i386-linux-gnu/libX11.so.6 (0xb6a05000) libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb69d9000) libz.so.1 => /lib/i386-linux-gnu/libz.so.1 (0xb69c0000) libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb69a5000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb67fb000) libpcre.so.3 => /lib/i386-linux-gnu/libpcre.so.3 (0xb67ba000) libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xb67b5000) libasound.so.2 => /usr/lib/i386-linux-gnu/libasound.so.2 (0xb66c3000) libpulse-simple.so.0 => /usr/lib/i386-linux-gnu/libpulse-simple.so.0 (0xb66be000) libpulse.so.0 => /usr/lib/i386-linux-gnu/libpulse.so.0 (0xb6670000) libXext.so.6 => /usr/lib/i386-linux-gnu/libXext.so.6 (0xb665d000) libcaca.so.0 => /usr/lib/i386-linux-gnu/libcaca.so.0 (0xb6591000) libxcb.so.1 => /usr/lib/i386-linux-gnu/libxcb.so.1 (0xb656f000) /lib/ld-linux.so.2 (0xb779c000) libpulsecommon-2.1.so => /usr/lib/i386-linux-gnu/pulseaudio/libpulsecommon-2.1.so (0xb650b000) libjson.so.0 => /usr/lib/i386-linux-gnu/libjson.so.0 (0xb6500000) libdbus-1.so.3 => /lib/i386-linux-gnu/libdbus-1.so.3 (0xb64b6000) libslang.so.2 => /lib/i386-linux-gnu/libslang.so.2 (0xb6387000) libncursesw.so.5 => /lib/i386-linux-gnu/libncursesw.so.5 (0xb6356000) libXau.so.6 => /usr/lib/i386-linux-gnu/libXau.so.6 (0xb6352000) libXdmcp.so.6 => /usr/lib/i386-linux-gnu/libXdmcp.so.6 (0xb634a000) libwrap.so.0 => /lib/i386-linux-gnu/libwrap.so.0 (0xb6340000) libsndfile.so.1 => /usr/lib/i386-linux-gnu/libsndfile.so.1 (0xb62cc000) libasyncns.so.0 => /usr/lib/i386-linux-gnu/libasyncns.so.0 (0xb62c5000) libnsl.so.1 => /lib/i386-linux-gnu/libnsl.so.1 (0xb62ab000) libFLAC.so.8 => /usr/lib/i386-linux-gnu/libFLAC.so.8 (0xb625a000) libvorbisenc.so.2 => /usr/lib/i386-linux-gnu/libvorbisenc.so.2 (0xb60e2000) libvorbis.so.0 => /usr/lib/i386-linux-gnu/libvorbis.so.0 (0xb60b6000) libogg.so.0 => /usr/lib/i386-linux-gnu/libogg.so.0 (0xb60ae000) libresolv.so.2 => /lib/i386-linux-gnu/libresolv.so.2 (0xb6097000)
Okay, at least I have it compiled, and tested that it “attempts” to run. It only attempts to run because I haven’t given it a virtual hard drive or CD-ROM to boot from yet, but I am able to cd into /usr/local/bin and list the directory, which shows:
qemu-ga qemu-img qemu-io qemu-nbd qemu-system-i386
drwxr-xr-x 2 root root 4096 Oct 4 12:58 . drwxr-xr-x 11 root root 4096 Sep 19 17:56 .. -rwxr-xr-x 1 root root 976506 Oct 4 12:58 qemu-ga -rwxr-xr-x 1 root root 1840790 Oct 4 12:58 qemu-img -rwxr-xr-x 1 root root 1876691 Oct 4 12:58 qemu-io -rwxr-xr-x 1 root root 1801305 Oct 4 12:58 qemu-nbd -rwxr-xr-x 1 root root 4104232 Oct 4 12:58 qemu-system-i386
It’s interesting that the QEMU binary itself is 4MB. It used to be much smaller than that. But at any rate, I test that qemu actually compiled correctly and has all its dependencies by typing:
…and a QEMU console window opens, flickers a bit (under Linux Ubuntu 12.10, but I’ve seen that behavior exhibited since back under 11.04). Sometimes the flickering ends up on the correct aspect-ratio window, and sometimes everything looks a bit stretched and anti-aliased. That’s not the only problem. Because of QEMU’s need to capture mouse input, if you click on that window, you actually lose your mouse pointer, and can only get it back by pressing the Ctrl+Alt combination, which is fine for advanced users, but I don’t even want graphics, much less mouse-control in QEMU. I want to run it as just a type-in server. And there actually is a way to run QEMU so that it “stays” in the terminal window you launched it from. To test that, I type:
…and I can easily confirm that the “curses” text-only version of QEMU is working. But I can’t even Ctrl+C out of the terminal now. So I hit the “x” close gadget and take note that QEMU can run in exactly the way I need now, and it’s just a matter of encapsulating this Linux version of QEMU, and move on to Mac and PC. But first, I need to actually get this thing running off of a virtual hard drive or CD-ROM.
So first, I’m going to download Tiny Core Linux. Or more precisely, what used to be called Micro Core Linux, but is now only called Core, located at http://distro.ibiblio.org/tinycorelinux/downloads.html
So, in order to avoid path stuff, I’m copying qemu-system-i386 and Core-current.iso into the same directory, and I’m cd’ing into it.
qemu-system-i386 -curses -cdrom Core-current.iso
…and voila! This is the biggest success I’ve had so far with compiling QEMU. The folder with just the qemu binary and 12.4 MB. This is all very good signs. Now, my virtual Linux actually booted up INSIDE the Linux terminal that I ran it from, but now since it actually booted from the CD-ROM image and I have a login prompt (it automatically logs you in), I can exit out of the session with:
Okay, that is enough for this post. The next one will be about encapsulating everything necessary to get this to happen inside of one portable folder, and then I will have the “model” for what I’m trying to make happen on the Mac and Windows platforms as well. The interesting thing is that even though the QEMU binary and some support files will be different on each platform, the hard drive image that it boots from (and saves-to for persistence) will be the same.