Using Debian Multiarch for cross-compiling

17 Jun 2012

Debian  Technology  Tools 

Originally posted at https://tech.labs.oliverwyman.com/blog/2012/06/17/using-debian-multiarch-for-cross-compiling/

I’ve recently acquired a Raspberry Pi, and was considering using it for SNES emulation. However, as it turns out that Zsnes is x86-only, and that Snes9x got kicked out of Debian a while back for having an annoying “no-commercial use” license, so we’re into the compile-it-yourself options. As Snes9x is a configure/makefile-type project, I should in theory be able to just compile in on the Pi directly, but then we hit the problem that it hasn’t got enough RAM to be able to do all the compiling… fine, fine, I’ll go back into the messy world of cross-compiling.

Actually, it turns out that things are in a good place these days if you’re running a recent Debian system. First off, follow the Emdebian install instructions and get the armel compiler mentioned in the example. Snes9x needs bits of the X libraries and other things, so if you’re feeling sensible then just use xapt and dpkg-cross (also on the Emdebian page) to get the packages you need.

xapt/dpkg-cross have a couple of problems though

These are minor issues, and I probably should have picked that option over the problems I had later on, but they were enough to persuade me away from it. Also in theory, I could have just gone with using Qemu and build the package on a pseudo-Pi but that also appears to have issues and my little netbook would probably start screaming.

Instead, I went with something completely different: Multiarch. I’d been hearing about this for a while but this is the first time I’ve actually used it. To quote the HOWTO:

Multiarch lets you install packages from multiple architectures on the same machine. This is useful in various ways, but the most common is installing both 64 and 32-bit software on the same machine and having dependencies correctly resolved automatically.

This is an upgrade slowly working it’s way through the Debian package repositories, as it requires some changes to packaging. More specifically, in order to have the multiple architecture packages not clash, architecture-specific files now get installed in architecture-specific paths. In addition, packages that can supply cross-architecture dependencies (i.e. removing the problem noted before of having to install multiple copies of architecture-independent packages), need to add a Multi-Arch field to note this.

Given all of that, you’re going to need a few bits of very up-to-date packages, as this is still pretty bleeding-edge stuff. libc6-dev should be at least 2.13-33, dpkg at least 1.16.2, and apt at least 0.8.13, but ideally 0.9.4+. If you’re an Aptitude user, you’ll need 0.6.6-1 which apparently is causing lots of problems for Ubuntu users right now…

After that for snes9x, I can do the following:

  1. Add the armel architecture to dpkg

dpkg --add-architecture armel

and update your package sources

apt-get update

This may well fail if you’re using any 3rd party archives that are i386 specific (or amd64 specific, or whatever other arch you’re on) e.g. Opera. If so, then you’re going to need to add a “[arch=i386]” after the “deb” bit to make those work again, i.e.

deb <http://deb.opera.com/opera/> stable non-free

becomes

deb [arch=i386] <http://deb.opera.com/opera/> stable non-free

as otherwise the default assumption is that all your existing sources are suitable for all your architectures (both i386 and armel now!), and they’ll break if that’s not true.

  1. Get the source of the last Debian version of snes9x
  2. Use dh-builddep-metapackage to make sure we get the right set of build dependencies
dh-builddep-metapackage -b -w -aarmel

and then install the resulting .deb. If it hadn’t been kicked out of Debian, then

aptitude build-dep snes9x:armel

would also work, but not any more. Now, one of the interesting problems with Multiarch is that anything marked “Multiarch: same” will conflict with anything other than exactly the same version of that package on other architectures. In other words, if you need both the i386 and armel versions of something like libc6, they must have precisely the same version number, and can only be both upgraded together, which may cause a few delays if your system is a tad out of date, as mine was.

  1. Ok, so here we go. In theory,
dpkg-buildpackage -aarmel -b

should do the trick. It doesn’t however, as the emdebian packages are looking for everything in the dpkg-cross-style locations, not the Multiarch-style ones. If however you add:

-I/usr/include/$(DEB\_HOST\_GNU\_TYPE) -I/usr/include

to the CFLAGS, and

-Xlinker -rpath-link /lib/$(DEB\_HOST\_GNU\_TYPE) -Xlinker -rpath-link /usr/lib/$(DEB\_HOST\_GNU\_TYPE)

to the LDFLAGS, then everything seems to work out ok. Those of you with a C background may be noting big warning signs about -rpath-link, having been informed about the evils of -rpath, but let me assure you:

The difference between -rpath and -rpath-link is that directories specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is only effective at link time. It is for the native linker only.

This is purely because our host system has things in a weird location, but the target will all be ok, and the executables will be built without the extra path data.

Tada, you now have a Multiarch-using cross-built package, which you can happily install on your Pi!

This was quite a difficult process, and the obvious question is: why was it so hard, and will it get easier? It was hard because Multiarch doesn’t actually support cross-building yet. The focus so far has been on machines with multiple architectures with the side aim of mixed instuction sets.

Cross-compiling isn’t supported yet properly, but there’s a spec and a GSoC student working on the problem, whose first post touched on one of the big problems, which is the lack of cross-arch dependencies. Most normal, sane packages require only stuff from one architecture. Cross-compilers need stuff from at least two (host and target), and the Emdebian dpkg-cross stuff works around the problem by repackaging a target-arch package as a host-arch package with different paths, so they’d need to be re-done once this is properly supported.

Now, I was hoping to be able have a “money shot” here i.e. a picture of the cross-compiled snes9x running Super Mario World to demo here. As it turns out, there’s no accelerated X drivers for the Pi yet, so getting anything to run at a reasonable speed is very hard currently…

The cross-compiled package installs and runs ok, but the graphics speed leaves something to be desired for the moment, even with heavy use of compiler flags. At least I know all of the above worked, even if the end system leaves something to be desired at the moment. As it is, this is the best I can show you, but hopefully there will be improvements later on…

Previously: Visualising your web browsing history Next: Making (Kindle) books from blogs