Compiling Firefox for a RHEL Shared Host

These are the steps I took to compile Firefox so it can run on a RHEL shared hosting server which doesn’t have D-Bus installed and only has GLibc 2.12.

Situation

  • You want to run headless Firefox on a shared host running RHEL
  • You don’t have privileged access (e.g. no root)
  • Your shared host only has GLibc 2.12
  • Your shared host doesn’t have D-Bus

Why would someone want to run Firefox on a shared host? Firefox can be run as a headless browser (without a GUI) in conjunction with Xvfb to automate interaction in web pages and take screenshots (similar to PhantomJS, but better).

Without yum or rpm available – because the target box is a shared server – uploading the official pre-compiled Firefox binary and support folders (from here) results in an intractable D-Bus problem:

A shared host likely won’t have D-Bus installed, or even the required /var/lib/dbus/machine-id, and it cannot be created by an unprivileged user. Firefox doesn’t actually need D-Bus, but the official pre-compiled binary was compiled with a mode that requires it. The only option is to compile Firefox ourselves with D-Bus support disabled.

If you have elevated privileges on the shared host, you can install dbus, or just do the following with a D-Bus utility: dbus-uuidgen > /var/lib/dbus/machine-id. Having your shared host create this file may allow Firefox to run.

Here are the steps I took to compile Firefox on 64-bit CentOS (essentially identical to RHEL) with D-Bus support disabled, and get it running on a RHEL shared host. Unfortunately this shared host still uses GLibc 2.12 (introduced in 2010) so I had to work around that.

Prerequisites:

  • Virtual machine with 64-bit CentOS built with GLibc 2.12 (e.g. CentOS 6.8, here)
  • Source code of Firefox (v49 is here)
  • Internet connection and root access on the source CentOS machine

To make the compiling easier without negatively affecting an existing system, obtain a VMWare image of CentOS 6.8 from osboxes.org. It already has Firefox pre-installed, but that installation will be removed.

The VMWare image of CentOS doesn’t have internet enabled. Run dhclient -v from root to fix this. Also, be sure VMWare Tools is installed if you use the VMWare image of CentOS.

In my case, I needed to verify the Glibc version with ldd --version to make sure it matches that of the shared host (in this case 2.12).

Before going any further, it’s best to yum update all the packages.

Running yum update on the VMWare image above may take a long time and could require 500+ MB. You may have to reinstall VMWare Tools afterwards.

Firefox sources (5 min)

Let’s first remove the existing Firefox. As root throughout, run this command:

To get started, download the Firefox Linux source from https://archive.mozilla.org/pub/firefox/releases/. Select the latest stable version (v49.0.1 at this time) and download the tarball from the sources/ folder. As the root user throughout we’ll set up the source files like so:

We’ll eventually, but not yet, run ./mach build from this folder and Firefox will be built in /tmp/firefox-49.0.1/firefox-build-dir.

Mozconfig configuration file (10 s)

To disable D-Bus support, let’s first create the configuration file1 which is used to customize the Firefox build. Still in /tmp/firefox-49.0.1, run the following command which will save a file called mozconfig. Enable or disable settings as desired. Here is what I used:

Python (10 min)

Next we will need at least Python 2.7.10 installed because Firefox uses it in their build system. On my CentOS 6.8 VMWare machine the highest version that can be installed is 2.6.6.

Use this command to install Python 2.7.102.

By changing the Python version number in the above command you may be able to get an even higher version, but this is sufficient. Do not go lower than 2.7.10 because it is a dependency of GLib which we have to upgrade later. Also note that if you enable RHSCL you can only get 2.7.8 or 3.3.2

GCC (5 min)

Once Python 2.7.10 is installed, configure will report that it needs GCC 4.8 installed. The GCC distributed with CentOS 6 is unfortunately only 4.4.7. Let’s work around that.

We need to use the RHSCL (Red Hat Software Collection) repo to get at least GCC 4.8 installed. We will install GCC 4.9.2 via the devtoolset-3 package. Run the following command:

After we set the environment above in the last command which enables GCC 4.9.2 immediately, let’s confirm the new GCC version:

Autoconf (30 s)

Next, configure reports that it needs Autoconf 2.13 to do some more configuration tasks. Install Autoconf 2.13 with this command:

Libffi (20 s)

The next obstacle to compiling Firefox on the CentOS 6.8 box is libffi:

We need another side channel install to get libffi >= 3.0.9 on this system.

There is a guide here. The latest library can be downloaded here, and it can be configured and compiled with the following shell command. To avoid a make error later (see the end of this article), we will install the latest version (3.2.1 at this time).

Firefox reports that it needs libffi 3.0.9 or higher. We will actually install 3.2.1 because it is a dependency of GLib which we will upgrade later.

GTK+ 2 (1 min)

Firefox will be using GIMP Toolkit 2 (GTK+ 2), so let’s install that now.

Yasm (1.5 min)

Next we are going to need the Yasm assembler as configure points out (only if we want WebM videos to play, which we do):

Yum-searching for “yasm” is initially non-resultant. Since EPEL (Extra Packages for Enterprise Linux, including RHEL) has yasm for CentOS 7, we can quickly install Yasm. Here are the commands:

