Compiling MAME for the Raspberry Pi with QEMU


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!]

About these ads

4 thoughts on “Compiling MAME for the Raspberry Pi with QEMU

  1. Pingback: Cross-compiling for the Pi made easy * | Midnight Yell

  2. Pingback: A good compromise: Cross-compiling with distcc | Midnight Yell

  3. Pingback: Compiling MAME for the Raspberry Pi with QEMU | Raspberry Pi | Scoop.it

  4. I had some issues with removing gcc-4.6 and cpp-4.6 and installing gcc-4.7 and cpp-4.7, however once I did it twice, it works! Now onto EmulationStation!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s