diff options
Diffstat (limited to 'build/unix')
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 = §ion->rels[init_array_reloc - 1]; + rel->r_info = ELF32_R_INFO(0, rel_type); // Set as a relative relocation + set_relative_reloc(§ion->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 |