Cross compiling a minimal Linux sytem for the ARM architecture and emulating it in QEMU.

Almost all the information from this and my previous post can be found in this nice tutorial by Kamel Messaoudi, which also includes debugging user programs. I'm posting my notes as I go through them again and which will use as reference points for future posts.

A Linux kernel that can be emulated by QEMU can be built using the versatile_defconfig and a working cross-compiler.

wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.3.3.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.3.3.tar.sign

gpg --recv 6092693E
unxz linux-4.3.3.tar.xz
gpg --verify linux-4.3.3.tar.sign

tar xf linux-4.3.3.tar

cd linux-4.3.3
make ARCH=arm versatile_defconfig
# to further configure the build
#make ARCH=arm menuconfig
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- all

# this builds a kernal image to `./arch/arm/boot/zImage`
cp linux-4.3.3/arch/arm/boot/zImage .

Busybox is a single binary combining many Unix utilities including a shell.

wget https://busybox.net/downloads/busybox-1.24.1.tar.bz2
wget https://busybox.net/downloads/busybox-1.24.1.tar.bz2.sign

gpg --recv ACC9965B
gpg --verify busybox-1.24.1.tar.bz2.sign

tar xf busybox-1.24.1.tar.bz2
cd xf busybox-1.24.1
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- defconfig
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- install

This builds a minimal filesystem in ./_install. Some additional directories are required.

cd _install
mkdir -p lib proc sys dev etc etc/init.d

Some shared libraries are required by the BusyBox binary. The exact libraries can be found using the arm-unknown-linux-gnueabi-readelf program. I'm just going to copy all the libraries from my toolchain to the target.

cp ~/x-tools/arm-unknown-linux-gnueabi/arm-unknown-linux-gnueabi/sysroot/lib/* lib

Next we add an init script at /etc/init.d/rcS which will be executed by the BusyBox init script.

cat > ./etc/init.d/rcS << EOF
#!/bin/sh

# Mount the /proc and /sys filesystems
mount -t proc none /proc
mount -t sysfs none /sys

# Populate /dev
/sbin/mdev -s

# Enable the localhost interface
ifconfig lo up
EOF

It needs to be executable.

chmod +x etc/init.d/rcS

A compressed disk image of this filesystem which can be read by qemu can be made using the cpio program.

find . | cpio -o --format=newc | gzip > ../../rootfs.img.gz

I use my system qemu package, but it is not hard to build.

qemu-system-arm -M versatilepb -m 128M -kernel ./zImage -initrd ./rootfs.img.gz -append "root=/dev/ram rdinit=/sbin/init"

qemu screenshot