From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- build/unix/aix.exp | 5 + build/unix/build-binutils/build-binutils.sh | 32 + build/unix/build-gcc/PR64905.patch | 11 + build/unix/build-gcc/build-gcc.sh | 52 ++ build/unix/build-gtk3/build-gtk3.sh | 150 ++++ build/unix/elfhack/Makefile.in | 48 ++ build/unix/elfhack/README | 28 + build/unix/elfhack/dummy.c | 9 + build/unix/elfhack/elf.cpp | 921 +++++++++++++++++++++ build/unix/elfhack/elfhack.cpp | 823 ++++++++++++++++++ build/unix/elfhack/elfxx.h | 701 ++++++++++++++++ build/unix/elfhack/inject.c | 52 ++ build/unix/elfhack/inject/Makefile.in | 15 + build/unix/elfhack/inject/moz.build | 26 + build/unix/elfhack/moz.build | 28 + build/unix/elfhack/test-array.c | 8 + build/unix/elfhack/test-ctors.c | 17 + build/unix/elfhack/test.c | 162 ++++ build/unix/gnu-ld-scripts/components-export-list | 1 + .../unix/gnu-ld-scripts/components-version-script | 7 + build/unix/moz.build | 22 + build/unix/mozconfig.asan | 27 + build/unix/mozconfig.gtk | 28 + build/unix/mozconfig.linux | 38 + build/unix/mozconfig.linux32 | 12 + build/unix/mozconfig.stdcxx | 15 + build/unix/mozconfig.tsan | 34 + build/unix/mozilla.in | 108 +++ build/unix/print-failed-commands.sh | 25 + build/unix/print-non-newline.sh | 35 + build/unix/rewrite_asan_dylib.py | 60 ++ build/unix/run-gprof.sh | 17 + build/unix/run-hiprof.sh | 25 + build/unix/run-mozilla.sh | 362 ++++++++ build/unix/run-third.sh | 25 + build/unix/stdc++compat/Makefile.in | 7 + build/unix/stdc++compat/moz.build | 24 + build/unix/stdc++compat/stdc++compat.cpp | 78 ++ 38 files changed, 4038 insertions(+) create mode 100644 build/unix/aix.exp create mode 100755 build/unix/build-binutils/build-binutils.sh create mode 100644 build/unix/build-gcc/PR64905.patch create mode 100755 build/unix/build-gcc/build-gcc.sh create mode 100644 build/unix/build-gtk3/build-gtk3.sh create mode 100644 build/unix/elfhack/Makefile.in create mode 100644 build/unix/elfhack/README create mode 100644 build/unix/elfhack/dummy.c create mode 100644 build/unix/elfhack/elf.cpp create mode 100644 build/unix/elfhack/elfhack.cpp create mode 100644 build/unix/elfhack/elfxx.h create mode 100644 build/unix/elfhack/inject.c create mode 100644 build/unix/elfhack/inject/Makefile.in create mode 100644 build/unix/elfhack/inject/moz.build create mode 100644 build/unix/elfhack/moz.build create mode 100644 build/unix/elfhack/test-array.c create mode 100644 build/unix/elfhack/test-ctors.c create mode 100644 build/unix/elfhack/test.c create mode 100644 build/unix/gnu-ld-scripts/components-export-list create mode 100644 build/unix/gnu-ld-scripts/components-version-script create mode 100644 build/unix/moz.build create mode 100644 build/unix/mozconfig.asan create mode 100644 build/unix/mozconfig.gtk create mode 100644 build/unix/mozconfig.linux create mode 100644 build/unix/mozconfig.linux32 create mode 100644 build/unix/mozconfig.stdcxx create mode 100644 build/unix/mozconfig.tsan create mode 100644 build/unix/mozilla.in create mode 100755 build/unix/print-failed-commands.sh create mode 100755 build/unix/print-non-newline.sh create mode 100644 build/unix/rewrite_asan_dylib.py create mode 100644 build/unix/run-gprof.sh create mode 100644 build/unix/run-hiprof.sh create mode 100755 build/unix/run-mozilla.sh create mode 100644 build/unix/run-third.sh create mode 100644 build/unix/stdc++compat/Makefile.in create mode 100644 build/unix/stdc++compat/moz.build create mode 100644 build/unix/stdc++compat/stdc++compat.cpp (limited to 'build/unix') 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 + + + + /usr/share/X11/fonts + +EOF + +cat < $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 +#include +#include "elfxx.h" + +template +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 +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 +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 +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 +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 +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 +void Elf_Rel_Traits::swap(T &t, R &r) +{ + r.r_offset = endian::swap(t.r_offset); + _Rel_info::swap(t.r_info, r.r_info); +} + +template +void Elf_Rela_Traits::swap(T &t, R &r) +{ + r.r_offset = endian::swap(t.r_offset); + _Rel_info::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(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::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(*tmp_shdr[index], tmp_file, this); + break; + case SHT_RELA: + sections[index] = new ElfRel_Section(*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::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::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::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::iterator seg = segments.begin(); seg != segments.end(); seg++, i++) { + std::list::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::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::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::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::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::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::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::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::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::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::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 +#include +#include +#include +#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 +struct wrapped { + T value; +}; + +class Elf_Addr_Traits { +public: + typedef wrapped Type32; + typedef wrapped Type64; + + template + static inline void swap(T &t, R &r) { + r.value = endian::swap(t.value); + } +}; + +typedef serializable Elf_Addr; + +class Elf_RelHack_Traits { +public: + typedef Elf32_Rel Type32; + typedef Elf32_Rel Type64; + + template + 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; + +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::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 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::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::iterator c = code.begin(); c != code.end(); c++) + (*c)->getShdr().sh_addr += getAddr(); + + // Apply relocations + for (std::vector::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 *)rel, *c); + else + apply_relocations((ElfRel_Section *)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 *)s); + else + scan_relocs_for_code((ElfRel_Section *)s); + } + } + } + + template + void scan_relocs_for_code(ElfRel_Section *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