Cross-compiling Servo to ARM

While Servo has been supporting Android for a while now, it turned out cross compiling to ARM devices requires some additional work. Now that most of these patches have been landed, it is much more easier to build Servo for ARM.

UPDATE: Most of the patches have landed, post has been updated.

For reference, in this post I'm going to use Servo at commit 73b52c0, which requires Rust version 2d0cbf3e3e25e092bd9e4c94d08e446b680869f0. You will need the standard Rust library of this particular version, compiled to ARM. The required version is stored in the file rust-snapshot-hash.

You might have to build that component for yourself too, as ARM libs are not distributed with Rust yet. The first part of the on-board building guide describes how can do it. UPDATE: Now you should also be able to compile it by simply set the appropriate configure flags.

You will also need the ARM system libraries, but you can copy them either from the system running on your board or from a downloaded distribution installer, eg. a Raspbian image.

Setting up a rootfs

First, I'm going to set up an Ubuntu 14.04 rootfs to build in a clean environment. You can safely skip this step (just don't forget to change the paths later).

sudo apt-get install debootstrap
sudo debootstrap --arch=amd64 trusty rootfs-trusty
sudo mount -o bind /dev  rootfs-trusty/dev
sudo mount -o bind /proc rootfs-trusty/proc
sudo mount -o bind /sys  rootfs-trusty/sys
sudo chroot rootfs-trusty /bin/bash

Add a new user inside the rootfs:

useradd -m servobuild -s /bin/bash
passwd servobuild
usermod -aG sudo servobuild
su - servobuild

Prerequisites

All right, let's install the ARM compiler. There are many kinds of toolchains (eg. optimized for a certain board, like the Raspberry Pi), but the default one will be fine:

sudo apt-get update
sudo apt-get install g++-arm-linux-gnueabihf

Install the Servo dependencies (copied from the README):

sudo apt-get install curl freeglut3-dev \
    libfreetype6-dev libgl1-mesa-dri libglib2.0-dev xorg-dev \
    gperf g++ cmake python-virtualenv \
    libssl-dev libbz2-dev libosmesa6-dev libxmu6 libxmu-dev

Note: if python-virtualenv is not found, install it like this:

sudo apt-get install python-setuptools
curl -O https://pypi.python.org/packages/source/v/virtualenv/virtualenv-13.0.3.tar.gz
tar xzf virtualenv-13.0.3.tar.gz && cd virtualenv-13.0.3
sudo python setup.py install

we will also need git:!

sudo apt-get install git

Finally, download Servo, but leave it alone for now:

git clone https://github.com/servo/servo

Updating dependencies

UPDATE: Most dependencies work correctly now.

What you still have to do, is to set up the build system:

git submodule init
git submodule update
./mach bootstrap-rust
./mach bootstrap-cargo

I have uploaded some ARM patches for Servo and for its dependencies, which have not been landed yet. Fortunately, we can use custom versions of these repositories without messing up the main Servo (Cargo) directory, so we can apply these changes easily.

I used to store these git repositories in servo_repos directory:

mkdir servo_repos && cd servo_repos

Download each dependency which has a pending patch and apply it:

git clone https://github.com/servo/libexpat.git && cd libexpat && \
curl -LO https://github.com/servo/libexpat/pull/6.diff && \
git apply 6.diff && \
cd ..
git clone https://github.com/servo/mozjs.git && cd mozjs && \
curl -LO https://github.com/servo/mozjs/pull/48.diff && \
git apply 48.diff && \
cd ..
git clone https://github.com/servo/rust-mozjs.git && cd rust-mozjs && \
curl -LO https://github.com/servo/rust-mozjs/pull/167.diff && \
git apply 167.diff && \
cd ..
git clone https://github.com/servo/rust-png.git && cd rust-png && \
curl -LO https://github.com/servo/rust-png/pull/77.diff && \
git apply 77.diff && \
cd ..
git clone https://github.com/servo/libfontconfig.git && cd libfontconfig && \
curl -LO https://github.com/servo/libfontconfig/pull/11.diff && \
git apply 11.diff && \
cd ..
git clone https://github.com/servo/glutin.git && cd glutin && \
curl -LO https://github.com/servo/glutin/pull/32.diff && \
git apply 32.diff && \
cd ..

Next step is to create a Cargo config file for Servo to describe the location of these custom-patched repositories:

cd ~/servo
mkdir .cargo
cat << 'EOF' > .cargo/config
paths = [
    "/home/servobuild/servo_repos/libexpat",
    "/home/servobuild/servo_repos/libfontconfig",
    "/home/servobuild/servo_repos/mozjs",
    "/home/servobuild/servo_repos/rust-mozjs",
    "/home/servobuild/servo_repos/rust-png",
    "/home/servobuild/servo_repos/glutin"
]
EOF

