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:
1 2 3 4 | process 423508: D-Bus library appears to be incorrectly set up; failed to read machine uuid: Failed to open "/var/lib/dbus/machine-id": No such file or directory See the manual page for dbus-uuidgen to correct this issue. D-Bus not built with -rdynamic so unable to print a backtrace Redirecting call to abort() to mozalloc_abort |
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.
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.
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).
1 2 | [osboxes@localhost ~]$ ldd --version ldd (GNU libc) 2.12 |
Before going any further, it’s best to yum update
all the packages.
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:
1 | yum remove firefox |
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:
1 2 3 4 | cd /tmp && wget https://archive.mozilla.org/pub/firefox/releases/49.0.1/source/firefox-49.0.1.source.tar.xz && tar -xJf firefox-49.0.1.source.tar.xz && cd firefox-49.0.1 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | cat > mozconfig << "EOF" # If you have a multicore machine, all cores will be used by default. # If desired, you can reduce the number of cores used, e.g. to 1, by # uncommenting the next line and setting a valid number of CPU cores. mk_add_options MOZ_MAKE_FLAGS="-j1" # If you have installed dbus-glib, comment out this line: ac_add_options --disable-dbus # If you have installed dbus-glib, and you have installed (or will install) # wireless-tools, and you wish to use geolocation web services, comment out # this line ac_add_options --disable-necko-wifi # Uncomment this option if you wish to build with gtk+-2 ac_add_options --enable-default-toolkit=cairo-gtk2 # Uncomment these lines if you have installed optional dependencies: #ac_add_options --enable-system-hunspell #ac_add_options --enable-startup-notification # Comment out following option if you have PulseAudio installed ac_add_options --disable-pulseaudio # If you have installed GConf, comment out this line ac_add_options --disable-gconf # Comment out following options if you have not installed # recommended dependencies: #ac_add_options --enable-system-sqlite #ac_add_options --with-system-libevent #ac_add_options --with-system-libvpx #ac_add_options --with-system-nspr #ac_add_options --with-system-nss #ac_add_options --with-system-icu # If you are going to apply the patch for system graphite # and system harfbuzz, uncomment these lines: #ac_add_options --with-system-graphite2 #ac_add_options --with-system-harfbuzz # Stripping is now enabled by default. # Uncomment these lines if you need to run a debugger: #ac_add_options --disable-strip #ac_add_options --disable-install-strip # The BLFS editors recommend not changing anything below this line: ac_add_options --prefix=/user/ff ac_add_options --enable-application=browser ac_add_options --disable-crashreporter ac_add_options --disable-updater ac_add_options --disable-tests ac_add_options --enable-optimize ac_add_options --enable-gio ac_add_options --enable-official-branding ac_add_options --enable-safe-browsing ac_add_options --enable-url-classifier # From firefox-40, using system cairo causes firefox to crash # frequently when it is doing background rendering in a tab. #ac_add_options --enable-system-cairo ac_add_options --enable-system-ffi ac_add_options --enable-system-pixman ac_add_options --with-pthreads ac_add_options --with-system-bz2 ac_add_options --with-system-jpeg #ac_add_options --with-system-png # System PNG doesn't support APNG ac_add_options --with-system-zlib mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/firefox-build-dir EOF |
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.
1 2 3 | [root@localhost ~]# yum install python.x86_64 Package python-2.6.6-66.el6_8.x86_64 already installed and latest version Nothing to do |
Use this command to install Python 2.7.102.
1 2 3 4 5 6 7 8 9 10 11 12 | cd /tmp && yum groupinstall -y 'development tools' && yum install -y zlib-dev openssl-devel sqlite-devel bzip2-devel && yum install -y xz-libs && wget http://www.python.org/ftp/python/2.7.10/Python-2.7.10.tar.xz && xz -d Python-2.7.10.tar.xz && tar -xvf Python-2.7.10.tar && cd Python-2.7.10 && ./configure --prefix=/usr/local && make && make altinstall && export PATH="/usr/local/bin:$PATH" |
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.
1 | ERROR: Only GCC 4.8 or newer is supported (found version 4.4.7). |
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:
1 2 3 4 | yum install -y centos-release-scl && yum-config-manager --enable rhel-server-rhscl-7-rpms && yum install -y devtoolset-3-toolchain && scl enable devtoolset-3 bash |
After we set the environment above in the last command which enables GCC 4.9.2 immediately, let’s confirm the new GCC version:
1 2 | [root@localhost osboxes]# gcc --version gcc (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6) |
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:
1 | yum install -y autoconf213.noarch |
Libffi (20 s)
The next obstacle to compiling Firefox on the CentOS 6.8 box is libffi
:
1 2 | checking for libffi >= 3.0.9... Requested 'libffi >= 3.0.9' but version of libffi is 3.0.5 configure: error: Library requirements (libffi >= 3.0.9) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them. |
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).
1 2 3 4 5 6 7 | cd /tmp && wget ftp://sourceware.org/pub/libffi/libffi-3.2.1.tar.gz && tar -xvf libffi-3.2.1.tar.gz && cd libffi-3.2.1 && ./configure --prefix=/usr --libdir=/usr/lib64 --disable-static && make && make install |
GTK+ 2 (1 min)
Firefox will be using GIMP Toolkit 2 (GTK+ 2), so let’s install that now.
1 | yum install -y gtk2-devel.x86_64 |
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):
1 | DEBUG: configure: error: yasm is a required build tool for this architecture when webm is enabled. You may either install yasm or --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details. |
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:
1 2 | yum --enablerepo=extras install -y epel-release && yum install -y yasm-devel.x86_64 |
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
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.
1 2 3 4 | 24:40.15 /home/osboxes/firefox-50.0b7/gfx/thebes/gfxFontconfigFonts.cpp: In member function ‘virtual already_AddRefed<gfxFont> gfxPangoFontGroup::FindFontForChar(uint32_t, uint32_t, uint32_t, gfxFontGroup::Script, gfxFont*, uint8_t*)’: 24:40.15 /home/osboxes/firefox-50.0b7/gfx/thebes/gfxFontconfigFonts.cpp:1628:66: error: ‘g_unicode_script_from_iso15924’ was not declared in this scope 24:40.15 (const PangoScript)g_unicode_script_from_iso15924(scriptTag); 24:40.15 ^ |
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:
1 | yum install -y readline-devel.x86_64 |
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:
1 2 3 4 5 6 7 8 9 | cd /tmp && yum install -y docbook-style-xsl && wget ftp://ftp.gnome.org/pub/gnome/sources/glib/2.44/glib-2.44.1.tar.xz && tar -xJf glib-2.44.1.tar.xz && cd glib-2.44.1 && export PKG_CONFIG_PATH=/usr/lib64/pkgconfig && ./configure --prefix=/usr --libdir=/usr/lib64 --with-pcre=internal && make && make 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.
1 2 3 4 5 | 37:46.66 In file included from /tmp/firefox-49.0.1/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc:16:0, 37:46.66 from /tmp/firefox-49.0.1/firefox-build-dir/media/webrtc/trunk/webrtc/modules/modules_desktop_capture/Unified_cpp_webrtc_modules1.cpp:11: 37:46.66 /tmp/firefox-49.0.1/firefox-build-dir/dist/system_wrappers/X11/extensions/Xdamage.h:3:41: fatal error: X11/extensions/Xdamage.h: No such file or directory 37:46.66 #include_next <X11/extensions/Xdamage.h> 37:46.66 ^ |
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.
1 2 | cd /tmp/firefox-49.0.1 && ./mach configure |
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.
1 2 | cd /tmp/firefox-49.0.1 && ./mach -l machlog.txt build |
./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.
1 2 3 4 5 6 7 | ... 97:43.19 945 compiler warnings present. 97:59.46 Notification center failed: Install the python dbus module to get a notification when the build finishes. 97:59.46 We know it took a while, but your build finally finished successfully! To view resource usage of the build, run |mach resource-usage|. To take your build for a test drive, run: |mach run| For more information on what to do now, see https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox |
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.
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.
1 2 3 4 5 6 7 8 9 10 | [root@localhost dist]# ldd /tmp/firefox-49.0.1/firefox-build-dir/dist/firefox/firefox linux-vdso.so.1 => (0x00007ffcb91d6000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00000038c9e00000) libdl.so.2 => /lib64/libdl.so.2 (0x00000038c9600000) librt.so.1 => /lib64/librt.so.1 (0x00000038ca200000) libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00000038d4600000) libm.so.6 => /lib64/libm.so.6 (0x00000038ca600000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00000038d4e00000) libc.so.6 => /lib64/libc.so.6 (0x00000038c9a00000) /lib64/ld-linux-x86-64.so.2 (0x00000038c9200000) |
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.
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:
1 | SRCFILE=/tmp/firefox-49.0.1/firefox-build-dir/dist/bin/libxul.so; tar cvzfh $(basename $SRCFILE).tar.gz --xform='s|.*/||'$(ldd $SRCFILE 2> /dev/null | grep -v 'not found' | grep '/usr/lib64/' | awk '{printf " %s", $3}') |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | [root@localhost tmp]# SRCFILE=/tmp/firefox-49.0.1/firefox-build-dir/dist/bin/libxul.so; tar cvzfh $(basename $SRCFILE).tar.gz --xform='s|.*/||'$(ldd $SRCFILE 2> /dev/null | grep -v 'not found' | grep '/usr/lib64/' | awk '{printf " %s", $3}') tar: Removing leading `/' from member names /usr/lib64/libnss3.so /usr/lib64/libsmime3.so /usr/lib64/libssl3.so /usr/lib64/libnssutil3.so /usr/lib64/xulrunner/libmozsqlite3.so /usr/lib64/libffi.so.6 /usr/lib64/libfreetype.so.6 /usr/lib64/libfontconfig.so.1 /usr/lib64/libXrender.so.1 /usr/lib64/libXext.so.6 /usr/lib64/libXdamage.so.1 /usr/lib64/libXfixes.so.3 /usr/lib64/libXcomposite.so.1 /usr/lib64/libpixman-1.so.0 /usr/lib64/libgtk-x11-2.0.so.0 /usr/lib64/libatk-1.0.so.0 /usr/lib64/libpangoft2-1.0.so.0 /usr/lib64/libgdk-x11-2.0.so.0 /usr/lib64/libpangocairo-1.0.so.0 /usr/lib64/libgdk_pixbuf-2.0.so.0 /usr/lib64/libpango-1.0.so.0 /usr/lib64/libcairo.so.2 /usr/lib64/libX11.so.6 /usr/lib64/libXt.so.6 /usr/lib64/libstdc++.so.6 /usr/lib64/libXinerama.so.1 /usr/lib64/libXi.so.6 /usr/lib64/libXrandr.so.2 /usr/lib64/libXcursor.so.1 /usr/lib64/libpng12.so.0 /usr/lib64/libxcb.so.1 /usr/lib64/libSM.so.6 /usr/lib64/libICE.so.6 /usr/lib64/libXau.so.6 |
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.
1 2 | drakes@b4pkcpnl0470 [~]$ ~/bin/xvfb-run -e ~/tmp/xvfb-error.log ~/firefox/firefox-version.sh -nolisten tcp -screen 0 1920x1080x24 Mozilla Firefox 49.0.1 |
The script firefox-version.sh
just contains firefox --version
to show the version number and exit Firefox gracefully. There we have it.
Notes:
- The original
mozconfig
file is courtesy of www.linuxfromscratch.org. ↩ - 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 ↩
- We won’t get to enjoy sound on a shared host, but this is a requirement of building Firefox. ↩
- More details on installing GLib 2.44.1 are here ↩
- See this guide for tips on making the build process faster. ↩
- This command is explained in detail here. ↩