Background
As part of my work on the Stylo / Quantum CSS team at Mozilla, I needed to be able to test changes to Firefox that only affect Linux 32-bit builds. These days, I believe you essentially have to use a 64-bit host to build Firefox to avoid OOM issues during linking and potentially other steps, so this means some form of cross-compiling from a Linux 64-bit host to a Linux 32-bit target.
I already had a Linux 64-bit machine running Ubuntu 16.04 LTS, so I set about attempting to make it build Firefox targeting Linux 32-bit.
I should note that I only use Linux occasionally at the moment, so there could certainly be a better solution than the one I describe. Also, I recreated these steps after the fact, so I might have missed something. Please let me know in the comments.
This article assumes you are already set up to build Firefox when targeting 64-bit.
Multiarch Packages (Or: How It’s Supposed to Work)
Recent versions of Debian and Ubuntu support the concept of “multiarch packages” which are intended to allow installing multiple architectures together to support use cases including… cross-compiling! Great, sounds like just the thing we need.
We should be able to install1 the
core Gecko development dependencies with an extra :i386
suffix to get
the 32-bit version on our 64-bit host:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Well, that doesn’t look good. It appears some of the Gecko libraries we need aren’t happy about being installed for multiple architectures.
Switch Approaches to chroot
Since multiarch packages don’t appear to be working here, I looked around for other approaches. Ideally, I would have something fairly self-contained so that it would be easy to remove when I no longer need 32-bit support.
One approach to multiple architectures that has been around for a while is to
create a chroot environment: effectively, a separate installation of
Linux for a different architecture. A utility like schroot
can then be used
to issue the chroot(2)
system call which makes the current session believe
this sub-installation is the root filesystem.
Let’s grab schroot
so we’ll be able to enter the chroot once it’s set up:
1
|
|
There are several different types of chroots you can use with schroot
.
We’ll use the directory
type, as it’s the simplest to understand (just another
directory on the existing filesystem), and it will make it simpler to expose a
few things to the host later on.
You can place the directory wherever, but some existing filesystems are mapped
into the chroot for convenience, so avoiding /home
is probably a good idea. I
went with /var/chroot/linux32
:
1
|
|
We need to update schroot.conf
to configure the new chroot:
1 2 3 4 5 6 7 8 9 10 11 |
|
In particular, personality
is important to set for this multi-arch use case.
(Make sure to replace the user names with your own!)
Firefox will want access to shared memory as well, so we’ll need to add that to the set of mapped filesystems in the chroot:
1 2 3 |
|
Now we need to install the 32-bit system inside the chroot. We can do
that with a utility called debootstrap
:
1 2 |
|
This will fetch all the packages for a 32-bit installation and place them in the
chroot. For a cross-arch bootstrap, we need to add --foreign
to skip the
unpacking step, which we will do momentarily from inside the chroot.
--variant=buildd
will help us out a bit by including common build tools.
To finish installation, we have to enter the chroot. You can enter the chroot
with schroot
and it remains active until you exit
. Any snippets that say
(chroot)
instead of (host)
are meant to be run inside the chroot.
So, inside the chroot, run the second stage of debootstrap
to actually unpack
everything:
1
|
|
Let’s double-check that things are working like we expect:
1 2 |
|
Great, we’re getting closer!
Install packages
Now that we have a basic 32-bit installation, let’s install the packages we need
for development. The apt
source list inside the chroot is pretty bare bones,
so we’ll want to expand it a bit to reach everything we need:
1 2 3 4 5 |
|
Let’s grab the same packages from before (without :i386
since that’s the
default inside the chroot):
1
|
|
You may need to install the 32-bit version of your graphics card’s GL library to get reasonable graphics output when running in the 32-bit environment.
1
|
|
We’ll also want to have access to the X display inside the chroot. The simple way to achieve this is to disable X security in the host and expose the same display in the chroot:
1 2 |
|
We can verify that we have accelerated graphics:
1 2 3 |
|
Building Firefox
In order for the host to build Firefox for the 32-bit target, it needs to access various 32-bit libraries and include files. We already have these installed in the chroot, so let’s cheat and expose them to the host via symlinks into the chroot’s file structure:
1 2 3 |
|
We also need Rust to be able to target 32-bit from the host, so let’s install support for that:
1
|
|
We’ll need a specialized .mozconfig
for Firefox to target 32-bit. Something
like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
This was adapted from the mozconfig.linux32
used for official 32-bit
builds. I modified the PKG_CONFIG_PATH
to point at more 32-bit files
installed inside the chroot, similar to the library and include changes above.
Now, we should be able to build successfully:
1
|
|
Then, from the chroot, you can run Firefox and other tests:
1
|
|
Footnotes
1. It’s commonly suggested that people should use ./mach
bootstrap
to install the Firefox build dependencies, so feel free to try that
if you wish. I dislike scripts that install system packages, so I’ve done it
manually here. The bootstrap script would likely need various adjustments to
support this use case. ↩