It’s become apparent that some people are more concerned that a build goes smoothly rather than as fast as possible. Having recently played around with cross-compiling X windows for the Pi, I can see how one might form that opinion. Sometimes, you just want things to work, even if they are slower than optimal.
So, let’s talk about another, easier way to build software for the Raspberry Pi on your larger Linux desktop or laptop. Indeed, we won’t even need a Pi to do this.
Emulation
An emulator is a program that acts like another CPU at the instruction level. In our case, we’re going to be running a program that acts like an ARM processor on an x86 Linux system. More specifically, we’re going to run ARM binaries on our x86, and the emulator will be taking ARM instructions and translating them to x86 instructions on the fly.
That’s actually pretty cool, when you think about it.
Of course, emulation comes at a cost: speed. An emulated compile will take considerably longer than a cross-compile. But that’s to be expected, when you consider how hard the CPU is working in order to be able to run non-native binaries.
Installing QEMU
The emulator we’ll be using is a fairly well-known one called QEMU. You install it on your Debian-based (Ubuntu, in my case) linux host by typing
apt-get install qemu-user-static
A similar “yum install” command will install QEMU on RedHat.
We’ll be using the static version of QEMU. This just means that it won’t be calling making any function calls to anything that resides in a shared library; everything QEMU needs is contained in the one executable. I’ll explain why later.
Getting a root file system
We’re going to need a root filesystem that’s filled with ARM software. I like Raspbian for this, so let’s go get it from http://www.raspberrypi.org/downloads. The current one as of this writing is 2012-09-18-wheezy-raspbian.zip.
Once it’s downloaded, unzip it.
unzip 2012-09-18-wheezy-raspbian.zip
Now let’s extract the files in the rootfs into a local directory. This isn’t strictly necessary — we could compile everything inside the mounted .img file, and when we’re finished, copy the img file to an SD card, and boot it on the Pi. That seems like it has too many moving parts for my tastes, though. So let’s keep it simple and just copy everything locally.
Normally, this is straightforward enough, you would just issue
sudo mount -o loop 2012-09-18-wheezy-raspbian.img /mnt
…and you could cd /mnt
and copy the files to a local directory.
But Raspberry Pi SD cards have 2 partitions on them and the above command would just mount the first partition, which only contains the files in /boot. Of course, we want the 2nd partition. Rather than explain how to calculate the offset of the 2nd partition inside the image file, and how to use that to tell mount how to mount the file properly, I’m going to provide a script that I wrote called rpi_copyrootfs.sh
.
#!/bin/bash
PATH=/sbin:${PATH}
set -e
usage()
{
cat <<EOF
`basename $0`:
Make a Raspberry Pi SD card image
-h : This help message
-d : Destination directory
-i : The name of the image file ( `basename ${IMGFILE}` )
-v : Turn on verbose output
EOF
}
while getopts “hi:d:v” OPTION
do
case $OPTION in
h)
HELP_OPT=1
;;
i)
IMAGEFILE_OPT=$OPTARG
;;
d)
DESTDIR_OPT=$OPTARG
;;
v)
VERBOSE=1
;;
?)
usage
exit
;;
esac
done
IMGFILE=${IMAGEFILE_OPT:-2012-09-18-wheezy-raspbian.img}
DESTDIR=${DESTDIR_OPT:-rootfs}
if [ ! -z "${HELP_OPT:-}" ] ; then
usage
exit
fi
if [[ ${EUID} != 0 && ${UID} != 0 ]] ; then
echo "$0 must be run as root"
usage
exit -1
fi
BYTES_PER_SECTOR=`fdisk -lu ${IMGFILE} | grep ^Units | awk '{print $9}'`
LINUX_START_SECTOR=`fdisk -lu ${IMGFILE} | grep ^${IMGFILE}2 | awk '{print $2}'`
LINUX_OFFSET=`expr ${LINUX_START_SECTOR} \* ${BYTES_PER_SECTOR}`
if [ ! -z "${DESTDIR}" ] ; then
if [ ! -d ${DESTDIR} ] ; then
mkdir -p ${DESTDIR}
fi
LINUXMOUNT="__linuxmnt.$$"
mkdir -p ${LINUXMOUNT}
mount -o loop,offset=${LINUX_OFFSET} ${IMGFILE} ${LINUXMOUNT}
cd ${LINUXMOUNT};
tar cf - * | ( cd ../${DESTDIR}; tar xvf - )
cd -
umount ${LINUXMOUNT}
rm -rf ${LINUXMOUNT}
fi
Running this script as follows will copy the Raspbian root file system into a local directory called rootfs. This is where we’ll build.
./rpi_copyrootfs.sh -i 2012-09-18-wheezy-raspbian.img -d rootfs
Cheating with automatic filesystems
This next step is actually not the perfectly correct thing to do, but it’s generally good enough to build with.
Our local rootfs copy isn’t a real root filesystem. In particular, /dev
, /proc
and /sys
are empty, and that might upset some build tools.
So rather than try to create something that looks like an RPi /proc
, /sys
, and /dev
, we’re going to cheat and mount the host system’s versions of these directories inside the rootfs.
sudo mount --bind /proc rootfs/proc
sudo mount --bind /sys rootfs/sys
sudo mount --bind /dev rootfs/dev
Magic
All we have to do now is copy the static version of the ARM emulator into the rootfs
directory, issue the chroot command, and then run the emulator.
export QEMU=`which qemu-arm-static`; sudo cp -p ${QEMU} rootfs/${QEMU}
cd rootfs
sudo chroot .
pwd
uname -m
${QEMU} /bin/bash
uname -m
Let’s take a look at what’s happened here. We copied the x86 QEMU program into the ARM-based rootfs
directory. Okay, that’s not terribly exciting. Then we issued the chroot command inside the rootfs
directory. This made it, for all intents and purposes, so that our rootfs
directory became /
. That is anytime we type /bin/bash
we’re actually referring to rootfs/bin/bash
.
But wait! That /bin/bash
is an ARM program that we just copied from the SD card image. There’s no way that’ll run on an x86!
Unless we run the ARM emulator. Note that uname -m now shows that we’re an ARM processor. Running ARM binaries.
Poke around! Explore! It’s like we’re in text-mode on an RPi.
The same utilities — the ones that don’t talk directly to hardware, at least — work as you would expect. The config files and libraries are in the right places. It looks like we’re ready to go.
Setting up a build environment
Are you ready for this? This emulated environment is like you’re on an RPi, right?
apt-get update
apt-get upgrade
This blew my mind when it worked.
root@midnightyell:/# gcc --version
gcc (Debian 4.6.3-8+rpi1) 4.6.3
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Building MAME
So, most packages will build inside this emulator just like they would on a real Linux host. That is, the usual steps are:
Untar the source package
cd into the directory
./configure
make
make install
Indeed, AdvanceMAME is very nearly like that. Except that I ran into problems building advanceMAME 0.106.1 with the standard raspbian gcc 4.6. I was getting bad object files as output! I thought this was a problem with the emulator, but I ran into the same problem when I built MAME natively on my RPi! After some trial and error, I discovered that gcc4.7 works just fine for MAME.
So, let’s get rid of gcc-4.6, and install gcc-4.7.
apt-get remove -y gcc-4.6
apt-get remove -y cpp-4.6
apt-get install -y gcc-4.7
apt-get install -y cpp-4.7
cd /usr/bin
ln -s cpp-4.7 cpp
ln -s gcc-4.7 gcc
ln -s gcc-ar-4.7 gcc-ar
ln -s gcc-nm-4.7 gcc-nm
ln -s gcc-ranlib-4.7 gcc-ranlib
Go and get the advancemame source from here: http://advancemame.sourceforge.net/download.html. You can place it into the rootfs
from another terminal window, and then access it inside the chroot’d process.
In any case, place it somewhere out of the way, and do the usual build steps:
tar xvzf advancemame-0.106.1.tar.gz
cd advancemame-0.106.1
./configure
make -j
That’s it.
Really.
[Update: It took 42 minutes to build AdvanceMAME inside QEMU; the same machine cross-compiles it in 50 seconds, but that’s after an hour of fighting the spec file so that it would cross-compile at all!]