This will solve the problem of the pending patches. Some of the dependencies have patches for ARM cross compilation, but Servo doesn't use the latest version of these. To update them, you have to initialize the build system first:

git submodule init
git submodule update
./mach bootstrap-rust
./mach bootstrap-cargo

These will download Rust and Cargo, and create the directory structure where we will copy the Rust libraries in the next section.

Alternatively, you can just run './mach build' command, which also does this for you (Note: this build command fails after the directory creation).

Now we have to setup the build system. First of all, update the dependencies:

./mach cargo-update -p freetype-sys
./mach cargo-update -p skia
./mach cargo-update -p stb_image

If you wish you can also specify the desired version for each dependency (just use the --precise flag for it). At the time of writing I used the following hashes:

./mach cargo-update -p freetype-sys --precise 39b368440712818ce8b071daf04d73541640de6b
./mach cargo-update -p skia --precise 7a2ce925644274b8468099c3900425a9349757eb
./mach cargo-update -p stb_image --precise 2235148b8230828ac0b96291def283e82fc4cfdd

Installing libraries

We will need to install the Rust standard libraries for ARM if we want to cross compile programs written in Rust. Let's exit from the rootfs for a minute:

exit    # log out from 'servobuild'
exit    # log out from the rootfs

and copy your ARM Rust libraries to the correct location:

cp -R path/to/your/rustc/lib/rustlib/arm-unknown-linux-gnueabihf \
rootfs-trusty/home/servobuild/servo/.servo/rust/2d0cbf3e3e25e092bd9e4c94d08e446b680869f0\
/rustc-1.2.0-dev-x86_64-unknown-linux-gnu/rustc/lib/rustlib/

Note: Your target directory might have a different Rust hash/version.

Note: In case you've skipped the preface, you have to build the Rust standard libraries of the version used by Servo for ARM

We will also need to install the ARM system libraries if we don't want to get linker errors later:

sudo cp -R path/to/your/lib/arm-linux-gnueabihf rootfs-trusty/lib/
sudo cp -R path/to/your/usr/lib/arm-linux-gnueabihf rootfs-trusty/usr/lib/

We are done with the libraries. Let's go back into the rootfs:

sudo chroot rootfs-trusty /bin/bash
su - servobuild
cd servo

There are two 'missing header' errors, for OpenSSL and Zlib. Apparently this is a packaging issue on certain distros. Anyway, you can fix it like this:

mkdir -p /usr/include/arm-linux-gnueabihf/
sudo ln -s /usr/include/x86_64-linux-gnu/openssl /usr/include/arm-linux-gnueabihf/
sudo ln -s /usr/include/x86_64-linux-gnu/zconf.h /usr/include/arm-linux-gnueabihf/

Building!

Finally! If you've followed the previous steps correctly, executing the following ...

PKG_CONFIG_ALLOW_CROSS=1 \
PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig \
EXPAT_NO_PKG_CONFIG=1 \
CC=arm-linux-gnueabihf-gcc \
CXX=arm-linux-gnueabihf-g++ \
CPP="arm-linux-gnueabihf-gcc -E" \
LD=arm-linux-gnueabihf-ld \
AR=arm-linux-gnueabihf-ar \
RANLIB=arm-linux-gnueabihf-ranlib \
./mach build --target=arm-unknown-linux-gnueabihf --dev

should build servo correctly. Congratulations! You have the final binary at ./target/arm-unknown-linux-gnueabihf/debug/servo.

Which will give you

+... a huge linker error. Don't worry, we'll fix this! It seems this is also a packaging problem on Ubuntu; several X11 linker flags are missing, and you will have to add them manually. Copy the failing command (starting from "arm-linux-gnueabihf-gcc" "-Wl,--as-needed" "-L" ... to ... "c" "-l" "m" "-l" "compiler-rt") to your favorite text editor, and edit this. First modify the text to create a proper GCC command:

  • replace all "-l" " with "-l
  • replace all "-L" " with "-L
  • delete any solo " character

Then add these three additional flags to the end:

-lXxf86vm -lXmu -lXcursor

Now, you should be lucky, and got Servo compiled correctly. Congratulations! Find your binary at ./target/arm-unknown-linux-gnueabihf/debug/servo.

Issues

Servo still has some visual (X11 and OpenGL) glitches on embedded Linux, whether you build on board or cross compile.

Happy cross compilation!

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • No HTML tags allowed
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Fill in the blank