summaryrefslogtreecommitdiffstats
path: root/build/unix
diff options
context:
space:
mode:
Diffstat (limited to 'build/unix')
-rw-r--r--build/unix/aix.exp5
-rwxr-xr-xbuild/unix/build-binutils/build-binutils.sh32
-rw-r--r--build/unix/build-gcc/PR64905.patch11
-rwxr-xr-xbuild/unix/build-gcc/build-gcc.sh52
-rw-r--r--build/unix/build-gtk3/build-gtk3.sh150
-rw-r--r--build/unix/elfhack/Makefile.in48
-rw-r--r--build/unix/elfhack/README28
-rw-r--r--build/unix/elfhack/dummy.c9
-rw-r--r--build/unix/elfhack/elf.cpp921
-rw-r--r--build/unix/elfhack/elfhack.cpp823
-rw-r--r--build/unix/elfhack/elfxx.h701
-rw-r--r--build/unix/elfhack/inject.c52
-rw-r--r--build/unix/elfhack/inject/Makefile.in15
-rw-r--r--build/unix/elfhack/inject/moz.build26
-rw-r--r--build/unix/elfhack/moz.build28
-rw-r--r--build/unix/elfhack/test-array.c8
-rw-r--r--build/unix/elfhack/test-ctors.c17
-rw-r--r--build/unix/elfhack/test.c162
-rw-r--r--build/unix/gnu-ld-scripts/components-export-list1
-rw-r--r--build/unix/gnu-ld-scripts/components-version-script7
-rw-r--r--build/unix/moz.build22
-rw-r--r--build/unix/mozconfig.asan27
-rw-r--r--build/unix/mozconfig.gtk28
-rw-r--r--build/unix/mozconfig.linux38
-rw-r--r--build/unix/mozconfig.linux3212
-rw-r--r--build/unix/mozconfig.stdcxx15
-rw-r--r--build/unix/mozconfig.tsan34
-rw-r--r--build/unix/mozilla.in108
-rwxr-xr-xbuild/unix/print-failed-commands.sh25
-rwxr-xr-xbuild/unix/print-non-newline.sh35
-rw-r--r--build/unix/rewrite_asan_dylib.py60
-rw-r--r--build/unix/run-gprof.sh17
-rw-r--r--build/unix/run-hiprof.sh25
-rwxr-xr-xbuild/unix/run-mozilla.sh362
-rw-r--r--build/unix/run-third.sh25
-rw-r--r--build/unix/stdc++compat/Makefile.in7
-rw-r--r--build/unix/stdc++compat/moz.build24
-rw-r--r--build/unix/stdc++compat/stdc++compat.cpp78
38 files changed, 4038 insertions, 0 deletions
diff --git a/build/unix/aix.exp b/build/unix/aix.exp
new file mode 100644
index 000000000..20f7cb431
--- /dev/null
+++ b/build/unix/aix.exp
@@ -0,0 +1,5 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+NSGetModule
diff --git a/build/unix/build-binutils/build-binutils.sh b/build/unix/build-binutils/build-binutils.sh
new file mode 100755
index 000000000..ab675f55a
--- /dev/null
+++ b/build/unix/build-binutils/build-binutils.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+binutils_version=2.25.1
+make_flags='-j12'
+
+root_dir="$1"
+if [ -z "$root_dir" -o ! -d "$root_dir" ]; then
+ root_dir=$(mktemp -d)
+fi
+cd $root_dir
+
+if test -z $TMPDIR; then
+ TMPDIR=/tmp/
+fi
+
+# Download the source of the specified version of binutils
+wget -c -P $TMPDIR ftp://ftp.gnu.org/gnu/binutils/binutils-${binutils_version}.tar.bz2 || exit 1
+tar xjf $TMPDIR/binutils-${binutils_version}.tar.bz2
+
+# Build binutils
+mkdir binutils-objdir
+cd binutils-objdir
+
+../binutils-$binutils_version/configure --prefix /tools/binutils/ --enable-gold --enable-plugins --disable-nls || exit 1
+make $make_flags || exit 1
+make install $make_flags DESTDIR=$root_dir || exit 1
+
+cd ..
+
+# Make a package of the built binutils
+cd $root_dir/tools
+tar caf $root_dir/binutils.tar.xz binutils/
diff --git a/build/unix/build-gcc/PR64905.patch b/build/unix/build-gcc/PR64905.patch
new file mode 100644
index 000000000..b86c99b9f
--- /dev/null
+++ b/build/unix/build-gcc/PR64905.patch
@@ -0,0 +1,11 @@
+--- trunk/gcc/lra-eliminations.c 2015/02/04 20:00:48 220415
++++ trunk/gcc/lra-eliminations.c 2015/02/04 20:02:21 220416
+@@ -182,6 +182,8 @@
+ if (! value
+ && ep->from == FRAME_POINTER_REGNUM && ep->to == STACK_POINTER_REGNUM)
+ frame_pointer_needed = 1;
++ if (!frame_pointer_needed)
++ REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM) = 0;
+ }
+
+ /* Map: eliminable "from" register -> its current elimination,
diff --git a/build/unix/build-gcc/build-gcc.sh b/build/unix/build-gcc/build-gcc.sh
new file mode 100755
index 000000000..df3bc5dfd
--- /dev/null
+++ b/build/unix/build-gcc/build-gcc.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+gcc_version=4.8.5
+binutils_version=2.25.1
+this_path=$(readlink -f $(dirname $0))
+make_flags='-j12'
+
+root_dir="$1"
+if [ -z "$root_dir" -o ! -d "$root_dir" ]; then
+ root_dir=$(mktemp -d)
+fi
+cd $root_dir
+
+if test -z $TMPDIR; then
+ TMPDIR=/tmp/
+fi
+
+wget -c -P $TMPDIR ftp://ftp.gnu.org/gnu/binutils/binutils-$binutils_version.tar.bz2 || exit 1
+tar xjf $TMPDIR/binutils-$binutils_version.tar.bz2
+mkdir binutils-objdir
+cd binutils-objdir
+# gold is disabled because we don't use it on automation, and also we ran into
+# some issues with it using this script in build-clang.py.
+../binutils-$binutils_version/configure --prefix /tools/gcc/ --disable-gold --enable-plugins --disable-nls || exit 1
+make $make_flags || exit 1
+make install $make_flags DESTDIR=$root_dir || exit 1
+cd ..
+
+case "$gcc_version" in
+*-*)
+ wget -c -P $TMPDIR ftp://gcc.gnu.org/pub/gcc/snapshots/$gcc_version/gcc-$gcc_version.tar.bz2 || exit 1
+ ;;
+*)
+ wget -c -P $TMPDIR ftp://ftp.gnu.org/gnu/gcc/gcc-$gcc_version/gcc-$gcc_version.tar.bz2 || exit 1
+ ;;
+esac
+tar xjf $TMPDIR/gcc-$gcc_version.tar.bz2
+cd gcc-$gcc_version
+
+./contrib/download_prerequisites
+
+patch -p1 < "${this_path}/PR64905.patch" || exit 1
+
+cd ..
+mkdir gcc-objdir
+cd gcc-objdir
+../gcc-$gcc_version/configure --prefix=/tools/gcc --enable-languages=c,c++ --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro || exit 1
+make $make_flags || exit 1
+make $make_flags install DESTDIR=$root_dir || exit 1
+
+cd $root_dir/tools
+tar caf $root_dir/gcc.tar.xz gcc/
diff --git a/build/unix/build-gtk3/build-gtk3.sh b/build/unix/build-gtk3/build-gtk3.sh
new file mode 100644
index 000000000..6e03d4a52
--- /dev/null
+++ b/build/unix/build-gtk3/build-gtk3.sh
@@ -0,0 +1,150 @@
+#!/bin/bash
+
+# Use "build-gtk.sh" or "build-gtk.sh 64" to build a 64-bits tarball for tooltool.
+# Use "build-gtk.sh 32" to build a 32-bits tarball for tooltool.
+
+# Mock environments used:
+# - 64-bits:
+# https://s3.amazonaws.com/mozilla-releng-mock-archive/67b65e51eb091fba7941a04d249343924a3ee653
+# + libxml2-devel.x86_64 gettext.x86_64 libjpeg-devel.x86_64
+# - 32-bits:
+# https://s3.amazonaws.com/mozilla-releng-mock-archive/58d76c6acca148a1aedcbec7fc1b8212e12807b4
+# + libxml2-devel.i686 gettext.i686 libjpeg-devel.i686
+
+set -e
+
+pkg_config_version=0.28
+fontconfig_version=2.8.0
+libffi_version=3.0.13
+glib_version=2.34.3
+gdk_pixbuf_version=2.26.5
+pixman_version=0.20.2
+cairo_version=1.10.2
+pango_version=1.30.1
+atk_version=2.2.0
+gtk__version=3.4.4
+
+pkg_config_url=http://pkgconfig.freedesktop.org/releases/pkg-config-${pkg_config_version}.tar.gz
+fontconfig_url=http://www.freedesktop.org/software/fontconfig/release/fontconfig-${fontconfig_version}.tar.gz
+libffi_url=ftp://sourceware.org/pub/libffi/libffi-${libffi_version}.tar.gz
+glib_url=http://ftp.gnome.org/pub/gnome/sources/glib/${glib_version%.*}/glib-${glib_version}.tar.xz
+gdk_pixbuf_url=http://ftp.gnome.org/pub/gnome/sources/gdk-pixbuf/${gdk_pixbuf_version%.*}/gdk-pixbuf-${gdk_pixbuf_version}.tar.xz
+pixman_url=http://cairographics.org/releases/pixman-${pixman_version}.tar.gz
+cairo_url=http://cairographics.org/releases/cairo-${cairo_version}.tar.gz
+pango_url=http://ftp.gnome.org/pub/GNOME/sources/pango/${pango_version%.*}/pango-${pango_version}.tar.xz
+atk_url=http://ftp.gnome.org/pub/GNOME/sources/atk/${atk_version%.*}/atk-${atk_version}.tar.xz
+gtk__url=http://ftp.gnome.org/pub/gnome/sources/gtk+/${gtk__version%.*}/gtk+-${gtk__version}.tar.xz
+
+cwd=$(pwd)
+root_dir=$(mktemp -d)
+cd $root_dir
+
+if test -z $TMPDIR; then
+ TMPDIR=/tmp/
+fi
+
+make_flags=-j12
+
+build() {
+ name=$1
+ shift
+ pkg=$(echo $name | tr '+-' '__')
+ version=$(eval echo \$${pkg}_version)
+ url=$(eval echo \$${pkg}_url)
+ wget -c -P $TMPDIR $url
+ tar -axf $TMPDIR/$name-$version.tar.*
+ mkdir -p build/$name
+ cd build/$name
+ eval ../../$name-$version/configure --disable-static $* $configure_args
+ make $make_flags
+ make install DESTDIR=$root_dir/gtk3
+ find $root_dir/gtk3 -name \*.la -delete
+ cd ../..
+}
+
+case "$1" in
+32)
+ configure_args='--host=i686-pc-linux --build=i686-pc-linux CC="gcc -m32" CXX="g++ -m32"'
+ lib=lib
+ ;;
+*)
+ configure_args=
+ lib=lib64
+ ;;
+esac
+
+export PKG_CONFIG_LIBDIR=/usr/$lib/pkgconfig:/usr/share/pkgconfig
+
+# The pkg-config version in the mock images is buggy is how it handles
+# PKG_CONFIG_SYSROOT_DIR. So we need our own.
+build pkg-config
+
+ln -sf /usr/include $root_dir/gtk3/usr/
+ln -sf /usr/$lib $root_dir/gtk3/usr/
+if [ "$lib" = lib64 ]; then
+ ln -s lib $root_dir/gtk3/usr/local/lib64
+fi
+export PKG_CONFIG_PATH=$root_dir/gtk3/usr/local/lib/pkgconfig
+export PKG_CONFIG_SYSROOT_DIR=$root_dir/gtk3
+export LD_LIBRARY_PATH=$root_dir/gtk3/usr/local/lib
+export PATH=$root_dir/gtk3/usr/local/bin:${PATH}
+
+build fontconfig
+build libffi
+build glib
+build gdk-pixbuf --without-libtiff
+build pixman --disable-gtk
+build cairo --enable-tee
+build pango
+build atk
+make_flags="$make_flags GLIB_COMPILE_SCHEMAS=glib-compile-schemas"
+build gtk+
+
+rm -rf $root_dir/gtk3/usr/local/share/gtk-doc
+rm -rf $root_dir/gtk3/usr/local/share/locale
+
+# mock build environment doesn't have fonts in /usr/share/fonts, but
+# has some in /usr/share/X11/fonts. Add this directory to the
+# fontconfig configuration without changing the gtk3 tooltool package.
+cat << EOF > $root_dir/gtk3/usr/local/etc/fonts/local.conf
+<?xml version="1.0"?>
+<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+<fontconfig>
+ <dir>/usr/share/X11/fonts</dir>
+</fontconfig>
+EOF
+
+cat <<EOF > $root_dir/gtk3/setup.sh
+#!/bin/sh
+
+cd \$(dirname \$0)
+HERE=\$(pwd)
+
+# pango expects absolute paths in pango.modules, and TOOLTOOL_DIR may vary...
+LD_LIBRARY_PATH=\$HERE/usr/local/lib \
+PANGO_SYSCONFDIR=\$HERE/usr/local/etc \
+PANGO_LIBDIR=\$HERE/usr/local/lib \
+\$HERE/usr/local/bin/pango-querymodules > \$HERE/usr/local/etc/pango/pango.modules
+
+# same with gdb-pixbuf and loaders.cache
+LD_LIBRARY_PATH=\$HERE/usr/local/lib \
+GDK_PIXBUF_MODULE_FILE=\$HERE/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \
+GDK_PIXBUF_MODULEDIR=\$HERE/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \
+\$HERE/usr/local/bin/gdk-pixbuf-query-loaders > \
+\$HERE/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
+
+# The fontconfig version in the tooltool package has known uses of
+# uninitialized memory when creating its cache, and while most users
+# will already have an existing cache, running Firefox on automation
+# will create it. Combined with valgrind, this generates irrelevant
+# errors.
+# So create the fontconfig cache beforehand.
+FONTCONFIG_PATH=\$HERE/usr/local/etc/fonts \
+LD_LIBRARY_PATH=\$HERE/usr/local/lib \
+\$HERE/usr/local/bin/fc-cache
+EOF
+
+chmod +x $root_dir/gtk3/setup.sh
+
+cd $cwd
+tar -C $root_dir -Jcf gtk3.tar.xz gtk3
diff --git a/build/unix/elfhack/Makefile.in b/build/unix/elfhack/Makefile.in
new file mode 100644
index 000000000..f1c8c91ee
--- /dev/null
+++ b/build/unix/elfhack/Makefile.in
@@ -0,0 +1,48 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+OS_CXXFLAGS := $(filter-out -fno-exceptions,$(OS_CXXFLAGS)) -fexceptions
+
+include $(topsrcdir)/config/rules.mk
+
+test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): %$(DLL_SUFFIX): %.$(OBJ_SUFFIX) elfhack
+ $(MKSHLIB) $(LDFLAGS) $< -nostartfiles
+ @echo ===
+ @echo === If you get failures below, please file a bug describing the error
+ @echo === and your environment \(compiler and linker versions\), and use
+ @echo === --disable-elf-hack until this is fixed.
+ @echo ===
+ # Fail if the library doesn't have $(DT_TYPE) .dynamic info
+ $(TOOLCHAIN_PREFIX)readelf -d $@ | grep '($(DT_TYPE))'
+ @rm -f $@.bak
+ $(CURDIR)/elfhack -b -f $@
+ # Fail if the backup file doesn't exist
+ [ -f '$@.bak' ]
+ # Fail if the new library doesn't contain less relocations
+ [ $$($(TOOLCHAIN_PREFIX)objdump -R $@.bak | wc -l) -gt $$(objdump -R $@ | wc -l) ]
+
+test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): DSO_SONAME=$@
+test-array$(DLL_SUFFIX): DT_TYPE=INIT_ARRAY
+test-ctors$(DLL_SUFFIX): DT_TYPE=INIT
+
+.PRECIOUS: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX)
+
+GARBAGE += test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) test-array$(DLL_SUFFIX).bak test-ctors$(DLL_SUFFIX).bak
+
+ifndef CROSS_COMPILE
+ifdef COMPILE_ENVIRONMENT
+libs:: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX)
+
+dummy: dummy.$(OBJ_SUFFIX)
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+libs:: dummy
+ # Will either crash or return exit code 1 if elfhack is broken
+ LD_PRELOAD=$(CURDIR)/test-array$(DLL_SUFFIX) $(CURDIR)/dummy
+ LD_PRELOAD=$(CURDIR)/test-ctors$(DLL_SUFFIX) $(CURDIR)/dummy
+
+GARBAGE += dummy
+endif
+endif
diff --git a/build/unix/elfhack/README b/build/unix/elfhack/README
new file mode 100644
index 000000000..8c68031e3
--- /dev/null
+++ b/build/unix/elfhack/README
@@ -0,0 +1,28 @@
+Elfhack is a program to optimize ELF binaries for size and cold startup
+speed.
+
+Presently, it is quite experimental, though it works well for the target
+it was created for: Firefox's libxul.so.
+
+Elfhack currently only does one thing: packing dynamic relocations ;
+which ends up being a quite complex task, that can be summarized this
+way:
+- Remove RELATIVE relocations from the .rel.dyn/.rela.dyn section.
+- Inject a small code able to apply relative relocations "by hand"
+ after the .rel.dyn/.rela.dyn section.
+- Inject a section containing relocative relocations in a different
+ and more packed format, after the small code.
+- Register the small code as DT_INIT function. Make the small code call
+ what was initially the DT_INIT function, if there was one.
+- Remove the hole between the new section containing relative
+ relocations and the following sections, adjusting offsets and base
+ addresses accordingly.
+- Adjust PT_LOAD entries to fit new offsets, and add an additional
+ PT_LOAD entry when that is necessary to handle the discrepancy between
+ offsets and base addresses, meaning the section offsets may yet again
+ need adjustments.
+- Adjust various DT_* dynamic tags to fit the new ELF layout.
+- Adjust section headers.
+- Adjust ELF headers.
+
+See http://glandium.org/blog/?p=1177#relocations for some figures.
diff --git a/build/unix/elfhack/dummy.c b/build/unix/elfhack/dummy.c
new file mode 100644
index 000000000..f30fcb5b1
--- /dev/null
+++ b/build/unix/elfhack/dummy.c
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+extern __attribute__((visibility("default"), weak)) int print_status();
+
+int main() {
+ return print_status();
+}
diff --git a/build/unix/elfhack/elf.cpp b/build/unix/elfhack/elf.cpp
new file mode 100644
index 000000000..743afdead
--- /dev/null
+++ b/build/unix/elfhack/elf.cpp
@@ -0,0 +1,921 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#undef NDEBUG
+#include <cstring>
+#include <assert.h>
+#include "elfxx.h"
+
+template <class endian, typename R, typename T>
+void Elf_Ehdr_Traits::swap(T &t, R &r)
+{
+ memcpy(r.e_ident, t.e_ident, sizeof(r.e_ident));
+ r.e_type = endian::swap(t.e_type);
+ r.e_machine = endian::swap(t.e_machine);
+ r.e_version = endian::swap(t.e_version);
+ r.e_entry = endian::swap(t.e_entry);
+ r.e_phoff = endian::swap(t.e_phoff);
+ r.e_shoff = endian::swap(t.e_shoff);
+ r.e_flags = endian::swap(t.e_flags);
+ r.e_ehsize = endian::swap(t.e_ehsize);
+ r.e_phentsize = endian::swap(t.e_phentsize);
+ r.e_phnum = endian::swap(t.e_phnum);
+ r.e_shentsize = endian::swap(t.e_shentsize);
+ r.e_shnum = endian::swap(t.e_shnum);
+ r.e_shstrndx = endian::swap(t.e_shstrndx);
+}
+
+template <class endian, typename R, typename T>
+void Elf_Phdr_Traits::swap(T &t, R &r)
+{
+ r.p_type = endian::swap(t.p_type);
+ r.p_offset = endian::swap(t.p_offset);
+ r.p_vaddr = endian::swap(t.p_vaddr);
+ r.p_paddr = endian::swap(t.p_paddr);
+ r.p_filesz = endian::swap(t.p_filesz);
+ r.p_memsz = endian::swap(t.p_memsz);
+ r.p_flags = endian::swap(t.p_flags);
+ r.p_align = endian::swap(t.p_align);
+}
+
+template <class endian, typename R, typename T>
+void Elf_Shdr_Traits::swap(T &t, R &r)
+{
+ r.sh_name = endian::swap(t.sh_name);
+ r.sh_type = endian::swap(t.sh_type);
+ r.sh_flags = endian::swap(t.sh_flags);
+ r.sh_addr = endian::swap(t.sh_addr);
+ r.sh_offset = endian::swap(t.sh_offset);
+ r.sh_size = endian::swap(t.sh_size);
+ r.sh_link = endian::swap(t.sh_link);
+ r.sh_info = endian::swap(t.sh_info);
+ r.sh_addralign = endian::swap(t.sh_addralign);
+ r.sh_entsize = endian::swap(t.sh_entsize);
+}
+
+template <class endian, typename R, typename T>
+void Elf_Dyn_Traits::swap(T &t, R &r)
+{
+ r.d_tag = endian::swap(t.d_tag);
+ r.d_un.d_val = endian::swap(t.d_un.d_val);
+}
+
+template <class endian, typename R, typename T>
+void Elf_Sym_Traits::swap(T &t, R &r)
+{
+ r.st_name = endian::swap(t.st_name);
+ r.st_value = endian::swap(t.st_value);
+ r.st_size = endian::swap(t.st_size);
+ r.st_info = t.st_info;
+ r.st_other = t.st_other;
+ r.st_shndx = endian::swap(t.st_shndx);
+}
+
+template <class endian>
+struct _Rel_info {
+ static inline void swap(Elf32_Word &t, Elf32_Word &r) { r = endian::swap(t); }
+ static inline void swap(Elf64_Xword &t, Elf64_Xword &r) { r = endian::swap(t); }
+ static inline void swap(Elf64_Xword &t, Elf32_Word &r) {
+ r = endian::swap(ELF32_R_INFO(ELF64_R_SYM(t), ELF64_R_TYPE(t)));
+ }
+ static inline void swap(Elf32_Word &t, Elf64_Xword &r) {
+ r = endian::swap(ELF64_R_INFO(ELF32_R_SYM(t), ELF32_R_TYPE(t)));
+ }
+};
+
+template <class endian, typename R, typename T>
+void Elf_Rel_Traits::swap(T &t, R &r)
+{
+ r.r_offset = endian::swap(t.r_offset);
+ _Rel_info<endian>::swap(t.r_info, r.r_info);
+}
+
+template <class endian, typename R, typename T>
+void Elf_Rela_Traits::swap(T &t, R &r)
+{
+ r.r_offset = endian::swap(t.r_offset);
+ _Rel_info<endian>::swap(t.r_info, r.r_info);
+ r.r_addend = endian::swap(t.r_addend);
+}
+
+static const Elf32_Shdr null32_section =
+ { 0, SHT_NULL, 0, 0, 0, 0, SHN_UNDEF, 0, 0, 0 };
+
+Elf_Shdr null_section(null32_section);
+
+Elf_Ehdr::Elf_Ehdr(std::ifstream &file, char ei_class, char ei_data)
+: serializable<Elf_Ehdr_Traits>(file, ei_class, ei_data),
+ ElfSection(null_section, nullptr, nullptr)
+{
+ shdr.sh_size = Elf_Ehdr::size(ei_class);
+}
+
+Elf::Elf(std::ifstream &file)
+{
+ if (!file.is_open())
+ throw std::runtime_error("Error opening file");
+
+ file.exceptions(std::ifstream::eofbit | std::ifstream::failbit | std::ifstream::badbit);
+ // Read ELF magic number and identification information
+ char e_ident[EI_VERSION];
+ file.seekg(0);
+ file.read(e_ident, sizeof(e_ident));
+ file.seekg(0);
+ ehdr = new Elf_Ehdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]);
+
+ // ELFOSABI_LINUX is kept unsupported because I haven't looked whether
+ // STB_GNU_UNIQUE or STT_GNU_IFUNC would need special casing.
+ if ((ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) && (ehdr->e_ident[EI_ABIVERSION] != 0))
+ throw std::runtime_error("unsupported ELF ABI");
+
+ if (ehdr->e_version != 1)
+ throw std::runtime_error("unsupported ELF version");
+
+ // Sanity checks
+ if (ehdr->e_shnum == 0)
+ throw std::runtime_error("sstripped ELF files aren't supported");
+
+ if (ehdr->e_ehsize != Elf_Ehdr::size(e_ident[EI_CLASS]))
+ throw std::runtime_error("unsupported ELF inconsistency: ehdr.e_ehsize != sizeof(ehdr)");
+
+ if (ehdr->e_shentsize != Elf_Shdr::size(e_ident[EI_CLASS]))
+ throw std::runtime_error("unsupported ELF inconsistency: ehdr.e_shentsize != sizeof(shdr)");
+
+ if (ehdr->e_phnum == 0) {
+ if (ehdr->e_phoff != 0)
+ throw std::runtime_error("unsupported ELF inconsistency: e_phnum == 0 && e_phoff != 0");
+ if (ehdr->e_phentsize != 0)
+ throw std::runtime_error("unsupported ELF inconsistency: e_phnum == 0 && e_phentsize != 0");
+ } else if (ehdr->e_phoff != ehdr->e_ehsize)
+ throw std::runtime_error("unsupported ELF inconsistency: ehdr->e_phoff != ehdr->e_ehsize");
+ else if (ehdr->e_phentsize != Elf_Phdr::size(e_ident[EI_CLASS]))
+ throw std::runtime_error("unsupported ELF inconsistency: ehdr->e_phentsize != sizeof(phdr)");
+
+ // Read section headers
+ Elf_Shdr **shdr = new Elf_Shdr *[ehdr->e_shnum];
+ file.seekg(ehdr->e_shoff);
+ for (int i = 0; i < ehdr->e_shnum; i++)
+ shdr[i] = new Elf_Shdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]);
+
+ // Sanity check in section header for index 0
+ if ((shdr[0]->sh_name != 0) || (shdr[0]->sh_type != SHT_NULL) ||
+ (shdr[0]->sh_flags != 0) || (shdr[0]->sh_addr != 0) ||
+ (shdr[0]->sh_offset != 0) || (shdr[0]->sh_size != 0) ||
+ (shdr[0]->sh_link != SHN_UNDEF) || (shdr[0]->sh_info != 0) ||
+ (shdr[0]->sh_addralign != 0) || (shdr[0]->sh_entsize != 0))
+ throw std::runtime_error("Section header for index 0 contains unsupported values");
+
+ if ((shdr[ehdr->e_shstrndx]->sh_link != 0) || (shdr[ehdr->e_shstrndx]->sh_info != 0))
+ throw std::runtime_error("unsupported ELF content: string table with sh_link != 0 || sh_info != 0");
+
+ // Store these temporarily
+ tmp_shdr = shdr;
+ tmp_file = &file;
+
+ // Fill sections list
+ sections = new ElfSection *[ehdr->e_shnum];
+ for (int i = 0; i < ehdr->e_shnum; i++)
+ sections[i] = nullptr;
+ for (int i = 1; i < ehdr->e_shnum; i++) {
+ if (sections[i] != nullptr)
+ continue;
+ getSection(i);
+ }
+ Elf_Shdr s;
+ s.sh_name = 0;
+ s.sh_type = SHT_NULL;
+ s.sh_flags = 0;
+ s.sh_addr = 0;
+ s.sh_offset = ehdr->e_shoff;
+ s.sh_entsize = Elf_Shdr::size(e_ident[EI_CLASS]);
+ s.sh_size = s.sh_entsize * ehdr->e_shnum;
+ s.sh_link = 0;
+ s.sh_info = 0;
+ s.sh_addralign = (e_ident[EI_CLASS] == ELFCLASS32) ? 4 : 8;
+ shdr_section = new ElfSection(s, nullptr, nullptr);
+
+ // Fake section for program headers
+ s.sh_offset = ehdr->e_phoff;
+ s.sh_addr = ehdr->e_phoff;
+ s.sh_entsize = Elf_Phdr::size(e_ident[EI_CLASS]);
+ s.sh_size = s.sh_entsize * ehdr->e_phnum;
+ phdr_section = new ElfSection(s, nullptr, nullptr);
+
+ phdr_section->insertAfter(ehdr, false);
+
+ sections[1]->insertAfter(phdr_section, false);
+ for (int i = 2; i < ehdr->e_shnum; i++) {
+ // TODO: this should be done in a better way
+ if ((shdr_section->getPrevious() == nullptr) && (shdr[i]->sh_offset > ehdr->e_shoff)) {
+ shdr_section->insertAfter(sections[i - 1], false);
+ sections[i]->insertAfter(shdr_section, false);
+ } else
+ sections[i]->insertAfter(sections[i - 1], false);
+ }
+ if (shdr_section->getPrevious() == nullptr)
+ shdr_section->insertAfter(sections[ehdr->e_shnum - 1], false);
+
+ tmp_file = nullptr;
+ tmp_shdr = nullptr;
+ for (int i = 0; i < ehdr->e_shnum; i++)
+ delete shdr[i];
+ delete[] shdr;
+
+ eh_shstrndx = (ElfStrtab_Section *)sections[ehdr->e_shstrndx];
+
+ // Skip reading program headers if there aren't any
+ if (ehdr->e_phnum == 0)
+ return;
+
+ // Read program headers
+ file.seekg(ehdr->e_phoff);
+ for (int i = 0; i < ehdr->e_phnum; i++) {
+ Elf_Phdr phdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]);
+ if (phdr.p_type == PT_LOAD) {
+ // Default alignment for PT_LOAD on x86-64 prevents elfhack from
+ // doing anything useful. However, the system doesn't actually
+ // require such a big alignment, so in order for elfhack to work
+ // efficiently, reduce alignment when it's originally the default
+ // one.
+ if ((ehdr->e_machine == EM_X86_64) && (phdr.p_align == 0x200000))
+ phdr.p_align = 0x1000;
+ }
+ ElfSegment *segment = new ElfSegment(&phdr);
+ // Some segments aren't entirely filled (if at all) by sections
+ // For those, we use fake sections
+ if ((phdr.p_type == PT_LOAD) && (phdr.p_offset == 0)) {
+ // Use a fake section for ehdr and phdr
+ ehdr->getShdr().sh_addr = phdr.p_vaddr;
+ phdr_section->getShdr().sh_addr += phdr.p_vaddr;
+ segment->addSection(ehdr);
+ segment->addSection(phdr_section);
+ }
+ if (phdr.p_type == PT_PHDR)
+ segment->addSection(phdr_section);
+ for (int j = 1; j < ehdr->e_shnum; j++)
+ if (phdr.contains(sections[j]))
+ segment->addSection(sections[j]);
+ // Make sure that our view of segments corresponds to the original
+ // ELF file.
+ // GNU gold likes to start some segments before the first section
+ // they contain. https://sourceware.org/bugzilla/show_bug.cgi?id=19392
+ unsigned int gold_adjustment = segment->getAddr() - phdr.p_vaddr;
+ assert(segment->getFileSize() == phdr.p_filesz - gold_adjustment);
+ // gold makes TLS segments end on an aligned virtual address, even
+ // when the underlying section ends before that, while bfd ld
+ // doesn't. It's fine if we don't keep that alignment.
+ unsigned int memsize = segment->getMemSize();
+ if (phdr.p_type == PT_TLS && memsize != phdr.p_memsz) {
+ unsigned int align = segment->getAlign();
+ memsize = (memsize + align - 1) & ~(align - 1);
+ }
+ assert(memsize == phdr.p_memsz - gold_adjustment);
+ segments.push_back(segment);
+ }
+
+ new (&eh_entry) ElfLocation(ehdr->e_entry, this);
+}
+
+Elf::~Elf()
+{
+ for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
+ delete *seg;
+ delete[] sections;
+ ElfSection *section = ehdr;
+ while (section != nullptr) {
+ ElfSection *next = section->getNext();
+ delete section;
+ section = next;
+ }
+}
+
+// TODO: This shouldn't fail after inserting sections
+ElfSection *Elf::getSection(int index)
+{
+ if ((index < -1) || (index >= ehdr->e_shnum))
+ throw std::runtime_error("Section index out of bounds");
+ if (index == -1)
+ index = ehdr->e_shstrndx; // TODO: should be fixed to use the actual current number
+ // Special case: the section at index 0 is void
+ if (index == 0)
+ return nullptr;
+ // Infinite recursion guard
+ if (sections[index] == (ElfSection *)this)
+ return nullptr;
+ if (sections[index] == nullptr) {
+ sections[index] = (ElfSection *)this;
+ switch (tmp_shdr[index]->sh_type) {
+ case SHT_DYNAMIC:
+ sections[index] = new ElfDynamic_Section(*tmp_shdr[index], tmp_file, this);
+ break;
+ case SHT_REL:
+ sections[index] = new ElfRel_Section<Elf_Rel>(*tmp_shdr[index], tmp_file, this);
+ break;
+ case SHT_RELA:
+ sections[index] = new ElfRel_Section<Elf_Rela>(*tmp_shdr[index], tmp_file, this);
+ break;
+ case SHT_DYNSYM:
+ case SHT_SYMTAB:
+ sections[index] = new ElfSymtab_Section(*tmp_shdr[index], tmp_file, this);
+ break;
+ case SHT_STRTAB:
+ sections[index] = new ElfStrtab_Section(*tmp_shdr[index], tmp_file, this);
+ break;
+ default:
+ sections[index] = new ElfSection(*tmp_shdr[index], tmp_file, this);
+ }
+ }
+ return sections[index];
+}
+
+ElfSection *Elf::getSectionAt(unsigned int offset)
+{
+ for (int i = 1; i < ehdr->e_shnum; i++) {
+ ElfSection *section = getSection(i);
+ if ((section != nullptr) && (section->getFlags() & SHF_ALLOC) && !(section->getFlags() & SHF_TLS) &&
+ (offset >= section->getAddr()) && (offset < section->getAddr() + section->getSize()))
+ return section;
+ }
+ return nullptr;
+}
+
+ElfSegment *Elf::getSegmentByType(unsigned int type, ElfSegment *last)
+{
+ std::vector<ElfSegment *>::iterator seg;
+ if (last) {
+ seg = std::find(segments.begin(), segments.end(), last);
+ ++seg;
+ } else
+ seg = segments.begin();
+ for (; seg != segments.end(); seg++)
+ if ((*seg)->getType() == type)
+ return *seg;
+ return nullptr;
+}
+
+void Elf::removeSegment(ElfSegment *segment)
+{
+ if (!segment)
+ return;
+ std::vector<ElfSegment *>::iterator seg;
+ seg = std::find(segments.begin(), segments.end(), segment);
+ if (seg == segments.end())
+ return;
+ segment->clear();
+ segments.erase(seg);
+}
+
+ElfDynamic_Section *Elf::getDynSection()
+{
+ for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
+ if (((*seg)->getType() == PT_DYNAMIC) && ((*seg)->getFirstSection() != nullptr) &&
+ (*seg)->getFirstSection()->getType() == SHT_DYNAMIC)
+ return (ElfDynamic_Section *)(*seg)->getFirstSection();
+
+ return nullptr;
+}
+
+void Elf::normalize()
+{
+ // fixup section headers sh_name; TODO: that should be done by sections
+ // themselves
+ for (ElfSection *section = ehdr; section != nullptr; section = section->getNext()) {
+ if (section->getIndex() == 0)
+ continue;
+ else
+ ehdr->e_shnum = section->getIndex() + 1;
+ section->getShdr().sh_name = eh_shstrndx->getStrIndex(section->getName());
+ }
+ ehdr->markDirty();
+ // Check segments consistency
+ int i = 0;
+ for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++, i++) {
+ std::list<ElfSection *>::iterator it = (*seg)->begin();
+ for (ElfSection *last = *(it++); it != (*seg)->end(); last = *(it++)) {
+ if (((*it)->getType() != SHT_NOBITS) &&
+ ((*it)->getAddr() - last->getAddr()) != ((*it)->getOffset() - last->getOffset())) {
+ throw std::runtime_error("Segments inconsistency");
+ }
+ }
+ }
+ // fixup ehdr before writing
+ if (ehdr->e_phnum != segments.size()) {
+ ehdr->e_phnum = segments.size();
+ phdr_section->getShdr().sh_size = segments.size() * Elf_Phdr::size(ehdr->e_ident[EI_CLASS]);
+ phdr_section->getNext()->markDirty();
+ }
+ // fixup shdr before writing
+ if (ehdr->e_shnum != shdr_section->getSize() / shdr_section->getEntSize())
+ shdr_section->getShdr().sh_size = ehdr->e_shnum * Elf_Shdr::size(ehdr->e_ident[EI_CLASS]);
+ ehdr->e_shoff = shdr_section->getOffset();
+ ehdr->e_entry = eh_entry.getValue();
+ ehdr->e_shstrndx = eh_shstrndx->getIndex();
+}
+
+void Elf::write(std::ofstream &file)
+{
+ normalize();
+ for (ElfSection *section = ehdr;
+ section != nullptr; section = section->getNext()) {
+ file.seekp(section->getOffset());
+ if (section == phdr_section) {
+ for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) {
+ Elf_Phdr phdr;
+ phdr.p_type = (*seg)->getType();
+ phdr.p_flags = (*seg)->getFlags();
+ phdr.p_offset = (*seg)->getOffset();
+ phdr.p_vaddr = (*seg)->getAddr();
+ phdr.p_paddr = phdr.p_vaddr + (*seg)->getVPDiff();
+ phdr.p_filesz = (*seg)->getFileSize();
+ phdr.p_memsz = (*seg)->getMemSize();
+ phdr.p_align = (*seg)->getAlign();
+ phdr.serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
+ }
+ } else if (section == shdr_section) {
+ null_section.serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
+ for (ElfSection *sec = ehdr; sec!= nullptr; sec = sec->getNext()) {
+ if (sec->getType() != SHT_NULL)
+ sec->getShdr().serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
+ }
+ } else
+ section->serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]);
+ }
+}
+
+ElfSection::ElfSection(Elf_Shdr &s, std::ifstream *file, Elf *parent)
+: shdr(s),
+ link(shdr.sh_link == SHN_UNDEF ? nullptr : parent->getSection(shdr.sh_link)),
+ next(nullptr), previous(nullptr), index(-1)
+{
+ if ((file == nullptr) || (shdr.sh_type == SHT_NULL) || (shdr.sh_type == SHT_NOBITS))
+ data = nullptr;
+ else {
+ data = new char[shdr.sh_size];
+ int pos = file->tellg();
+ file->seekg(shdr.sh_offset);
+ file->read(data, shdr.sh_size);
+ file->seekg(pos);
+ }
+ if (shdr.sh_name == 0)
+ name = nullptr;
+ else {
+ ElfStrtab_Section *strtab = (ElfStrtab_Section *) parent->getSection(-1);
+ // Special case (see elfgeneric.cpp): if strtab is nullptr, the
+ // section being created is the strtab.
+ if (strtab == nullptr)
+ name = &data[shdr.sh_name];
+ else
+ name = strtab->getStr(shdr.sh_name);
+ }
+ // Only SHT_REL/SHT_RELA sections use sh_info to store a section
+ // number.
+ if ((shdr.sh_type == SHT_REL) || (shdr.sh_type == SHT_RELA))
+ info.section = shdr.sh_info ? parent->getSection(shdr.sh_info) : nullptr;
+ else
+ info.index = shdr.sh_info;
+}
+
+unsigned int ElfSection::getAddr()
+{
+ if (shdr.sh_addr != (Elf32_Word)-1)
+ return shdr.sh_addr;
+
+ // It should be safe to adjust sh_addr for all allocated sections that
+ // are neither SHT_NOBITS nor SHT_PROGBITS
+ if ((previous != nullptr) && isRelocatable()) {
+ unsigned int addr = previous->getAddr();
+ if (previous->getType() != SHT_NOBITS)
+ addr += previous->getSize();
+
+ if (addr & (getAddrAlign() - 1))
+ addr = (addr | (getAddrAlign() - 1)) + 1;
+
+ return (shdr.sh_addr = addr);
+ }
+ return shdr.sh_addr;
+}
+
+unsigned int ElfSection::getOffset()
+{
+ if (shdr.sh_offset != (Elf32_Word)-1)
+ return shdr.sh_offset;
+
+ if (previous == nullptr)
+ return (shdr.sh_offset = 0);
+
+ unsigned int offset = previous->getOffset();
+
+ ElfSegment *ptload = getSegmentByType(PT_LOAD);
+ ElfSegment *prev_ptload = previous->getSegmentByType(PT_LOAD);
+
+ if (ptload && (ptload == prev_ptload)) {
+ offset += getAddr() - previous->getAddr();
+ return (shdr.sh_offset = offset);
+ }
+
+ if (previous->getType() != SHT_NOBITS)
+ offset += previous->getSize();
+
+ Elf32_Word align = 0x1000;
+ for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
+ align = std::max(align, (*seg)->getAlign());
+
+ Elf32_Word mask = align - 1;
+ // SHF_TLS is used for .tbss which is some kind of special case.
+ if (((getType() != SHT_NOBITS) || (getFlags() & SHF_TLS)) && (getFlags() & SHF_ALLOC)) {
+ if ((getAddr() & mask) < (offset & mask))
+ offset = (offset | mask) + (getAddr() & mask) + 1;
+ else
+ offset = (offset & ~mask) + (getAddr() & mask);
+ }
+ if ((getType() != SHT_NOBITS) && (offset & (getAddrAlign() - 1)))
+ offset = (offset | (getAddrAlign() - 1)) + 1;
+
+ // Two subsequent sections can't be mapped in the same page in memory
+ // if they aren't in the same 4K block on disk.
+ if ((getType() != SHT_NOBITS) && getAddr()) {
+ if (((offset >> 12) != (previous->getOffset() >> 12)) &&
+ ((getAddr() >> 12) == (previous->getAddr() >> 12)))
+ throw std::runtime_error("Moving section would require overlapping segments");
+ }
+
+ return (shdr.sh_offset = offset);
+}
+
+int ElfSection::getIndex()
+{
+ if (index != -1)
+ return index;
+ if (getType() == SHT_NULL)
+ return (index = 0);
+ ElfSection *reference;
+ for (reference = previous; (reference != nullptr) && (reference->getType() == SHT_NULL); reference = reference->getPrevious());
+ if (reference == nullptr)
+ return (index = 1);
+ return (index = reference->getIndex() + 1);
+}
+
+Elf_Shdr &ElfSection::getShdr()
+{
+ getOffset();
+ if (shdr.sh_link == (Elf32_Word)-1)
+ shdr.sh_link = getLink() ? getLink()->getIndex() : 0;
+ if (shdr.sh_info == (Elf32_Word)-1)
+ shdr.sh_info = ((getType() == SHT_REL) || (getType() == SHT_RELA)) ?
+ (getInfo().section ? getInfo().section->getIndex() : 0) :
+ getInfo().index;
+
+ return shdr;
+}
+
+ElfSegment::ElfSegment(Elf_Phdr *phdr)
+: type(phdr->p_type), v_p_diff(phdr->p_paddr - phdr->p_vaddr),
+ flags(phdr->p_flags), align(phdr->p_align), vaddr(phdr->p_vaddr),
+ filesz(phdr->p_filesz), memsz(phdr->p_memsz) {}
+
+void ElfSegment::addSection(ElfSection *section)
+{
+ // Make sure all sections in PT_GNU_RELRO won't be moved by elfhack
+ assert(!((type == PT_GNU_RELRO) && (section->isRelocatable())));
+
+ //TODO: Check overlapping sections
+ std::list<ElfSection *>::iterator i;
+ for (i = sections.begin(); i != sections.end(); ++i)
+ if ((*i)->getAddr() > section->getAddr())
+ break;
+ sections.insert(i, section);
+ section->addToSegment(this);
+}
+
+void ElfSegment::removeSection(ElfSection *section)
+{
+ sections.remove(section);
+ section->removeFromSegment(this);
+}
+
+unsigned int ElfSegment::getFileSize()
+{
+ if (type == PT_GNU_RELRO || isElfHackFillerSegment())
+ return filesz;
+
+ if (sections.empty())
+ return 0;
+ // Search the last section that is not SHT_NOBITS
+ std::list<ElfSection *>::reverse_iterator i;
+ for (i = sections.rbegin(); (i != sections.rend()) && ((*i)->getType() == SHT_NOBITS); ++i);
+ // All sections are SHT_NOBITS
+ if (i == sections.rend())
+ return 0;
+
+ unsigned int end = (*i)->getAddr() + (*i)->getSize();
+
+ return end - sections.front()->getAddr();
+}
+
+unsigned int ElfSegment::getMemSize()
+{
+ if (type == PT_GNU_RELRO || isElfHackFillerSegment())
+ return memsz;
+
+ if (sections.empty())
+ return 0;
+
+ unsigned int end = sections.back()->getAddr() + sections.back()->getSize();
+
+ return end - sections.front()->getAddr();
+}
+
+unsigned int ElfSegment::getOffset()
+{
+ if ((type == PT_GNU_RELRO) && !sections.empty() &&
+ (sections.front()->getAddr() != vaddr))
+ throw std::runtime_error("PT_GNU_RELRO segment doesn't start on a section start");
+
+ // Neither bionic nor glibc linkers seem to like when the offset of that segment is 0
+ if (isElfHackFillerSegment())
+ return vaddr;
+
+ return sections.empty() ? 0 : sections.front()->getOffset();
+}
+
+unsigned int ElfSegment::getAddr()
+{
+ if ((type == PT_GNU_RELRO) && !sections.empty() &&
+ (sections.front()->getAddr() != vaddr))
+ throw std::runtime_error("PT_GNU_RELRO segment doesn't start on a section start");
+
+ if (isElfHackFillerSegment())
+ return vaddr;
+
+ return sections.empty() ? 0 : sections.front()->getAddr();
+}
+
+void ElfSegment::clear()
+{
+ for (std::list<ElfSection *>::iterator i = sections.begin(); i != sections.end(); ++i)
+ (*i)->removeFromSegment(this);
+ sections.clear();
+}
+
+ElfValue *ElfDynamic_Section::getValueForType(unsigned int tag)
+{
+ for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++)
+ if (dyns[i].tag == tag)
+ return dyns[i].value;
+
+ return nullptr;
+}
+
+ElfSection *ElfDynamic_Section::getSectionForType(unsigned int tag)
+{
+ ElfValue *value = getValueForType(tag);
+ return value ? value->getSection() : nullptr;
+}
+
+bool ElfDynamic_Section::setValueForType(unsigned int tag, ElfValue *val)
+{
+ unsigned int i;
+ unsigned int shnum = shdr.sh_size / shdr.sh_entsize;
+ for (i = 0; (i < shnum) && (dyns[i].tag != DT_NULL); i++)
+ if (dyns[i].tag == tag) {
+ delete dyns[i].value;
+ dyns[i].value = val;
+ return true;
+ }
+ // If we get here, this means we didn't match for the given tag
+ // Most of the time, there are a few DT_NULL entries, that we can
+ // use to add our value, but if we are on the last entry, we can't.
+ if (i >= shnum - 1)
+ return false;
+
+ dyns[i].tag = tag;
+ dyns[i].value = val;
+ return true;
+}
+
+ElfDynamic_Section::ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
+: ElfSection(s, file, parent)
+{
+ int pos = file->tellg();
+ dyns.resize(s.sh_size / s.sh_entsize);
+ file->seekg(shdr.sh_offset);
+ // Here we assume tags refer to only one section (e.g. DT_RELSZ accounts
+ // for .rel.dyn size)
+ for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) {
+ Elf_Dyn dyn(*file, parent->getClass(), parent->getData());
+ dyns[i].tag = dyn.d_tag;
+ switch (dyn.d_tag) {
+ case DT_NULL:
+ case DT_SYMBOLIC:
+ case DT_TEXTREL:
+ case DT_BIND_NOW:
+ dyns[i].value = new ElfValue();
+ break;
+ case DT_NEEDED:
+ case DT_SONAME:
+ case DT_RPATH:
+ case DT_PLTREL:
+ case DT_RUNPATH:
+ case DT_FLAGS:
+ case DT_RELACOUNT:
+ case DT_RELCOUNT:
+ case DT_VERDEFNUM:
+ case DT_VERNEEDNUM:
+ dyns[i].value = new ElfPlainValue(dyn.d_un.d_val);
+ break;
+ case DT_PLTGOT:
+ case DT_HASH:
+ case DT_STRTAB:
+ case DT_SYMTAB:
+ case DT_RELA:
+ case DT_INIT:
+ case DT_FINI:
+ case DT_REL:
+ case DT_JMPREL:
+ case DT_INIT_ARRAY:
+ case DT_FINI_ARRAY:
+ case DT_GNU_HASH:
+ case DT_VERSYM:
+ case DT_VERNEED:
+ case DT_VERDEF:
+ dyns[i].value = new ElfLocation(dyn.d_un.d_ptr, parent);
+ break;
+ default:
+ dyns[i].value = nullptr;
+ }
+ }
+ // Another loop to get the section sizes
+ for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++)
+ switch (dyns[i].tag) {
+ case DT_PLTRELSZ:
+ dyns[i].value = new ElfSize(getSectionForType(DT_JMPREL));
+ break;
+ case DT_RELASZ:
+ dyns[i].value = new ElfSize(getSectionForType(DT_RELA));
+ break;
+ case DT_STRSZ:
+ dyns[i].value = new ElfSize(getSectionForType(DT_STRTAB));
+ break;
+ case DT_RELSZ:
+ dyns[i].value = new ElfSize(getSectionForType(DT_REL));
+ break;
+ case DT_INIT_ARRAYSZ:
+ dyns[i].value = new ElfSize(getSectionForType(DT_INIT_ARRAY));
+ break;
+ case DT_FINI_ARRAYSZ:
+ dyns[i].value = new ElfSize(getSectionForType(DT_FINI_ARRAY));
+ break;
+ case DT_RELAENT:
+ dyns[i].value = new ElfEntSize(getSectionForType(DT_RELA));
+ break;
+ case DT_SYMENT:
+ dyns[i].value = new ElfEntSize(getSectionForType(DT_SYMTAB));
+ break;
+ case DT_RELENT:
+ dyns[i].value = new ElfEntSize(getSectionForType(DT_REL));
+ break;
+ }
+
+ file->seekg(pos);
+}
+
+ElfDynamic_Section::~ElfDynamic_Section()
+{
+ for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++)
+ delete dyns[i].value;
+}
+
+void ElfDynamic_Section::serialize(std::ofstream &file, char ei_class, char ei_data)
+{
+ for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
+ Elf_Dyn dyn;
+ dyn.d_tag = dyns[i].tag;
+ dyn.d_un.d_val = (dyns[i].value != nullptr) ? dyns[i].value->getValue() : 0;
+ dyn.serialize(file, ei_class, ei_data);
+ }
+}
+
+ElfSymtab_Section::ElfSymtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
+: ElfSection(s, file, parent)
+{
+ int pos = file->tellg();
+ syms.resize(s.sh_size / s.sh_entsize);
+ ElfStrtab_Section *strtab = (ElfStrtab_Section *)getLink();
+ file->seekg(shdr.sh_offset);
+ for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
+ Elf_Sym sym(*file, parent->getClass(), parent->getData());
+ syms[i].name = strtab->getStr(sym.st_name);
+ syms[i].info = sym.st_info;
+ syms[i].other = sym.st_other;
+ ElfSection *section = (sym.st_shndx == SHN_ABS) ? nullptr : parent->getSection(sym.st_shndx);
+ new (&syms[i].value) ElfLocation(section, sym.st_value, ElfLocation::ABSOLUTE);
+ syms[i].size = sym.st_size;
+ syms[i].defined = (sym.st_shndx != SHN_UNDEF);
+ }
+ file->seekg(pos);
+}
+
+void
+ElfSymtab_Section::serialize(std::ofstream &file, char ei_class, char ei_data)
+{
+ ElfStrtab_Section *strtab = (ElfStrtab_Section *)getLink();
+ for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
+ Elf_Sym sym;
+ sym.st_name = strtab->getStrIndex(syms[i].name);
+ sym.st_info = syms[i].info;
+ sym.st_other = syms[i].other;
+ sym.st_value = syms[i].value.getValue();
+ ElfSection *section = syms[i].value.getSection();
+ if (syms[i].defined)
+ sym.st_shndx = section ? section->getIndex() : SHN_ABS;
+ else
+ sym.st_shndx = SHN_UNDEF;
+ sym.st_size = syms[i].size;
+ sym.serialize(file, ei_class, ei_data);
+ }
+}
+
+Elf_SymValue *
+ElfSymtab_Section::lookup(const char *name, unsigned int type_filter)
+{
+ for (std::vector<Elf_SymValue>::iterator sym = syms.begin();
+ sym != syms.end(); sym++) {
+ if ((type_filter & (1 << ELF32_ST_TYPE(sym->info))) &&
+ (strcmp(sym->name, name) == 0)) {
+ return &*sym;
+ }
+ }
+ return nullptr;
+}
+
+const char *
+ElfStrtab_Section::getStr(unsigned int index)
+{
+ for (std::vector<table_storage>::iterator t = table.begin();
+ t != table.end(); t++) {
+ if (index < t->used)
+ return t->buf + index;
+ index -= t->used;
+ }
+ assert(1 == 0);
+ return nullptr;
+}
+
+const char *
+ElfStrtab_Section::getStr(const char *string)
+{
+ if (string == nullptr)
+ return nullptr;
+
+ // If the given string is within the section, return it
+ for (std::vector<table_storage>::iterator t = table.begin();
+ t != table.end(); t++)
+ if ((string >= t->buf) && (string < t->buf + t->used))
+ return string;
+
+ // TODO: should scan in the section to find an existing string
+
+ // If not, we need to allocate the string in the section
+ size_t len = strlen(string) + 1;
+
+ if (table.back().size - table.back().used < len)
+ table.resize(table.size() + 1);
+
+ char *alloc_str = table.back().buf + table.back().used;
+ memcpy(alloc_str, string, len);
+ table.back().used += len;
+
+ shdr.sh_size += len;
+ markDirty();
+
+ return alloc_str;
+}
+
+unsigned int
+ElfStrtab_Section::getStrIndex(const char *string)
+{
+ if (string == nullptr)
+ return 0;
+
+ unsigned int index = 0;
+ string = getStr(string);
+ for (std::vector<table_storage>::iterator t = table.begin();
+ t != table.end(); t++) {
+ if ((string >= t->buf) && (string < t->buf + t->used))
+ return index + (string - t->buf);
+ index += t->used;
+ }
+
+ assert(1 == 0);
+ return 0;
+}
+
+void
+ElfStrtab_Section::serialize(std::ofstream &file, char ei_class, char ei_data)
+{
+ file.seekp(getOffset());
+ for (std::vector<table_storage>::iterator t = table.begin();
+ t != table.end(); t++)
+ file.write(t->buf, t->used);
+}
diff --git a/build/unix/elfhack/elfhack.cpp b/build/unix/elfhack/elfhack.cpp
new file mode 100644
index 000000000..8c1184237
--- /dev/null
+++ b/build/unix/elfhack/elfhack.cpp
@@ -0,0 +1,823 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#undef NDEBUG
+#include <assert.h>
+#include <cstring>
+#include <cstdlib>
+#include <cstdio>
+#include "elfxx.h"
+
+#define ver "0"
+#define elfhack_data ".elfhack.data.v" ver
+#define elfhack_text ".elfhack.text.v" ver
+
+#ifndef R_ARM_V4BX
+#define R_ARM_V4BX 0x28
+#endif
+#ifndef R_ARM_CALL
+#define R_ARM_CALL 0x1c
+#endif
+#ifndef R_ARM_JUMP24
+#define R_ARM_JUMP24 0x1d
+#endif
+#ifndef R_ARM_THM_JUMP24
+#define R_ARM_THM_JUMP24 0x1e
+#endif
+
+char *rundir = nullptr;
+
+template <typename T>
+struct wrapped {
+ T value;
+};
+
+class Elf_Addr_Traits {
+public:
+ typedef wrapped<Elf32_Addr> Type32;
+ typedef wrapped<Elf64_Addr> Type64;
+
+ template <class endian, typename R, typename T>
+ static inline void swap(T &t, R &r) {
+ r.value = endian::swap(t.value);
+ }
+};
+
+typedef serializable<Elf_Addr_Traits> Elf_Addr;
+
+class Elf_RelHack_Traits {
+public:
+ typedef Elf32_Rel Type32;
+ typedef Elf32_Rel Type64;
+
+ template <class endian, typename R, typename T>
+ static inline void swap(T &t, R &r) {
+ r.r_offset = endian::swap(t.r_offset);
+ r.r_info = endian::swap(t.r_info);
+ }
+};
+
+typedef serializable<Elf_RelHack_Traits> Elf_RelHack;
+
+class ElfRelHack_Section: public ElfSection {
+public:
+ ElfRelHack_Section(Elf_Shdr &s)
+ : ElfSection(s, nullptr, nullptr)
+ {
+ name = elfhack_data;
+ };
+
+ void serialize(std::ofstream &file, char ei_class, char ei_data)
+ {
+ for (std::vector<Elf_RelHack>::iterator i = rels.begin();
+ i != rels.end(); ++i)
+ (*i).serialize(file, ei_class, ei_data);
+ }
+
+ bool isRelocatable() {
+ return true;
+ }
+
+ void push_back(Elf_RelHack &r) {
+ rels.push_back(r);
+ shdr.sh_size = rels.size() * shdr.sh_entsize;
+ }
+private:
+ std::vector<Elf_RelHack> rels;
+};
+
+class ElfRelHackCode_Section: public ElfSection {
+public:
+ ElfRelHackCode_Section(Elf_Shdr &s, Elf &e, unsigned int init)
+ : ElfSection(s, nullptr, nullptr), parent(e), init(init) {
+ std::string file(rundir);
+ file += "/inject/";
+ switch (parent.getMachine()) {
+ case EM_386:
+ file += "x86";
+ break;
+ case EM_X86_64:
+ file += "x86_64";
+ break;
+ case EM_ARM:
+ file += "arm";
+ break;
+ default:
+ throw std::runtime_error("unsupported architecture");
+ }
+ file += ".o";
+ std::ifstream inject(file.c_str(), std::ios::in|std::ios::binary);
+ elf = new Elf(inject);
+ if (elf->getType() != ET_REL)
+ throw std::runtime_error("object for injected code is not ET_REL");
+ if (elf->getMachine() != parent.getMachine())
+ throw std::runtime_error("architecture of object for injected code doesn't match");
+
+ ElfSymtab_Section *symtab = nullptr;
+
+ // Find the symbol table.
+ for (ElfSection *section = elf->getSection(1); section != nullptr;
+ section = section->getNext()) {
+ if (section->getType() == SHT_SYMTAB)
+ symtab = (ElfSymtab_Section *) section;
+ }
+ if (symtab == nullptr)
+ throw std::runtime_error("Couldn't find a symbol table for the injected code");
+
+ // Find the init symbol
+ entry_point = -1;
+ Elf_SymValue *sym = symtab->lookup(init ? "init" : "init_noinit");
+ if (!sym)
+ throw std::runtime_error("Couldn't find an 'init' symbol in the injected code");
+
+ entry_point = sym->value.getValue();
+
+ // Get all relevant sections from the injected code object.
+ add_code_section(sym->value.getSection());
+
+ // Adjust code sections offsets according to their size
+ std::vector<ElfSection *>::iterator c = code.begin();
+ (*c)->getShdr().sh_addr = 0;
+ for(ElfSection *last = *(c++); c != code.end(); c++) {
+ unsigned int addr = last->getShdr().sh_addr + last->getSize();
+ if (addr & ((*c)->getAddrAlign() - 1))
+ addr = (addr | ((*c)->getAddrAlign() - 1)) + 1;
+ (*c)->getShdr().sh_addr = addr;
+ // We need to align this section depending on the greater
+ // alignment required by code sections.
+ if (shdr.sh_addralign < (*c)->getAddrAlign())
+ shdr.sh_addralign = (*c)->getAddrAlign();
+ }
+ shdr.sh_size = code.back()->getAddr() + code.back()->getSize();
+ data = new char[shdr.sh_size];
+ char *buf = data;
+ for (c = code.begin(); c != code.end(); c++) {
+ memcpy(buf, (*c)->getData(), (*c)->getSize());
+ buf += (*c)->getSize();
+ }
+ name = elfhack_text;
+ }
+
+ ~ElfRelHackCode_Section() {
+ delete elf;
+ }
+
+ void serialize(std::ofstream &file, char ei_class, char ei_data)
+ {
+ // Readjust code offsets
+ for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++)
+ (*c)->getShdr().sh_addr += getAddr();
+
+ // Apply relocations
+ for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++) {
+ for (ElfSection *rel = elf->getSection(1); rel != nullptr; rel = rel->getNext())
+ if (((rel->getType() == SHT_REL) ||
+ (rel->getType() == SHT_RELA)) &&
+ (rel->getInfo().section == *c)) {
+ if (rel->getType() == SHT_REL)
+ apply_relocations((ElfRel_Section<Elf_Rel> *)rel, *c);
+ else
+ apply_relocations((ElfRel_Section<Elf_Rela> *)rel, *c);
+ }
+ }
+
+ ElfSection::serialize(file, ei_class, ei_data);
+ }
+
+ bool isRelocatable() {
+ return true;
+ }
+
+ unsigned int getEntryPoint() {
+ return entry_point;
+ }
+private:
+ void add_code_section(ElfSection *section)
+ {
+ if (section) {
+ /* Don't add section if it's already been added in the past */
+ for (auto s = code.begin(); s != code.end(); ++s) {
+ if (section == *s)
+ return;
+ }
+ code.push_back(section);
+ find_code(section);
+ }
+ }
+
+ /* Look at the relocations associated to the given section to find other
+ * sections that it requires */
+ void find_code(ElfSection *section)
+ {
+ for (ElfSection *s = elf->getSection(1); s != nullptr;
+ s = s->getNext()) {
+ if (((s->getType() == SHT_REL) ||
+ (s->getType() == SHT_RELA)) &&
+ (s->getInfo().section == section)) {
+ if (s->getType() == SHT_REL)
+ scan_relocs_for_code((ElfRel_Section<Elf_Rel> *)s);
+ else
+ scan_relocs_for_code((ElfRel_Section<Elf_Rela> *)s);
+ }
+ }
+ }
+
+ template <typename Rel_Type>
+ void scan_relocs_for_code(ElfRel_Section<Rel_Type> *rel)
+ {
+ ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink();
+ for (auto r = rel->rels.begin(); r != rel->rels.end(); r++) {
+ ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection();
+ add_code_section(section);
+ }
+ }
+
+ class pc32_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
+ Elf32_Word addend, unsigned int addr)
+ {
+ return addr + addend - offset - base_addr;
+ }
+ };
+
+ class arm_plt32_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
+ Elf32_Word addend, unsigned int addr)
+ {
+ // We don't care about sign_extend because the only case where this is
+ // going to be used only jumps forward.
+ Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr) >> 2;
+ tmp = (addend + tmp) & 0x00ffffff;
+ return (addend & 0xff000000) | tmp;
+ }
+ };
+
+ class arm_thm_jump24_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
+ Elf32_Word addend, unsigned int addr)
+ {
+ /* Follows description of b.w and bl instructions as per
+ ARM Architecture Reference Manual ARM® v7-A and ARM® v7-R edition, A8.6.16
+ We limit ourselves to Encoding T4 of b.w and Encoding T1 of bl.
+ We don't care about sign_extend because the only case where this is
+ going to be used only jumps forward. */
+ Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr);
+ unsigned int word0 = addend & 0xffff,
+ word1 = addend >> 16;
+
+ /* Encoding T4 of B.W is 10x1 ; Encoding T1 of BL is 11x1. */
+ unsigned int type = (word1 & 0xd000) >> 12;
+ if (((word0 & 0xf800) != 0xf000) || ((type & 0x9) != 0x9))
+ throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for B.W <label> and BL <label>");
+
+ /* When the target address points to ARM code, switch a BL to a
+ * BLX. This however can't be done with a B.W without adding a
+ * trampoline, which is not supported as of now. */
+ if ((addr & 0x1) == 0) {
+ if (type == 0x9)
+ throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for BL <label> when label points to ARM code");
+ /* The address of the target is always relative to a 4-bytes
+ * aligned address, so if the address of the BL instruction is
+ * not 4-bytes aligned, adjust for it. */
+ if ((base_addr + offset) & 0x2)
+ tmp += 2;
+ /* Encoding T2 of BLX is 11x0. */
+ type = 0xc;
+ }
+
+ unsigned int s = (word0 & (1 << 10)) >> 10;
+ unsigned int j1 = (word1 & (1 << 13)) >> 13;
+ unsigned int j2 = (word1 & (1 << 11)) >> 11;
+ unsigned int i1 = j1 ^ s ? 0 : 1;
+ unsigned int i2 = j2 ^ s ? 0 : 1;
+
+ tmp += ((s << 24) | (i1 << 23) | (i2 << 22) | ((word0 & 0x3ff) << 12) | ((word1 & 0x7ff) << 1));
+
+ s = (tmp & (1 << 24)) >> 24;
+ j1 = ((tmp & (1 << 23)) >> 23) ^ !s;
+ j2 = ((tmp & (1 << 22)) >> 22) ^ !s;
+
+ return 0xf000 | (s << 10) | ((tmp & (0x3ff << 12)) >> 12) |
+ (type << 28) | (j1 << 29) | (j2 << 27) | ((tmp & 0xffe) << 15);
+ }
+ };
+
+ class gotoff_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
+ Elf32_Word addend, unsigned int addr)
+ {
+ return addr + addend;
+ }
+ };
+
+ template <class relocation_type>
+ void apply_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr)
+ {
+ relocation_type relocation;
+ Elf32_Addr value;
+ memcpy(&value, base + r->r_offset, 4);
+ value = relocation(the_code->getAddr(), r->r_offset, value, addr);
+ memcpy(base + r->r_offset, &value, 4);
+ }
+
+ template <class relocation_type>
+ void apply_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr)
+ {
+ relocation_type relocation;
+ Elf32_Addr value = relocation(the_code->getAddr(), r->r_offset, r->r_addend, addr);
+ memcpy(base + r->r_offset, &value, 4);
+ }
+
+ template <typename Rel_Type>
+ void apply_relocations(ElfRel_Section<Rel_Type> *rel, ElfSection *the_code)
+ {
+ assert(rel->getType() == Rel_Type::sh_type);
+ char *buf = data + (the_code->getAddr() - code.front()->getAddr());
+ // TODO: various checks on the sections
+ ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink();
+ for (typename std::vector<Rel_Type>::iterator r = rel->rels.begin(); r != rel->rels.end(); r++) {
+ // TODO: various checks on the symbol
+ const char *name = symtab->syms[ELF32_R_SYM(r->r_info)].name;
+ unsigned int addr;
+ if (symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection() == nullptr) {
+ if (strcmp(name, "relhack") == 0) {
+ addr = getNext()->getAddr();
+ } else if (strcmp(name, "elf_header") == 0) {
+ // TODO: change this ungly hack to something better
+ ElfSection *ehdr = parent.getSection(1)->getPrevious()->getPrevious();
+ addr = ehdr->getAddr();
+ } else if (strcmp(name, "original_init") == 0) {
+ addr = init;
+ } else if (strcmp(name, "_GLOBAL_OFFSET_TABLE_") == 0) {
+ // We actually don't need a GOT, but need it as a reference for
+ // GOTOFF relocations. We'll just use the start of the ELF file
+ addr = 0;
+ } else if (strcmp(name, "") == 0) {
+ // This is for R_ARM_V4BX, until we find something better
+ addr = -1;
+ } else {
+ throw std::runtime_error("Unsupported symbol in relocation");
+ }
+ } else {
+ ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection();
+ assert((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR));
+ addr = symtab->syms[ELF32_R_SYM(r->r_info)].value.getValue();
+ }
+ // Do the relocation
+#define REL(machine, type) (EM_ ## machine | (R_ ## machine ## _ ## type << 8))
+ switch (elf->getMachine() | (ELF32_R_TYPE(r->r_info) << 8)) {
+ case REL(X86_64, PC32):
+ case REL(386, PC32):
+ case REL(386, GOTPC):
+ case REL(ARM, GOTPC):
+ case REL(ARM, REL32):
+ apply_relocation<pc32_relocation>(the_code, buf, &*r, addr);
+ break;
+ case REL(ARM, CALL):
+ case REL(ARM, JUMP24):
+ case REL(ARM, PLT32):
+ apply_relocation<arm_plt32_relocation>(the_code, buf, &*r, addr);
+ break;
+ case REL(ARM, THM_PC22 /* THM_CALL */):
+ case REL(ARM, THM_JUMP24):
+ apply_relocation<arm_thm_jump24_relocation>(the_code, buf, &*r, addr);
+ break;
+ case REL(386, GOTOFF):
+ case REL(ARM, GOTOFF):
+ apply_relocation<gotoff_relocation>(the_code, buf, &*r, addr);
+ break;
+ case REL(ARM, V4BX):
+ // Ignore R_ARM_V4BX relocations
+ break;
+ default:
+ throw std::runtime_error("Unsupported relocation type");
+ }
+ }
+ }
+
+ Elf *elf, &parent;
+ std::vector<ElfSection *> code;
+ unsigned int init;
+ int entry_point;
+};
+
+unsigned int get_addend(Elf_Rel *rel, Elf *elf) {
+ ElfLocation loc(rel->r_offset, elf);
+ Elf_Addr addr(loc.getBuffer(), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData());
+ return addr.value;
+}
+
+unsigned int get_addend(Elf_Rela *rel, Elf *elf) {
+ return rel->r_addend;
+}
+
+void set_relative_reloc(Elf_Rel *rel, Elf *elf, unsigned int value) {
+ ElfLocation loc(rel->r_offset, elf);
+ Elf_Addr addr;
+ addr.value = value;
+ addr.serialize(const_cast<char *>(loc.getBuffer()), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData());
+}
+
+void set_relative_reloc(Elf_Rela *rel, Elf *elf, unsigned int value) {
+ // ld puts the value of relocated relocations both in the addend and
+ // at r_offset. For consistency, keep it that way.
+ set_relative_reloc((Elf_Rel *)rel, elf, value);
+ rel->r_addend = value;
+}
+
+void maybe_split_segment(Elf *elf, ElfSegment *segment, bool fill)
+{
+ std::list<ElfSection *>::iterator it = segment->begin();
+ for (ElfSection *last = *(it++); it != segment->end(); last = *(it++)) {
+ // When two consecutive non-SHT_NOBITS sections are apart by more
+ // than the alignment of the section, the second can be moved closer
+ // to the first, but this requires the segment to be split.
+ if (((*it)->getType() != SHT_NOBITS) && (last->getType() != SHT_NOBITS) &&
+ ((*it)->getOffset() - last->getOffset() - last->getSize() > segment->getAlign())) {
+ // Probably very wrong.
+ Elf_Phdr phdr;
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0;
+ phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff();
+ phdr.p_flags = segment->getFlags();
+ phdr.p_align = segment->getAlign();
+ phdr.p_filesz = (unsigned int)-1;
+ phdr.p_memsz = (unsigned int)-1;
+ ElfSegment *newSegment = new ElfSegment(&phdr);
+ elf->insertSegmentAfter(segment, newSegment);
+ ElfSection *section = *it;
+ for (; it != segment->end(); ++it) {
+ newSegment->addSection(*it);
+ }
+ for (it = newSegment->begin(); it != newSegment->end(); it++) {
+ segment->removeSection(*it);
+ }
+ // Fill the virtual address space gap left between the two PT_LOADs
+ // with a new PT_LOAD with no permissions. This avoids the linker
+ // (especially bionic's) filling the gap with anonymous memory,
+ // which breakpad doesn't like.
+ // /!\ running strip on a elfhacked binary will break this filler
+ // PT_LOAD.
+ if (!fill)
+ break;
+ // Insert dummy segment to normalize the entire Elf with the header
+ // sizes adjusted, before inserting a filler segment.
+ {
+ memset(&phdr, 0, sizeof(phdr));
+ ElfSegment dummySegment(&phdr);
+ elf->insertSegmentAfter(segment, &dummySegment);
+ elf->normalize();
+ elf->removeSegment(&dummySegment);
+ }
+ ElfSection *previous = section->getPrevious();
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = (previous->getAddr() + previous->getSize() + segment->getAlign() - 1) & ~(segment->getAlign() - 1);
+ phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff();
+ phdr.p_flags = 0;
+ phdr.p_align = 0;
+ phdr.p_filesz = (section->getAddr() & ~(newSegment->getAlign() - 1)) - phdr.p_vaddr;
+ phdr.p_memsz = phdr.p_filesz;
+ if (phdr.p_filesz) {
+ newSegment = new ElfSegment(&phdr);
+ assert(newSegment->isElfHackFillerSegment());
+ elf->insertSegmentAfter(segment, newSegment);
+ } else {
+ elf->normalize();
+ }
+ break;
+ }
+ }
+}
+
+template <typename Rel_Type>
+int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type2, bool force, bool fill)
+{
+ ElfDynamic_Section *dyn = elf->getDynSection();
+ if (dyn == nullptr) {
+ fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n");
+ return -1;
+ }
+
+ ElfSegment *relro = elf->getSegmentByType(PT_GNU_RELRO);
+
+ ElfRel_Section<Rel_Type> *section = (ElfRel_Section<Rel_Type> *)dyn->getSectionForType(Rel_Type::d_tag);
+ assert(section->getType() == Rel_Type::sh_type);
+
+ Elf32_Shdr relhack32_section =
+ { 0, SHT_PROGBITS, SHF_ALLOC, 0, (Elf32_Off)-1, 0, SHN_UNDEF, 0,
+ Elf_RelHack::size(elf->getClass()), Elf_RelHack::size(elf->getClass()) }; // TODO: sh_addralign should be an alignment, not size
+ Elf32_Shdr relhackcode32_section =
+ { 0, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, (Elf32_Off)-1, 0,
+ SHN_UNDEF, 0, 1, 0 };
+
+ unsigned int entry_sz = Elf_Addr::size(elf->getClass());
+
+ // The injected code needs to be executed before any init code in the
+ // binary. There are three possible cases:
+ // - The binary has no init code at all. In this case, we will add a
+ // DT_INIT entry pointing to the injected code.
+ // - The binary has a DT_INIT entry. In this case, we will interpose:
+ // we change DT_INIT to point to the injected code, and have the
+ // injected code call the original DT_INIT entry point.
+ // - The binary has no DT_INIT entry, but has a DT_INIT_ARRAY. In this
+ // case, we interpose as well, by replacing the first entry in the
+ // array to point to the injected code, and have the injected code
+ // call the original first entry.
+ // The binary may have .ctors instead of DT_INIT_ARRAY, for its init
+ // functions, but this falls into the second case above, since .ctors
+ // are actually run by DT_INIT code.
+ ElfValue *value = dyn->getValueForType(DT_INIT);
+ unsigned int original_init = value ? value->getValue() : 0;
+ ElfSection *init_array = nullptr;
+ if (!value || !value->getValue()) {
+ value = dyn->getValueForType(DT_INIT_ARRAYSZ);
+ if (value && value->getValue() >= entry_sz)
+ init_array = dyn->getSectionForType(DT_INIT_ARRAY);
+ }
+
+ Elf_Shdr relhack_section(relhack32_section);
+ Elf_Shdr relhackcode_section(relhackcode32_section);
+ ElfRelHack_Section *relhack = new ElfRelHack_Section(relhack_section);
+
+ ElfSymtab_Section *symtab = (ElfSymtab_Section *) section->getLink();
+ Elf_SymValue *sym = symtab->lookup("__cxa_pure_virtual");
+
+ std::vector<Rel_Type> new_rels;
+ Elf_RelHack relhack_entry;
+ relhack_entry.r_offset = relhack_entry.r_info = 0;
+ size_t init_array_reloc = 0;
+ for (typename std::vector<Rel_Type>::iterator i = section->rels.begin();
+ i != section->rels.end(); i++) {
+ // We don't need to keep R_*_NONE relocations
+ if (!ELF32_R_TYPE(i->r_info))
+ continue;
+ ElfLocation loc(i->r_offset, elf);
+ // __cxa_pure_virtual is a function used in vtables to point at pure
+ // virtual methods. The __cxa_pure_virtual function usually abort()s.
+ // These functions are however normally never called. In the case
+ // where they would, jumping to the null address instead of calling
+ // __cxa_pure_virtual is going to work just as well. So we can remove
+ // relocations for the __cxa_pure_virtual symbol and null out the
+ // content at the offset pointed by the relocation.
+ if (sym) {
+ if (sym->defined) {
+ // If we are statically linked to libstdc++, the
+ // __cxa_pure_virtual symbol is defined in our lib, and we
+ // have relative relocations (rel_type) for it.
+ if (ELF32_R_TYPE(i->r_info) == rel_type) {
+ Elf_Addr addr(loc.getBuffer(), entry_sz, elf->getClass(), elf->getData());
+ if (addr.value == sym->value.getValue()) {
+ memset((char *)loc.getBuffer(), 0, entry_sz);
+ continue;
+ }
+ }
+ } else {
+ // If we are dynamically linked to libstdc++, the
+ // __cxa_pure_virtual symbol is undefined in our lib, and we
+ // have absolute relocations (rel_type2) for it.
+ if ((ELF32_R_TYPE(i->r_info) == rel_type2) &&
+ (sym == &symtab->syms[ELF32_R_SYM(i->r_info)])) {
+ memset((char *)loc.getBuffer(), 0, entry_sz);
+ continue;
+ }
+ }
+ }
+ // Keep track of the relocation associated with the first init_array entry.
+ if (init_array && i->r_offset == init_array->getAddr()) {
+ if (init_array_reloc) {
+ fprintf(stderr, "Found multiple relocations for the first init_array entry. Skipping\n");
+ return -1;
+ }
+ new_rels.push_back(*i);
+ init_array_reloc = new_rels.size();
+ } else if (!(loc.getSection()->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type) ||
+ (relro && (i->r_offset >= relro->getAddr()) &&
+ (i->r_offset < relro->getAddr() + relro->getMemSize()))) {
+ // Don't pack relocations happening in non writable sections.
+ // Our injected code is likely not to be allowed to write there.
+ new_rels.push_back(*i);
+ } else {
+ // TODO: check that i->r_addend == *i->r_offset
+ if (i->r_offset == relhack_entry.r_offset + relhack_entry.r_info * entry_sz) {
+ relhack_entry.r_info++;
+ } else {
+ if (relhack_entry.r_offset)
+ relhack->push_back(relhack_entry);
+ relhack_entry.r_offset = i->r_offset;
+ relhack_entry.r_info = 1;
+ }
+ }
+ }
+ if (relhack_entry.r_offset)
+ relhack->push_back(relhack_entry);
+ // Last entry must be nullptr
+ relhack_entry.r_offset = relhack_entry.r_info = 0;
+ relhack->push_back(relhack_entry);
+
+ unsigned int old_end = section->getOffset() + section->getSize();
+
+ if (init_array) {
+ if (! init_array_reloc) {
+ fprintf(stderr, "Didn't find relocation for DT_INIT_ARRAY's first entry. Skipping\n");
+ return -1;
+ }
+ Rel_Type *rel = &new_rels[init_array_reloc - 1];
+ unsigned int addend = get_addend(rel, elf);
+ // Use relocated value of DT_INIT_ARRAY's first entry for the
+ // function to be called by the injected code.
+ if (ELF32_R_TYPE(rel->r_info) == rel_type) {
+ original_init = addend;
+ } else if (ELF32_R_TYPE(rel->r_info) == rel_type2) {
+ ElfSymtab_Section *symtab = (ElfSymtab_Section *)section->getLink();
+ original_init = symtab->syms[ELF32_R_SYM(rel->r_info)].value.getValue() + addend;
+ } else {
+ fprintf(stderr, "Unsupported relocation type for DT_INIT_ARRAY's first entry. Skipping\n");
+ return -1;
+ }
+ }
+
+ section->rels.assign(new_rels.begin(), new_rels.end());
+ section->shrink(new_rels.size() * section->getEntSize());
+
+ ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf, original_init);
+ relhackcode->insertBefore(section);
+ relhack->insertAfter(relhackcode);
+ if (section->getOffset() + section->getSize() >= old_end) {
+ fprintf(stderr, "No gain. Skipping\n");
+ return -1;
+ }
+
+ // Adjust PT_LOAD segments
+ for (ElfSegment *segment = elf->getSegmentByType(PT_LOAD); segment;
+ segment = elf->getSegmentByType(PT_LOAD, segment)) {
+ maybe_split_segment(elf, segment, fill);
+ }
+
+ // Ensure Elf sections will be at their final location.
+ elf->normalize();
+ ElfLocation *init = new ElfLocation(relhackcode, relhackcode->getEntryPoint());
+ if (init_array) {
+ // Adjust the first DT_INIT_ARRAY entry to point at the injected code
+ // by transforming its relocation into a relative one pointing to the
+ // address of the injected code.
+ Rel_Type *rel = &section->rels[init_array_reloc - 1];
+ rel->r_info = ELF32_R_INFO(0, rel_type); // Set as a relative relocation
+ set_relative_reloc(&section->rels[init_array_reloc - 1], elf, init->getValue());
+ } else if (!dyn->setValueForType(DT_INIT, init)) {
+ fprintf(stderr, "Can't grow .dynamic section to set DT_INIT. Skipping\n");
+ return -1;
+ }
+ // TODO: adjust the value according to the remaining number of relative relocations
+ if (dyn->getValueForType(Rel_Type::d_tag_count))
+ dyn->setValueForType(Rel_Type::d_tag_count, new ElfPlainValue(0));
+
+ return 0;
+}
+
+static inline int backup_file(const char *name)
+{
+ std::string fname(name);
+ fname += ".bak";
+ return rename(name, fname.c_str());
+}
+
+void do_file(const char *name, bool backup = false, bool force = false, bool fill = false)
+{
+ std::ifstream file(name, std::ios::in|std::ios::binary);
+ Elf elf(file);
+ unsigned int size = elf.getSize();
+ fprintf(stderr, "%s: ", name);
+ if (elf.getType() != ET_DYN) {
+ fprintf(stderr, "Not a shared object. Skipping\n");
+ return;
+ }
+
+ for (ElfSection *section = elf.getSection(1); section != nullptr;
+ section = section->getNext()) {
+ if (section->getName() &&
+ (strncmp(section->getName(), ".elfhack.", 9) == 0)) {
+ fprintf(stderr, "Already elfhacked. Skipping\n");
+ return;
+ }
+ }
+
+ int exit = -1;
+ switch (elf.getMachine()) {
+ case EM_386:
+ exit = do_relocation_section<Elf_Rel>(&elf, R_386_RELATIVE, R_386_32, force, fill);
+ break;
+ case EM_X86_64:
+ exit = do_relocation_section<Elf_Rela>(&elf, R_X86_64_RELATIVE, R_X86_64_64, force, fill);
+ break;
+ case EM_ARM:
+ exit = do_relocation_section<Elf_Rel>(&elf, R_ARM_RELATIVE, R_ARM_ABS32, force, fill);
+ break;
+ }
+ if (exit == 0) {
+ if (!force && (elf.getSize() >= size)) {
+ fprintf(stderr, "No gain. Skipping\n");
+ } else if (backup && backup_file(name) != 0) {
+ fprintf(stderr, "Couln't create backup file\n");
+ } else {
+ std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
+ elf.write(ofile);
+ fprintf(stderr, "Reduced by %d bytes\n", size - elf.getSize());
+ }
+ }
+}
+
+void undo_file(const char *name, bool backup = false)
+{
+ std::ifstream file(name, std::ios::in|std::ios::binary);
+ Elf elf(file);
+ unsigned int size = elf.getSize();
+ fprintf(stderr, "%s: ", name);
+ if (elf.getType() != ET_DYN) {
+ fprintf(stderr, "Not a shared object. Skipping\n");
+ return;
+ }
+
+ ElfSection *data = nullptr, *text = nullptr;
+ for (ElfSection *section = elf.getSection(1); section != nullptr;
+ section = section->getNext()) {
+ if (section->getName() &&
+ (strcmp(section->getName(), elfhack_data) == 0))
+ data = section;
+ if (section->getName() &&
+ (strcmp(section->getName(), elfhack_text) == 0))
+ text = section;
+ }
+
+ if (!data || !text) {
+ fprintf(stderr, "Not elfhacked. Skipping\n");
+ return;
+ }
+ if (data != text->getNext()) {
+ fprintf(stderr, elfhack_data " section not following " elfhack_text ". Skipping\n");
+ return;
+ }
+
+ ElfSegment *first = elf.getSegmentByType(PT_LOAD);
+ ElfSegment *second = elf.getSegmentByType(PT_LOAD, first);
+ ElfSegment *filler = nullptr;
+ // If the second PT_LOAD is a filler from elfhack --fill, check the third.
+ if (second->isElfHackFillerSegment()) {
+ filler = second;
+ second = elf.getSegmentByType(PT_LOAD, filler);
+ }
+ if (second->getFlags() != first->getFlags()) {
+ fprintf(stderr, "Couldn't identify elfhacked PT_LOAD segments. Skipping\n");
+ return;
+ }
+ // Move sections from the second PT_LOAD to the first, and remove the
+ // second PT_LOAD segment.
+ for (std::list<ElfSection *>::iterator section = second->begin();
+ section != second->end(); ++section)
+ first->addSection(*section);
+
+ elf.removeSegment(second);
+ if (filler)
+ elf.removeSegment(filler);
+
+ if (backup && backup_file(name) != 0) {
+ fprintf(stderr, "Couln't create backup file\n");
+ } else {
+ std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
+ elf.write(ofile);
+ fprintf(stderr, "Grown by %d bytes\n", elf.getSize() - size);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int arg;
+ bool backup = false;
+ bool force = false;
+ bool revert = false;
+ bool fill = false;
+ char *lastSlash = rindex(argv[0], '/');
+ if (lastSlash != nullptr)
+ rundir = strndup(argv[0], lastSlash - argv[0]);
+ for (arg = 1; arg < argc; arg++) {
+ if (strcmp(argv[arg], "-f") == 0)
+ force = true;
+ else if (strcmp(argv[arg], "-b") == 0)
+ backup = true;
+ else if (strcmp(argv[arg], "-r") == 0)
+ revert = true;
+ else if (strcmp(argv[arg], "--fill") == 0)
+ fill = true;
+ else if (revert) {
+ undo_file(argv[arg], backup);
+ } else
+ do_file(argv[arg], backup, force, fill);
+ }
+
+ free(rundir);
+ return 0;
+}
diff --git a/build/unix/elfhack/elfxx.h b/build/unix/elfhack/elfxx.h
new file mode 100644
index 000000000..a05b02348
--- /dev/null
+++ b/build/unix/elfhack/elfxx.h
@@ -0,0 +1,701 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdexcept>
+#include <list>
+#include <vector>
+#include <cstring>
+#include <iostream>
+#include <fstream>
+#include <algorithm>
+#include <elf.h>
+#include <asm/byteorder.h>
+
+// Technically, __*_to_cpu and __cpu_to* function are equivalent,
+// so swap can use either of both.
+#define def_swap(endian, type, bits) \
+static inline type ## bits ## _t swap(type ## bits ## _t i) { \
+ return __ ## endian ## bits ## _to_cpu(i); \
+}
+
+class little_endian {
+public:
+def_swap(le, uint, 16);
+def_swap(le, uint, 32);
+def_swap(le, uint, 64);
+def_swap(le, int, 16);
+def_swap(le, int, 32);
+def_swap(le, int, 64);
+};
+
+class big_endian {
+public:
+def_swap(be, uint, 16);
+def_swap(be, uint, 32);
+def_swap(be, uint, 64);
+def_swap(be, int, 16);
+def_swap(be, int, 32);
+def_swap(be, int, 64);
+};
+
+// forward declaration
+class ElfSection;
+class ElfSegment;
+// TODO: Rename Elf_* types
+class Elf_Ehdr;
+class Elf_Phdr;
+class Elf;
+class ElfDynamic_Section;
+class ElfStrtab_Section;
+
+class Elf_Ehdr_Traits {
+public:
+ typedef Elf32_Ehdr Type32;
+ typedef Elf64_Ehdr Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T &t, R &r);
+};
+
+class Elf_Phdr_Traits {
+public:
+ typedef Elf32_Phdr Type32;
+ typedef Elf64_Phdr Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T &t, R &r);
+};
+
+class Elf_Shdr_Traits {
+public:
+ typedef Elf32_Shdr Type32;
+ typedef Elf64_Shdr Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T &t, R &r);
+};
+
+class Elf_Dyn_Traits {
+public:
+ typedef Elf32_Dyn Type32;
+ typedef Elf64_Dyn Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T &t, R &r);
+};
+
+class Elf_Sym_Traits {
+public:
+ typedef Elf32_Sym Type32;
+ typedef Elf64_Sym Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T &t, R &r);
+};
+
+class Elf_Rel_Traits {
+public:
+ typedef Elf32_Rel Type32;
+ typedef Elf64_Rel Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T &t, R &r);
+};
+
+class Elf_Rela_Traits {
+public:
+ typedef Elf32_Rela Type32;
+ typedef Elf64_Rela Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T &t, R &r);
+};
+
+class ElfValue {
+public:
+ virtual unsigned int getValue() { return 0; }
+ virtual ElfSection *getSection() { return nullptr; }
+};
+
+class ElfPlainValue: public ElfValue {
+ unsigned int value;
+public:
+ ElfPlainValue(unsigned int val): value(val) {};
+ unsigned int getValue() { return value; }
+};
+
+class ElfLocation: public ElfValue {
+ ElfSection *section;
+ unsigned int offset;
+public:
+ enum position { ABSOLUTE, RELATIVE };
+ ElfLocation(): section(nullptr), offset(0) {};
+ ElfLocation(ElfSection *section, unsigned int off, enum position pos = RELATIVE);
+ ElfLocation(unsigned int location, Elf *elf);
+ unsigned int getValue();
+ ElfSection *getSection() { return section; }
+ const char *getBuffer();
+};
+
+class ElfSize: public ElfValue {
+ ElfSection *section;
+public:
+ ElfSize(ElfSection *s): section(s) {};
+ unsigned int getValue();
+ ElfSection *getSection() { return section; }
+};
+
+class ElfEntSize: public ElfValue {
+ ElfSection *section;
+public:
+ ElfEntSize(ElfSection *s): section(s) {};
+ unsigned int getValue();
+ ElfSection *getSection() { return section; }
+};
+
+template <typename T>
+class serializable: public T::Type32 {
+public:
+ serializable() {};
+ serializable(const typename T::Type32 &p): T::Type32(p) {};
+
+private:
+ template <typename R>
+ void init(const char *buf, size_t len, char ei_data)
+ {
+ R e;
+ assert(len >= sizeof(e));
+ memcpy(&e, buf, sizeof(e));
+ if (ei_data == ELFDATA2LSB) {
+ T::template swap<little_endian>(e, *this);
+ return;
+ } else if (ei_data == ELFDATA2MSB) {
+ T::template swap<big_endian>(e, *this);
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF data encoding");
+ }
+
+ template <typename R>
+ void serialize(const char *buf, size_t len, char ei_data)
+ {
+ assert(len >= sizeof(R));
+ if (ei_data == ELFDATA2LSB) {
+ T::template swap<little_endian>(*this, *(R *)buf);
+ return;
+ } else if (ei_data == ELFDATA2MSB) {
+ T::template swap<big_endian>(*this, *(R *)buf);
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF data encoding");
+ }
+
+public:
+ serializable(const char *buf, size_t len, char ei_class, char ei_data)
+ {
+ if (ei_class == ELFCLASS32) {
+ init<typename T::Type32>(buf, len, ei_data);
+ return;
+ } else if (ei_class == ELFCLASS64) {
+ init<typename T::Type64>(buf, len, ei_data);
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF class");
+ }
+
+ serializable(std::ifstream &file, char ei_class, char ei_data)
+ {
+ if (ei_class == ELFCLASS32) {
+ typename T::Type32 e;
+ file.read((char *)&e, sizeof(e));
+ init<typename T::Type32>((char *)&e, sizeof(e), ei_data);
+ return;
+ } else if (ei_class == ELFCLASS64) {
+ typename T::Type64 e;
+ file.read((char *)&e, sizeof(e));
+ init<typename T::Type64>((char *)&e, sizeof(e), ei_data);
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF class or data encoding");
+ }
+
+ void serialize(std::ofstream &file, char ei_class, char ei_data)
+ {
+ if (ei_class == ELFCLASS32) {
+ typename T::Type32 e;
+ serialize<typename T::Type32>((char *)&e, sizeof(e), ei_data);
+ file.write((char *)&e, sizeof(e));
+ return;
+ } else if (ei_class == ELFCLASS64) {
+ typename T::Type64 e;
+ serialize<typename T::Type64>((char *)&e, sizeof(e), ei_data);
+ file.write((char *)&e, sizeof(e));
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF class or data encoding");
+ }
+
+ void serialize(char *buf, size_t len, char ei_class, char ei_data)
+ {
+ if (ei_class == ELFCLASS32) {
+ serialize<typename T::Type32>(buf, len, ei_data);
+ return;
+ } else if (ei_class == ELFCLASS64) {
+ serialize<typename T::Type64>(buf, len, ei_data);
+ return;
+ }
+ throw std::runtime_error("Unsupported ELF class");
+ }
+
+ static inline unsigned int size(char ei_class)
+ {
+ if (ei_class == ELFCLASS32)
+ return sizeof(typename T::Type32);
+ else if (ei_class == ELFCLASS64)
+ return sizeof(typename T::Type64);
+ return 0;
+ }
+};
+
+typedef serializable<Elf_Shdr_Traits> Elf_Shdr;
+
+class Elf {
+public:
+ Elf(std::ifstream &file);
+ ~Elf();
+
+ /* index == -1 is treated as index == ehdr.e_shstrndx */
+ ElfSection *getSection(int index);
+
+ ElfSection *getSectionAt(unsigned int offset);
+
+ ElfSegment *getSegmentByType(unsigned int type, ElfSegment *last = nullptr);
+
+ ElfDynamic_Section *getDynSection();
+
+ void normalize();
+ void write(std::ofstream &file);
+
+ char getClass();
+ char getData();
+ char getType();
+ char getMachine();
+ unsigned int getSize();
+
+ void insertSegmentAfter(ElfSegment *previous, ElfSegment *segment) {
+ std::vector<ElfSegment *>::iterator prev = std::find(segments.begin(), segments.end(), previous);
+ segments.insert(prev + 1, segment);
+ }
+
+ void removeSegment(ElfSegment *segment);
+
+private:
+ Elf_Ehdr *ehdr;
+ ElfLocation eh_entry;
+ ElfStrtab_Section *eh_shstrndx;
+ ElfSection **sections;
+ std::vector<ElfSegment *> segments;
+ ElfSection *shdr_section, *phdr_section;
+ /* Values used only during initialization */
+ Elf_Shdr **tmp_shdr;
+ std::ifstream *tmp_file;
+};
+
+class ElfSection {
+public:
+ typedef union {
+ ElfSection *section;
+ int index;
+ } SectionInfo;
+
+ ElfSection(Elf_Shdr &s, std::ifstream *file, Elf *parent);
+
+ virtual ~ElfSection() {
+ delete[] data;
+ }
+
+ const char *getName() { return name; }
+ unsigned int getType() { return shdr.sh_type; }
+ unsigned int getFlags() { return shdr.sh_flags; }
+ unsigned int getAddr();
+ unsigned int getSize() { return shdr.sh_size; }
+ unsigned int getAddrAlign() { return shdr.sh_addralign; }
+ unsigned int getEntSize() { return shdr.sh_entsize; }
+ const char *getData() { return data; }
+ ElfSection *getLink() { return link; }
+ SectionInfo getInfo() { return info; }
+
+ void shrink(unsigned int newsize) {
+ if (newsize < shdr.sh_size)
+ shdr.sh_size = newsize;
+ }
+
+ unsigned int getOffset();
+ int getIndex();
+ Elf_Shdr &getShdr();
+
+ ElfSection *getNext() { return next; }
+ ElfSection *getPrevious() { return previous; }
+
+ virtual bool isRelocatable() {
+ return ((getType() == SHT_SYMTAB) ||
+ (getType() == SHT_STRTAB) ||
+ (getType() == SHT_RELA) ||
+ (getType() == SHT_HASH) ||
+ (getType() == SHT_NOTE) ||
+ (getType() == SHT_REL) ||
+ (getType() == SHT_DYNSYM) ||
+ (getType() == SHT_GNU_HASH) ||
+ (getType() == SHT_GNU_verdef) ||
+ (getType() == SHT_GNU_verneed) ||
+ (getType() == SHT_GNU_versym) ||
+ getSegmentByType(PT_INTERP)) &&
+ (getFlags() & SHF_ALLOC);
+ }
+
+ void insertAfter(ElfSection *section, bool dirty = true) {
+ if (previous != nullptr)
+ previous->next = next;
+ if (next != nullptr)
+ next->previous = previous;
+ previous = section;
+ if (section != nullptr) {
+ next = section->next;
+ section->next = this;
+ } else
+ next = nullptr;
+ if (next != nullptr)
+ next->previous = this;
+ if (dirty)
+ markDirty();
+ insertInSegments(section->segments);
+ }
+
+ void insertBefore(ElfSection *section, bool dirty = true) {
+ if (previous != nullptr)
+ previous->next = next;
+ if (next != nullptr)
+ next->previous = previous;
+ next = section;
+ if (section != nullptr) {
+ previous = section->previous;
+ section->previous = this;
+ } else
+ previous = nullptr;
+ if (previous != nullptr)
+ previous->next = this;
+ if (dirty)
+ markDirty();
+ insertInSegments(section->segments);
+ }
+
+ void markDirty() {
+ if (link != nullptr)
+ shdr.sh_link = -1;
+ if (info.index)
+ shdr.sh_info = -1;
+ shdr.sh_offset = -1;
+ if (isRelocatable())
+ shdr.sh_addr = -1;
+ if (next)
+ next->markDirty();
+ }
+
+ virtual void serialize(std::ofstream &file, char ei_class, char ei_data)
+ {
+ if (getType() == SHT_NOBITS)
+ return;
+ file.seekp(getOffset());
+ file.write(data, getSize());
+ }
+
+private:
+ friend class ElfSegment;
+
+ void addToSegment(ElfSegment *segment) {
+ segments.push_back(segment);
+ }
+
+ void removeFromSegment(ElfSegment *segment) {
+ std::vector<ElfSegment *>::iterator i = std::find(segments.begin(), segments.end(), segment);
+ segments.erase(i, i + 1);
+ }
+
+ ElfSegment *getSegmentByType(unsigned int type);
+
+ void insertInSegments(std::vector<ElfSegment *> &segs);
+
+protected:
+ Elf_Shdr shdr;
+ char *data;
+ const char *name;
+private:
+ ElfSection *link;
+ SectionInfo info;
+ ElfSection *next, *previous;
+ int index;
+ std::vector<ElfSegment *> segments;
+};
+
+class ElfSegment {
+public:
+ ElfSegment(Elf_Phdr *phdr);
+
+ unsigned int getType() { return type; }
+ unsigned int getFlags() { return flags; }
+ unsigned int getAlign() { return align; }
+
+ ElfSection *getFirstSection() { return sections.empty() ? nullptr : sections.front(); }
+ int getVPDiff() { return v_p_diff; }
+ unsigned int getFileSize();
+ unsigned int getMemSize();
+ unsigned int getOffset();
+ unsigned int getAddr();
+
+ void addSection(ElfSection *section);
+ void removeSection(ElfSection *section);
+
+ std::list<ElfSection *>::iterator begin() { return sections.begin(); }
+ std::list<ElfSection *>::iterator end() { return sections.end(); }
+
+ void clear();
+
+ bool isElfHackFillerSegment() {
+ return type == PT_LOAD && flags == 0;
+ }
+private:
+ unsigned int type;
+ int v_p_diff; // Difference between physical and virtual address
+ unsigned int flags;
+ unsigned int align;
+ std::list<ElfSection *> sections;
+ // The following are only really used for PT_GNU_RELRO until something
+ // better is found.
+ unsigned int vaddr;
+ unsigned int filesz, memsz;
+};
+
+class Elf_Ehdr: public serializable<Elf_Ehdr_Traits>, public ElfSection {
+public:
+ Elf_Ehdr(std::ifstream &file, char ei_class, char ei_data);
+ void serialize(std::ofstream &file, char ei_class, char ei_data)
+ {
+ serializable<Elf_Ehdr_Traits>::serialize(file, ei_class, ei_data);
+ }
+};
+
+class Elf_Phdr: public serializable<Elf_Phdr_Traits> {
+public:
+ Elf_Phdr() {};
+ Elf_Phdr(std::ifstream &file, char ei_class, char ei_data)
+ : serializable<Elf_Phdr_Traits>(file, ei_class, ei_data) {};
+ bool contains(ElfSection *section)
+ {
+ unsigned int size = section->getSize();
+ unsigned int addr = section->getAddr();
+ // This may be biased, but should work in most cases
+ if ((section->getFlags() & SHF_ALLOC) == 0)
+ return false;
+ // Special case for PT_DYNAMIC. Eventually, this should
+ // be better handled than special cases
+ if ((p_type == PT_DYNAMIC) && (section->getType() != SHT_DYNAMIC))
+ return false;
+ // Special case for PT_TLS.
+ if ((p_type == PT_TLS) && !(section->getFlags() & SHF_TLS))
+ return false;
+ return (addr >= p_vaddr) &&
+ (addr + size <= p_vaddr + p_memsz);
+
+ }
+};
+
+typedef serializable<Elf_Dyn_Traits> Elf_Dyn;
+
+struct Elf_DynValue {
+ unsigned int tag;
+ ElfValue *value;
+};
+
+class ElfDynamic_Section: public ElfSection {
+public:
+ ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent);
+ ~ElfDynamic_Section();
+
+ void serialize(std::ofstream &file, char ei_class, char ei_data);
+
+ ElfValue *getValueForType(unsigned int tag);
+ ElfSection *getSectionForType(unsigned int tag);
+ bool setValueForType(unsigned int tag, ElfValue *val);
+private:
+ std::vector<Elf_DynValue> dyns;
+};
+
+typedef serializable<Elf_Sym_Traits> Elf_Sym;
+
+struct Elf_SymValue {
+ const char *name;
+ unsigned char info;
+ unsigned char other;
+ ElfLocation value;
+ unsigned int size;
+ bool defined;
+};
+
+#define STT(type) (1 << STT_ ##type)
+
+class ElfSymtab_Section: public ElfSection {
+public:
+ ElfSymtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent);
+
+ void serialize(std::ofstream &file, char ei_class, char ei_data);
+
+ Elf_SymValue *lookup(const char *name, unsigned int type_filter = STT(OBJECT) | STT(FUNC));
+
+//private: // Until we have a real API
+ std::vector<Elf_SymValue> syms;
+};
+
+class Elf_Rel: public serializable<Elf_Rel_Traits> {
+public:
+ Elf_Rel(std::ifstream &file, char ei_class, char ei_data)
+ : serializable<Elf_Rel_Traits>(file, ei_class, ei_data) {};
+
+ static const unsigned int sh_type = SHT_REL;
+ static const unsigned int d_tag = DT_REL;
+ static const unsigned int d_tag_count = DT_RELCOUNT;
+};
+
+class Elf_Rela: public serializable<Elf_Rela_Traits> {
+public:
+ Elf_Rela(std::ifstream &file, char ei_class, char ei_data)
+ : serializable<Elf_Rela_Traits>(file, ei_class, ei_data) {};
+
+ static const unsigned int sh_type = SHT_RELA;
+ static const unsigned int d_tag = DT_RELA;
+ static const unsigned int d_tag_count = DT_RELACOUNT;
+};
+
+template <class Rel>
+class ElfRel_Section: public ElfSection {
+public:
+ ElfRel_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
+ : ElfSection(s, file, parent)
+ {
+ int pos = file->tellg();
+ file->seekg(shdr.sh_offset);
+ for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) {
+ Rel r(*file, parent->getClass(), parent->getData());
+ rels.push_back(r);
+ }
+ file->seekg(pos);
+ }
+
+ void serialize(std::ofstream &file, char ei_class, char ei_data)
+ {
+ for (typename std::vector<Rel>::iterator i = rels.begin();
+ i != rels.end(); ++i)
+ (*i).serialize(file, ei_class, ei_data);
+ }
+//private: // Until we have a real API
+ std::vector<Rel> rels;
+};
+
+class ElfStrtab_Section: public ElfSection {
+public:
+ ElfStrtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
+ : ElfSection(s, file, parent)
+ {
+ table.push_back(table_storage(data, shdr.sh_size));
+ }
+
+ ~ElfStrtab_Section()
+ {
+ for (std::vector<table_storage>::iterator t = table.begin() + 1;
+ t != table.end(); t++)
+ delete[] t->buf;
+ }
+
+ const char *getStr(unsigned int index);
+
+ const char *getStr(const char *string);
+
+ unsigned int getStrIndex(const char *string);
+
+ void serialize(std::ofstream &file, char ei_class, char ei_data);
+private:
+ struct table_storage {
+ unsigned int size, used;
+ char *buf;
+
+ table_storage(): size(4096), used(0), buf(new char[4096]) {}
+ table_storage(const char *data, unsigned int sz)
+ : size(sz), used(sz), buf(const_cast<char *>(data)) {}
+ };
+ std::vector<table_storage> table;
+};
+
+inline char Elf::getClass() {
+ return ehdr->e_ident[EI_CLASS];
+}
+
+inline char Elf::getData() {
+ return ehdr->e_ident[EI_DATA];
+}
+
+inline char Elf::getType() {
+ return ehdr->e_type;
+}
+
+inline char Elf::getMachine() {
+ return ehdr->e_machine;
+}
+
+inline unsigned int Elf::getSize() {
+ ElfSection *section;
+ for (section = shdr_section /* It's usually not far from the end */;
+ section->getNext() != nullptr; section = section->getNext());
+ return section->getOffset() + section->getSize();
+}
+
+inline ElfSegment *ElfSection::getSegmentByType(unsigned int type) {
+ for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
+ if ((*seg)->getType() == type)
+ return *seg;
+ return nullptr;
+}
+
+inline void ElfSection::insertInSegments(std::vector<ElfSegment *> &segs) {
+ for (std::vector<ElfSegment *>::iterator it = segs.begin(); it != segs.end(); ++it) {
+ (*it)->addSection(this);
+ }
+}
+
+inline ElfLocation::ElfLocation(ElfSection *section, unsigned int off, enum position pos)
+: section(section) {
+ if ((pos == ABSOLUTE) && section)
+ offset = off - section->getAddr();
+ else
+ offset = off;
+}
+
+inline ElfLocation::ElfLocation(unsigned int location, Elf *elf) {
+ section = elf->getSectionAt(location);
+ offset = location - (section ? section->getAddr() : 0);
+}
+
+inline unsigned int ElfLocation::getValue() {
+ return (section ? section->getAddr() : 0) + offset;
+}
+
+inline const char *ElfLocation::getBuffer() {
+ return section ? section->getData() + offset : nullptr;
+}
+
+inline unsigned int ElfSize::getValue() {
+ return section->getSize();
+}
+
+inline unsigned int ElfEntSize::getValue() {
+ return section->getEntSize();
+}
diff --git a/build/unix/elfhack/inject.c b/build/unix/elfhack/inject.c
new file mode 100644
index 000000000..942426330
--- /dev/null
+++ b/build/unix/elfhack/inject.c
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h>
+#include <elf.h>
+
+/* The Android NDK headers define those */
+#undef Elf_Ehdr
+#undef Elf_Addr
+
+#if defined(__LP64__)
+#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Addr Elf64_Addr
+#else
+#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Addr Elf32_Addr
+#endif
+
+extern __attribute__((visibility("hidden"))) void original_init(int argc, char **argv, char **env);
+
+extern __attribute__((visibility("hidden"))) Elf32_Rel relhack[];
+extern __attribute__((visibility("hidden"))) Elf_Ehdr elf_header;
+
+static inline __attribute__((always_inline))
+void do_relocations(void)
+{
+ Elf32_Rel *rel;
+ Elf_Addr *ptr, *start;
+ for (rel = relhack; rel->r_offset; rel++) {
+ start = (Elf_Addr *)((intptr_t)&elf_header + rel->r_offset);
+ for (ptr = start; ptr < &start[rel->r_info]; ptr++)
+ *ptr += (intptr_t)&elf_header;
+ }
+}
+
+__attribute__((section(".text._init_noinit")))
+int init_noinit(int argc, char **argv, char **env)
+{
+ do_relocations();
+ return 0;
+}
+
+__attribute__((section(".text._init")))
+int init(int argc, char **argv, char **env)
+{
+ do_relocations();
+ original_init(argc, argv, env);
+ // Ensure there is no tail-call optimization, avoiding the use of the
+ // B.W instruction in Thumb for the call above.
+ return 0;
+}
diff --git a/build/unix/elfhack/inject/Makefile.in b/build/unix/elfhack/inject/Makefile.in
new file mode 100644
index 000000000..ef27b077b
--- /dev/null
+++ b/build/unix/elfhack/inject/Makefile.in
@@ -0,0 +1,15 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include $(topsrcdir)/config/rules.mk
+
+export:: $(CSRCS:.c=.$(OBJ_SUFFIX))
+
+$(CSRCS): %.c: ../inject.c
+ cp $< $@
+
+GARBAGE += $(CSRCS)
+
+CFLAGS := -O2 -fno-stack-protector $(filter -m% -I%,$(CFLAGS))
diff --git a/build/unix/elfhack/inject/moz.build b/build/unix/elfhack/inject/moz.build
new file mode 100644
index 000000000..44d2ea76c
--- /dev/null
+++ b/build/unix/elfhack/inject/moz.build
@@ -0,0 +1,26 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# dummy library name to avoid skipping building the source here, which
+# we only need the object for.
+Library('elfhack_inject')
+
+DIST_INSTALL = False
+
+if CONFIG['TARGET_CPU'].endswith('86'):
+ cpu = 'x86'
+elif CONFIG['TARGET_CPU'].startswith('arm'):
+ cpu = 'arm'
+else:
+ cpu = CONFIG['TARGET_CPU']
+
+SOURCES += [
+ "!%s.c" % cpu,
+]
+
+NO_PGO = True
+
+NO_VISIBILITY_FLAGS = True
diff --git a/build/unix/elfhack/moz.build b/build/unix/elfhack/moz.build
new file mode 100644
index 000000000..abc4c2e01
--- /dev/null
+++ b/build/unix/elfhack/moz.build
@@ -0,0 +1,28 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIST_INSTALL = False
+DIRS += ['inject']
+
+if not CONFIG['CROSS_COMPILE']:
+ SOURCES += [
+ 'dummy.c',
+ 'test-array.c',
+ 'test-ctors.c',
+ ]
+
+ if '-flto' in CONFIG['OS_CFLAGS']:
+ SOURCES['test-array.c'].flags += ['-fno-lto']
+ SOURCES['test-ctors.c'].flags += ['-fno-lto']
+
+HOST_SOURCES += [
+ 'elf.cpp',
+ 'elfhack.cpp',
+]
+
+HostProgram('elfhack')
+
+NO_PGO = True
diff --git a/build/unix/elfhack/test-array.c b/build/unix/elfhack/test-array.c
new file mode 100644
index 000000000..202c26666
--- /dev/null
+++ b/build/unix/elfhack/test-array.c
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "test.c"
+
+__attribute__((section(".init_array"), used))
+static void (*init_array[])() = { end_test, test };
diff --git a/build/unix/elfhack/test-ctors.c b/build/unix/elfhack/test-ctors.c
new file mode 100644
index 000000000..4fff05807
--- /dev/null
+++ b/build/unix/elfhack/test-ctors.c
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "test.c"
+
+/* Recent binutils would put .ctors content into a .init_array section */
+__attribute__((section(".manual_ctors"), used))
+static void (*ctors[])() = { (void (*)())-1, end_test, test, NULL };
+
+__attribute__((section(".init")))
+void _init() {
+ void (**func)() = &ctors[sizeof(ctors) / sizeof(void (*)()) - 1];
+ while (*(--func) != (void (*)())-1) {
+ (*func)();
+ }
+}
diff --git a/build/unix/elfhack/test.c b/build/unix/elfhack/test.c
new file mode 100644
index 000000000..3393f03b5
--- /dev/null
+++ b/build/unix/elfhack/test.c
@@ -0,0 +1,162 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef DEF
+DEF(This)
+DEF(is)
+DEF(a)
+DEF(test)
+DEF(of)
+DEF(string)
+DEF(array)
+DEF(for)
+DEF(use)
+DEF(with)
+DEF(elfhack)
+DEF(to)
+DEF(see)
+DEF(whether)
+DEF(it)
+DEF(breaks)
+DEF(anything)
+DEF(but)
+DEF(one)
+DEF(needs)
+DEF(quite)
+DEF(some)
+DEF(strings)
+DEF(before)
+DEF(the)
+DEF(program)
+DEF(can)
+DEF(do)
+DEF(its)
+DEF(work)
+DEF(efficiently)
+DEF(Without)
+DEF(enough)
+DEF(data)
+DEF(relocation)
+DEF(sections)
+DEF(are)
+DEF(not)
+DEF(sufficiently)
+DEF(large)
+DEF(and)
+DEF(injected)
+DEF(code)
+DEF(wouldnt)
+DEF(fit)
+DEF(Said)
+DEF(otherwise)
+DEF(we)
+DEF(need)
+DEF(more)
+DEF(words)
+DEF(than)
+DEF(up)
+DEF(here)
+DEF(so)
+DEF(that)
+DEF(relocations)
+DEF(take)
+DEF(significant)
+DEF(bytes)
+DEF(amounts)
+DEF(which)
+DEF(isnt)
+DEF(exactly)
+DEF(easily)
+DEF(achieved)
+DEF(like)
+DEF(this)
+DEF(Actually)
+DEF(I)
+DEF(must)
+DEF(cheat)
+DEF(by)
+DEF(including)
+DEF(these)
+DEF(phrases)
+DEF(several)
+DEF(times)
+
+#else
+#pragma GCC visibility push(default)
+#include <stdlib.h>
+#include <stdio.h>
+
+#define DEF(w) static const char str_ ## w[] = #w;
+#include "test.c"
+#undef DEF
+
+const char *strings[] = {
+#define DEF(w) str_ ## w,
+#include "test.c"
+#include "test.c"
+#include "test.c"
+};
+
+/* Create a hole between two zones of relative relocations */
+const int hole[] = {
+ 42, 42, 42, 42
+};
+
+const char *strings2[] = {
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#undef DEF
+};
+
+static int ret = 1;
+
+int print_status() {
+ fprintf(stderr, "%s\n", ret ? "FAIL" : "PASS");
+ return ret;
+}
+
+/* On ARM, this creates a .tbss section before .init_array, which
+ * elfhack could then pick instead of .init_array.
+ * Also, when .tbss is big enough, elfhack may wrongfully consider
+ * following sections as part of the PT_TLS segment.
+ * Finally, gold makes TLS segments end on an aligned virtual address,
+ * even when the underlying section ends before that, and elfhack
+ * sanity checks may yield an error. */
+__thread int foo;
+__thread long long int bar[512];
+
+void end_test() {
+ static int count = 0;
+ /* Only exit when both constructors have been called */
+ if (++count == 2)
+ ret = 0;
+}
+
+void test() {
+ int i = 0, j = 0;
+#define DEF_(a,i,w) \
+ if (a[i++] != str_ ## w) return;
+#define DEF(w) DEF_(strings,i,w)
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#undef DEF
+#define DEF(w) DEF_(strings2,j,w)
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#include "test.c"
+#undef DEF
+ if (i != sizeof(strings)/sizeof(strings[0]) &&
+ j != sizeof(strings2)/sizeof(strings2[0]))
+ fprintf(stderr, "WARNING: Test doesn't cover the whole array\n");
+ end_test();
+}
+
+#pragma GCC visibility pop
+#endif
diff --git a/build/unix/gnu-ld-scripts/components-export-list b/build/unix/gnu-ld-scripts/components-export-list
new file mode 100644
index 000000000..8c2470886
--- /dev/null
+++ b/build/unix/gnu-ld-scripts/components-export-list
@@ -0,0 +1 @@
+_NSModule
diff --git a/build/unix/gnu-ld-scripts/components-version-script b/build/unix/gnu-ld-scripts/components-version-script
new file mode 100644
index 000000000..bc1d5ec33
--- /dev/null
+++ b/build/unix/gnu-ld-scripts/components-version-script
@@ -0,0 +1,7 @@
+EXPORTED {
+ global:
+ NSModule;
+ NSGetModule;
+ __RLD_MAP;
+ local: *;
+};
diff --git a/build/unix/moz.build b/build/unix/moz.build
new file mode 100644
index 000000000..99d0280b7
--- /dev/null
+++ b/build/unix/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION'] or CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
+ DIRS += ['stdc++compat']
+
+if CONFIG['USE_ELF_HACK']:
+ DIRS += ['elfhack']
+
+if CONFIG['LLVM_SYMBOLIZER']:
+ FINAL_TARGET_FILES += ['/' + CONFIG['LLVM_SYMBOLIZER']]
+
+SDK_FILES.bin += [
+ 'run-mozilla.sh',
+]
+
+FINAL_TARGET_FILES += [
+ 'run-mozilla.sh',
+]
diff --git a/build/unix/mozconfig.asan b/build/unix/mozconfig.asan
new file mode 100644
index 000000000..84113a417
--- /dev/null
+++ b/build/unix/mozconfig.asan
@@ -0,0 +1,27 @@
+MOZ_AUTOMATION_L10N_CHECK=0
+
+. "$topsrcdir/build/mozconfig.common"
+
+# Use Clang as specified in manifest
+export CC="$topsrcdir/clang/bin/clang -fgnu89-inline"
+export CXX="$topsrcdir/clang/bin/clang++"
+export LLVM_SYMBOLIZER="$topsrcdir/clang/bin/llvm-symbolizer"
+
+# Use a newer binutils, from the tooltool gcc package, if it's there
+if [ -e "$topsrcdir/gcc/bin/ld" ]; then
+ export CC="$CC -B $topsrcdir/gcc/bin"
+ export CXX="$CXX -B $topsrcdir/gcc/bin"
+fi
+
+# Enable ASan specific code and build workarounds
+ac_add_options --enable-address-sanitizer
+
+# Mandatory options required for ASan builds (both on Linux and Mac)
+export MOZ_DEBUG_SYMBOLS=1
+ac_add_options --enable-debug-symbols
+ac_add_options --disable-install-strip
+ac_add_options --disable-jemalloc
+ac_add_options --disable-crashreporter
+ac_add_options --disable-elf-hack
+
+. "$topsrcdir/build/unix/mozconfig.stdcxx"
diff --git a/build/unix/mozconfig.gtk b/build/unix/mozconfig.gtk
new file mode 100644
index 000000000..6f535f8a7
--- /dev/null
+++ b/build/unix/mozconfig.gtk
@@ -0,0 +1,28 @@
+# To do try builds with Gtk+2, uncomment the following line, and remove
+# everything after that.
+#ac_add_options --enable-default-toolkit=cairo-gtk2
+
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+# $TOOLTOOL_DIR/gtk3 comes from tooltool, and must be included in the tooltool manifest.
+if [ -z "$PKG_CONFIG_LIBDIR" ]; then
+ echo PKG_CONFIG_LIBDIR must be set >&2
+ exit 1
+fi
+export PKG_CONFIG_SYSROOT_DIR="$TOOLTOOL_DIR/gtk3"
+export PKG_CONFIG_PATH="$TOOLTOOL_DIR/gtk3/usr/local/lib/pkgconfig"
+PKG_CONFIG="$TOOLTOOL_DIR/gtk3/usr/local/bin/pkg-config"
+export PATH="$TOOLTOOL_DIR/gtk3/usr/local/bin:${PATH}"
+# Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages.
+LDFLAGS="-L$TOOLTOOL_DIR/gtk3/usr/local/lib ${LDFLAGS}"
+ac_add_options --enable-default-toolkit=cairo-gtk3
+
+# Set things up to use Gtk+3 from the tooltool package
+mk_add_options "export FONTCONFIG_PATH=$TOOLTOOL_DIR/gtk3/usr/local/etc/fonts"
+mk_add_options "export PANGO_SYSCONFDIR=$TOOLTOOL_DIR/gtk3/usr/local/etc"
+mk_add_options "export PANGO_LIBDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib"
+mk_add_options "export GDK_PIXBUF_MODULE_FILE=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"
+mk_add_options "export GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders"
+
+LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOOLTOOL_DIR/gtk3/usr/local/lib
+mk_add_options "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
diff --git a/build/unix/mozconfig.linux b/build/unix/mozconfig.linux
new file mode 100644
index 000000000..f63f406e6
--- /dev/null
+++ b/build/unix/mozconfig.linux
@@ -0,0 +1,38 @@
+if [ "x$IS_NIGHTLY" = "xyes" ]; then
+ # Some nightlies (eg: Mulet) don't want these set.
+ MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
+ MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
+ MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
+fi
+
+. "$topsrcdir/build/mozconfig.common"
+
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+# some b2g desktop builds still happen on i686 machines, and the tooltool
+# toolchain is x86_64 only.
+# We also deal with valgrind builds here, they don't use tooltool manifests at
+# all yet.
+if [ -z "$no_tooltool" ]
+then
+ CC="$TOOLTOOL_DIR/gcc/bin/gcc"
+ CXX="$TOOLTOOL_DIR/gcc/bin/g++"
+
+ # We want to make sure we use binutils and other binaries in the tooltool
+ # package.
+ mk_add_options PATH="$TOOLTOOL_DIR/gcc/bin:$PATH"
+else
+ CC="/tools/gcc-4.7.3-0moz1/bin/gcc"
+ CXX="/tools/gcc-4.7.3-0moz1/bin/g++"
+fi
+
+ac_add_options --enable-elf-hack
+
+. "$topsrcdir/build/unix/mozconfig.stdcxx"
+
+# PKG_CONFIG_LIBDIR is appropriately overridden in mozconfig.linux32
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+
+export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
+
+. "$topsrcdir/build/unix/mozconfig.gtk"
diff --git a/build/unix/mozconfig.linux32 b/build/unix/mozconfig.linux32
new file mode 100644
index 000000000..309767751
--- /dev/null
+++ b/build/unix/mozconfig.linux32
@@ -0,0 +1,12 @@
+. "$topsrcdir/build/unix/mozconfig.linux"
+
+export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
+
+if test `uname -m` = "x86_64"; then
+ # -march=pentiumpro is what our 32-bit native toolchain defaults to
+ CC="$CC -m32 -march=pentiumpro"
+ CXX="$CXX -m32 -march=pentiumpro"
+ ac_add_options --target=i686-pc-linux
+ ac_add_options --host=i686-pc-linux
+ ac_add_options --x-libraries=/usr/lib
+fi
diff --git a/build/unix/mozconfig.stdcxx b/build/unix/mozconfig.stdcxx
new file mode 100644
index 000000000..787e9b443
--- /dev/null
+++ b/build/unix/mozconfig.stdcxx
@@ -0,0 +1,15 @@
+# Avoid dependency on libstdc++ 4.7
+ac_add_options --enable-stdcxx-compat
+
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+if [ -f "$TOOLTOOL_DIR/clang/lib/libstdc++.so" ]; then
+ LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOOLTOOL_DIR/clang/lib
+elif [ -f "$TOOLTOOL_DIR/gcc/lib/libstdc++.so" ]; then
+ # We put both 32-bits and 64-bits library path in LD_LIBRARY_PATH: ld.so
+ # will prefer the files in the 32-bits path when loading 32-bits executables,
+ # and the files in the 64-bits path when loading 64-bits executables.
+ LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOOLTOOL_DIR/gcc/lib64:$TOOLTOOL_DIR/gcc/lib
+fi
+
+mk_add_options "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
diff --git a/build/unix/mozconfig.tsan b/build/unix/mozconfig.tsan
new file mode 100644
index 000000000..f78c1071f
--- /dev/null
+++ b/build/unix/mozconfig.tsan
@@ -0,0 +1,34 @@
+MOZ_AUTOMATION_L10N_CHECK=0
+
+. "$topsrcdir/build/mozconfig.common"
+
+# Use Clang as specified in manifest
+export CC="$topsrcdir/clang/bin/clang"
+export CXX="$topsrcdir/clang/bin/clang++"
+export LLVM_SYMBOLIZER="$topsrcdir/clang/bin/llvm-symbolizer"
+
+# Use a newer binutils, from the tooltool gcc package, if it's there
+if [ -e "$topsrcdir/gcc/bin/ld" ]; then
+ export CC="$CC -B $topsrcdir/gcc/bin"
+ export CXX="$CXX -B $topsrcdir/gcc/bin"
+fi
+
+# Enable TSan specific code and build workarounds
+ac_add_options --enable-thread-sanitizer
+
+# The ThreadSanitizer is not compatible with sandboxing
+# (see bug 1182565)
+ac_add_options --disable-sandbox
+
+# These are required by TSan
+ac_add_options --disable-jemalloc
+ac_add_options --disable-crashreporter
+ac_add_options --disable-elf-hack
+ac_add_options --enable-pie
+
+# Keep symbols to symbolize TSan traces
+ac_add_options --disable-install-strip
+# -gline-tables-only results in significantly smaller binaries.
+ac_add_options --enable-debug-symbols="-gline-tables-only"
+
+. "$topsrcdir/build/unix/mozconfig.stdcxx"
diff --git a/build/unix/mozilla.in b/build/unix/mozilla.in
new file mode 100644
index 000000000..d2251eae0
--- /dev/null
+++ b/build/unix/mozilla.in
@@ -0,0 +1,108 @@
+#!/bin/sh
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+##
+## Usage:
+##
+## $ mozilla [args]
+##
+## This script is meant to run the application binary from mozilla/dist/bin.
+##
+## The script will setup all the environment voodoo needed to make
+## the application binary to work.
+##
+
+#uncomment for debugging
+#set -x
+
+moz_libdir=%MOZAPPDIR%
+
+# Use run-mozilla.sh in the current dir if it exists
+# If not, then start resolving symlinks until we find run-mozilla.sh
+found=0
+progname="$0"
+curdir=`dirname "$progname"`
+progbase=`basename "$progname"`
+run_moz="$curdir/run-mozilla.sh"
+if test -x "$run_moz"; then
+ dist_bin="$curdir"
+ found=1
+else
+ here=`/bin/pwd`
+ while [ -h "$progname" ]; do
+ bn=`basename "$progname"`
+ cd `dirname "$progname"`
+ # Resolve symlink of dirname
+ cd `/bin/pwd`
+ progname=`/bin/ls -l "$bn" | sed -e 's/^.* -> //' `
+ progbase=`basename "$progname"`
+ if [ ! -x "$progname" ]; then
+ break
+ fi
+ curdir=`dirname "$progname"`
+ run_moz="$curdir/run-mozilla.sh"
+ if [ -x "$run_moz" ]; then
+ cd "$curdir"
+ dist_bin=`/bin/pwd`
+ run_moz="$dist_bin/run-mozilla.sh"
+ found=1
+ break
+ fi
+ done
+ cd "$here"
+fi
+if [ $found = 0 ]; then
+ # Check default compile-time libdir
+ if [ -x "$moz_libdir/run-mozilla.sh" ]; then
+ dist_bin="$moz_libdir"
+ run_moz="$moz_libdir/run-mozilla.sh"
+ else
+ echo "Cannot find %MOZ_APP_DISPLAYNAME% runtime directory. Exiting."
+ exit 1
+ fi
+fi
+
+script_args=""
+debugging=0
+MOZILLA_BIN="${progbase}-bin"
+
+if [ "$OSTYPE" = "beos" ]; then
+ mimeset -F "$MOZILLA_BIN"
+fi
+
+pass_arg_count=0
+while [ $# -gt $pass_arg_count ]
+do
+ case "$1" in
+ -p | --pure | -pure)
+ MOZILLA_BIN="${MOZILLA_BIN}.pure"
+ shift
+ ;;
+ -g | --debug)
+ script_args="$script_args -g"
+ debugging=1
+ shift
+ ;;
+ -d | --debugger)
+ script_args="$script_args -d $2"
+ shift 2
+ ;;
+ *)
+ # Move the unrecognized argument to the end of the list.
+ arg="$1"
+ shift
+ set -- "$@" "$arg"
+ pass_arg_count=`expr $pass_arg_count + 1`
+ ;;
+ esac
+done
+
+if [ $debugging = 1 ]
+then
+ echo $dist_bin/run-mozilla.sh $script_args $dist_bin/$MOZILLA_BIN "$@"
+fi
+exec "$dist_bin/run-mozilla.sh" $script_args "$dist_bin/$MOZILLA_BIN" "$@"
+# EOF.
diff --git a/build/unix/print-failed-commands.sh b/build/unix/print-failed-commands.sh
new file mode 100755
index 000000000..7f6b73d33
--- /dev/null
+++ b/build/unix/print-failed-commands.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#
+# Usage from makefile:
+# ELOG = . $(topdir)/build/autoconf/print-failed-commands.sh
+# $(ELOG) $(CC) $CFLAGS -o $@ $<
+#
+# This shell script is used by the build system to print out commands that fail
+# to execute properly. It is designed to make the "make -s" command more
+# useful.
+#
+# Note that in the example we are sourcing rather than execing the script.
+# Since make already started a shell for us, we might as well use it rather
+# than starting a new one.
+
+( exec "$@" ) || {
+ echo
+ echo "In the directory " `pwd`
+ echo "The following command failed to execute properly:"
+ echo "$@"
+ exit 1;
+}
diff --git a/build/unix/print-non-newline.sh b/build/unix/print-non-newline.sh
new file mode 100755
index 000000000..5e0cf292d
--- /dev/null
+++ b/build/unix/print-non-newline.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#
+# The purpose of this file is to find the files that do not end with a
+# newline. Some compilers fail if the source files do not end with a
+# newline.
+#
+
+#
+test_file=newline_test
+test_dummy=newline_testee
+inlist="$*"
+broken_list=
+
+if test "$inlist" = ""; then
+ echo "Usage: $0 *.c *.cpp";
+ exit 0;
+fi
+
+echo "" > $test_file
+
+for f in $inlist; do
+ if test -f $f; then
+ tail -c 1 $f > $test_dummy
+ if ! `cmp -s $test_file $test_dummy`; then
+ broken_list="$broken_list $f"
+ fi
+ fi
+done
+
+rm -f $test_file $test_dummy
+echo $broken_list
diff --git a/build/unix/rewrite_asan_dylib.py b/build/unix/rewrite_asan_dylib.py
new file mode 100644
index 000000000..6e30374b7
--- /dev/null
+++ b/build/unix/rewrite_asan_dylib.py
@@ -0,0 +1,60 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys
+import os
+import subprocess
+import shutil
+from buildconfig import substs
+
+'''
+Scans the given directories for binaries referencing the AddressSanitizer
+runtime library, copies it to the main directory and rewrites binaries to not
+reference it with absolute paths but with @executable_path instead.
+'''
+
+# This is the dylib we're looking for
+DYLIB_NAME='libclang_rt.asan_osx_dynamic.dylib'
+
+def scan_directory(path):
+ dylibCopied = False
+
+ for root, subdirs, files in os.walk(path):
+ for filename in files:
+ filename = os.path.join(root, filename)
+
+ # Skip all files that aren't either dylibs or executable
+ if not (filename.endswith('.dylib') or os.access(filename, os.X_OK)):
+ continue
+
+ try:
+ otoolOut = subprocess.check_output([substs['OTOOL'], '-L', filename])
+ except:
+ # Errors are expected on non-mach executables, ignore them and continue
+ continue
+
+ for line in otoolOut.splitlines():
+ if line.find(DYLIB_NAME) != -1:
+ absDylibPath = line.split()[0]
+
+ # Don't try to rewrite binaries twice
+ if absDylibPath.find('@executable_path/') == 0:
+ continue
+
+ if not dylibCopied:
+ # Copy the runtime once to the main directory, which is passed
+ # as the argument to this function.
+ shutil.copy(absDylibPath, path)
+
+ # Now rewrite the library itself
+ subprocess.check_call(['install_name_tool', '-id', '@executable_path/' + DYLIB_NAME, os.path.join(path, DYLIB_NAME)])
+ dylibCopied = True
+
+ # Now use install_name_tool to rewrite the path in our binary
+ subprocess.check_call(['install_name_tool', '-change', absDylibPath, '@executable_path/' + DYLIB_NAME, filename])
+ break
+
+if __name__ == '__main__':
+ for d in sys.argv[1:]:
+ scan_directory(d)
diff --git a/build/unix/run-gprof.sh b/build/unix/run-gprof.sh
new file mode 100644
index 000000000..794d146b9
--- /dev/null
+++ b/build/unix/run-gprof.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+LD_LIBRARY_PATH=.
+export LD_LIBRARY_PATH
+
+PROG=mozilla-bin
+PLIBS=""
+
+for l in *.so components/*.so; do
+ PLIBS="$PLIBS -incobj $l"
+done
+
+$ECHO /bin/gprof -L. -Lcomponents -all $PLIBS $PROG $PROG.hiout
diff --git a/build/unix/run-hiprof.sh b/build/unix/run-hiprof.sh
new file mode 100644
index 000000000..cb4ec851f
--- /dev/null
+++ b/build/unix/run-hiprof.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+LD_LIBRARY_PATH=.
+export LD_LIBRARY_PATH
+
+PROG=mozilla-bin
+PLIBS="-L."
+
+TOOL=hiprof
+
+for l in ./*.so components/*.so; do
+ PLIBS="$PLIBS -incobj $l"
+done
+
+$ECHO atom $PROG -tool $TOOL -env threads -toolargs="-calltime -systime" -all $PLIBS
+
+cd components && (
+ for f in lib*.so; do
+ mv ../$f.$PROG.$TOOL.threads .
+ done
+)
diff --git a/build/unix/run-mozilla.sh b/build/unix/run-mozilla.sh
new file mode 100755
index 000000000..c053e60b1
--- /dev/null
+++ b/build/unix/run-mozilla.sh
@@ -0,0 +1,362 @@
+#!/bin/sh
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+cmdname=`basename "$0"`
+MOZ_DIST_BIN=`dirname "$0"`
+MOZ_DEFAULT_NAME="./${cmdname}-bin"
+MOZ_APPRUNNER_NAME="./mozilla-bin"
+MOZ_PROGRAM=""
+
+exitcode=1
+#
+##
+## Functions
+##
+##########################################################################
+moz_usage()
+{
+echo "Usage: ${cmdname} [options] [program]"
+echo ""
+echo " options:"
+echo ""
+echo " -g Run in debugger."
+echo " --debug"
+echo ""
+echo " -d debugger Debugger to use."
+echo " --debugger debugger"
+echo ""
+echo " -a debugger_args Arguments passed to [debugger]."
+echo " --debugger-args debugger_args"
+echo ""
+echo " Examples:"
+echo ""
+echo " Run the mozilla-bin binary"
+echo ""
+echo " ${cmdname} mozilla-bin"
+echo ""
+echo " Debug the mozilla-bin binary in gdb"
+echo ""
+echo " ${cmdname} -g mozilla-bin -d gdb"
+echo ""
+echo " Run mozilla-bin under valgrind with arguments"
+echo ""
+echo " ${cmdname} -g -d valgrind -a '--tool=memcheck --leak-check=full' mozilla-bin"
+echo ""
+ return 0
+}
+##########################################################################
+moz_bail()
+{
+ message=$1
+ echo
+ echo "$cmdname: $message"
+ echo
+ exit 1
+}
+##########################################################################
+moz_test_binary()
+{
+ binary=$1
+ if [ -f "$binary" ]
+ then
+ if [ -x "$binary" ]
+ then
+ return 1
+ fi
+ fi
+ return 0
+}
+##########################################################################
+moz_get_debugger()
+{
+ debuggers="ddd gdb dbx bdb native-gdb"
+ debugger="notfound"
+ done="no"
+ for d in $debuggers
+ do
+ moz_test_binary /bin/which
+ if [ $? -eq 1 ]
+ then
+ dpath=`which ${d}`
+ else
+ dpath=`LC_MESSAGES=C type ${d} | awk '{print $3;}' | sed -e 's/\.$//'`
+ fi
+ if [ -x "$dpath" ]
+ then
+ debugger=$dpath
+ break
+ fi
+ done
+ echo $debugger
+ return 0
+}
+##########################################################################
+moz_run_program()
+{
+ prog=$MOZ_PROGRAM
+ ##
+ ## Make sure the program is executable
+ ##
+ if [ ! -x "$prog" ]
+ then
+ moz_bail "Cannot execute $prog."
+ fi
+ ##
+ ## Run the program
+ ##
+ exec "$prog" ${1+"$@"}
+ exitcode=$?
+}
+##########################################################################
+moz_debug_program()
+{
+ prog=$MOZ_PROGRAM
+ ##
+ ## Make sure the program is executable
+ ##
+ if [ ! -x "$prog" ]
+ then
+ moz_bail "Cannot execute $prog."
+ fi
+ if [ -n "$moz_debugger" ]
+ then
+ moz_test_binary /bin/which
+ if [ $? -eq 1 ]
+ then
+ debugger=`which $moz_debugger`
+ else
+ debugger=`LC_MESSAGES=C type $moz_debugger | awk '{print $3;}' | sed -e 's/\.$//'`
+ fi
+ else
+ debugger=`moz_get_debugger`
+ fi
+ if [ -x "$debugger" ]
+ then
+# If you are not using ddd, gdb and know of a way to convey the arguments
+# over to the prog then add that here- Gagan Saksena 03/15/00
+ case `basename $debugger` in
+ native-gdb) echo "$debugger $moz_debugger_args --args $prog" ${1+"$@"}
+ exec "$debugger" $moz_debugger_args --args "$prog" ${1+"$@"}
+ exitcode=$?
+ ;;
+ gdb) echo "$debugger $moz_debugger_args --args $prog" ${1+"$@"}
+ exec "$debugger" $moz_debugger_args --args "$prog" ${1+"$@"}
+ exitcode=$?
+ ;;
+ ddd) echo "$debugger $moz_debugger_args --gdb -- --args $prog" ${1+"$@"}
+ exec "$debugger" $moz_debugger_args --gdb -- --args "$prog" ${1+"$@"}
+ exitcode=$?
+ ;;
+ *) echo "$debugger $moz_debugger_args $prog ${1+"$@"}"
+ exec $debugger $moz_debugger_args "$prog" ${1+"$@"}
+ exitcode=$?
+ ;;
+ esac
+ else
+ moz_bail "Could not find a debugger on your system."
+ fi
+}
+##########################################################################
+##
+## Command line arg defaults
+##
+moz_debug=0
+moz_debugger=""
+moz_debugger_args=""
+#
+##
+## Parse the command line
+##
+while [ $# -gt 0 ]
+do
+ case $1 in
+ -g | --debug)
+ moz_debug=1
+ shift
+ ;;
+ -d | --debugger)
+ moz_debugger=$2;
+ if [ "${moz_debugger}" != "" ]; then
+ shift 2
+ else
+ echo "-d requires an argument"
+ exit 1
+ fi
+ ;;
+ -a | --debugger-args)
+ moz_debugger_args=$2;
+ if [ "${moz_debugger_args}" != "" ]; then
+ shift 2
+ else
+ echo "-a requires an argument"
+ exit 1
+ fi
+ ;;
+ *)
+ break;
+ ;;
+ esac
+done
+#
+##
+## Program name given in $1
+##
+if [ $# -gt 0 ]
+then
+ MOZ_PROGRAM=$1
+ shift
+fi
+##
+## Program not given, try to guess a default
+##
+if [ -z "$MOZ_PROGRAM" ]
+then
+ ##
+ ## Try this script's name with '-bin' appended
+ ##
+ if [ -x "$MOZ_DEFAULT_NAME" ]
+ then
+ MOZ_PROGRAM=$MOZ_DEFAULT_NAME
+ ##
+ ## Try mozilla-bin
+ ##
+ elif [ -x "$MOZ_APPRUNNER_NAME" ]
+ then
+ MOZ_PROGRAM=$MOZ_APPRUNNER_NAME
+ fi
+fi
+#
+#
+##
+## Make sure the program is executable
+##
+if [ ! -x "$MOZ_PROGRAM" ]
+then
+ moz_bail "Cannot execute $MOZ_PROGRAM."
+fi
+#
+##
+## Set MOZILLA_FIVE_HOME
+##
+MOZILLA_FIVE_HOME=$MOZ_DIST_BIN
+
+if [ -z "$MRE_HOME" ]; then
+ MRE_HOME=$MOZILLA_FIVE_HOME
+fi
+##
+## Set LD_LIBRARY_PATH
+##
+## On Solaris we use $ORIGIN (set in RUNPATH) instead of LD_LIBRARY_PATH
+## to locate shared libraries.
+##
+## When a shared library is a symbolic link, $ORIGIN will be replaced with
+## the real path (i.e., what the symbolic link points to) by the runtime
+## linker. For example, if dist/bin/libxul.so is a symbolic link to
+## toolkit/library/libxul.so, $ORIGIN will be "toolkit/library" instead of "dist/bin".
+## So the runtime linker will use "toolkit/library" NOT "dist/bin" to locate the
+## other shared libraries that libxul.so depends on. This only happens
+## when a user (developer) tries to start firefox, thunderbird, or seamonkey
+## under dist/bin. To solve the problem, we should rely on LD_LIBRARY_PATH
+## to locate shared libraries.
+##
+## Note:
+## We test $MOZ_DIST_BIN/*.so. If any of them is a symbolic link,
+## we need to set LD_LIBRARY_PATH.
+##########################################################################
+moz_should_set_ld_library_path()
+{
+ [ `uname -s` != "SunOS" ] && return 0
+ for sharedlib in $MOZ_DIST_BIN/*.so
+ do
+ [ -h $sharedlib ] && return 0
+ done
+ return 1
+}
+if moz_should_set_ld_library_path
+then
+ LD_LIBRARY_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARY_PATH:+":$LD_LIBRARY_PATH"}
+fi
+
+if [ -n "$LD_LIBRARYN32_PATH" ]
+then
+ LD_LIBRARYN32_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARYN32_PATH:+":$LD_LIBRARYN32_PATH"}
+fi
+if [ -n "$LD_LIBRARYN64_PATH" ]
+then
+ LD_LIBRARYN64_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARYN64_PATH:+":$LD_LIBRARYN64_PATH"}
+fi
+if [ -n "$LD_LIBRARY_PATH_64" ]; then
+ LD_LIBRARY_PATH_64=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARY_PATH_64:+":$LD_LIBRARY_PATH_64"}
+fi
+#
+#
+## Set SHLIB_PATH for HPUX
+SHLIB_PATH=${MOZ_DIST_BIN}:${MRE_HOME}${SHLIB_PATH:+":$SHLIB_PATH"}
+#
+## Set LIBPATH for AIX
+LIBPATH=${MOZ_DIST_BIN}:${MRE_HOME}${LIBPATH:+":$LIBPATH"}
+#
+## Set DYLD_LIBRARY_PATH for Mac OS X (Darwin)
+DYLD_LIBRARY_PATH=${MOZ_DIST_BIN}:${MRE_HOME}${DYLD_LIBRARY_PATH:+":$DYLD_LIBRARY_PATH"}
+#
+## Solaris Xserver(Xsun) tuning - use shared memory transport if available
+if [ "$XSUNTRANSPORT" = "" ]
+then
+ XSUNTRANSPORT="shmem"
+ XSUNSMESIZE="512"
+ export XSUNTRANSPORT XSUNSMESIZE
+fi
+
+# Disable Gnome crash dialog
+GNOME_DISABLE_CRASH_DIALOG=1
+export GNOME_DISABLE_CRASH_DIALOG
+
+if [ "$moz_debug" -eq 1 ]
+then
+ echo "MOZILLA_FIVE_HOME=$MOZILLA_FIVE_HOME"
+ echo " LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
+ if [ -n "$LD_LIBRARYN32_PATH" ]
+ then
+ echo "LD_LIBRARYN32_PATH=$LD_LIBRARYN32_PATH"
+ fi
+ if [ -n "$LD_LIBRARYN64_PATH" ]
+ then
+ echo "LD_LIBRARYN64_PATH=$LD_LIBRARYN64_PATH"
+ fi
+ if [ -n "$LD_LIBRARY_PATH_64" ]; then
+ echo "LD_LIBRARY_PATH_64=$LD_LIBRARY_PATH_64"
+ fi
+ if [ -n "$DISPLAY" ]; then
+ echo "DISPLAY=$DISPLAY"
+ fi
+ if [ -n "$FONTCONFIG_PATH" ]; then
+ echo "FONTCONFIG_PATH=$FONTCONFIG_PATH"
+ fi
+ if [ -n "$MOZILLA_POSTSCRIPT_PRINTER_LIST" ]; then
+ echo "MOZILLA_POSTSCRIPT_PRINTER_LIST=$MOZILLA_POSTSCRIPT_PRINTER_LIST"
+ fi
+ echo "DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH"
+ echo " LIBRARY_PATH=$LIBRARY_PATH"
+ echo " SHLIB_PATH=$SHLIB_PATH"
+ echo " LIBPATH=$LIBPATH"
+ echo " ADDON_PATH=$ADDON_PATH"
+ echo " MOZ_PROGRAM=$MOZ_PROGRAM"
+ echo " MOZ_TOOLKIT=$MOZ_TOOLKIT"
+ echo " moz_debug=$moz_debug"
+ echo " moz_debugger=$moz_debugger"
+ echo "moz_debugger_args=$moz_debugger_args"
+fi
+#
+export MOZILLA_FIVE_HOME LD_LIBRARY_PATH
+export SHLIB_PATH LIBPATH LIBRARY_PATH ADDON_PATH DYLD_LIBRARY_PATH
+
+if [ $moz_debug -eq 1 ]
+then
+ moz_debug_program ${1+"$@"}
+else
+ moz_run_program ${1+"$@"}
+fi
+
+exit $exitcode
diff --git a/build/unix/run-third.sh b/build/unix/run-third.sh
new file mode 100644
index 000000000..cb2c66137
--- /dev/null
+++ b/build/unix/run-third.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+LD_LIBRARY_PATH=.
+export LD_LIBRARY_PATH
+
+PROG=mozilla-bin
+PLIBS="-L."
+
+TOOL=third
+
+for l in ./*.so components/*.so; do
+ PLIBS="$PLIBS -incobj $l"
+done
+
+$ECHO atom $PROG -tool $TOOL -env threads -g -all $PLIBS -toolargs="-leaks all -before NS_ShutdownXPCOM"
+
+cd components && (
+ for f in lib*.so; do
+ mv ../$f.$PROG.$TOOL.threads .
+ done
+)
diff --git a/build/unix/stdc++compat/Makefile.in b/build/unix/stdc++compat/Makefile.in
new file mode 100644
index 000000000..054c1c023
--- /dev/null
+++ b/build/unix/stdc++compat/Makefile.in
@@ -0,0 +1,7 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ENABLE_CLANG_PLUGIN :=
+
+include $(topsrcdir)/config/rules.mk
diff --git a/build/unix/stdc++compat/moz.build b/build/unix/stdc++compat/moz.build
new file mode 100644
index 000000000..01663f37d
--- /dev/null
+++ b/build/unix/stdc++compat/moz.build
@@ -0,0 +1,24 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION']:
+ Library('stdc++compat')
+ SOURCES += ['stdc++compat.cpp']
+
+if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
+ HostLibrary('host_stdc++compat')
+ HOST_SOURCES += [
+ 'stdc++compat.cpp',
+ ]
+
+FORCE_STATIC_LIB = True
+
+NO_PGO = True
+
+DISABLE_STL_WRAPPING = True
+
+DEFINES['MOZ_LIBSTDCXX_VERSION'] = CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION']
+HOST_DEFINES['MOZ_LIBSTDCXX_VERSION'] = CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']
diff --git a/build/unix/stdc++compat/stdc++compat.cpp b/build/unix/stdc++compat/stdc++compat.cpp
new file mode 100644
index 000000000..95a7a9afe
--- /dev/null
+++ b/build/unix/stdc++compat/stdc++compat.cpp
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <ostream>
+#include <istream>
+#include <string>
+#include <stdarg.h>
+#include <stdio.h>
+#include <mozilla/Assertions.h>
+
+/* GLIBCXX_3.4.8 is from gcc 4.1.1 (111691)
+ GLIBCXX_3.4.9 is from gcc 4.2.0 (111690)
+ GLIBCXX_3.4.10 is from gcc 4.3.0 (126287)
+ GLIBCXX_3.4.11 is from gcc 4.4.0 (133006)
+ GLIBCXX_3.4.12 is from gcc 4.4.1 (147138)
+ GLIBCXX_3.4.13 is from gcc 4.4.2 (151127)
+ GLIBCXX_3.4.14 is from gcc 4.5.0 (151126)
+ GLIBCXX_3.4.15 is from gcc 4.6.0 (160071)
+ GLIBCXX_3.4.16 is from gcc 4.6.1 (172240)
+ GLIBCXX_3.4.17 is from gcc 4.7.0 (174383)
+ GLIBCXX_3.4.18 is from gcc 4.8.0 (190787)
+ GLIBCXX_3.4.19 is from gcc 4.8.1 (199309)
+ GLIBCXX_3.4.20 is from gcc 4.9.0 (199307)
+ GLIBCXX_3.4.21 is from gcc 5.0 (210290)
+
+This file adds the necessary compatibility tricks to avoid symbols with
+version GLIBCXX_3.4.16 and bigger, keeping binary compatibility with
+libstdc++ 4.6.1.
+
+*/
+
+#define GLIBCXX_VERSION(a, b, c) (((a) << 16) | ((b) << 8) | (c))
+
+#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 20)
+namespace std {
+
+ /* We shouldn't be throwing exceptions at all, but it sadly turns out
+ we call STL (inline) functions that do. */
+ void __throw_out_of_range_fmt(char const* fmt, ...)
+ {
+ va_list ap;
+ char buf[1024]; // That should be big enough.
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ buf[sizeof(buf) - 1] = 0;
+ va_end(ap);
+
+ __throw_range_error(buf);
+ }
+
+}
+#endif
+
+#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 20)
+/* Technically, this symbol is not in GLIBCXX_3.4.20, but in CXXABI_1.3.8,
+ but that's equivalent, version-wise. Those calls are added by the compiler
+ itself on `new Class[n]` calls. */
+extern "C" void
+__cxa_throw_bad_array_new_length()
+{
+ MOZ_CRASH();
+}
+#endif
+
+#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 21)
+/* While we generally don't build with exceptions, we have some host tools
+ * that do use them. libstdc++ from GCC 5.0 added exception constructors with
+ * char const* argument. Older versions only have a constructor with
+ * std::string. */
+namespace std {
+ runtime_error::runtime_error(char const* s)
+ : runtime_error(std::string(s))
+ {
+ }
+}
+#endif