Alsa & libXt (1 min)

We have to install a couple more packages as pointed out by configure for sound3, and some X headers:

  • Sound (alsa) – yum install -y alsa-lib-devel.x86_64
  • X headers (libXt) – yum install -y libXt-devel.x86_64
Firefox doesn’t display helpful messages for CentOS/RHEL users. For example, “Can’t find X headers (install libxt-dev (Debian/Ubuntu), libXt-devel (Fedora), or xorg-x11-libXt-devel (SuSE))“. If you enable more options in mozconfig, you can search for the right packages with yum search or yum whatprovides.

Glib & Readline (4 min)

I’ll save you the trouble of compiling Firefox only to discover after 24 minutes that you need to upgrade GLib. A make error may happen related to GLib (not Glibc). There is no warning in the configure stage, but GLib version 2.30 or higher is required. I know this because the symbol g_unicode_script_from_iso15924 is new in version 2.30. This took a bit of research.

When installing glib-devel.x86_64 via yum, the highest version on CentOS 6.8 available is 2.28. We need 2.30 or higher. Just to be safe we’ll install GLib 2.44.14 because it itself requires libffi 3.2.1 and Python 2.7.10, both of which we just installed.

But first, Glib needs the readline library. Run this command to install that library:

If you install glib > 2.44.1, you will need a higher version of Python than we installed earlier, and may need a higher version of libffi as well.

This command will install Glib 2.44.1. Leave --with-pcre=internal as it is otherwise you will need to install PCRE too which requires another side channel install:

You may have noticed the installation of docbook-style-xsl. This took a while to investigate, but installing it solves a build error related to needing to download an XSLT stylesheet that cannot be found as it creates a local copy instead.

Xdamage (20 s)

Here’s another gotcha.

This took some research too, but long story short, run yum install -y libXdamage-devel.x86_64 to install the Xdamage headers.

Building Firefox (1.5 hours)

Finally we can build Firefox. This is going to take a very long time5. First, configure Firefox to check for any additional dependencies that are missing.

If there are any missing dependencies, please add them as needed. Hopefully there wont be any more unless you are compiling a different version of Firefox, or the mozconfig settings have changed. Let’s build Firefox now.

./mach -l LOGNAME build will save to disk all the make activity including any errors which may reveal missing dependencies. Also, if you have a failed build, run ./mach clobber to clean the previous attempt (the same as make clean)

Hopefully you will get a message like the following. It took over 1.5 hours to build Firefox on an i7 with just one core dedicated to the VMWare machine.

Packaging Firefox (2 min)

You may notice as I did that the build folder is about 2 GB. Yikes. Mozilla explains,

Our official builds are packaged before shipping, which entails stripping (among many other things). You can produce a packaged build by running “./mach package” in your source directory or “make package” in your object directory.

Thus, run ./mach package. You should find something similar to the following.

Firefox package distribution
Firefox package distribution

The uncompressed Firefox build we just created is only about 100 MB. Much more reasonable. Upload the whole /dist folder to the shared host.

Running Firefox on the shared server

Now let’s see the shared libraries our shiny new build of Firefox requires.

So we know right away the main binary requires us to bring over libstdc++.so.6 to the shared server because it is in the /usr/lib64 folder. You can try to dereference the symlink to that binary and upload it manually now, or you can wait a few moments for a more automated way.

On your shared server you can set the LD_LIBRARY_PATH environment variable to some location in your home folder where you will store your libraries (e.g. ~/lib64).

Packaging libxul.so dependencies (10 s)

The Firefox binary’s dependencies are covered, but libxul.so, which is present in the Firefox binary directory (because you copied the whole /dist folder over), reports many missing libraries. The great news is that if you follow my guide on transplanting Linux binaries then the libxul.so dependencies can be copied over to the LD_LIBRARY_PATH easily. Here is the command you can use6:

All the dependent shared libraries will be combined into the base of a tarball named libxul.so.tar.gz. Notice that /usr/lib64/libstdc++.so.6 is included as well, so that is why we skipped copying it earlier.

Packaged libxul.so dependencies
Packaged libxul.so dependencies

Upload the Firefox /dist folder and all the shared libraries, set the proper environment variables (i.e.. PATH, LD_LIBRARY_PATH) and Firefox should now run on a shared host.

Test out Firefox

Using Xvfb and Firefox you can test if the installation works. Here is a sample out from the build I made above.

The script firefox-version.sh just contains firefox --version to show the version number and exit Firefox gracefully. There we have it.

Firefox build successful
Firefox build successful

Notes:

  1. The original mozconfig file is courtesy of www.linuxfromscratch.org.
  2. The original Python 2.7 install script is courtesy of https://www.digitalocean.com/community/tutorials/how-to-set-up-python-2-7-6-and-3-3-3-on-centos-6-4
  3. We won’t get to enjoy sound on a shared host, but this is a requirement of building Firefox.
  4. More details on installing GLib 2.44.1 are here
  5. See this guide for tips on making the build process faster.
  6. This command is explained in detail here.