A really nice tool to customize disk images for ARM-based single board computers, like the Raspberry Pi, is QEMU. QEMU advertises itself as a generic and open source machine emulator and virtualizer [1, 2]. In other words, QEMU can run operating systems and programs made for different architectures, like ARM, on a developer's x86-64 development machine.
This is why QEMU is an ideal tool in combination with chroot [2,3] to customize Arch Linux ARM images for ARM-based devices, such as the RPi. So once we have an ARM image, we can simply chroot into the root filesystem and install extra software packages, like vim, zsh or avahi. In brief, chroot is an operation that allows a user on a Unix-like operating system to change the root directory of the current running process and its children . Basically, this allows one to run commands within a different root filesystem environment also known as a chroot jail.
On most GNU/Linux distributions these handy tools can be easily installed via the package management. The rest of this blog post provides instructions how we can customize an existing Arch Linux ARM image on an Arch-based system. As described in my last blog post, these images can be created with basic Linux/CLI knowledge.
Basically, we just need to install three packages, including
update-binfmts, so we can emulate an ARM root filesystem
on a x86-64 host system:
pacman -S qemu qemu-arch-extra
Lastly, we need to install
update-binfmts from the AUR:
git clone https://aur.archlinux.org/binfmt-support cd binfmt-support makepkg -si
binftm-support contains a helper script to easily register and
unregister binary formats with the kernel using the
binfrm_misc module. In brief, this
module provides the Linux kernel on host system to recognize arbitrary executable
file formats and to pass them to certain user space applications .
update-binfmts --enable qemu-arm
With the help of the command
update-binfmts --display we can double check if
the binfmt entry for ARM was successfully registered:
update-binfmts --display | grep -w 'qemu-arm' qemu-arm (enabled): interpreter = /usr/bin/qemu-arm-static
Mount the image
In this step, it's assumed that we have already created an Arch Linux image. These images can be created with basic Linux/CLI knowledge as described in this blog post.
First, we need to mount the root filesystem of the image. This can be done with
the help of the command
losetup -f image.img
Second, we need to find out the corresponding loopback device for the image:
losetup -a /dev/loop0: : (/media/data/xander/src/archlinux-arm/raspberry-pi2/image.img)
In my case this is the loop device
loop0. Now, we need to reload the partition
table of the device
loop0 so the partitions are known to the host OS.
To double check that the OS knows the partition table of the device
can use the command
lsblk, which is used to list block devices:
lsblk /dev/loop0 NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT loop0 7:0 0 1G 0 loop ├─loop0p1 259:0 0 100M 0 loop └─loop0p2 259:1 0 923M 0 loop
As one can see, both partitions of the image are known to our host OS.
Chroot into the root filesystem
In this last step, we chroot into the root filesystem of the Arch Linux ARM image.
First, we create a directory called
/mnt/ on the host system:
Now, we can mount root filesystem under the path
mount /dev/loop0p2 /mnt/arm-chroot
However, before we can chroot into the root filesystem, it is necessary to copy
qemu-arm-static binary to
/mnt/arm-chroot/usr/bin. Thanks to this binary,
it is possible to execute non-native target executables just as native
exeutables once we are in the chroot jail.
For copying the
qemu-arm-static binary, we can run the following command:
cp $(which qemu-arm-static) /mnt/arm-chroot/usr/bin
Moreover, it's a good idea to copy temporarily a working
resolv.conf file from
your host system to the image's root filesystem, so you have a working DNS
resolution once you chroot into it:
mv /mnt/arm-chroot/etc/resolv.conf /mnt/arm-chroot/etc/resolv.conf.bak cp /etc/resolv.conf /mnt/arm-chroot/etc/resolv.conf
As a last step before we can run
chroot, we need to mount the necessary device
files so we have a working chroot environment where we can install software via
pacman. In short, we need to mount
mount --bind /proc /mnt/arm-chroot/proc mount --bind /sys /mnt/arm-chroot/sys mount --bind /dev /mnt/arm-chroot/dev mount --bind /dev/pts /mnt/arm-chroot/dev/pts
Finally, we are ready to chroot into the root filesystem:
chroot /mnt/arm-chroot /bin/bash
Customizing the image
Now that we are in the chroot environment, we can customize the image to our liking. But before we do that, we should upgrade the system and double check if we have a working package mirror list:
Useful development tools
In case you intend to compile and develop some Arch Linux ARM packages, you should definitely install the following packges:
pacman -S base-devel git vim zsh sudo wget
Changing the hostname is just a matter of editing the files
echo chaotix > /etc/hostname
In case of the
hosts file we should add an extra line like the
one below. For example, I decided to use the hostname
127.0.1.1 chaotix.localdomain chaotix
Create a new user
A default user on a new Arch Linux ARM system is the user alarm. If you like to create a new user you can do that as follows:
useradd -m -s /bin/zsh xander passwd xander
As you can see, my new user is called xander and uses the awesome
zsh shell :-).
I often use sudo instead of a root account to administer my Linux systems,
so I added my user account to the group
gpasswd -a xander wheel
To complete the sudo configuration, we must not forget to uncomment
the first line starting with
In other words, remove the Hashbang
Also note that you should use
visudo to edit the
Delete default user
And if you're like me and don't need the existing
you can simply remove the user from the image:
userdel -r alarm
Lastly, we should change the default root password of the Arch Linux ARM image:
In order to make the single board computer discoverable on the home network, we can install avahi and mdns. Overall, the installation procedure is the same as described on the Arch Linux Wiki .
pacman -S avahi nss-mdns
Once installed, we should adapt the
/etc/nsswitch.conf so the hostname
resolution using the
.local naming scheme works:
hosts: ... mdns_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns ...
And finally, to enable the avahi service on every boot, we can run the following
systemctl enable avahi-daemon.service
Leave the chroot and unmount things
Once you made all the customization, leave the chroot, unmount the root
filesystem of the image, and detach the loop device
exit umount -R /mnt/arm-chroot losetup -d /dev/loop0
Talk is cheap, show me something!
It's testing time. So let's test the customized image on real RPi2 hardware!