summaryrefslogtreecommitdiffstats
path: root/build/unix
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /build/unix
parentInitial commit. (diff)
downloadfirefox-e51783d008170d9ab27d25da98ca3a38b0a41b67.tar.xz
firefox-e51783d008170d9ab27d25da98ca3a38b0a41b67.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'build/unix')
-rw-r--r--build/unix/aix.exp5
-rw-r--r--build/unix/build-binutils/3A24BC1E8FB409FA9F14371813FCEF89DD9E3C4F.key51
-rwxr-xr-xbuild/unix/build-binutils/build-binutils.sh106
-rw-r--r--build/unix/build-gcc/07F3DBBECC1A39605078094D980C197698C3739D.key53
-rw-r--r--build/unix/build-gcc/13975A70E63C361C73AE69EF6EEB81F8981C74C7.key82
-rw-r--r--build/unix/build-gcc/33C235A34C46AA3FFB293709A328C3A2C3C45C06.key33
-rw-r--r--build/unix/build-gcc/343C2FF0FBEE5EC2EDBEF399F3599FF828C67298.key35
-rw-r--r--build/unix/build-gcc/5ED46A6721D365587791E2AA783FCD8E58BCAFBA.key38
-rw-r--r--build/unix/build-gcc/7F74F97C103468EE5D750B583AB00996FC26A641.key54
-rw-r--r--build/unix/build-gcc/AD17A21EF8AED8F1CC02DBD9F7D5C9BF765C61E3.key57
-rw-r--r--build/unix/build-gcc/D3A93CAD751C2AF4F8C7AD516C35B99309B5FA62.key62
-rw-r--r--build/unix/build-gcc/DA23579A74D4AD9AF9D3F945CEFAC8EAAF17519D.key52
-rw-r--r--build/unix/build-gcc/EAF1C276A747E9ED86210CBAC3126D3B4AE55E93.key29
-rwxr-xr-xbuild/unix/build-gcc/build-gcc.sh64
-rwxr-xr-xbuild/unix/build-hfsplus/build-hfsplus.sh50
-rw-r--r--build/unix/elfhack/Makefile.in46
-rw-r--r--build/unix/elfhack/README28
-rw-r--r--build/unix/elfhack/dummy.c7
-rw-r--r--build/unix/elfhack/elf.cpp935
-rw-r--r--build/unix/elfhack/elfhack.cpp1458
-rw-r--r--build/unix/elfhack/elfxx.h700
-rw-r--r--build/unix/elfhack/inject.c225
-rw-r--r--build/unix/elfhack/inject/copy_source.py10
-rw-r--r--build/unix/elfhack/inject/moz.build56
-rw-r--r--build/unix/elfhack/moz.build46
-rw-r--r--build/unix/elfhack/relrhack.cpp567
-rw-r--r--build/unix/elfhack/relrhack.h25
-rw-r--r--build/unix/elfhack/test-array.c8
-rw-r--r--build/unix/elfhack/test-ctors.c16
-rw-r--r--build/unix/elfhack/test.c221
-rw-r--r--build/unix/moz.build12
-rw-r--r--build/unix/mozconfig.asan14
-rw-r--r--build/unix/mozconfig.linux15
-rw-r--r--build/unix/mozconfig.linux328
-rw-r--r--build/unix/mozconfig.stdcxx2
-rw-r--r--build/unix/mozconfig.tsan17
-rw-r--r--build/unix/mozconfig.unix22
-rwxr-xr-xbuild/unix/print-non-newline.sh35
-rw-r--r--build/unix/rewrite_sanitizer_dylib.py123
-rw-r--r--build/unix/run-gprof.sh17
-rw-r--r--build/unix/run-hiprof.sh25
-rwxr-xr-xbuild/unix/run-mozilla.sh356
-rw-r--r--build/unix/run-third.sh25
-rw-r--r--build/unix/stdc++compat/hide_std.ld5
-rw-r--r--build/unix/stdc++compat/moz.build24
-rw-r--r--build/unix/stdc++compat/stdc++compat.cpp196
46 files changed, 6015 insertions, 0 deletions
diff --git a/build/unix/aix.exp b/build/unix/aix.exp
new file mode 100644
index 0000000000..20f7cb4313
--- /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/3A24BC1E8FB409FA9F14371813FCEF89DD9E3C4F.key b/build/unix/build-binutils/3A24BC1E8FB409FA9F14371813FCEF89DD9E3C4F.key
new file mode 100644
index 0000000000..a76485a9fc
--- /dev/null
+++ b/build/unix/build-binutils/3A24BC1E8FB409FA9F14371813FCEF89DD9E3C4F.key
@@ -0,0 +1,51 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+8Ko5T/h16susi2ReUyNJEJaSnjO5z13TQ1U=
+=93P0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/unix/build-binutils/build-binutils.sh b/build/unix/build-binutils/build-binutils.sh
new file mode 100755
index 0000000000..338a9dd29a
--- /dev/null
+++ b/build/unix/build-binutils/build-binutils.sh
@@ -0,0 +1,106 @@
+#!/bin/bash
+
+set -x
+
+make_flags="-j$(nproc) MAKEINFO=true"
+
+root_dir="$1"
+
+cd $root_dir/binutils-source
+
+patch -p1 <<'EOF'
+From 4476cc67e657d6b26cd453c555a611f1ab956660 Mon Sep 17 00:00:00 2001
+From: "H.J. Lu" <hjl.tools@gmail.com>
+Date: Thu, 30 Aug 2018 09:21:57 -0700
+Subject: [PATCH] ld: Lookup section in output with the same name
+
+When there are more than one input sections with the same section name,
+SECNAME, linker picks the first one to define __start_SECNAME and
+__stop_SECNAME symbols. When the first input section is removed by
+comdat group, we need to check if there is still an output section
+with section name SECNAME.
+
+ PR ld/23591
+ * ldlang.c (undef_start_stop): Lookup section in output with
+ the same name.
+---
+ ld/ldlang.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+diff --git a/ld/ldlang.c b/ld/ldlang.c
+index 8878ccd..d644b56 100644
+--- a/ld/ldlang.c
++++ b/ld/ldlang.c
+@@ -6097,6 +6097,24 @@ undef_start_stop (struct bfd_link_hash_entry *h)
+ || strcmp (h->u.def.section->name,
+ h->u.def.section->output_section->name) != 0)
+ {
++ asection *sec = bfd_get_section_by_name (link_info.output_bfd,
++ h->u.def.section->name);
++ if (sec != NULL)
++ {
++ /* When there are more than one input sections with the same
++ section name, SECNAME, linker picks the first one to define
++ __start_SECNAME and __stop_SECNAME symbols. When the first
++ input section is removed by comdat group, we need to check
++ if there is still an output section with section name
++ SECNAME. */
++ asection *i;
++ for (i = sec->map_head.s; i != NULL; i = i->map_head.s)
++ if (strcmp (h->u.def.section->name, i->name) == 0)
++ {
++ h->u.def.section = i;
++ return;
++ }
++ }
+ h->type = bfd_link_hash_undefined;
+ h->u.undef.abfd = NULL;
+ }
+--
+2.17.1
+EOF
+
+cd ..
+
+TARGETS="aarch64-linux-gnu arm-linux-gnueabi i686-w64-mingw32"
+
+gcc_major=8
+if [ -d $MOZ_FETCHES_DIR/sysroot ]; then
+ # Don't silently use a non-existing directory for C++ headers.
+ [ -d $MOZ_FETCHES_DIR/sysroot/usr/include/c++/$gcc_major ] || exit 1
+ export CFLAGS="-g -O2 --sysroot=$MOZ_FETCHES_DIR/sysroot"
+ export CXXFLAGS="$CFLAGS -isystem $MOZ_FETCHES_DIR/sysroot/usr/include/c++/$gcc_major -isystem $MOZ_FETCHES_DIR/sysroot/usr/include/x86_64-linux-gnu/c++/$gcc_major"
+ export LDFLAGS="-L$MOZ_FETCHES_DIR/sysroot/lib/x86_64-linux-gnu -L$MOZ_FETCHES_DIR/sysroot/usr/lib/x86_64-linux-gnu -L$MOZ_FETCHES_DIR/sysroot/usr/lib/gcc/x86_64-linux-gnu/8"
+fi
+
+
+# Build target-specific GNU as ; build them first so that the few documentation
+# files they install are overwritten by the full binutils build.
+
+for target in $TARGETS; do
+
+ mkdir binutils-$target
+ cd binutils-$target
+
+ ../binutils-source/configure --prefix /tools/binutils/ --disable-gold --disable-ld --disable-binutils --disable-gprof --disable-nls --target=$target $EXTRA_CONFIGURE_FLAGS || exit 1
+ make $make_flags || exit 1
+ make install $make_flags DESTDIR=$root_dir || exit 1
+
+ cd ..
+done
+
+# Build binutils
+mkdir binutils-objdir
+cd binutils-objdir
+
+# --enable-targets builds extra target support in ld.
+# Enabling aarch64 support brings in arm support, so we don't need to specify that too.
+../binutils-source/configure --prefix /tools/binutils/ --enable-gold --enable-plugins --disable-nls --enable-targets="$TARGETS" $EXTRA_CONFIGURE_FLAGS || 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.zst binutils/
diff --git a/build/unix/build-gcc/07F3DBBECC1A39605078094D980C197698C3739D.key b/build/unix/build-gcc/07F3DBBECC1A39605078094D980C197698C3739D.key
new file mode 100644
index 0000000000..d78c28c2e6
--- /dev/null
+++ b/build/unix/build-gcc/07F3DBBECC1A39605078094D980C197698C3739D.key
@@ -0,0 +1,53 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+Lt44/DBv052TOb47tw==
+=ERlK
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/unix/build-gcc/13975A70E63C361C73AE69EF6EEB81F8981C74C7.key b/build/unix/build-gcc/13975A70E63C361C73AE69EF6EEB81F8981C74C7.key
new file mode 100644
index 0000000000..53591cf752
--- /dev/null
+++ b/build/unix/build-gcc/13975A70E63C361C73AE69EF6EEB81F8981C74C7.key
@@ -0,0 +1,82 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+FBOFnLOQGGr6Glw=
+=XJ6e
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/unix/build-gcc/33C235A34C46AA3FFB293709A328C3A2C3C45C06.key b/build/unix/build-gcc/33C235A34C46AA3FFB293709A328C3A2C3C45C06.key
new file mode 100644
index 0000000000..f183ce9e49
--- /dev/null
+++ b/build/unix/build-gcc/33C235A34C46AA3FFB293709A328C3A2C3C45C06.key
@@ -0,0 +1,33 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQGiBECGYZsRBAC9VE8N8vHAG87MTC2wbtnmbSD8Wc2xYCaLofZAH+vXyio3Dnwx
+jQLlj7IgwRWNAVq13uL0wn0WAsGop5Cs7nA/JD4MEBBNSdnvq1bMYitch2PTtAU+
+h6HaI9JXBDUh4AKZz2rllKgbigMHlgIugxnKTAMJIhS63lCTHWEDlnycJwCgqSX9
+hDs9eBC5coearGDhc0BDvTsD/A05YkZkQBgsYD6cjWFwNLJIcaHORKlLLZ9gRJO5
+LVcKaCEgYSWAM7dadJeqIFi9RkXdv+cWozxTgrGlY4T7/PakIBB7wWj2Zl72mW5a
+NHT2vAemB8IFV1saiFXZM+qDhCHbV4yKSmNOQHY1VnSCUrgINiM0qlTz08yjUazK
+fm2BBACDF3ZfUQNeHC9zwfsgCzKnqOm7FSlwOyI0f+j83B5PH2+KuzuyEqYoxGp+
+2d1zTxvbOeBBaX8T1M4n5d9ixiFMhgbTzuyit3nn6cp5j2L0IAS9pw0kaWpPMhpQ
+zydNgnaBxHs1Y+cP4iM/4FWFCvfjUdR7xULdEzkgGxevu8pNEbQgSmFrdWIgSmVs
+aW5layA8amFrdWJAcmVkaGF0LmNvbT6IZAQTEQIAJAIbAwYLCQgHAwIDFQIDAxYC
+AQIeAQIXgAUCSe3VIgUJEs109wAKCRCjKMOiw8RcBqANAJ0VlFMTtevlkEM+ym4k
+yE3YOrGZ+wCeP7lZGc2jVLHJfrOKxXsTM5YPWhqIZAQTEQIAJAIbAwYLCQgHAwID
+FQIDAxYCAQIeAQIXgAUCTI3tMgUJHtOOlwAKCRCjKMOiw8RcBjySAJ9ApMXF3+gW
+Ir0zpMxvWb53/oxsHgCaAl6V5JS9GJUnrPiHKdR+sMFPkd6IZAQTEQIAJAUCQIZh
+mwIbAwUJCWYBgAYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRCjKMOiw8RcBrC+AJ9d
+mQcWoZHFGoinHck309KD0m2FegCeMBjr/M6Ec1myCYMUhtpl5DI7zY25Ag0EQIZh
+ohAIALrI1X59CM30/Ufg+O9FFRRyM8GefACfItrIvp6jx+0ZMY+/ZbYnlMzI7Gz4
+xNXc+83Zsz7zE5xogNcq9LILdhB7Ta1ZRkRttM8AdfyakRQTjzCPtxSPgSao/Dcu
+CL09BZdaeeqMAxLmw9DnY3xmZqQtCau8PlgIiClq2db9Wy0bpQ+DDfQV4MlX6eoI
+33TG9Moy59QQUG5reQ2JNkQZRebPxJAPiAgHoF/Q+XO1pLeCccIN7SApe7yVd/4A
+sS3Y9lZj2JvEvutLojsRGL0E/CAwH8cJqPAt65qbOgQzCILhcc9aYZ234g9n7Kpx
+Ck1h2QMtXfsmaA7GsrXo1Ddfra8ABA0H/0sa4SCQhWQ14tOFkN15xzuaqGOxUD+O
+uAsgRdKaFdIhZnj0MRmvOfBSP7hONw7fE0m9DVq9NDPqFcMeyCuBNIMpGIuN6CAK
+/G0K2UgzoCxMXUEYGncFfVnOoNURV9u2lGq7ZMNJmuzt0BhxXtUYRlH3WRPqPyGv
+s/OrIqvgN+Kf9+i0kQSObWz6CeYnBKzCc++MPkVhYj8KR5Y6n3zPZpnOfmO3c0rY
+C+KiNoMwchlZmiOh7zgcTybv4zuOU7bppEidreIq2/o4nBNTao/5uzYdDX9FBpDT
+hhU9ErdO8Vd7Vf2I1/WQdt6dHUXPLfkwI8+ODE/4R/Oz8opFC5L22kSITwQYEQIA
+DwIbDAUCTI3tTQUJHtOOqwAKCRCjKMOiw8RcBrBvAKCTFx5FOuuxM2VoQka8iBGj
+f1vcugCdHV/JIhOwETTqOQEbkw3y9ng2+4U=
+=K9Jj
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/unix/build-gcc/343C2FF0FBEE5EC2EDBEF399F3599FF828C67298.key b/build/unix/build-gcc/343C2FF0FBEE5EC2EDBEF399F3599FF828C67298.key
new file mode 100644
index 0000000000..548a560202
--- /dev/null
+++ b/build/unix/build-gcc/343C2FF0FBEE5EC2EDBEF399F3599FF828C67298.key
@@ -0,0 +1,35 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQFNBFDrIWMBCgCyyYoTAD/aL6Yl90eSJ1xuFpODTcwyRZsNSUZKSmKwnqXo9LgS
+2B00yVZ2nO2OrSmWPiYikTciitv04bAqFaggSstx6hlni6n3h2PL0jXpf9EI6qOO
+oKwi2IVtbBnJAhWpfRcAce6WEqvnav6KjuBM3lr8/5GzDV8tm6+X/G/paTnBqTB9
+pBxrH7smB+iRjDt/6ykWkbYLd6uBKzIkAp4HqAZb/aZMvxI28PeWGjZJQYq2nVPf
+LroM6Ub/sNlXpv/bmHJusFQjUL368njhZD1+aVLCUfBCCDzvZc3EYt3wBkbmuCiA
+xOb9ramHgiVkNENtzXR+sbQHtKRQv/jllY1qxROM2/rWmL+HohdxL5E0VPple2bg
+U/zqX0Hg2byb8FbpzPJO5PnBD+1PME3Uirsly4N7XT80OvhXlYe4t+9X0QARAQAB
+tCROaWVscyBNw7ZsbGVyIDxuaXNzZUBseXNhdG9yLmxpdS5zZT6JAX4EEwECACgF
+AlDrIWMCGwMFCRLMAwAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEPNZn/go
+xnKYqm0J/A4b6TE5qPWiWj0kriUBSmpys3qUz93gR6Ft7w2f478KJuzbSadvyn0u
+PcnP26AGTOQq75RhtgCJgdYbvRocTjlMh9jOX584Hx8hi/QSrpCSYMnj6dQKbu0Y
+QIFjZx8gPeYvzG8t34FCNEzZ09RQZqy/ukRyN99LkwEuP4FWq486b7dpgv7GC+SH
+lZcMco6VW8FLOT7KMalH06cmdhFPrFSYAIHDu3CsYhC8knIQV99Xzno/KeSkEwkq
+tYDOdz0x4HWdOwHrl2S2X6Ex1q3QRXcq84EYQwHz2WEGaPR7Vd76P5J1wiHN6rwO
+4exfgsRyTvc6NDQPTFqmoCzwuPviYk6JNnHr9E5TkLT7lAnESEhMLyyIG/7Uwpgu
+5C71IMaTpOpf8DEU9NU/zuxgHoMaKBZaeYKs0S26s1zwGOlQX0T9uQFNBFDrIWMB
+CgDKlONI+5Bqcu69+72fmLZPizzEUsIRA2Y0w2RE7+uJ5Es9/YTp5PnWANpPT7GS
+8JJnc6NJJeh6GkMkGGwq5Op7CDsjW9pQZ0vAW90XjnyniDa9W0W+m5+X/LPOzh+n
+is9Zcf17P91tprLCLi+TOOb35xt396pZ+S+PwuV0dLiIYdVYV3e6LNCV0LjhEqp5
+3TRwTrLTNPQVnt0DPYTh/Kn1x6d5zOS0MK4QybKN1WJU6nYIQRXyWKkixjbs++jc
+gV/juck96Ve0blvn6DfqfpG8YzbmqRCufLo683LtlBUZ0c+znrD1nouqX2Eb/Cyl
+G8Q8ZUHXimCJ+g6RfH9kOmtVH/208u/nDofVL/Q0dvAXfU5MX49c7XYy7B2rTlk+
+4nuNeaHM0aU2Y14+SQy+sR6zydu7eGLdqjzV0CX/ekgrjQARAQABiQFlBBgBAgAP
+BQJQ6yFjAhsMBQkSzAMAAAoJEPNZn/goxnKYGUcJ/j+L0/uzfwCR1aTBZ6FBT9Od
+NyatVjmz20ahskF3BySmkT1R06K08YOGJ//LPajj0eKqU8WKgxMc7pWi5SG+yMFn
+2db5HnJDGiSmSjCXW/BzsSt1786LtO0m0ehatj9kl6JrxQNXazOkRJ2ww13P6/91
+RBaV6R08BmFTrUco2P6w+djCF4NlnkOLa7fM6QtNZM+yB+EzaPjSBFjZG52BVWZk
+cXEVN0cEjPuznuQOmx8Dny7lQikp49NumrbamaxZEilx2Bi9gSbovNaKBuncKi9X
+boiEiNbAarGxP40Qvlk2AuXWvq+fiBnU1e1nU2oV7/7nAWH7kj/Vr/JxcBeOpsND
+GkW7Yrd3mkJCrhG+jMs1V2qNb9Uhr5ZLOA40sIz2PHfDrR+gc8THm2p5OvCWEAeu
+kYJ22XTUIt6XoPO0ERYD
+=MH4q
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/unix/build-gcc/5ED46A6721D365587791E2AA783FCD8E58BCAFBA.key b/build/unix/build-gcc/5ED46A6721D365587791E2AA783FCD8E58BCAFBA.key
new file mode 100644
index 0000000000..7cc6ba735c
--- /dev/null
+++ b/build/unix/build-gcc/5ED46A6721D365587791E2AA783FCD8E58BCAFBA.key
@@ -0,0 +1,38 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+pMCmSQ==
+=5shY
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/unix/build-gcc/7F74F97C103468EE5D750B583AB00996FC26A641.key b/build/unix/build-gcc/7F74F97C103468EE5D750B583AB00996FC26A641.key
new file mode 100644
index 0000000000..6f23744afe
--- /dev/null
+++ b/build/unix/build-gcc/7F74F97C103468EE5D750B583AB00996FC26A641.key
@@ -0,0 +1,54 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQGiBDs4dV0RBACZII57dgbfnCC7RTrJ1yc0F1ofEZJJ/x4tAtSHMDNj2zTnLR25
+5AHmxN85namwJdn7ixXSZv1FMPCeTs6jDk98YuA9r5uuCNPqCNZsuQtREpN7h+wO
+IeRrhvg9/F11mty/5NthXNh8P2ELnkWXSHu6DvTQyGppAtxueOL0CjRrpwCggVYu
+vxui5mqNq9+lILbMi2Zm3UkD/0T/0HupthZFXbuzY/h/nyqzoPOxnSAAAx6N7SiE
+2w9OQ1w3K8WOFoPH9P0cnIQ+KnMSGQV4C2WY/d8YtShnKkXRYZVvlK+aiwmvf1kU
+yNyUqaA/GhW5FWN26zFQc3G5Y9TDjgBqjd6SequZztK5M5cknJGJn+otpdQtA1Dx
+2KEABACSYjdRNT3OvQJ7OSz4x4C58JKz/P69WsNZxqYVo66P7PGxM7V2GykFPbG7
+agyEMWP1alvUK551IamVtXN+mD7h3uwi5Er0cFBBfV8bSLjmhSchVpyQpiMe2iAr
+IFeWox7IUp3zoT35/CP4xMu5l8pza61U5+hK3G7ud5ZQzVvh8bQtUmljaGFyZCBH
+dWVudGhlciA8cmljaGFyZC5ndWVudGhlckBnbWFpbC5jb20+iGUEExECACUCGwMC
+HgECF4ACGQEFAlZi3pMGCwkIBwMCBhUIAgkKCwQWAgMBAAoJEG7rgfiYHHTHIBIA
+n20wZDYF0KrfbJNzK4/VwAEAzN+wAJ9Dpbhtq4sRoH3cbadBsD2mXXthOohiBBMR
+AgAiAhsDAh4BAheABQJWYt6YBgsJCAcDAgYVCAIJCgsEFgIDAQAKCRBu64H4mBx0
+x2iyAJ4tmLvgNsphsrpKKfDDyV0tzR5FuACeNymltMsgfFyvoueBvji/h+HyObmI
+YgQTEQIAIgIbAwIeAQIXgAUCVmLemAYLCQgHAwIGFQgCCQoLBBYCAwEACgkQbuuB
++JgcdMdeDQCfZRUFDCB8sLK6B6wqRmwCsb3EK6MAnjSG6ZtgrdEjSQSmfAcIV/9W
+367MiGIEExECACICGwMCHgECF4AFAlZi3pgGCwkIBwMCBhUIAgkKCwQWAgMBAAoJ
+EG7rgfiYHHTH1PAAnj/1LWl3pxLYweV1ZClR0i44GJQcAJoCM0+92pI3VIsSMfkY
+aUVmOjVzf4haBDARAgAaBQJJt43uEx0gQWNjb3VudCBkaXNhYmxlZC4ACgkQbuuB
++JgcdMcPKQCfX9/AR3wmHBQRa82rFiedGmIqvWoAniXN/YsV4seK5zmvL8wb0qiC
+1NydiFoEMBECABoFAkm3jo0THSBBY2NvdW50IGRpc2FibGVkLgAKCRBu64H4mBx0
+x7I5AJ95CfnMCLlo0dO3/9S/O4QtcTBpgACeOSVDaFIJeOfiWFFWpiPkp6NNq6C5
+AQsEQybx0QEIAPjHD/kts6GQbtsV+6+aZgfCK6MVZe14MOXFG60FmnHPzXymorzu
+7DxSQOkKiKU49mUklTIQ1ErGIr8nCzjmVHmm7CH53dy8/OklPgpecBLGSP9WiqQ8
+TJxNUiDWQA0r2HWVAsi86N+E3e9ubN4VSK4yd6JMR1Mp2KgyS2LK2PjRo+o7mulO
+FaAAoxmi8gWIVR6sv5dkh3g/6/DfKg32U8CWjFp5IXKmkyMPSH5eOZL4eBfx4Ia1
+cFcWfDJZdsYQ+EB+auzHqyr/DS5on4aS72WAppWkwH1Mu/fYnOEY37yF4GwYPb/M
+5Loz2wTMxdjfflzCMdfQ56CuMlfc84MeT/MABimJAWcEGBECAAkCGwIFAkl7HCkB
+KcBdIAQZAQIABgUCQybx0QAKCRA6sAmW/CamQbh0CADqF4FDBMsQh8+vkhFvXOTQ
+vtXkQMuQedryaCGHcS/e5/J0xb5uLybMnnFyh9tIy8cj9sc79yeTTuXSQLJgU02X
+h6EL4osGpe5JWWvFbY4SyNkyR3UcpXgzkH80crsHF5ixwCxy7PTEjQ48yT09hig4
+eiDHQ1fS4ox0F5aUQ4q2mk4bNtU6WHEP/8l6BKwSUC9/lfFQmMnk7SeIQqTwgC9T
+agPZhCQz/tNZBo+t3ETlRcfPZ2djCGRJm5mbMG/pwEy1L9frdopzBYk56yEpuA97
+HjmIDvt34YbAOlPYPSCsvnhzZdoVrRv/qBcGxNhdYA7zwiGDlrjRf7Rg1KT8izvo
+CRBu64H4mBx0x1WDAJUTqjXmf02pQphfYo7qalOuVr1tAJ0UUgg+PXpgrP0lp4LQ
+8SbkxvBRhbkBCwRDJvH8AQgAtyi1+vdUzhYos5lmUznkTURFBGWMvPSOnB62I2Mq
+0ZAazhyRjb6EuSTuGcusJXPWzRlsUFPdmyQjIqhPJ9ZkUSLOieIBMU4VqgWc7GZX
+K7P4Luh8TxQrz5YOtnpj5Hev/yj63ACDLIbzShizBSteZ+TZL+aH7/8XK/36o4rC
+Ep5OH69RiPcCUFEHQkxF7vaPdnqyH82/JtUOxSW1zYcr+7XHpHa+UNtI62Q4MV9x
+9Wi6vBBvJ6zZSarpVi7ViIf5PVMGuWb5nA1YShEtQVKnQnn6pBqRUF4iLcyrunIL
+PGp13htUUahaDr4qWUP5VKmELT9IitOTR0BX6e3E2h6NnwAGKYhJBBgRAgAJBQJD
+JvH8AhsMAAoJEG7rgfiYHHTHMuEAnRcN4qTQ1V92e+2RzJm2IYbVJjPBAJwNY6s3
+lbrcC7Zc2E/k5fxwwenSTrkBDQQ7OHVeEAQAl3WryGIZfi9uPLNZlIvRFBErvUKL
+zc7n+/c1GaUVMxXcF/Iauegblh41OoV2Kcz1sFx52MLDSDTV1DwDn4fNzwP7DYOe
+9h4EBpMePG1DS7LQ0LoD682rvey6Cvww+eFmBBXdiEqCXvPuW4d3WMnOsQqL5BiS
+QH+GiwIrLFN7yj8AAwUD/j9FOzif1GLdoSG8fsEi//axq0sXI+NtRUOrvmrkTCG3
+o5rZOJNwz+KBQpP45LdzosO4V/kPVuJ5U4EprEPRqejfSTW+oK+Bgm0mfImgw7Jv
+adkNeXfJdwYidutyF1jjroVdqprSjAAaoZgSi2sw03CFx1WkdL+GCccwN6IVl5OI
+iEYEGBECAAYFAjs4dV4ACgkQbuuB+JgcdMeqzACfeHjT2PFYdy88PHNVGw5se9Pq
+GPYAnArpX32fDdu/xhuqjqHrNkwyO/Yo
+=TzkT
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/unix/build-gcc/AD17A21EF8AED8F1CC02DBD9F7D5C9BF765C61E3.key b/build/unix/build-gcc/AD17A21EF8AED8F1CC02DBD9F7D5C9BF765C61E3.key
new file mode 100644
index 0000000000..95a04ebe6a
--- /dev/null
+++ b/build/unix/build-gcc/AD17A21EF8AED8F1CC02DBD9F7D5C9BF765C61E3.key
@@ -0,0 +1,57 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+8GOBvibrC/azKvoBKLY1/stpKCrecZdRFkk=
+=SDN9
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/unix/build-gcc/D3A93CAD751C2AF4F8C7AD516C35B99309B5FA62.key b/build/unix/build-gcc/D3A93CAD751C2AF4F8C7AD516C35B99309B5FA62.key
new file mode 100644
index 0000000000..21cd94ec1d
--- /dev/null
+++ b/build/unix/build-gcc/D3A93CAD751C2AF4F8C7AD516C35B99309B5FA62.key
@@ -0,0 +1,62 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBF7Ps4YBEAC5i0PA1CA3te8UeAxWm8zH5KRyoXyD+IuVHar9fPR13J/IkUgO
+0f4kebDaGQGjyPoBuLHWtshQwSjDP9059eMbfne6fhe3UxqRjfknWxr83S0pSrDI
+xgdIsxMQT6dxm1YYpp+pK6PRs/tHMtXHtSJc4HwkW187nx7c7lfKXmwVoqUuEjvW
+irKyJRVNw68WZjYLmmIsRIIZcUMOE2lItPkejerHZobOuTkuXslgkWH3zeKCK8JD
+em9npzxIkLgrl8Ub0HxWdkAc6o+gj3Ih0QthvC8P7gxNuTJyf8SVaZFla+ky/t7Z
+kLmIhSBLzNSosscOtz9sdI4seXsOGgWeGRORp/+zF5ISnD3kFg3OtIudW8p4J7oA
+OICWkPIuEOXPCz5VIUmaY2Eswh76YgW7u60JMv/v0Agpjy23hovvG6HArMO8Letr
+Y5CWC+G9wp/xTo3TeyQ9mrYcKMjvrZzCos+SFaGF0lcExWpk610XQf+8/1FlhJ4U
+SiQCy78o1pW8dOpLWvWe7y9YtRm3DTgYDCDpcMzYVZPrp2oPg5h4nW5sfhPJ01yu
+gwTLDo/AILMQSkr1IVbfMkP7Mxtev51nRjxL7JCMB4bHx+uyNs56LCqdLctrF6Aa
+HrS7yaP3ym3BsHrH3TqAaTGW7rs/hrZ6MaWbU4bBxL49z4GyXWRJqglePwARAQAB
+tCBKYWt1YiBKZWxpbmVrIDxqYWt1YkByZWRoYXQuY29tPokCOAQTAQIAIgUCXs+z
+hgIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQbDW5kwm1+mJqRxAAtvq9
+NevjleUPVMJhz3X5pprhCNM/8LGSbhE284tDYv0inT9huKaMeaw/hQvSSoniac87
+zb0S5JDWYHEZ6PhOs7ZASuSwtBp3SdGtjQTMJMSuAHvEjSvBR+0EkCu+85oJxwDS
+wMMNEy1xubsyqhETD6DF/wudDr5r8IpB0A4vzFmaFv5wdbdnywd/sQzU9Fnl7GyX
+/1Wgd94dIiznr7fdxJtsdphw4WzoJf5EYTdozs+biVhnz+NJuniTjg8IKoEOl7oY
+Nrmqwfijw7FqgcbuXb9UAsxaVLFHZl3GVhXAmbQoz4io3PVw5BR+p+zeWvndeONu
+jndE8QN3PyFb/WHyWRnCv7goUb5uLZU4aDqf34PP8fral3HBaaaXB53NybvJVjEU
+nMzvjpVQj4J0yFzy+NlFwJb3oT03EDNkEK+SQrKHv4xz/atgXbfFYZ8oynCkypmt
+aw/udPuLpy2dBY6wTAhSvdzkiD9swgbgX2idNLXfoeU1AaiKd+mez3X51Arf49pn
+nL5wDCBkT2BUwX41ntIGgrNMcMNFbfNt99kUNaZ15oWI9Ia3DyWMxvgmDg4Ev8m8
+pGHH3Hq3eueyPySdJh+I8x+ipyIpFW+7AYS49L4LI7A7jFs8Nas22Qi6RoTFV3ep
+0qkEL4Lgrh8ooxhhSLOmsfyJDiXAHiz5sVUZ4wWJAjgEEwECACIFAl7PvJYCGwMG
+CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEGw1uZMJtfpirV0P/jBD7DrOCdCt
+AgS4F9uUM9MtR4uBvp+8dVuaGX37fMr8W5rDE+wYzBiYeIzc3UJ3QG2Nuh+VN7OT
+rZS9cNpDKre2+/6PQT4y/p3SI7o4JEqzoecWlITwWy2hpoYmEs47y7F6L0RpeNQ/
+sJ3hQydx+X6JxXe/IB+ftpOWpO5oGIDrdBoU3hBTcQYL9TjDMWSfmoiIgKTBVbXO
+Ktsy1sc0TcjHk1ACoSj08+Q8+Swq56cd/dXuIMfaU6mNuJgA1UUaw7T+2HYLDAz9
+J10ak28kbHP9MHFHZff3M1Ub80gAwmaJ96u4G+2q9yX6/+QhXTf9gEF8PyBKpBd6
+Uk/dNLPtVjGYTjkPxQ7fyehE7KunC9uPOze0AEnhnxe3HM1TikFNbbAYaZNlqsXt
+HPCp7ATfCpautoNarO7CfFer77OuECzoLHpDNXqesPZ+/WeZsURtik2MoNN0to3h
+CZJQBptcBQ4CAy+5LlWKZURU0Cxu2GkSNSM0YuiQnmKReLZbzX4Rrr7c1/AAx5Ps
+mG55LIF4sSygbWPvY6kBL7XEhvMa6x3B4+pELNCJUUSgp4aW3oL3u05DQWVJDgZd
+t+XPP2DTiEgFtZbOS6sXdqpPY5/fC+69HU9spcmEdaVCJeIOICy+sDfFTOTv0Tcq
+XD7TIZko4pKA97L4lzekQENFcni9/YHkuQINBF7Ps4YBEAC4rhogalczpaUmgnMD
+kRGpv7WFgpv96PQBKCoWfFTTsB8vW0Ge0J7ctOGevOjRqupDnNK1ycTuXUnqogOD
+n4ZpKYoXg9xoBCmrlXAjdRA7Z9GG9Pv5SVgah4lwGvoWhFDbEZGXWywGggWOmht0
+vGPYUm9Xam3nfUKA3QuQ5Y6pUyVM0W0C93xQZbGHo7VkzVlBz/TnXlEyM3Twn9oh
+DTKPj2GRty8cnMEDM2tEw85rlUi0WwWMS42MM41aIiO0WL1fmhbNh2UoLQND9gZg
+vqY5Xnkn1nh4jQGcTx+krXm/EzDi/wMnS6zHBp45XkRHKNWFDS/IaNs1AL9Ifkt2
+8xoW+4LZDsVc7uhtRB6H5Uwq8HbAH3gPFggWTPii6iyXJ/OXoR+/zdGQcv2OQbu+
+iYrWDvwyb6gEeo0okdnyQUC4khAt0jr4VsCRGHsTViN3kqQ2L8XY0N97Rc+kE1B5
+3on7NWgcpejB9FsUQ2LKSLNXO1+mn6iFq1VLF5o4fyE9fIRjhHVQ2fJLStVNmnUD
+99jpmXUOYo/OmHcsqa/XMByve+lXbbUSzSrOyhJRXpqZB7XXu7nvl8pa83jGEI9T
+JrcOG2i/I8YOAB5pNOlN1RGk+z0CCaYWTMCzHrN4ZOHuO29wvGnggAGANbH5muKw
+lPIU26wKEleaoQwg20WYgIRdAQARAQABiQIfBBgBAgAJBQJez7OGAhsMAAoJEGw1
+uZMJtfpiKzcP/izcvGB0oVTvhLkHomtxNHFbuv6gsjP1HYwn2kmB/TcEJXR1G3ne
+9CGO9jKmH5PrKk6B7BmggKEnnwkkS7BC0O7ADLH6r8DW/HYmzjeqvSbuSRElMLuL
+cZlZS5ZFxZcsYJP303gU0/PcE+2WF40lWu+VkQw8CaPASydTe9ulMVQ3FJuheBm/
+npWjItI1RgpSlRv2Ry4FkZXyuPHadWtgj66r0QHhr56skMuKulLVdRXMGJcc5dxt
+rmwfGI5UINRRuwqEn4Bz2RWfnz7Qr1t5FI3kzmZQ0PFDRLMr1LcC/p7SgqSsMUXQ
+BS1m31YJdR0bXCtHl0H/jXqZ+fpUYDUX1JcxSJ2FJJ9EBBSUEIAJncbkFS7rS84H
+IcUfeDAa+nNfI4bvveaqCiHzaXkmjqcQ2cfVqpzArAZp7PmCpnWhWo+TLpMz1yEM
+cOqPRM/gID2PVUZiA72iv9ASa4Fv3XhRnBddrTB82kOPiLe3onQ3s2J+glqr0QF+
+OcKVuYoOxlF5uV6fDEqvR1B6b8UhEg2qPfekIH0epRSe2CMvnEHAkucpx3CZKZyx
+nUtBvFMzeImE/swLJzsmzaxExcRT7EwHXbvXPrDo0VB1BfK/ZBkGd3isQqd6zXxC
+1FU48hr9sjqbp/mdcRwHiyKuTBKuDwcSic/+06T28Z+qcFGkqy3SQZF6
+=3+NJ
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/unix/build-gcc/DA23579A74D4AD9AF9D3F945CEFAC8EAAF17519D.key b/build/unix/build-gcc/DA23579A74D4AD9AF9D3F945CEFAC8EAAF17519D.key
new file mode 100644
index 0000000000..1b658e615b
--- /dev/null
+++ b/build/unix/build-gcc/DA23579A74D4AD9AF9D3F945CEFAC8EAAF17519D.key
@@ -0,0 +1,52 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+=6vF7
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/unix/build-gcc/EAF1C276A747E9ED86210CBAC3126D3B4AE55E93.key b/build/unix/build-gcc/EAF1C276A747E9ED86210CBAC3126D3B4AE55E93.key
new file mode 100644
index 0000000000..13ea6d69ec
--- /dev/null
+++ b/build/unix/build-gcc/EAF1C276A747E9ED86210CBAC3126D3B4AE55E93.key
@@ -0,0 +1,29 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQGiBEj3S14RBACUE+e2hRWwM6AFWaNKsLgDg6ebDNCI6z/Pk38t6JUeM+D5MAvq
+fnL45bF/3CUrZRK+/qLg5iwRWehKh08VQ7GqDxMerZkPmfvirVxLwpc5ngCOGJwv
+ba13xdaxfTLMkHxaQyGWiUqHIwdzFoNBgjq9XTY0GGqHwVA1Hb+xTAL8PwCg+wru
+41p/aOq9cfPN1U1BjulWCSMD/2YP23pI19o9Hr26ltyJcd/xkSRiCUk84efIw5JH
+7QlxoMoW/SdJQAGi2pZN9o4I/fPDB3Gna9M9rZfacKda857dwkALPK8xTsfHiZzH
+40g+eYUvl9nloNidneSFcxbLO/euURcCJ6Ri6nb4QWWLVH1XF6wQxEGyn7ojYMOn
+ihlkA/9KATtSt+T0zgWskqckgV1ZQg9Ysqwp3GAvezJuyUTXlB02ApVFEsRTQtLZ
+2WPvo/gzfih/EdNLrq7UeeB4dr/nlpANn4IyBRN6EmBHaJ4MMKN77B7bB0GaBfGJ
+rgNNp7W0xG0FLjaMoQzP62qqAtZHNlW1qCmkyJGUpAa6tJw9CbQlVHJpc3RhbiBH
+aW5nb2xkIDxnaW5nb2xkQGFkYWNvcmUuY29tPohgBBMRAgAgBQJI90teAhsDBgsJ
+CAcDAgQVAggDBBYCAwECHgECF4AACgkQwxJtO0rlXpMVcwCgoQ91OLI+m2bsu2SS
+d5MsRQH3FWsAnRFG2YGk5o5zuoLzdZd6KlL9xJ+uuQINBEj3S2MQCACnDo5dHujc
+u7QHRPnxNwiKhMP6eIZaEm9tavab3UxsRufMyVC8nQ8+EmCOwfBrqstfRVoQnoDI
+s5UY1XAM3mBFXYqfY9wR6NISUlzK/HPyFhGE7t3lVjOkiqbWOftDt6GgRETeqYsW
+XkDV/dL4+P3eSaOSP6KMZwdjgXPOciN59KIiii9NK4icxP0lJHDk5WJFwfucEyUt
+Sz7uwuUFcajHZmMxxHAnWT3uJ+ZasSijduZevsHhKTTXaZRideqf+ur1/TcUaZDQ
+O3wist1qc03NkL+oGu6HYPx9ZV40p/axdTaUXMcBjtAZIzvy984HF9EsFQnvbiXt
+R8zg6SYXLRmbAAMGB/9JMKWsCuxUzXmU1jyJvMXdRBZ4YQYkKFYWrEXwjYlBEGx6
+01PkR//4QJVR4zFjy4zVnaUrOxtR+65Eedf+9fNZzSNeI24TGaqyVM0OYYQtp9cH
+kRDu3wif1k2NW3BnrmTjVefdAWVH6zKT9lP9m6RPHCwVGyORhVQtB3+ZXOehNJwL
+9NBU4MUpGKpoQCuODdgZ8iQXbo+plg0eCxcpNaYzSnq9DMAU+2qnP6d3x4DeWzlL
+wvJ2K2Mw89gvCImy/JDe05EXqKowR6aiIPvw5ou9xSHmjT6rcaIBiROCe+1hh4XC
+djCdb4kOskWCEXfFKcHax4N5fI9vmk3P5068BELMiEkEGBECAAkFAkj3S2MCGwwA
+CgkQwxJtO0rlXpNxdwCgjr4sQRf2cyDkCSWe4AElbI74BREAoPdet3XvE6ZcZJGl
+UIZySRkdpk/A
+=dGh0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/unix/build-gcc/build-gcc.sh b/build/unix/build-gcc/build-gcc.sh
new file mode 100755
index 0000000000..0a27af1ecd
--- /dev/null
+++ b/build/unix/build-gcc/build-gcc.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+set -e
+set -x
+
+make_flags="-j$(nproc)"
+
+apply_patch() {
+ if [ $# -ge 2 ]; then
+ pushd $root_dir/$1
+ shift
+ else
+ pushd $root_dir/gcc-source
+ fi
+ patch -p1 < $1
+ popd
+}
+
+build_binutils() {
+ # if binutils_configure_flags is not set at all, give it the default value
+ if [ -z "${binutils_configure_flags+xxx}" ];
+ then
+ # 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.
+ #
+ # --enable-targets builds extra target support in ld.
+ # Enabling aarch64 support brings in arm support, so we don't need to specify that too.
+ #
+ # It is important to have the binutils --target and the gcc --target match,
+ # so binutils will install binaries in a place that gcc will look for them.
+ binutils_configure_flags="--enable-targets=aarch64-unknown-linux-gnu --build=x86_64-unknown-linux-gnu --target=x86_64-unknown-linux-gnu --disable-gold --enable-plugins --disable-nls --with-sysroot=/"
+ fi
+
+ mkdir $root_dir/binutils-objdir
+ pushd $root_dir/binutils-objdir
+ ../binutils-source/configure --prefix=${prefix-/tools/gcc}/ $binutils_configure_flags
+ make $make_flags
+ make install $make_flags DESTDIR=$root_dir
+ export PATH=$root_dir/${prefix-/tools/gcc}/bin:$PATH
+ popd
+}
+
+build_gcc() {
+ # Be explicit about --build and --target so header and library install
+ # directories are consistent.
+ local target="${1:-x86_64-unknown-linux-gnu}"
+
+ mkdir $root_dir/gcc-objdir
+ pushd $root_dir/gcc-objdir
+
+ if [ -d $MOZ_FETCHES_DIR/sysroot ]; then
+ EXTRA_CONFIGURE_FLAGS="--with-build-sysroot=$MOZ_FETCHES_DIR/sysroot"
+ export CFLAGS_FOR_BUILD="--sysroot=$MOZ_FETCHES_DIR/sysroot"
+ fi
+ ../gcc-source/configure --prefix=${prefix-/tools/gcc} --build=x86_64-unknown-linux-gnu --target="${target}" --enable-languages=c,c++ --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro --with-sysroot=/ $EXTRA_CONFIGURE_FLAGS
+ make $make_flags
+ make $make_flags install-strip DESTDIR=$root_dir
+
+ cd $root_dir/tools
+ ln -s gcc gcc/bin/cc
+
+ tar caf $root_dir/gcc.tar.zst gcc/
+ popd
+}
diff --git a/build/unix/build-hfsplus/build-hfsplus.sh b/build/unix/build-hfsplus/build-hfsplus.sh
new file mode 100755
index 0000000000..5d3417e63e
--- /dev/null
+++ b/build/unix/build-hfsplus/build-hfsplus.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# hfsplus needs to be rebuilt when changing the clang version used to build it.
+# Until bug 1471905 is addressed, increase the following number
+# when that happens: 1
+
+set -e
+set -x
+
+hfplus_version=540.1.linux3
+dirname=diskdev_cmds-${hfplus_version}
+make_flags="-j$(nproc)"
+
+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
+
+# Build
+cd $dirname
+# We want to statically link against libcrypto. On CentOS, that requires zlib
+# and libdl, because of FIPS functions pulling in more than necessary from
+# libcrypto (only SHA1 functions are used), but not on Debian, thus
+# --as-needed.
+patch -p1 << 'EOF'
+--- a/newfs_hfs.tproj/Makefile.lnx
++++ b/newfs_hfs.tproj/Makefile.lnx
+@@ -6,3 +6,3 @@
+ newfs_hfs: $(OFILES)
+- ${CC} ${CFLAGS} ${LDFLAGS} -o newfs_hfs ${OFILES} -lcrypto
++ ${CC} ${CFLAGS} ${LDFLAGS} -o newfs_hfs ${OFILES} -Wl,-Bstatic -lcrypto -Wl,-Bdynamic,--as-needed,-lz,-ldl
+
+EOF
+grep -rl sysctl.h . | xargs sed -i /sysctl.h/d
+make $make_flags || exit 1
+cd ..
+
+mkdir hfsplus
+cp $dirname/newfs_hfs.tproj/newfs_hfs hfsplus/newfs_hfs
+## XXX fsck_hfs is unused, but is small and built from the package.
+cp $dirname/fsck_hfs.tproj/fsck_hfs hfsplus/fsck_hfs
+
+# Make a package of the built utils
+cd $root_dir
+tar caf $root_dir/hfsplus.tar.zst hfsplus
diff --git a/build/unix/elfhack/Makefile.in b/build/unix/elfhack/Makefile.in
new file mode 100644
index 0000000000..d5e58efa67
--- /dev/null
+++ b/build/unix/elfhack/Makefile.in
@@ -0,0 +1,46 @@
+#
+# 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
+
+ifdef COMPILE_ENVIRONMENT
+ifndef RELRHACK
+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
+ @echo === provide the pre-elfhacked library as an attachment.
+ @echo === Use --disable-elf-hack until this is fixed.
+ @echo ===
+ # Fail if the library doesn't have $(DT_TYPE) .dynamic info
+ $(READELF) -d $@ | grep '\b$(DT_TYPE)\b'
+ @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
+ [ $$($(READELF) -r $@.bak | wc -l) -gt $$($(READELF) -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
+
+libs:: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX)
+
+.PRECIOUS: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX)
+
+ifndef CROSS_COMPILE
+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
+
+endif
+endif
+endif
diff --git a/build/unix/elfhack/README b/build/unix/elfhack/README
new file mode 100644
index 0000000000..8c68031e33
--- /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 0000000000..2cde16102e
--- /dev/null
+++ b/build/unix/elfhack/dummy.c
@@ -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/. */
+
+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 0000000000..b2a21e4901
--- /dev/null
+++ b/build/unix/elfhack/elf.cpp
@@ -0,0 +1,935 @@
+/* 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 Elf64_Shdr null64_section = {0, SHT_NULL, 0, 0, 0,
+ 0, SHN_UNDEF, 0, 0, 0};
+
+Elf_Shdr null_section(null64_section);
+
+Elf_Ehdr::Elf_Ehdr(std::ifstream& file, unsigned char ei_class,
+ unsigned 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
+ unsigned char e_ident[EI_VERSION];
+ file.seekg(0);
+ file.read((char*)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++) {
+ // The .dynamic section is going to have references to other sections,
+ // so it's better to start with that one and recursively initialize those
+ // other sections first, to avoid possible infinite recursion (bug 1606739).
+ if (tmp_shdr[i]->sh_type == SHT_DYNAMIC) {
+ getSection(i);
+ }
+ }
+ 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;
+
+ bool adjusted_phdr_section = false;
+ // 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;
+ if (!adjusted_phdr_section) {
+ phdr_section->getShdr().sh_addr += phdr.p_vaddr;
+ adjusted_phdr_section = true;
+ }
+ segment->addSection(ehdr);
+ segment->addSection(phdr_section);
+ }
+ if (phdr.p_type == PT_PHDR) {
+ if (!adjusted_phdr_section) {
+ phdr_section->getShdr().sh_addr = phdr.p_vaddr;
+ adjusted_phdr_section = true;
+ }
+ 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(Elf64_Off 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");
+ }
+ }
+ }
+
+ ElfSegment* prevLoad = nullptr;
+ for (auto& it : segments) {
+ if (it->getType() == PT_LOAD) {
+ if (prevLoad) {
+ size_t alignedPrevEnd = (prevLoad->getAddr() + prevLoad->getMemSize() +
+ prevLoad->getAlign() - 1) &
+ ~(prevLoad->getAlign() - 1);
+ size_t alignedStart = it->getAddr() & ~(it->getAlign() - 1);
+ if (alignedPrevEnd > alignedStart) {
+ throw std::runtime_error("Segments overlap");
+ }
+ }
+ prevLoad = it;
+ }
+ }
+
+ // 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();
+
+ // Check sections consistency
+ unsigned int minOffset = 0;
+ for (ElfSection* section = ehdr; section != nullptr;
+ section = section->getNext()) {
+ unsigned int offset = section->getOffset();
+ if (offset < minOffset) {
+ throw std::runtime_error("Sections overlap");
+ }
+ if (section->getType() != SHT_NOBITS) {
+ minOffset = offset + section->getSize();
+ }
+ }
+}
+
+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 = static_cast<char*>(malloc(shdr.sh_size));
+ if (!data) {
+ throw std::runtime_error("Could not malloc ElfSection data");
+ }
+ auto 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;
+}
+
+Elf64_Addr ElfSection::getAddr() {
+ if (shdr.sh_addr != (Elf64_Addr)-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;
+}
+
+Elf64_Off ElfSection::getOffset() {
+ if (shdr.sh_offset != (Elf64_Off)-1) return shdr.sh_offset;
+
+ if (previous == nullptr) return (shdr.sh_offset = 0);
+
+ Elf64_Off 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;
+
+ 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 == (Elf64_Word)-1)
+ shdr.sh_link = getLink() ? getLink()->getIndex() : 0;
+ if (shdr.sh_info == (Elf64_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) 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) 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");
+
+ 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");
+
+ 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) {
+ auto 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, unsigned char ei_class,
+ unsigned 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) {
+ auto 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, unsigned char ei_class,
+ unsigned 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, unsigned char ei_class,
+ unsigned 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 0000000000..719d4ac8f5
--- /dev/null
+++ b/build/unix/elfhack/elfhack.cpp
@@ -0,0 +1,1458 @@
+/* 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 <memory>
+
+#include "elfxx.h"
+#include "mozilla/CheckedInt.h"
+
+#define ver "1"
+#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 ElfRelHack_Section : public ElfSection {
+ public:
+ ElfRelHack_Section(Elf_Shdr& s)
+ : ElfSection(s, nullptr, nullptr),
+ block_size((8 * s.sh_entsize - 1) * s.sh_entsize) {
+ name = elfhack_data;
+ };
+
+ void serialize(std::ofstream& file, unsigned char ei_class,
+ unsigned char ei_data) {
+ if (bitmap) {
+ relr.push_back((bitmap << 1) | 1);
+ }
+ for (std::vector<Elf64_Addr>::iterator i = relr.begin(); i != relr.end();
+ ++i) {
+ Elf_Addr out;
+ out.value = *i;
+ out.serialize(file, ei_class, ei_data);
+ }
+ }
+
+ bool isRelocatable() { return true; }
+
+ void push_back(Elf64_Addr offset) {
+ // The format used for the packed relocations is SHT_RELR, described in
+ // https://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ
+ // The gist of it is that an address is recorded, and the following words,
+ // if their LSB is 1, represent a bitmap of word-size-spaced relocations
+ // at the addresses that follow. There can be multiple such bitmaps, such
+ // that very long streaks of (possibly spaced) relocations can be recorded
+ // in a very compact way.
+ for (;;) {
+ // [block_start; block_start + block_size] represents the range of offsets
+ // the current bitmap can record. If the offset doesn't fall in that
+ // range, or if doesn't align properly to be recorded, we record the
+ // bitmap, and slide the block corresponding to a new bitmap. If the
+ // offset doesn't fall in the range for the new bitmap, or if there wasn't
+ // an active bitmap in the first place, we record the offset and start a
+ // new bitmap for the block that follows it.
+ if (!block_start || offset < block_start ||
+ offset >= block_start + block_size ||
+ (offset - block_start) % shdr.sh_entsize) {
+ if (bitmap) {
+ relr.push_back((bitmap << 1) | 1);
+ block_start += block_size;
+ bitmap = 0;
+ continue;
+ }
+ relr.push_back(offset);
+ block_start = offset + shdr.sh_entsize;
+ break;
+ }
+ bitmap |= 1ULL << ((offset - block_start) / shdr.sh_entsize);
+ break;
+ }
+ shdr.sh_size = (relr.size() + (bitmap ? 1 : 0)) * shdr.sh_entsize;
+ }
+
+ private:
+ std::vector<Elf64_Addr> relr;
+ size_t block_size;
+ Elf64_Addr block_start = 0;
+ Elf64_Addr bitmap = 0;
+};
+
+class ElfRelHackCode_Section : public ElfSection {
+ public:
+ ElfRelHackCode_Section(Elf_Shdr& s, Elf& e,
+ ElfRelHack_Section& relhack_section, unsigned int init,
+ unsigned int mprotect_cb, unsigned int sysconf_cb)
+ : ElfSection(s, nullptr, nullptr),
+ parent(e),
+ relhack_section(relhack_section),
+ init(init),
+ init_trampoline(nullptr),
+ mprotect_cb(mprotect_cb),
+ sysconf_cb(sysconf_cb) {
+ 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;
+ case EM_AARCH64:
+ file += "aarch64";
+ 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");
+
+ relro = parent.getSegmentByType(PT_GNU_RELRO);
+
+ // Find the init symbol
+ entry_point = -1;
+ std::string symbol = "init";
+ if (!init) symbol += "_noinit";
+ if (relro) symbol += "_relro";
+ Elf_SymValue* sym = symtab->lookup(symbol.c_str());
+ 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());
+
+ // If the original init function is located too far away, we're going to
+ // need to use a trampoline. See comment in inject.c.
+ // Theoretically, we should check for (init - instr) > boundary, where
+ // boundary is the platform-dependent limit, and instr is the virtual
+ // address of the instruction that calls the original init, but we don't
+ // have it at this point, so punt to just init.
+ if ((init > 0xffffff && parent.getMachine() == EM_ARM) ||
+ (init > 0x07ffffff && parent.getMachine() == EM_AARCH64)) {
+ Elf_SymValue* trampoline = symtab->lookup("init_trampoline");
+ if (!trampoline) {
+ throw std::runtime_error(
+ "Couldn't find an 'init_trampoline' symbol in the injected code");
+ }
+
+ init_trampoline = trampoline->value.getSection();
+ add_code_section(init_trampoline);
+ }
+
+ // 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();
+ last = *c;
+ }
+ shdr.sh_size = code.back()->getAddr() + code.back()->getSize();
+ data = static_cast<char*>(malloc(shdr.sh_size));
+ if (!data) {
+ throw std::runtime_error("Could not malloc ElfSection data");
+ }
+ 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, unsigned char ei_class,
+ unsigned char ei_data) override {
+ // 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() override { return false; }
+
+ unsigned int getEntryPoint() { return entry_point; }
+
+ void insertBefore(ElfSection* section, bool dirty = true) override {
+ // Adjust the address so that this section is adjacent to the one it's
+ // being inserted before. This avoids creating holes which subsequently
+ // might lead the PHDR-adjusting code to create unnecessary additional
+ // PT_LOADs.
+ shdr.sh_addr =
+ (section->getAddr() - shdr.sh_size) & ~(shdr.sh_addralign - 1);
+ ElfSection::insertBefore(section, dirty);
+ }
+
+ 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[ELF64_R_SYM(r->r_info)].value.getSection();
+ add_code_section(section);
+ }
+ }
+
+ // TODO: sort out which non-aarch64 relocation types should be using
+ // `value` (even though in practice it's either 0 or the same as addend)
+ class pc32_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf64_Off offset,
+ Elf64_Sxword addend, unsigned int addr,
+ Elf64_Word value) {
+ return addr + addend - offset - base_addr;
+ }
+ };
+
+ class arm_plt32_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf64_Off offset,
+ Elf64_Sxword addend, unsigned int addr,
+ Elf64_Word value) {
+ // 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, Elf64_Off offset,
+ Elf64_Sxword addend, unsigned int addr,
+ Elf64_Word value) {
+ /* 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, Elf64_Off offset,
+ Elf64_Sxword addend, unsigned int addr,
+ Elf64_Word value) {
+ return addr + addend;
+ }
+ };
+
+ template <int start, int end>
+ class abs_lo12_nc_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf64_Off offset,
+ Elf64_Sxword addend, unsigned int addr,
+ Elf64_Word value) {
+ // Fill the bits [end:start] of the immediate value in an ADD, LDR or STR
+ // instruction, at bits [21:10].
+ // per ARM® Architecture Reference Manual ARMv8, for ARMv8-A architecture
+ // profile C5.6.4, C5.6.83 or C5.6.178 and ELF for the ARM® 64-bit
+ // Architecture (AArch64) 4.6.6, Table 4-9.
+ Elf64_Word mask = (1 << (end + 1)) - 1;
+ return value | (((((addr + addend) & mask) >> start) & 0xfff) << 10);
+ }
+ };
+
+ class adr_prel_pg_hi21_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf64_Off offset,
+ Elf64_Sxword addend, unsigned int addr,
+ Elf64_Word value) {
+ // Fill the bits [32:12] of the immediate value in a ADRP instruction,
+ // at bits [23:5]+[30:29].
+ // per ARM® Architecture Reference Manual ARMv8, for ARMv8-A architecture
+ // profile C5.6.10 and ELF for the ARM® 64-bit Architecture
+ // (AArch64) 4.6.6, Table 4-9.
+ Elf64_Word imm = ((addr + addend) >> 12) - ((base_addr + offset) >> 12);
+ Elf64_Word immLo = (imm & 0x3) << 29;
+ Elf64_Word immHi = (imm & 0x1ffffc) << 3;
+ return value & 0x9f00001f | immLo | immHi;
+ }
+ };
+
+ class call26_relocation {
+ public:
+ Elf32_Addr operator()(unsigned int base_addr, Elf64_Off offset,
+ Elf64_Sxword addend, unsigned int addr,
+ Elf64_Word value) {
+ // Fill the bits [27:2] of the immediate value in a BL instruction,
+ // at bits [25:0].
+ // per ARM® Architecture Reference Manual ARMv8, for ARMv8-A architecture
+ // profile C5.6.26 and ELF for the ARM® 64-bit Architecture
+ // (AArch64) 4.6.6, Table 4-10.
+ return value | (((addr + addend - offset - base_addr) & 0x0ffffffc) >> 2);
+ }
+ };
+
+ 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, value);
+ 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;
+ Elf64_Word value;
+ memcpy(&value, base + r->r_offset, 4);
+ Elf32_Addr new_value =
+ relocation(the_code->getAddr(), r->r_offset, r->r_addend, addr, value);
+ memcpy(base + r->r_offset, &new_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[ELF64_R_SYM(r->r_info)].name;
+ unsigned int addr;
+ if (symtab->syms[ELF64_R_SYM(r->r_info)].value.getSection() == nullptr) {
+ if (strcmp(name, "relhack") == 0) {
+ addr = relhack_section.getAddr();
+ } else if (strcmp(name, "relhack_end") == 0) {
+ addr = relhack_section.getAddr() + relhack_section.getSize();
+ } else if (strcmp(name, "__ehdr_start") == 0) {
+ // TODO: change this ugly hack to something better
+ ElfSection* ehdr = parent.getSection(1)->getPrevious()->getPrevious();
+ addr = ehdr->getAddr();
+ } else if (strcmp(name, "original_init") == 0) {
+ if (init_trampoline) {
+ addr = init_trampoline->getAddr();
+ } else {
+ addr = init;
+ }
+ } else if (strcmp(name, "real_original_init") == 0) {
+ addr = init;
+ } else if (relro && strcmp(name, "mprotect_cb") == 0) {
+ addr = mprotect_cb;
+ } else if (relro && strcmp(name, "sysconf_cb") == 0) {
+ addr = sysconf_cb;
+ } else if (relro && strcmp(name, "relro_start") == 0) {
+ addr = relro->getAddr();
+ } else if (relro && strcmp(name, "relro_end") == 0) {
+ addr = (relro->getAddr() + relro->getMemSize());
+ } 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[ELF64_R_SYM(r->r_info)].value.getSection();
+ assert((section->getType() == SHT_PROGBITS) &&
+ (section->getFlags() & SHF_EXECINSTR));
+ addr = symtab->syms[ELF64_R_SYM(r->r_info)].value.getValue();
+ }
+ // Do the relocation
+#define REL(machine, type) (EM_##machine | (R_##machine##_##type << 8))
+ switch (elf->getMachine() | (ELF64_R_TYPE(r->r_info) << 8)) {
+ case REL(X86_64, PC32):
+ case REL(X86_64, PLT32):
+ case REL(386, PC32):
+ case REL(386, GOTPC):
+ case REL(ARM, GOTPC):
+ case REL(ARM, REL32):
+ case REL(AARCH64, PREL32):
+ case REL(AARCH64,
+ PREL64): // In theory PREL64 should have its own relocation
+ // function, but in practice it doesn't matter.
+ 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(AARCH64, ADD_ABS_LO12_NC):
+ apply_relocation<abs_lo12_nc_relocation<0, 11>>(the_code, buf, &*r,
+ addr);
+ break;
+ case REL(AARCH64, ADR_PREL_PG_HI21):
+ apply_relocation<adr_prel_pg_hi21_relocation>(the_code, buf, &*r,
+ addr);
+ break;
+ case REL(AARCH64, LDST32_ABS_LO12_NC):
+ apply_relocation<abs_lo12_nc_relocation<2, 11>>(the_code, buf, &*r,
+ addr);
+ break;
+ case REL(AARCH64, LDST64_ABS_LO12_NC):
+ apply_relocation<abs_lo12_nc_relocation<3, 11>>(the_code, buf, &*r,
+ addr);
+ break;
+ case REL(AARCH64, CALL26):
+ apply_relocation<call26_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;
+ ElfRelHack_Section& relhack_section;
+ std::vector<ElfSection*> code;
+ unsigned int init;
+ ElfSection* init_trampoline;
+ unsigned int mprotect_cb;
+ unsigned int sysconf_cb;
+ int entry_point;
+ ElfSegment* relro;
+};
+
+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) {
+ 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 = (Elf64_Xword)-1LL;
+ phdr.p_memsz = (Elf64_Xword)-1LL;
+ ElfSegment* newSegment = new ElfSegment(&phdr);
+ elf->insertSegmentAfter(segment, newSegment);
+ for (; it != segment->end(); ++it) {
+ newSegment->addSection(*it);
+ }
+ for (it = newSegment->begin(); it != newSegment->end(); ++it) {
+ segment->removeSection(*it);
+ }
+ break;
+ }
+ }
+}
+
+// EH_FRAME constants
+static const unsigned char DW_EH_PE_absptr = 0x00;
+static const unsigned char DW_EH_PE_omit = 0xff;
+
+// Data size
+static const unsigned char DW_EH_PE_LEB128 = 0x01;
+static const unsigned char DW_EH_PE_data2 = 0x02;
+static const unsigned char DW_EH_PE_data4 = 0x03;
+static const unsigned char DW_EH_PE_data8 = 0x04;
+
+// Data signedness
+static const unsigned char DW_EH_PE_signed = 0x08;
+
+// Modifiers
+static const unsigned char DW_EH_PE_pcrel = 0x10;
+
+// Return the data size part of the encoding value
+static unsigned char encoding_data_size(unsigned char encoding) {
+ return encoding & 0x07;
+}
+
+// Advance `step` bytes in the buffer at `data` with size `size`, returning
+// the advanced buffer pointer and remaining size.
+// Returns true if step <= size.
+static bool advance_buffer(char** data, size_t* size, size_t step) {
+ if (step > *size) return false;
+
+ *data += step;
+ *size -= step;
+ return true;
+}
+
+// Advance in the given buffer, skipping the full length of the variable-length
+// encoded LEB128 type in CIE/FDE data.
+static bool skip_LEB128(char** data, size_t* size) {
+ if (!*size) return false;
+
+ while (*size && (*(*data)++ & (char)0x80)) {
+ (*size)--;
+ }
+ return true;
+}
+
+// Advance in the given buffer, skipping the full length of a pointer encoded
+// with the given encoding.
+static bool skip_eh_frame_pointer(char** data, size_t* size,
+ unsigned char encoding) {
+ switch (encoding_data_size(encoding)) {
+ case DW_EH_PE_data2:
+ return advance_buffer(data, size, 2);
+ case DW_EH_PE_data4:
+ return advance_buffer(data, size, 4);
+ case DW_EH_PE_data8:
+ return advance_buffer(data, size, 8);
+ case DW_EH_PE_LEB128:
+ return skip_LEB128(data, size);
+ }
+ throw std::runtime_error("unreachable");
+}
+
+// Specialized implementations for adjust_eh_frame_pointer().
+template <typename T>
+static bool adjust_eh_frame_sized_pointer(char** data, size_t* size,
+ ElfSection* eh_frame,
+ unsigned int origAddr, Elf* elf) {
+ if (*size < sizeof(T)) return false;
+
+ serializable<FixedSizeData<T>> pointer(*data, *size, elf->getClass(),
+ elf->getData());
+ mozilla::CheckedInt<T> value = pointer.value;
+ if (origAddr < eh_frame->getAddr()) {
+ unsigned int diff = eh_frame->getAddr() - origAddr;
+ value -= diff;
+ } else {
+ unsigned int diff = origAddr - eh_frame->getAddr();
+ value += diff;
+ }
+ if (!value.isValid())
+ throw std::runtime_error("Overflow while adjusting eh_frame");
+ pointer.value = value.value();
+ pointer.serialize(*data, *size, elf->getClass(), elf->getData());
+ return advance_buffer(data, size, sizeof(T));
+}
+
+// In the given eh_frame section, adjust the pointer with the given encoding,
+// pointed to by the given buffer (`data`, `size`), considering the eh_frame
+// section was originally at `origAddr`. Also advances in the buffer.
+static bool adjust_eh_frame_pointer(char** data, size_t* size,
+ unsigned char encoding,
+ ElfSection* eh_frame, unsigned int origAddr,
+ Elf* elf) {
+ if ((encoding & 0x70) != DW_EH_PE_pcrel)
+ return skip_eh_frame_pointer(data, size, encoding);
+
+ if (encoding & DW_EH_PE_signed) {
+ switch (encoding_data_size(encoding)) {
+ case DW_EH_PE_data2:
+ return adjust_eh_frame_sized_pointer<int16_t>(data, size, eh_frame,
+ origAddr, elf);
+ case DW_EH_PE_data4:
+ return adjust_eh_frame_sized_pointer<int32_t>(data, size, eh_frame,
+ origAddr, elf);
+ case DW_EH_PE_data8:
+ return adjust_eh_frame_sized_pointer<int64_t>(data, size, eh_frame,
+ origAddr, elf);
+ }
+ } else {
+ switch (encoding_data_size(encoding)) {
+ case DW_EH_PE_data2:
+ return adjust_eh_frame_sized_pointer<uint16_t>(data, size, eh_frame,
+ origAddr, elf);
+ case DW_EH_PE_data4:
+ return adjust_eh_frame_sized_pointer<uint32_t>(data, size, eh_frame,
+ origAddr, elf);
+ case DW_EH_PE_data8:
+ return adjust_eh_frame_sized_pointer<uint64_t>(data, size, eh_frame,
+ origAddr, elf);
+ }
+ }
+
+ throw std::runtime_error("Unsupported eh_frame pointer encoding");
+}
+
+// The eh_frame section may contain "PC"-relative pointers. If we move the
+// section, those need to be adjusted. Other type of pointers are relative to
+// sections we don't touch.
+static void adjust_eh_frame(ElfSection* eh_frame, unsigned int origAddr,
+ Elf* elf) {
+ if (eh_frame->getAddr() == origAddr) // nothing to do;
+ return;
+
+ char* data = const_cast<char*>(eh_frame->getData());
+ size_t size = eh_frame->getSize();
+ unsigned char LSDAencoding = DW_EH_PE_omit;
+ unsigned char FDEencoding = DW_EH_PE_absptr;
+ bool hasZ = false;
+
+ // Decoding of eh_frame based on https://www.airs.com/blog/archives/460
+ while (size) {
+ if (size < sizeof(uint32_t)) goto malformed;
+
+ serializable<FixedSizeData<uint32_t>> entryLength(
+ data, size, elf->getClass(), elf->getData());
+ if (!advance_buffer(&data, &size, sizeof(uint32_t))) goto malformed;
+
+ char* cursor = data;
+ size_t length = entryLength.value;
+
+ if (length == 0) {
+ continue;
+ }
+
+ if (size < sizeof(uint32_t)) goto malformed;
+
+ serializable<FixedSizeData<uint32_t>> id(data, size, elf->getClass(),
+ elf->getData());
+ if (!advance_buffer(&cursor, &length, sizeof(uint32_t))) goto malformed;
+
+ if (id.value == 0) {
+ // This is a Common Information Entry
+ if (length < 2) goto malformed;
+ // Reset LSDA and FDE encodings, and hasZ for subsequent FDEs.
+ LSDAencoding = DW_EH_PE_omit;
+ FDEencoding = DW_EH_PE_absptr;
+ hasZ = false;
+ // CIE version. Should only be 1 or 3.
+ char version = *cursor++;
+ length--;
+ if (version != 1 && version != 3) {
+ throw std::runtime_error("Unsupported eh_frame version");
+ }
+ // NUL terminated string.
+ const char* augmentationString = cursor;
+ size_t l = strnlen(augmentationString, length - 1);
+ if (l == length - 1) goto malformed;
+ if (!advance_buffer(&cursor, &length, l + 1)) goto malformed;
+ // Skip code alignment factor (LEB128)
+ if (!skip_LEB128(&cursor, &length)) goto malformed;
+ // Skip data alignment factor (LEB128)
+ if (!skip_LEB128(&cursor, &length)) goto malformed;
+ // Skip return address register (single byte in CIE version 1, LEB128
+ // in CIE version 3)
+ if (version == 1) {
+ if (!advance_buffer(&cursor, &length, 1)) goto malformed;
+ } else {
+ if (!skip_LEB128(&cursor, &length)) goto malformed;
+ }
+ // Past this, it's data driven by the contents of the augmentation string.
+ for (size_t i = 0; i < l; i++) {
+ if (!length) goto malformed;
+ switch (augmentationString[i]) {
+ case 'z':
+ if (!skip_LEB128(&cursor, &length)) goto malformed;
+ hasZ = true;
+ break;
+ case 'L':
+ LSDAencoding = *cursor++;
+ length--;
+ break;
+ case 'R':
+ FDEencoding = *cursor++;
+ length--;
+ break;
+ case 'P': {
+ unsigned char encoding = (unsigned char)*cursor++;
+ length--;
+ if (!adjust_eh_frame_pointer(&cursor, &length, encoding, eh_frame,
+ origAddr, elf))
+ goto malformed;
+ } break;
+ default:
+ goto malformed;
+ }
+ }
+ } else {
+ // This is a Frame Description Entry
+ // Starting address
+ if (!adjust_eh_frame_pointer(&cursor, &length, FDEencoding, eh_frame,
+ origAddr, elf))
+ goto malformed;
+
+ if (LSDAencoding != DW_EH_PE_omit) {
+ // Skip number of bytes, same size as the starting address.
+ if (!skip_eh_frame_pointer(&cursor, &length, FDEencoding))
+ goto malformed;
+ if (hasZ) {
+ if (!skip_LEB128(&cursor, &length)) goto malformed;
+ }
+ // pointer to the LSDA.
+ if (!adjust_eh_frame_pointer(&cursor, &length, LSDAencoding, eh_frame,
+ origAddr, elf))
+ goto malformed;
+ }
+ }
+
+ data += entryLength.value;
+ size -= entryLength.value;
+ }
+ return;
+
+malformed:
+ throw std::runtime_error("malformed .eh_frame");
+}
+
+template <typename Rel_Type>
+int do_relocation_section(Elf* elf, unsigned int rel_type,
+ unsigned int rel_type2, bool force) {
+ ElfDynamic_Section* dyn = elf->getDynSection();
+ if (dyn == nullptr) {
+ fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n");
+ return -1;
+ }
+
+ ElfRel_Section<Rel_Type>* section =
+ (ElfRel_Section<Rel_Type>*)dyn->getSectionForType(Rel_Type::d_tag);
+ if (section == nullptr) {
+ fprintf(stderr, "No relocations\n");
+ return -1;
+ }
+ assert(section->getType() == Rel_Type::sh_type);
+
+ Elf64_Shdr relhack64_section = {0,
+ SHT_PROGBITS,
+ SHF_ALLOC,
+ 0,
+ (Elf64_Off)-1LL,
+ 0,
+ SHN_UNDEF,
+ 0,
+ Elf_Addr::size(elf->getClass()),
+ Elf_Addr::size(elf->getClass())};
+ Elf64_Shdr relhackcode64_section = {0,
+ SHT_PROGBITS,
+ SHF_ALLOC | SHF_EXECINSTR,
+ 0,
+ (Elf64_Off)-1LL,
+ 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(relhack64_section);
+ Elf_Shdr relhackcode_section(relhackcode64_section);
+ auto relhack_ptr = std::make_unique<ElfRelHack_Section>(relhack_section);
+ auto relhack = relhack_ptr.get();
+
+ ElfSymtab_Section* symtab = (ElfSymtab_Section*)section->getLink();
+ Elf_SymValue* sym = symtab->lookup("__cxa_pure_virtual");
+
+ std::vector<Rel_Type> new_rels;
+ std::vector<Rel_Type> init_array_relocs;
+ size_t init_array_insert = 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 (!ELF64_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 (ELF64_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 ((ELF64_R_TYPE(i->r_info) == rel_type2) &&
+ (sym == &symtab->syms[ELF64_R_SYM(i->r_info)])) {
+ memset((char*)loc.getBuffer(), 0, entry_sz);
+ continue;
+ }
+ }
+ }
+ // Keep track of the relocations associated with the init_array section.
+ if (init_array && i->r_offset >= init_array->getAddr() &&
+ i->r_offset < init_array->getAddr() + init_array->getSize()) {
+ init_array_relocs.push_back(*i);
+ init_array_insert = new_rels.size();
+ } else if (!(loc.getSection()->getFlags() & SHF_WRITE) ||
+ (ELF64_R_TYPE(i->r_info) != rel_type)) {
+ // 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 if (i->r_offset & 1) {
+ // RELR packing doesn't support relocations at an odd address, but
+ // there shouldn't be any.
+ new_rels.push_back(*i);
+ } else {
+ // With Elf_Rel, the value pointed by the relocation offset is the addend.
+ // With Elf_Rela, the addend is in the relocation entry, but the elfhacked
+ // relocation info doesn't contain it. Elfhack relies on the value pointed
+ // by the relocation offset to also contain the addend. Which is true with
+ // BFD ld and gold, but not lld, which leaves that nulled out. So if that
+ // value is nulled out, we update it to the addend.
+ Elf_Addr addr(loc.getBuffer(), entry_sz, elf->getClass(), elf->getData());
+ unsigned int addend = get_addend(&*i, elf);
+ if (addr.value == 0) {
+ addr.value = addend;
+ addr.serialize(const_cast<char*>(loc.getBuffer()), entry_sz,
+ elf->getClass(), elf->getData());
+ } else if (addr.value != addend) {
+ fprintf(stderr,
+ "Relocation addend inconsistent with content. Skipping\n");
+ return -1;
+ }
+ relhack->push_back(i->r_offset);
+ }
+ }
+
+ if (init_array) {
+ // Some linkers create a DT_INIT_ARRAY section that, for all purposes,
+ // is empty: it only contains 0x0 or 0xffffffff pointers with no
+ // relocations. In some other cases, there can be null pointers with no
+ // relocations in the middle of the section. Example: crtend_so.o in the
+ // Android NDK contains a sized .init_array with a null pointer and no
+ // relocation, which ends up in all Android libraries, and in some cases it
+ // ends up in the middle of the final .init_array section. If we have such a
+ // reusable slot at the beginning of .init_array, we just use it. It we have
+ // one in the middle of .init_array, we slide its content to move the "hole"
+ // at the beginning and use it there (we need our injected code to run
+ // before any other). Otherwise, replace the first entry and keep the
+ // original pointer.
+ std::sort(init_array_relocs.begin(), init_array_relocs.end(),
+ [](Rel_Type& a, Rel_Type& b) { return a.r_offset < b.r_offset; });
+ size_t expected = init_array->getAddr();
+ const size_t zero = 0;
+ const size_t all = SIZE_MAX;
+ const char* data = init_array->getData();
+ size_t length = Elf_Addr::size(elf->getClass());
+ size_t off = 0;
+ for (; off < init_array_relocs.size(); off++) {
+ auto& r = init_array_relocs[off];
+ if (r.r_offset >= expected + length &&
+ (memcmp(data + off * length, &zero, length) == 0 ||
+ memcmp(data + off * length, &all, length) == 0)) {
+ // We found a hole, move the preceding entries.
+ while (off) {
+ auto& p = init_array_relocs[--off];
+ if (ELF64_R_TYPE(p.r_info) == rel_type) {
+ unsigned int addend = get_addend(&p, elf);
+ p.r_offset += length;
+ set_relative_reloc(&p, elf, addend);
+ } else {
+ fprintf(stderr,
+ "Unsupported relocation type in DT_INIT_ARRAY. Skipping\n");
+ return -1;
+ }
+ }
+ break;
+ }
+ expected = r.r_offset + length;
+ }
+
+ if (off == 0) {
+ // We either found a hole above, and can now use the first entry,
+ // or the init_array section is effectively empty (see further above)
+ // and we also can use the first entry.
+ // Either way, code further below will take care of actually setting
+ // the right r_info and r_added for the relocation.
+ Rel_Type rel;
+ rel.r_offset = init_array->getAddr();
+ init_array_relocs.insert(init_array_relocs.begin(), rel);
+ } else {
+ // Use relocated value of DT_INIT_ARRAY's first entry for the
+ // function to be called by the injected code.
+ auto& rel = init_array_relocs[0];
+ unsigned int addend = get_addend(&rel, elf);
+ if (ELF64_R_TYPE(rel.r_info) == rel_type) {
+ original_init = addend;
+ } else if (ELF64_R_TYPE(rel.r_info) == rel_type2) {
+ ElfSymtab_Section* symtab = (ElfSymtab_Section*)section->getLink();
+ original_init =
+ symtab->syms[ELF64_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;
+ }
+ }
+
+ new_rels.insert(std::next(new_rels.begin(), init_array_insert),
+ init_array_relocs.begin(), init_array_relocs.end());
+ }
+
+ unsigned int mprotect_cb = 0;
+ unsigned int sysconf_cb = 0;
+ // If there is a relro segment, our injected code will run after the linker
+ // sets the corresponding pages read-only. We need to make our code change
+ // that to read-write before applying relocations, which means it needs to
+ // call mprotect. To do that, we need to find a reference to the mprotect
+ // symbol. In case the library already has one, we use that, but otherwise, we
+ // add the symbol. Then the injected code needs to be able to call the
+ // corresponding function, which means it needs access to a pointer to it. We
+ // get such a pointer by making the linker apply a relocation for the symbol
+ // at an address our code can read. The problem here is that there is not much
+ // relocated space where we can put such a pointer, so we abuse the bss
+ // section temporarily (it will be restored to a null value before any code
+ // can actually use it)
+ if (elf->getSegmentByType(PT_GNU_RELRO)) {
+ ElfSection* gnu_versym = dyn->getSectionForType(DT_VERSYM);
+ auto lookup = [&symtab, &gnu_versym](const char* symbol) {
+ Elf_SymValue* sym_value = symtab->lookup(symbol, STT(FUNC));
+ if (!sym_value) {
+ symtab->syms.emplace_back();
+ sym_value = &symtab->syms.back();
+ symtab->grow(symtab->syms.size() * symtab->getEntSize());
+ sym_value->name =
+ ((ElfStrtab_Section*)symtab->getLink())->getStr(symbol);
+ sym_value->info = ELF64_ST_INFO(STB_GLOBAL, STT_FUNC);
+ sym_value->other = STV_DEFAULT;
+ new (&sym_value->value) ElfLocation(nullptr, 0, ElfLocation::ABSOLUTE);
+ sym_value->size = 0;
+ sym_value->defined = false;
+
+ // The DT_VERSYM data (in the .gnu.version section) has the same number
+ // of entries as the symbols table. Since we added one entry there, we
+ // need to add one entry here. Zeroes in the extra data means no version
+ // for that symbol, which is the simplest thing to do.
+ if (gnu_versym) {
+ gnu_versym->grow(gnu_versym->getSize() + gnu_versym->getEntSize());
+ }
+ }
+ return sym_value;
+ };
+
+ Elf_SymValue* mprotect = lookup("mprotect");
+ Elf_SymValue* sysconf = lookup("sysconf");
+
+ // Add relocations for the mprotect and sysconf symbols.
+ auto add_relocation_to = [&new_rels, &symtab, rel_type2](
+ Elf_SymValue* symbol, unsigned int location) {
+ new_rels.emplace_back();
+ Rel_Type& rel = new_rels.back();
+ memset(&rel, 0, sizeof(rel));
+ rel.r_info = ELF64_R_INFO(
+ std::distance(symtab->syms.begin(),
+ std::vector<Elf_SymValue>::iterator(symbol)),
+ rel_type2);
+ rel.r_offset = location;
+ return location;
+ };
+
+ // Find the beginning of the bss section, and use an aligned location in
+ // there for the relocation.
+ for (ElfSection* s = elf->getSection(1); s != nullptr; s = s->getNext()) {
+ if (s->getType() != SHT_NOBITS ||
+ (s->getFlags() & (SHF_TLS | SHF_WRITE)) != SHF_WRITE) {
+ continue;
+ }
+ size_t ptr_size = Elf_Addr::size(elf->getClass());
+ size_t usable_start = (s->getAddr() + ptr_size - 1) & ~(ptr_size - 1);
+ size_t usable_end = (s->getAddr() + s->getSize()) & ~(ptr_size - 1);
+ if (usable_end - usable_start >= 2 * ptr_size) {
+ mprotect_cb = add_relocation_to(mprotect, usable_start);
+ sysconf_cb = add_relocation_to(sysconf, usable_start + ptr_size);
+ break;
+ }
+ }
+
+ if (mprotect_cb == 0 || sysconf_cb == 0) {
+ fprintf(stderr, "Couldn't find .bss. Skipping\n");
+ return -1;
+ }
+ }
+
+ size_t old_size = section->getSize();
+
+ section->rels.assign(new_rels.begin(), new_rels.end());
+ section->shrink(new_rels.size() * section->getEntSize());
+
+ auto relhackcode_ptr = std::make_unique<ElfRelHackCode_Section>(
+ relhackcode_section, *elf, *relhack, original_init, mprotect_cb,
+ sysconf_cb);
+ auto relhackcode = relhackcode_ptr.get();
+ // Find the first executable section, and insert the relhack code before
+ // that. The relhack data is inserted between .rel.dyn and .rel.plt.
+ ElfSection* first_executable = nullptr;
+ for (ElfSection* s = elf->getSection(1); s != nullptr; s = s->getNext()) {
+ if (s->getFlags() & SHF_EXECINSTR) {
+ first_executable = s;
+ break;
+ }
+ }
+
+ if (!first_executable) {
+ fprintf(stderr, "Couldn't find executable section. Skipping\n");
+ return -1;
+ }
+
+ // Once the pointers for relhack, relhackcode, and init are inserted,
+ // their ownership is transferred to the Elf object, which will free
+ // them when itself is freed. Hence the .release() calls here (and
+ // the init.release() call later on). Please note that the raw
+ // pointers will continue to be used after .release(), which is why
+ // we are caching them (since .release() will end up setting the
+ // smart pointer's internal raw pointer to nullptr).
+
+ relhack->insertBefore(section);
+ relhack_ptr.release();
+
+ relhackcode->insertBefore(first_executable);
+ relhackcode_ptr.release();
+
+ // Don't try further if we can't gain from the relocation section size change.
+ // We account for the fact we're going to split the PT_LOAD before the
+ // injected code section, so the overhead of the page alignment for section
+ // needs to be accounted for.
+ size_t align = first_executable->getSegmentByType(PT_LOAD)->getAlign();
+ size_t new_size = relhack->getSize() + section->getSize() +
+ relhackcode->getSize() +
+ (relhackcode->getAddr() & (align - 1));
+ if (!force && (new_size >= old_size || old_size - new_size < align)) {
+ fprintf(stderr, "No gain. Skipping\n");
+ return -1;
+ }
+
+ // .eh_frame/.eh_frame_hdr may be between the relocation sections and the
+ // executable sections. When that happens, we may end up creating a separate
+ // PT_LOAD for just both of them because they are not considered relocatable.
+ // But they are, in fact, kind of relocatable, albeit with some manual work.
+ // Which we'll do here.
+ ElfSegment* eh_frame_segment = elf->getSegmentByType(PT_GNU_EH_FRAME);
+ ElfSection* eh_frame_hdr =
+ eh_frame_segment ? eh_frame_segment->getFirstSection() : nullptr;
+ // The .eh_frame section usually follows the eh_frame_hdr section.
+ ElfSection* eh_frame = eh_frame_hdr ? eh_frame_hdr->getNext() : nullptr;
+ ElfSection* first = eh_frame_hdr;
+ ElfSection* second = eh_frame;
+ if (eh_frame && strcmp(eh_frame->getName(), ".eh_frame")) {
+ // But sometimes it appears *before* the eh_frame_hdr section.
+ eh_frame = eh_frame_hdr->getPrevious();
+ first = eh_frame;
+ second = eh_frame_hdr;
+ }
+ if (eh_frame_hdr && (!eh_frame || strcmp(eh_frame->getName(), ".eh_frame"))) {
+ throw std::runtime_error(
+ "Expected to find an .eh_frame section adjacent to .eh_frame_hdr");
+ }
+ if (eh_frame && first->getAddr() > relhack->getAddr() &&
+ second->getAddr() < first_executable->getAddr()) {
+ // The distance between both sections needs to be preserved because
+ // eh_frame_hdr contains relative offsets to eh_frame. Well, they could be
+ // relocated too, but it's not worth the effort for the few number of bytes
+ // this would save.
+ Elf64_Off distance = second->getAddr() - first->getAddr();
+ Elf64_Addr origAddr = eh_frame->getAddr();
+ ElfSection* previous = first->getPrevious();
+ first->getShdr().sh_addr = (previous->getAddr() + previous->getSize() +
+ first->getAddrAlign() - 1) &
+ ~(first->getAddrAlign() - 1);
+ second->getShdr().sh_addr =
+ (first->getAddr() + std::min(first->getSize(), distance) +
+ second->getAddrAlign() - 1) &
+ ~(second->getAddrAlign() - 1);
+ // Re-adjust to keep the original distance.
+ // If the first section has a smaller alignment requirement than the second,
+ // the second will be farther away, so we need to adjust the first.
+ // If the second section has a smaller alignment requirement than the first,
+ // it will already be at the right distance.
+ first->getShdr().sh_addr = second->getAddr() - distance;
+ assert(distance == second->getAddr() - first->getAddr());
+ first->markDirty();
+ adjust_eh_frame(eh_frame, origAddr, elf);
+ }
+
+ // Adjust PT_LOAD segments
+ for (ElfSegment* segment = elf->getSegmentByType(PT_LOAD); segment;
+ segment = elf->getSegmentByType(PT_LOAD, segment)) {
+ maybe_split_segment(elf, segment);
+ }
+
+ // Ensure Elf sections will be at their final location.
+ elf->normalize();
+ auto init =
+ std::make_unique<ElfLocation>(relhackcode, relhackcode->getEntryPoint());
+ if (init_array) {
+ // Adjust the first DT_INIT_ARRAY entry to point at the injected code
+ // by transforming its relocation into a relative one pointing to the
+ // address of the injected code.
+ Rel_Type* rel = &section->rels[init_array_insert];
+ rel->r_info = ELF64_R_INFO(0, rel_type); // Set as a relative relocation
+ set_relative_reloc(rel, elf, init->getValue());
+ } else {
+ if (dyn->setValueForType(DT_INIT, init.get())) {
+ init.release();
+ } else {
+ 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) {
+ 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);
+ break;
+ case EM_X86_64:
+ exit = do_relocation_section<Elf_Rela>(&elf, R_X86_64_RELATIVE,
+ R_X86_64_64, force);
+ break;
+ case EM_ARM:
+ exit = do_relocation_section<Elf_Rel>(&elf, R_ARM_RELATIVE, R_ARM_ABS32,
+ force);
+ break;
+ case EM_AARCH64:
+ exit = do_relocation_section<Elf_Rela>(&elf, R_AARCH64_RELATIVE,
+ R_AARCH64_ABS64, force);
+ break;
+ default:
+ throw std::runtime_error("unsupported architecture");
+ }
+ 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;
+ }
+
+ // When both elfhack sections are in the same segment, try to merge
+ // the segment that contains them both and the following segment.
+ // When the elfhack sections are in separate segments, try to merge
+ // those segments.
+ ElfSegment* first = data->getSegmentByType(PT_LOAD);
+ ElfSegment* second = text->getSegmentByType(PT_LOAD);
+ if (first == second) {
+ second = elf.getSegmentByType(PT_LOAD, first);
+ }
+
+ // Only merge the segments when their flags match.
+ if (second->getFlags() != first->getFlags()) {
+ fprintf(stderr, "Couldn't merge 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);
+ elf.normalize();
+
+ 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;
+ 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 (revert) {
+ undo_file(argv[arg], backup);
+ } else
+ do_file(argv[arg], backup, force);
+ }
+
+ free(rundir);
+ return 0;
+}
diff --git a/build/unix/elfhack/elfxx.h b/build/unix/elfhack/elfxx.h
new file mode 100644
index 0000000000..26dce9b9cd
--- /dev/null
+++ b/build/unix/elfhack/elfxx.h
@@ -0,0 +1,700 @@
+/* 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 <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;
+
+template <typename X>
+class FixedSizeData {
+ public:
+ struct Wrapper {
+ X value;
+ };
+ typedef Wrapper Type32;
+ typedef Wrapper Type64;
+
+ template <class endian, typename R, typename T>
+ static void swap(T& t, R& r) {
+ r.value = endian::swap(t.value);
+ }
+};
+
+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::Type64 {
+ public:
+ serializable(){};
+ serializable(const typename T::Type64& p) : T::Type64(p){};
+
+ private:
+ template <typename R>
+ void init(const char* buf, size_t len, unsigned 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, unsigned 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, unsigned char ei_class,
+ unsigned 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, unsigned char ei_class,
+ unsigned 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, unsigned char ei_class,
+ unsigned 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, unsigned char ei_class,
+ unsigned 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(unsigned 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(Elf64_Off offset);
+
+ ElfSegment* getSegmentByType(unsigned int type, ElfSegment* last = nullptr);
+
+ ElfDynamic_Section* getDynSection();
+
+ void normalize();
+ void write(std::ofstream& file);
+
+ unsigned char getClass();
+ unsigned char getData();
+ unsigned char getType();
+ unsigned 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() { free(data); }
+
+ const char* getName() { return name; }
+ unsigned int getType() { return shdr.sh_type; }
+ unsigned int getFlags() { return shdr.sh_flags; }
+ Elf64_Addr getAddr();
+ Elf64_Off 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;
+ markDirty();
+ }
+ }
+
+ void grow(unsigned int newsize) {
+ if (newsize > shdr.sh_size) {
+ data = static_cast<char*>(realloc(data, newsize));
+ memset(data + shdr.sh_size, 0, newsize - shdr.sh_size);
+ shdr.sh_size = newsize;
+ markDirty();
+ }
+ }
+
+ Elf64_Off 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);
+ }
+
+ virtual 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, unsigned char ei_class,
+ unsigned char ei_data) {
+ if (getType() == SHT_NOBITS) return;
+ file.seekp(getOffset());
+ file.write(data, getSize());
+ }
+
+ ElfSegment* getSegmentByType(unsigned int type);
+
+ 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);
+ }
+
+ 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();
+
+ 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, unsigned char ei_class, unsigned char ei_data);
+ void serialize(std::ofstream& file, unsigned char ei_class,
+ unsigned 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, unsigned char ei_class, unsigned 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, unsigned char ei_class,
+ unsigned 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, unsigned char ei_class,
+ unsigned 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() : serializable<Elf_Rel_Traits>(){};
+
+ Elf_Rel(std::ifstream& file, unsigned char ei_class, unsigned 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() : serializable<Elf_Rela_Traits>(){};
+
+ Elf_Rela(std::ifstream& file, unsigned char ei_class, unsigned 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) {
+ auto 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, unsigned char ei_class,
+ unsigned 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, unsigned char ei_class,
+ unsigned 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 unsigned char Elf::getClass() { return ehdr->e_ident[EI_CLASS]; }
+
+inline unsigned char Elf::getData() { return ehdr->e_ident[EI_DATA]; }
+
+inline unsigned char Elf::getType() { return ehdr->e_type; }
+
+inline unsigned 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 0000000000..f1a8e36e1c
--- /dev/null
+++ b/build/unix/elfhack/inject.c
@@ -0,0 +1,225 @@
+/* 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 <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.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_Phdr Elf64_Phdr
+# define Elf_Addr Elf64_Addr
+# define Elf_Word Elf64_Word
+# define Elf_Dyn Elf64_Dyn
+#else
+# define Elf_Phdr Elf32_Phdr
+# define Elf_Ehdr Elf32_Ehdr
+# define Elf_Addr Elf32_Addr
+# define Elf_Word Elf32_Word
+# define Elf_Dyn Elf32_Dyn
+#endif
+
+#ifdef RELRHACK
+# include "relrhack.h"
+# define mprotect_cb mprotect
+# define sysconf_cb sysconf
+
+#else
+// On ARM, PC-relative function calls have a limit in how far they can jump,
+// which might not be enough for e.g. libxul.so. The easy way out would be
+// to use the long_call attribute, which forces the compiler to generate code
+// that can call anywhere, but clang doesn't support the attribute yet
+// (https://bugs.llvm.org/show_bug.cgi?id=40623), and while the command-line
+// equivalent does exist, it's currently broken
+// (https://bugs.llvm.org/show_bug.cgi?id=40624). So we create a manual
+// trampoline, corresponding to the code GCC generates with long_call.
+# ifdef __arm__
+__attribute__((section(".text._init_trampoline"), naked)) int init_trampoline(
+ int argc, char** argv, char** env) {
+ __asm__ __volatile__(
+ // thumb doesn't allow to use r12/ip with ldr, and thus would require an
+ // additional push/pop to save/restore the modified register, which would
+ // also change the call into a blx. It's simpler to switch to arm.
+ ".arm\n"
+ " ldr ip, .LADDR\n"
+ ".LAFTER:\n"
+ " add ip, pc, ip\n"
+ " bx ip\n"
+ ".LADDR:\n"
+ " .word real_original_init-(.LAFTER+8)\n");
+}
+# endif
+
+// On aarch64, a similar problem exists, but long_call is not an option at all
+// (even GCC doesn't support them on aarch64).
+# ifdef __aarch64__
+__attribute__((section(".text._init_trampoline"), naked)) int init_trampoline(
+ int argc, char** argv, char** env) {
+ __asm__ __volatile__(
+ " adrp x8, .LADDR\n"
+ " add x8, x8, :lo12:.LADDR\n" // adrp + add gives us the full address
+ // for .LADDR
+ " ldr x0, [x8]\n" // Load the address of real_original_init relative to
+ // .LADDR
+ " add x0, x8, x0\n" // Add the address of .LADDR
+ " br x0\n" // Branch to real_original_init
+ ".LADDR:\n"
+ " .xword real_original_init-.LADDR\n");
+}
+# endif
+
+extern __attribute__((visibility("hidden"))) void original_init(int argc,
+ char** argv,
+ char** env);
+
+extern __attribute__((visibility("hidden"))) Elf_Addr relhack[];
+extern __attribute__((visibility("hidden"))) Elf_Addr relhack_end[];
+
+extern __attribute__((visibility("hidden"))) int (*mprotect_cb)(void* addr,
+ size_t len,
+ int prot);
+extern __attribute__((visibility("hidden"))) long (*sysconf_cb)(int name);
+extern __attribute__((visibility("hidden"))) char relro_start[];
+extern __attribute__((visibility("hidden"))) char relro_end[];
+#endif
+
+extern __attribute__((visibility("hidden"))) Elf_Ehdr __ehdr_start;
+
+static inline __attribute__((always_inline)) void do_relocations(
+ Elf_Addr* relhack, Elf_Addr* relhack_end) {
+ Elf_Addr* ptr;
+ for (Elf_Addr* entry = relhack; entry < relhack_end; entry++) {
+ if ((*entry & 1) == 0) {
+ ptr = (Elf_Addr*)((intptr_t)&__ehdr_start + *entry);
+ *ptr += (intptr_t)&__ehdr_start;
+ } else {
+ Elf_Addr bits = *entry;
+ Elf_Addr* end = ptr + 8 * sizeof(Elf_Addr) - 1;
+ do {
+ ptr++;
+ bits >>= 1;
+ if (bits & 1) {
+ *ptr += (intptr_t)&__ehdr_start;
+ }
+ } while (ptr < end);
+ }
+ }
+}
+
+#ifndef RELRHACK
+__attribute__((section(".text._init_noinit"))) int init_noinit(int argc,
+ char** argv,
+ char** env) {
+ do_relocations(relhack, relhack_end);
+ return 0;
+}
+
+__attribute__((section(".text._init"))) int init(int argc, char** argv,
+ char** env) {
+ do_relocations(relhack, relhack_end);
+ 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;
+}
+#endif
+
+static inline __attribute__((always_inline)) void do_relocations_with_relro(
+ Elf_Addr* relhack, Elf_Addr* relhack_end, char* relro_start,
+ char* relro_end) {
+ long page_size = sysconf_cb(_SC_PAGESIZE);
+ uintptr_t aligned_relro_start = ((uintptr_t)relro_start) & ~(page_size - 1);
+ // The relro segment may not end at a page boundary. If that's the case, the
+ // remainder of the page needs to stay read-write, so the last page is never
+ // set read-only. Thus the aligned relro end is page-rounded down.
+ uintptr_t aligned_relro_end = ((uintptr_t)relro_end) & ~(page_size - 1);
+ // By the time the injected code runs, the relro segment is read-only. But
+ // we want to apply relocations in it, so we set it r/w first. We'll restore
+ // it to read-only in relro_post.
+ mprotect_cb((void*)aligned_relro_start,
+ aligned_relro_end - aligned_relro_start, PROT_READ | PROT_WRITE);
+
+ do_relocations(relhack, relhack_end);
+
+ mprotect_cb((void*)aligned_relro_start,
+ aligned_relro_end - aligned_relro_start, PROT_READ);
+#ifndef RELRHACK
+ // mprotect_cb and sysconf_cb are allocated in .bss, so we need to restore
+ // them to a NULL value.
+ mprotect_cb = NULL;
+ sysconf_cb = NULL;
+#endif
+}
+
+#ifndef RELRHACK
+__attribute__((section(".text._init_noinit_relro"))) int init_noinit_relro(
+ int argc, char** argv, char** env) {
+ do_relocations_with_relro(relhack, relhack_end, relro_start, relro_end);
+ return 0;
+}
+
+__attribute__((section(".text._init_relro"))) int init_relro(int argc,
+ char** argv,
+ char** env) {
+ do_relocations_with_relro(relhack, relhack_end, relro_start, relro_end);
+ original_init(argc, argv, env);
+ return 0;
+}
+#else
+
+extern __attribute__((visibility("hidden"))) Elf_Dyn _DYNAMIC[];
+
+static void _relrhack_init(void) {
+ // Get the location of the SHT_RELR data from the PT_DYNAMIC segment.
+ uintptr_t elf_header = (uintptr_t)&__ehdr_start;
+ Elf_Addr* relhack = NULL;
+ Elf_Word size = 0;
+ for (Elf_Dyn* dyn = _DYNAMIC; dyn->d_tag != DT_NULL; dyn++) {
+ if ((dyn->d_tag & ~DT_RELRHACK_BIT) == DT_RELR) {
+ relhack = (Elf_Addr*)(elf_header + dyn->d_un.d_ptr);
+ } else if ((dyn->d_tag & ~DT_RELRHACK_BIT) == DT_RELRSZ) {
+ size = dyn->d_un.d_val;
+ }
+ }
+
+ Elf_Addr* relhack_end = (Elf_Addr*)((uintptr_t)relhack + size);
+
+ // Find the location of the PT_GNU_RELRO segment in the program headers.
+ Elf_Phdr* phdr = (Elf_Phdr*)(elf_header + __ehdr_start.e_phoff);
+ char* relro_start = NULL;
+ char* relro_end = NULL;
+ for (int i = 0; i < __ehdr_start.e_phnum; i++) {
+ if (phdr[i].p_type == PT_GNU_RELRO) {
+ relro_start = (char*)(elf_header + phdr[i].p_vaddr);
+ relro_end = (char*)(relro_start + phdr[i].p_memsz);
+ break;
+ }
+ }
+
+ if (relro_start != relro_end) {
+ do_relocations_with_relro(relhack, relhack_end, relro_start, relro_end);
+ } else {
+ do_relocations(relhack, relhack_end);
+ }
+}
+
+// The Android CRT doesn't contain an init function.
+# ifndef ANDROID
+extern __attribute__((visibility("hidden"))) void _init(int argc, char** argv,
+ char** env);
+# endif
+
+void _relrhack_wrap_init(int argc, char** argv, char** env) {
+ _relrhack_init();
+# ifndef ANDROID
+ _init(argc, argv, env);
+# endif
+}
+#endif
diff --git a/build/unix/elfhack/inject/copy_source.py b/build/unix/elfhack/inject/copy_source.py
new file mode 100644
index 0000000000..02b4f6237e
--- /dev/null
+++ b/build/unix/elfhack/inject/copy_source.py
@@ -0,0 +1,10 @@
+# -*- 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/.
+
+
+def copy(out_file, in_path):
+ with open(in_path, "r") as fh:
+ out_file.write(fh.read())
diff --git a/build/unix/elfhack/inject/moz.build b/build/unix/elfhack/inject/moz.build
new file mode 100644
index 0000000000..92f48e6c0a
--- /dev/null
+++ b/build/unix/elfhack/inject/moz.build
@@ -0,0 +1,56 @@
+# -*- 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
+
+stem = CONFIG["TARGET_CPU"]
+if CONFIG["RELRHACK"] and CONFIG["OS_TARGET"] == "Android":
+ stem += "-android"
+
+gen_src = "%s.c" % stem
+GeneratedFile(
+ gen_src, script="copy_source.py", entry_point="copy", inputs=["../inject.c"]
+)
+
+SOURCES += [
+ "!%s" % gen_src,
+]
+
+if CONFIG["RELRHACK"]:
+ DEFINES["RELRHACK"] = True
+ LOCAL_INCLUDES += [".."]
+
+NO_PGO = True
+
+for v in ("OS_CPPFLAGS", "OS_CFLAGS", "DEBUG", "CLANG_PLUGIN", "OPTIMIZE", "FRAMEPTR"):
+ flags = []
+ idx = 0
+ for flag in COMPILE_FLAGS[v]:
+ if flag == "-isystem":
+ flags.append("".join(COMPILE_FLAGS[v][idx : idx + 2]))
+ elif (
+ flag.startswith(("-g", "-m", "-I", "-isystem", "--sysroot="))
+ or flag == "-fPIC"
+ ):
+ flags.append(flag)
+ idx += 1
+ COMPILE_FLAGS[v] = flags
+
+COMPILE_FLAGS["OS_CFLAGS"] += [
+ "-O2",
+ "-fno-stack-protector",
+ "-fno-lto",
+ # The injected code runs early enough that it supporting unwinding is useless.
+ # Moreover, elfhack doesn't inject the eh_frame section anyways.
+ "-fno-asynchronous-unwind-tables",
+]
+
+AllowCompilerWarnings()
+NoVisibilityFlags()
diff --git a/build/unix/elfhack/moz.build b/build/unix/elfhack/moz.build
new file mode 100644
index 0000000000..6f8c81af25
--- /dev/null
+++ b/build/unix/elfhack/moz.build
@@ -0,0 +1,46 @@
+# -*- 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 CONFIG["RELRHACK"]:
+ HOST_SOURCES += [
+ "relrhack.cpp",
+ ]
+
+ HostProgram(CONFIG["RELRHACK_LINKER"])
+
+ HOST_OS_LIBS += CONFIG["RELRHACK_LIBS"]
+
+ if CONFIG["MOZ_STDCXX_COMPAT"]:
+ HOST_DEFINES["MOZ_STDCXX_COMPAT"] = True
+else:
+ HOST_SOURCES += [
+ "elf.cpp",
+ "elfhack.cpp",
+ ]
+
+ HostProgram("elfhack")
+
+ if not CONFIG["CROSS_COMPILE"]:
+ SOURCES += [
+ "dummy.c",
+ ]
+ SOURCES["dummy.c"].flags += ["-fno-lto"]
+
+ SOURCES += [
+ "test-array.c",
+ "test-ctors.c",
+ ]
+ SOURCES["test-array.c"].flags += ["-fno-lto"]
+ SOURCES["test-ctors.c"].flags += ["-fno-lto"]
+
+NO_PGO = True
+
+COMPILE_FLAGS["OS_CXXFLAGS"] = [
+ f for f in COMPILE_FLAGS["OS_CXXFLAGS"] if f != "-fno-exceptions"
+] + ["-fexceptions"]
diff --git a/build/unix/elfhack/relrhack.cpp b/build/unix/elfhack/relrhack.cpp
new file mode 100644
index 0000000000..2d78d783c9
--- /dev/null
+++ b/build/unix/elfhack/relrhack.cpp
@@ -0,0 +1,567 @@
+/* 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/. */
+
+// This program acts as a linker wrapper. Its executable name is meant
+// to be that of a linker, and it will find the next linker with the same
+// name in $PATH. However, if for some reason the next linker cannot be
+// found this way, the caller may pass its path via the --real-linker
+// option.
+//
+// More in-depth background on https://glandium.org/blog/?p=4297
+
+#include "relrhack.h"
+#include <algorithm>
+#include <cstring>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <optional>
+#include <spawn.h>
+#include <sstream>
+#include <stdexcept>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+namespace fs = std::filesystem;
+
+class CantSwapSections : public std::runtime_error {
+ public:
+ CantSwapSections(const char* what) : std::runtime_error(what) {}
+};
+
+template <int bits>
+struct Elf {};
+
+#define ELF(bits) \
+ template <> \
+ struct Elf<bits> { \
+ using Ehdr = Elf##bits##_Ehdr; \
+ using Phdr = Elf##bits##_Phdr; \
+ using Shdr = Elf##bits##_Shdr; \
+ using Dyn = Elf##bits##_Dyn; \
+ using Addr = Elf##bits##_Addr; \
+ using Word = Elf##bits##_Word; \
+ using Off = Elf##bits##_Off; \
+ using Verneed = Elf##bits##_Verneed; \
+ using Vernaux = Elf##bits##_Vernaux; \
+ }
+
+ELF(32);
+ELF(64);
+
+template <int bits>
+struct RelR : public Elf<bits> {
+ using Elf_Ehdr = typename Elf<bits>::Ehdr;
+ using Elf_Phdr = typename Elf<bits>::Phdr;
+ using Elf_Shdr = typename Elf<bits>::Shdr;
+ using Elf_Dyn = typename Elf<bits>::Dyn;
+ using Elf_Addr = typename Elf<bits>::Addr;
+ using Elf_Word = typename Elf<bits>::Word;
+ using Elf_Off = typename Elf<bits>::Off;
+ using Elf_Verneed = typename Elf<bits>::Verneed;
+ using Elf_Vernaux = typename Elf<bits>::Vernaux;
+
+#define TAG_NAME(t) \
+ { t, #t }
+ class DynInfo {
+ public:
+ using Tag = decltype(Elf_Dyn::d_tag);
+ using Value = decltype(Elf_Dyn::d_un.d_val);
+ bool is_wanted(Tag tag) const { return tag_names.count(tag); }
+ void insert(off_t offset, Tag tag, Value val) {
+ data[tag] = std::make_pair(offset, val);
+ }
+ off_t offset(Tag tag) const { return data.at(tag).first; }
+ bool contains(Tag tag) const { return data.count(tag); }
+ Value& operator[](Tag tag) {
+ if (!is_wanted(tag)) {
+ std::stringstream msg;
+ msg << "Tag 0x" << std::hex << tag << " is not in DynInfo::tag_names";
+ throw std::runtime_error(msg.str());
+ }
+ return data[tag].second;
+ }
+ const char* name(Tag tag) const { return tag_names.at(tag); }
+
+ private:
+ std::unordered_map<Tag, std::pair<off_t, Value>> data;
+
+ const std::unordered_map<Tag, const char*> tag_names = {
+ TAG_NAME(DT_JMPREL), TAG_NAME(DT_PLTRELSZ), TAG_NAME(DT_RELR),
+ TAG_NAME(DT_RELRENT), TAG_NAME(DT_RELRSZ), TAG_NAME(DT_RELA),
+ TAG_NAME(DT_RELASZ), TAG_NAME(DT_RELAENT), TAG_NAME(DT_REL),
+ TAG_NAME(DT_RELSZ), TAG_NAME(DT_RELENT), TAG_NAME(DT_STRTAB),
+ TAG_NAME(DT_STRSZ), TAG_NAME(DT_VERNEED), TAG_NAME(DT_VERNEEDNUM),
+ };
+ };
+
+ // Translate a virtual address into an offset in the file based on the program
+ // headers' PT_LOAD.
+ static Elf_Addr get_offset(const std::vector<Elf_Phdr>& phdr, Elf_Addr addr) {
+ for (const auto& p : phdr) {
+ if (p.p_type == PT_LOAD && addr >= p.p_vaddr &&
+ addr < p.p_vaddr + p.p_filesz) {
+ return addr - (p.p_vaddr - p.p_paddr);
+ }
+ }
+ return 0;
+ }
+
+ static bool hack(std::fstream& f);
+};
+
+template <typename T>
+T read_one_at(std::istream& in, off_t pos) {
+ T result;
+ in.seekg(pos, std::ios::beg);
+ in.read(reinterpret_cast<char*>(&result), sizeof(T));
+ return result;
+}
+
+template <typename T>
+std::vector<T> read_vector_at(std::istream& in, off_t pos, size_t num) {
+ std::vector<T> result(num);
+ in.seekg(pos, std::ios::beg);
+ in.read(reinterpret_cast<char*>(result.data()), num * sizeof(T));
+ return result;
+}
+
+void write_at(std::ostream& out, off_t pos, const char* buf, size_t len) {
+ out.seekp(pos, std::ios::beg);
+ out.write(buf, len);
+}
+
+template <typename T>
+void write_one_at(std::ostream& out, off_t pos, const T& data) {
+ write_at(out, pos, reinterpret_cast<const char*>(&data), sizeof(T));
+}
+
+template <typename T>
+void write_vector_at(std::ostream& out, off_t pos, const std::vector<T>& vec) {
+ write_at(out, pos, reinterpret_cast<const char*>(&vec.front()),
+ vec.size() * sizeof(T));
+}
+
+template <int bits>
+bool RelR<bits>::hack(std::fstream& f) {
+ auto ehdr = read_one_at<Elf_Ehdr>(f, 0);
+ if (ehdr.e_phentsize != sizeof(Elf_Phdr)) {
+ throw std::runtime_error("Invalid ELF?");
+ }
+ auto phdr = read_vector_at<Elf_Phdr>(f, ehdr.e_phoff, ehdr.e_phnum);
+ const auto& dyn_phdr =
+ std::find_if(phdr.begin(), phdr.end(),
+ [](const auto& p) { return p.p_type == PT_DYNAMIC; });
+ if (dyn_phdr == phdr.end()) {
+ return false;
+ }
+ if (dyn_phdr->p_filesz % sizeof(Elf_Dyn)) {
+ throw std::runtime_error("Invalid ELF?");
+ }
+ auto dyn = read_vector_at<Elf_Dyn>(f, dyn_phdr->p_offset,
+ dyn_phdr->p_filesz / sizeof(Elf_Dyn));
+ off_t dyn_offset = dyn_phdr->p_offset;
+ DynInfo dyn_info;
+ for (const auto& d : dyn) {
+ if (d.d_tag == DT_NULL) {
+ break;
+ }
+
+ if (dyn_info.is_wanted(d.d_tag)) {
+ if (dyn_info.contains(d.d_tag)) {
+ std::stringstream msg;
+ msg << dyn_info.name(d.d_tag) << " appears twice?";
+ throw std::runtime_error(msg.str());
+ }
+ dyn_info.insert(dyn_offset, d.d_tag, d.d_un.d_val);
+ }
+ dyn_offset += sizeof(Elf_Dyn);
+ }
+
+ // Find the location and size of the SHT_RELR section, which contains the
+ // packed-relative-relocs.
+ Elf_Addr relr_off =
+ dyn_info.contains(DT_RELR) ? get_offset(phdr, dyn_info[DT_RELR]) : 0;
+ Elf_Off relrsz = dyn_info[DT_RELRSZ];
+ const decltype(Elf_Dyn::d_tag) rel_tags[3][2] = {
+ {DT_REL, DT_RELA}, {DT_RELSZ, DT_RELASZ}, {DT_RELENT, DT_RELAENT}};
+ for (const auto& [rel_tag, rela_tag] : rel_tags) {
+ if (dyn_info.contains(rel_tag) && dyn_info.contains(rela_tag)) {
+ std::stringstream msg;
+ msg << "Both " << dyn_info.name(rel_tag) << " and "
+ << dyn_info.name(rela_tag) << " appear?";
+ throw std::runtime_error(msg.str());
+ }
+ }
+ Elf_Off relent =
+ dyn_info.contains(DT_RELENT) ? dyn_info[DT_RELENT] : dyn_info[DT_RELAENT];
+
+ // Estimate the size of the unpacked relative relocations corresponding
+ // to the SHT_RELR section.
+ auto relr = read_vector_at<Elf_Addr>(f, relr_off, relrsz / sizeof(Elf_Addr));
+ size_t relocs = 0;
+ for (const auto& entry : relr) {
+ if ((entry & 1) == 0) {
+ // LSB is 0, this is a pointer for a single relocation.
+ relocs++;
+ } else {
+ // LSB is 1, remaining bits are a bitmap. Each bit represents a
+ // relocation.
+ relocs += __builtin_popcount(entry) - 1;
+ }
+ }
+ // If the packed relocations + some overhead (we pick 4K arbitrarily, the
+ // real size would require digging into the section sizes of the injected
+ // .o file, which is not worth the error) is larger than the estimated
+ // unpacked relocations, we'll just relink without packed relocations.
+ if (relocs * relent < relrsz + 4096) {
+ return false;
+ }
+
+ // Change DT_RELR* tags to add DT_RELRHACK_BIT.
+ for (const auto tag : {DT_RELR, DT_RELRSZ, DT_RELRENT}) {
+ write_one_at(f, dyn_info.offset(tag), tag | DT_RELRHACK_BIT);
+ }
+
+ bool is_glibc = false;
+
+ if (dyn_info.contains(DT_VERNEEDNUM) && dyn_info.contains(DT_VERNEED) &&
+ dyn_info.contains(DT_STRSZ) && dyn_info.contains(DT_STRTAB)) {
+ // Scan SHT_VERNEED for the GLIBC_ABI_DT_RELR version on the libc
+ // library.
+ Elf_Addr verneed_off = get_offset(phdr, dyn_info[DT_VERNEED]);
+ Elf_Off verneednum = dyn_info[DT_VERNEEDNUM];
+ // SHT_STRTAB section, which contains the string table for, among other
+ // things, the symbol versions in the SHT_VERNEED section.
+ auto strtab = read_vector_at<char>(f, get_offset(phdr, dyn_info[DT_STRTAB]),
+ dyn_info[DT_STRSZ]);
+ // Guarantee a nul character at the end of the string table.
+ strtab.push_back(0);
+ while (verneednum--) {
+ auto verneed = read_one_at<Elf_Verneed>(f, verneed_off);
+ if (std::string_view{"libc.so.6"} == &strtab.at(verneed.vn_file)) {
+ is_glibc = true;
+ Elf_Addr vernaux_off = verneed_off + verneed.vn_aux;
+ Elf_Addr relr = 0;
+ Elf_Vernaux reuse;
+ for (auto n = 0; n < verneed.vn_cnt; n++) {
+ auto vernaux = read_one_at<Elf_Vernaux>(f, vernaux_off);
+ if (std::string_view{"GLIBC_ABI_DT_RELR"} ==
+ &strtab.at(vernaux.vna_name)) {
+ relr = vernaux_off;
+ } else {
+ reuse = vernaux;
+ }
+ vernaux_off += vernaux.vna_next;
+ }
+ // In the case where we do have the GLIBC_ABI_DT_RELR version, we
+ // need to edit the binary to make the following changes:
+ // - Remove the GLIBC_ABI_DT_RELR version, we replace it with an
+ // arbitrary other version entry, which is simpler than completely
+ // removing it. We need to remove it because older versions of glibc
+ // don't have the version (after all, that's why the symbol version
+ // is there in the first place, to avoid running against older versions
+ // of glibc that don't support packed relocations).
+ // - Alter the DT_RELR* tags in the dynamic section, so that they
+ // are not recognized by ld.so, because, while all versions of ld.so
+ // ignore tags they don't know, glibc's ld.so versions that support
+ // packed relocations don't want to load a binary that has DT_RELR*
+ // tags but *not* a dependency on the GLIBC_ABI_DT_RELR version.
+ if (relr) {
+ // Don't overwrite vn_aux.
+ write_at(f, relr, reinterpret_cast<char*>(&reuse),
+ sizeof(reuse) - sizeof(Elf_Word));
+ }
+ }
+ verneed_off += verneed.vn_next;
+ }
+ }
+
+ // Location of the .rel.plt section.
+ Elf_Addr jmprel = dyn_info.contains(DT_JMPREL) ? dyn_info[DT_JMPREL] : 0;
+ if (is_glibc) {
+#ifndef MOZ_STDCXX_COMPAT
+ try {
+#endif
+ // ld.so in glibc 2.16 to 2.23 expects .rel.plt to strictly follow
+ // .rel.dyn. (https://sourceware.org/bugzilla/show_bug.cgi?id=14341)
+ // BFD ld places .relr.dyn after .rel.plt, so this works fine, but lld
+ // places it between both sections, which doesn't work out for us. In that
+ // case, we want to swap .relr.dyn and .rel.plt.
+ Elf_Addr rel_end = dyn_info.contains(DT_REL)
+ ? (dyn_info[DT_REL] + dyn_info[DT_RELSZ])
+ : (dyn_info[DT_RELA] + dyn_info[DT_RELASZ]);
+ if (dyn_info.contains(DT_JMPREL) && dyn_info[DT_PLTRELSZ] &&
+ dyn_info[DT_JMPREL] != rel_end) {
+ if (dyn_info[DT_RELR] != rel_end) {
+ throw CantSwapSections("RELR section doesn't follow REL/RELA?");
+ }
+ if (dyn_info[DT_JMPREL] != dyn_info[DT_RELR] + dyn_info[DT_RELRSZ]) {
+ throw CantSwapSections("PLT REL/RELA doesn't follow RELR?");
+ }
+ auto plt_rel = read_vector_at<char>(
+ f, get_offset(phdr, dyn_info[DT_JMPREL]), dyn_info[DT_PLTRELSZ]);
+ // Write the content of both sections swapped, and adjust the
+ // corresponding PT_DYNAMIC entries.
+ write_vector_at(f, relr_off, plt_rel);
+ write_vector_at(f, relr_off + plt_rel.size(), relr);
+ dyn_info[DT_JMPREL] = rel_end;
+ dyn_info[DT_RELR] = rel_end + plt_rel.size();
+ for (const auto tag : {DT_JMPREL, DT_RELR}) {
+ write_one_at(f, dyn_info.offset(tag) + sizeof(typename DynInfo::Tag),
+ dyn_info[tag]);
+ }
+ }
+#ifndef MOZ_STDCXX_COMPAT
+ } catch (const CantSwapSections& err) {
+ // When binary compatibility with older libstdc++/glibc is not enabled, we
+ // only emit a warning about why swapping the sections is not happening.
+ std::cerr << "WARNING: " << err.what() << std::endl;
+ }
+#endif
+ }
+
+ off_t shdr_offset = ehdr.e_shoff;
+ auto shdr = read_vector_at<Elf_Shdr>(f, ehdr.e_shoff, ehdr.e_shnum);
+ for (auto& s : shdr) {
+ // Some tools don't like sections of types they don't know, so change
+ // SHT_RELR, which might be unknown on older systems, to SHT_PROGBITS.
+ if (s.sh_type == SHT_RELR) {
+ s.sh_type = SHT_PROGBITS;
+ // If DT_RELR has been adjusted to swap with DT_JMPREL, also adjust
+ // the corresponding SHT_RELR section header.
+ if (s.sh_addr != dyn_info[DT_RELR]) {
+ s.sh_offset += dyn_info[DT_RELR] - s.sh_addr;
+ s.sh_addr = dyn_info[DT_RELR];
+ }
+ write_one_at(f, shdr_offset, s);
+ } else if (jmprel && (s.sh_addr == jmprel) &&
+ (s.sh_addr != dyn_info[DT_JMPREL])) {
+ // If DT_JMPREL has been adjusted to swap with DT_RELR, also adjust
+ // the corresponding section header.
+ s.sh_offset -= s.sh_addr - dyn_info[DT_JMPREL];
+ s.sh_addr = dyn_info[DT_JMPREL];
+ write_one_at(f, shdr_offset, s);
+ }
+ shdr_offset += sizeof(Elf_Shdr);
+ }
+ return true;
+}
+
+std::vector<std::string> get_path() {
+ std::vector<std::string> result;
+ std::stringstream stream{std::getenv("PATH")};
+ std::string item;
+
+ while (std::getline(stream, item, ':')) {
+ result.push_back(std::move(item));
+ }
+
+ return result;
+}
+
+std::optional<fs::path> next_program(fs::path& this_program,
+ std::optional<fs::path>& program) {
+ auto program_name = program ? *program : this_program.filename();
+ for (const auto& dir : get_path()) {
+ auto path = fs::path(dir) / program_name;
+ auto status = fs::status(path);
+ if ((status.type() == fs::file_type::regular) &&
+ ((status.permissions() & fs::perms::owner_exec) ==
+ fs::perms::owner_exec) &&
+ !fs::equivalent(path, this_program))
+ return path;
+ }
+ return std::nullopt;
+}
+
+unsigned char get_elf_class(unsigned char (&e_ident)[EI_NIDENT]) {
+ if (std::string_view{reinterpret_cast<char*>(e_ident), SELFMAG} !=
+ std::string_view{ELFMAG, SELFMAG}) {
+ throw std::runtime_error("Not ELF?");
+ }
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ if (e_ident[EI_DATA] != ELFDATA2LSB) {
+ throw std::runtime_error("Not Little Endian ELF?");
+ }
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ if (e_ident[EI_DATA] != ELFDATA2MSB) {
+ throw std::runtime_error("Not Big Endian ELF?");
+ }
+#else
+# error Unknown byte order.
+#endif
+ if (e_ident[EI_VERSION] != 1) {
+ throw std::runtime_error("Not ELF version 1?");
+ }
+ auto elf_class = e_ident[EI_CLASS];
+ if (elf_class != ELFCLASS32 && elf_class != ELFCLASS64) {
+ throw std::runtime_error("Not 32 or 64-bits ELF?");
+ }
+ return elf_class;
+}
+
+unsigned char get_elf_class(std::istream& in) {
+ unsigned char e_ident[EI_NIDENT];
+ in.read(reinterpret_cast<char*>(e_ident), sizeof(e_ident));
+ return get_elf_class(e_ident);
+}
+
+uint16_t get_elf_machine(std::istream& in) {
+ // As far as e_machine is concerned, both Elf32_Ehdr and Elf64_Ehdr are equal.
+ Elf32_Ehdr ehdr;
+ in.read(reinterpret_cast<char*>(&ehdr), sizeof(ehdr));
+ // get_elf_class will throw exceptions for the cases we don't handle.
+ get_elf_class(ehdr.e_ident);
+ return ehdr.e_machine;
+}
+
+int run_command(std::vector<const char*>& args) {
+ pid_t child_pid;
+ if (posix_spawn(&child_pid, args[0], nullptr, nullptr,
+ const_cast<char* const*>(args.data()), environ) != 0) {
+ throw std::runtime_error("posix_spawn failed");
+ }
+
+ int status;
+ waitpid(child_pid, &status, 0);
+ return WEXITSTATUS(status);
+}
+
+int main(int argc, char* argv[]) {
+ auto this_program = fs::absolute(argv[0]);
+
+ std::vector<const char*> args;
+
+ int i, crti = 0;
+ std::optional<fs::path> output = std::nullopt;
+ std::optional<fs::path> real_linker = std::nullopt;
+ bool shared = false;
+ bool is_android = false;
+ uint16_t elf_machine = EM_NONE;
+ // Scan argv in order to prepare the following:
+ // - get the output file. That's the file we may need to adjust.
+ // - get the --real-linker if one was passed.
+ // - detect whether we're linking a shared library or something else. As of
+ // now, only shared libraries are handled. Technically speaking, programs
+ // could be handled as well, but for the purpose of Firefox, that actually
+ // doesn't work because programs contain a memory allocator that ends up
+ // being called before the injected code has any chance to apply relocations,
+ // and the allocator itself needs the relocations to have been applied.
+ // - detect the position of crti.o so that we can inject our own object
+ // right after it, and also to detect the machine type to pick the right
+ // object to inject.
+ //
+ // At the same time, we also construct a new list of arguments, with
+ // --real-linker filtered out. We'll later inject arguments in that list.
+ for (i = 1, argv++; i < argc && *argv; argv++, i++) {
+ std::string_view arg{*argv};
+ if (arg == "-shared") {
+ shared = true;
+ } else if (arg == "-o") {
+ args.push_back(*(argv++));
+ ++i;
+ output = *argv;
+ } else if (arg == "--real-linker") {
+ ++i;
+ real_linker = *(++argv);
+ continue;
+ } else if (elf_machine == EM_NONE) {
+ auto filename = fs::path(arg).filename();
+ if (filename == "crti.o" || filename == "crtbegin_so.o") {
+ is_android = (filename == "crtbegin_so.o");
+ crti = i;
+ std::fstream f{std::string(arg), f.binary | f.in};
+ f.exceptions(f.failbit);
+ elf_machine = get_elf_machine(f);
+ }
+ }
+ args.push_back(*argv);
+ }
+
+ if (!output) {
+ std::cerr << "Could not determine output file." << std::endl;
+ return 1;
+ }
+
+ if (!crti) {
+ std::cerr << "Could not find CRT object on the command line." << std::endl;
+ return 1;
+ }
+
+ if (!real_linker || !real_linker->has_parent_path()) {
+ auto linker = next_program(this_program, real_linker);
+ if (!linker) {
+ std::cerr << "Could not find next "
+ << (real_linker ? real_linker->filename()
+ : this_program.filename())
+ << std::endl;
+ return 1;
+ }
+ real_linker = linker;
+ }
+ args.insert(args.begin(), real_linker->c_str());
+ args.push_back(nullptr);
+
+ std::string stem;
+ switch (elf_machine) {
+ case EM_NONE:
+ std::cerr << "Could not determine target machine type." << std::endl;
+ return 1;
+ case EM_386:
+ stem = "x86";
+ break;
+ case EM_X86_64:
+ stem = "x86_64";
+ break;
+ case EM_ARM:
+ stem = "arm";
+ break;
+ case EM_AARCH64:
+ stem = "aarch64";
+ break;
+ default:
+ std::cerr << "Unsupported target machine type." << std::endl;
+ return 1;
+ }
+ if (is_android) {
+ stem += "-android";
+ }
+
+ if (shared) {
+ std::vector<const char*> hacked_args(args);
+ auto inject = this_program.parent_path() / "inject" / (stem + ".o");
+ hacked_args.insert(hacked_args.begin() + crti + 1, inject.c_str());
+ hacked_args.insert(hacked_args.end() - 1, {"-z", "pack-relative-relocs",
+ "-init=_relrhack_wrap_init"});
+ int status = run_command(hacked_args);
+ if (status) {
+ return status;
+ }
+ bool hacked = false;
+ try {
+ std::fstream f{*output, f.binary | f.in | f.out};
+ f.exceptions(f.failbit);
+ auto elf_class = get_elf_class(f);
+ f.seekg(0, std::ios::beg);
+ if (elf_class == ELFCLASS32) {
+ hacked = RelR<32>::hack(f);
+ } else if (elf_class == ELFCLASS64) {
+ hacked = RelR<64>::hack(f);
+ }
+ } catch (const std::runtime_error& err) {
+ std::cerr << "Failed to hack " << output->string() << ": " << err.what()
+ << std::endl;
+ return 1;
+ }
+ if (hacked) {
+ return 0;
+ }
+ }
+
+ return run_command(args);
+}
diff --git a/build/unix/elfhack/relrhack.h b/build/unix/elfhack/relrhack.h
new file mode 100644
index 0000000000..3501f21079
--- /dev/null
+++ b/build/unix/elfhack/relrhack.h
@@ -0,0 +1,25 @@
+/* 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/. */
+
+#ifndef __RELRHACK_H__
+#define __RELRHACK_H__
+
+#include <elf.h>
+
+#define DT_RELRHACK_BIT 0x8000000
+
+#ifndef DT_RELRSZ
+# define DT_RELRSZ 35
+#endif
+#ifndef DT_RELR
+# define DT_RELR 36
+#endif
+#ifndef DT_RELRENT
+# define DT_RELRENT 37
+#endif
+#ifndef SHR_RELR
+# define SHT_RELR 19
+#endif
+
+#endif /* __RELRHACK_H__ */
diff --git a/build/unix/elfhack/test-array.c b/build/unix/elfhack/test-array.c
new file mode 100644
index 0000000000..21aec3d360
--- /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 0000000000..d082411e3d
--- /dev/null
+++ b/build/unix/elfhack/test-ctors.c
@@ -0,0 +1,16 @@
+/* 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 0000000000..f690ad8f6a
--- /dev/null
+++ b/build/unix/elfhack/test.c
@@ -0,0 +1,221 @@
+/* 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)
+// clang-format off
+DEF(not)
+// clang-format on
+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 */
+int small_hole[] = {42, 42, 42, 42};
+
+const char* strings2[] = {
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+};
+
+/* Create a bigger hole between two zones of relative relocations */
+int bigger_hole[] = {
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+};
+
+const char* strings3[] = {
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# include "test.c"
+# 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];
+
+/* We need a .bss that can hold at least 2 pointers. The static in
+ * end_test() plus this variable should do. */
+size_t dummy;
+
+void end_test() {
+ static size_t count = 0;
+ /* Only exit when both constructors have been called */
+ if (++count == 2) {
+ ret = 0;
+ // Avoid the dummy variable being stripped out at link time because
+ // it's unused.
+ dummy = 1;
+ }
+}
+
+void test() {
+ int i = 0, j = 0, k = 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
+# define DEF(w) DEF_(strings3, k, 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]) &&
+ k != sizeof(strings3) / sizeof(strings3[0]))
+ fprintf(stderr, "WARNING: Test doesn't cover the whole array\n");
+ end_test();
+}
+
+# pragma GCC visibility pop
+#endif
diff --git a/build/unix/moz.build b/build/unix/moz.build
new file mode 100644
index 0000000000..259e7e9c7d
--- /dev/null
+++ b/build/unix/moz.build
@@ -0,0 +1,12 @@
+# -*- 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["USE_ELF_HACK"] or CONFIG["RELRHACK"]:
+ DIRS += ["elfhack"]
+
+FINAL_TARGET_FILES += [
+ "run-mozilla.sh",
+]
diff --git a/build/unix/mozconfig.asan b/build/unix/mozconfig.asan
new file mode 100644
index 0000000000..9c1e7d45e2
--- /dev/null
+++ b/build/unix/mozconfig.asan
@@ -0,0 +1,14 @@
+. "$topsrcdir/build/unix/mozconfig.unix"
+
+export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/bin/llvm-symbolizer"
+#
+# 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-profiling
diff --git a/build/unix/mozconfig.linux b/build/unix/mozconfig.linux
new file mode 100644
index 0000000000..b63cebe59e
--- /dev/null
+++ b/build/unix/mozconfig.linux
@@ -0,0 +1,15 @@
+. "$topsrcdir/build/unix/mozconfig.unix"
+
+case "$PERFHERDER_EXTRA_OPTIONS" in
+base-toolchains*)
+ # We don't build against wayland for base-toolchain builds.
+ ;;
+x11)
+ ac_add_options --enable-default-toolkit=cairo-gtk3
+ ;;
+*)
+ ac_add_options --enable-default-toolkit=cairo-gtk3-x11-wayland
+ ;;
+esac
+
+export MOZ_NO_PIE_COMPAT=1
diff --git a/build/unix/mozconfig.linux32 b/build/unix/mozconfig.linux32
new file mode 100644
index 0000000000..8da778465d
--- /dev/null
+++ b/build/unix/mozconfig.linux32
@@ -0,0 +1,8 @@
+. "$topsrcdir/build/unix/mozconfig.linux"
+
+export MOZ_LINUX_32_SSE2_STARTUP_ERROR=1
+
+CFLAGS="$CFLAGS -march=pentium-m -msse -msse2 -mfpmath=sse"
+CXXFLAGS="$CXXFLAGS -march=pentium-m -msse -msse2 -mfpmath=sse"
+
+ac_add_options --target=i686-pc-linux
diff --git a/build/unix/mozconfig.stdcxx b/build/unix/mozconfig.stdcxx
new file mode 100644
index 0000000000..0146f3af9a
--- /dev/null
+++ b/build/unix/mozconfig.stdcxx
@@ -0,0 +1,2 @@
+# Avoid dependency on libstdc++ 4.7
+export MOZ_STDCXX_COMPAT=1
diff --git a/build/unix/mozconfig.tsan b/build/unix/mozconfig.tsan
new file mode 100644
index 0000000000..f38a16a28f
--- /dev/null
+++ b/build/unix/mozconfig.tsan
@@ -0,0 +1,17 @@
+export LLVM_SYMBOLIZER="$MOZ_FETCHES_DIR/llvm-symbolizer/bin/llvm-symbolizer"
+
+# 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-profiling
+
+# Keep symbols to symbolize TSan traces
+ac_add_options --enable-debug-symbols
+ac_add_options --disable-install-strip
diff --git a/build/unix/mozconfig.unix b/build/unix/mozconfig.unix
new file mode 100644
index 0000000000..05bf666b39
--- /dev/null
+++ b/build/unix/mozconfig.unix
@@ -0,0 +1,22 @@
+. "$topsrcdir/build/mozconfig.common"
+
+if [ -n "$FORCE_GCC" ]; then
+ CC="$MOZ_FETCHES_DIR/gcc/bin/gcc"
+ CXX="$MOZ_FETCHES_DIR/gcc/bin/g++"
+
+ # We want to make sure we use binutils and other binaries in the tooltool
+ # package.
+ mk_add_options "export PATH=$MOZ_FETCHES_DIR/gcc/bin:$MOZ_FETCHES_DIR/binutils/bin:$PATH"
+else
+ # For some builds we don't want to have Clang based static-analysis activated
+ if [ -z "$DISABLE_CLANG_PLUGIN" ]; then
+ export ENABLE_CLANG_PLUGIN=1
+ fi
+
+ export CFLAGS="$CFLAGS -fcrash-diagnostics-dir=${UPLOAD_PATH}"
+ export CXXFLAGS="$CXXFLAGS -fcrash-diagnostics-dir=${UPLOAD_PATH}"
+
+ mk_add_options "export PATH=$MOZ_FETCHES_DIR/binutils/bin:$PATH"
+fi
+
+. "$topsrcdir/build/unix/mozconfig.stdcxx"
diff --git a/build/unix/print-non-newline.sh b/build/unix/print-non-newline.sh
new file mode 100755
index 0000000000..5e0cf292d9
--- /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_sanitizer_dylib.py b/build/unix/rewrite_sanitizer_dylib.py
new file mode 100644
index 0000000000..59198b13c8
--- /dev/null
+++ b/build/unix/rewrite_sanitizer_dylib.py
@@ -0,0 +1,123 @@
+# 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 os
+import re
+import shutil
+import subprocess
+import sys
+from argparse import ArgumentParser
+from pathlib import Path
+
+from buildconfig import substs
+
+"""
+Scans the given directories for binaries referencing the AddressSanitizer
+runtime library, copies it to the main directory.
+"""
+
+# This is the dylib name pattern
+DYLIB_NAME_PATTERN = re.compile(r"libclang_rt\.(a|ub)san_osx_dynamic\.dylib")
+
+
+def resolve_rpath(filename):
+ otoolOut = subprocess.check_output([substs["OTOOL"], "-l", filename], text=True)
+ currentCmd = None
+
+ # The lines we need to find look like this:
+ # ...
+ # Load command 22
+ # cmd LC_RPATH
+ # cmdsize 80
+ # path /home/build/src/clang/bin/../lib/clang/3.8.0/lib/darwin (offset 12)
+ # Load command 23
+ # ...
+ # Other load command types have a varying number of fields.
+ for line in otoolOut.splitlines():
+ cmdMatch = re.match(r"^\s+cmd ([A-Z_]+)", line)
+ if cmdMatch is not None:
+ currentCmd = cmdMatch.group(1)
+ continue
+
+ if currentCmd == "LC_RPATH":
+ pathMatch = re.match(r"^\s+path (.*) \(offset \d+\)", line)
+ if pathMatch is not None:
+ path = pathMatch.group(1)
+ if Path(path).is_dir():
+ return path
+
+ print(f"@rpath could not be resolved from {filename}", file=sys.stderr)
+ sys.exit(1)
+
+
+def scan_directory(path):
+ dylibsCopied = set()
+ dylibsRequired = set()
+
+ if not path.is_dir():
+ print(f"Input path {path} is not a folder", file=sys.stderr)
+ sys.exit(1)
+
+ for file in path.rglob("*"):
+ if not file.is_file():
+ continue
+
+ # Skip all files that aren't either dylibs or executable
+ if not (file.suffix == ".dylib" or os.access(str(file), os.X_OK)):
+ continue
+
+ try:
+ otoolOut = subprocess.check_output(
+ [substs["OTOOL"], "-L", str(file)], text=True
+ )
+ except Exception:
+ # Errors are expected on non-mach executables, ignore them and continue
+ continue
+
+ for line in otoolOut.splitlines():
+ match = DYLIB_NAME_PATTERN.search(line)
+
+ if match is not None:
+ dylibName = match.group(0)
+ absDylibPath = line.split()[0]
+
+ dylibsRequired.add(dylibName)
+
+ if dylibName not in dylibsCopied:
+ if absDylibPath.startswith("@rpath/"):
+ rpath = resolve_rpath(str(file))
+ copyDylibPath = absDylibPath.replace("@rpath", rpath)
+ else:
+ copyDylibPath = absDylibPath
+
+ if Path(copyDylibPath).is_file():
+ # Copy the runtime once to the main directory, which is passed
+ # as the argument to this function.
+ shutil.copy(copyDylibPath, str(path))
+ dylibsCopied.add(dylibName)
+ else:
+ print(
+ f"dylib path in {file} was not found at: {copyDylibPath}",
+ file=sys.stderr,
+ )
+
+ break
+
+ dylibsMissing = dylibsRequired - dylibsCopied
+ if dylibsMissing:
+ for dylibName in dylibsMissing:
+ print(f"{dylibName} could not be found", file=sys.stderr)
+ sys.exit(1)
+
+
+def parse_args(argv=None):
+ parser = ArgumentParser()
+ parser.add_argument("paths", metavar="path", type=Path, nargs="+")
+ return parser.parse_args(argv)
+
+
+if __name__ == "__main__":
+ args = parse_args()
+ for d in args.paths:
+ scan_directory(d)
diff --git a/build/unix/run-gprof.sh b/build/unix/run-gprof.sh
new file mode 100644
index 0000000000..794d146b94
--- /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 0000000000..cb4ec851ff
--- /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 0000000000..876fd7f7e6
--- /dev/null
+++ b/build/unix/run-mozilla.sh
@@ -0,0 +1,356 @@
+#!/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
+#
+if [ -z "$MRE_HOME" ]; then
+ MRE_HOME=$MOZ_DIST_BIN
+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 " 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 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 0000000000..cb2c661377
--- /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/hide_std.ld b/build/unix/stdc++compat/hide_std.ld
new file mode 100644
index 0000000000..4b3400b0f3
--- /dev/null
+++ b/build/unix/stdc++compat/hide_std.ld
@@ -0,0 +1,5 @@
+hidden {
+ local:
+ # std::thread::_M_start_thread(...)
+ _ZNSt6thread15_M_start_thread*;
+};
diff --git a/build/unix/stdc++compat/moz.build b/build/unix/stdc++compat/moz.build
new file mode 100644
index 0000000000..a3681d15d8
--- /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["OS_TARGET"] == "Linux":
+ Library("stdc++compat")
+ SOURCES += ["stdc++compat.cpp"]
+
+if CONFIG["HOST_OS_ARCH"] == "Linux":
+ HostLibrary("host_stdc++compat")
+ HOST_SOURCES += [
+ "stdc++compat.cpp",
+ ]
+
+FORCE_STATIC_LIB = True
+
+NO_PGO = True
+
+DisableStlWrapping()
+COMPILE_FLAGS["CLANG_PLUGIN"] = []
+
+OS_LIBS += ["-Wl,--version-script,%s/hide_std.ld" % SRCDIR]
diff --git a/build/unix/stdc++compat/stdc++compat.cpp b/build/unix/stdc++compat/stdc++compat.cpp
new file mode 100644
index 0000000000..0180f6bcfa
--- /dev/null
+++ b/build/unix/stdc++compat/stdc++compat.cpp
@@ -0,0 +1,196 @@
+/* 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 <sstream>
+#include <memory>
+#include <string>
+#include <stdarg.h>
+#include <stdio.h>
+#include <mozilla/Assertions.h>
+#include <cxxabi.h>
+
+/*
+ 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)
+ GLIBCXX_3.4.22 is from gcc 6.0 (222482)
+ GLIBCXX_3.4.23 is from gcc 7
+ GLIBCXX_3.4.24 is from gcc 8
+ GLIBCXX_3.4.25 is from gcc 8
+ GLIBCXX_3.4.26 is from gcc 9
+ GLIBCXX_3.4.27 is from gcc 10
+ GLIBCXX_3.4.28 is from gcc 10
+ GLIBCXX_3.4.29 is from gcc 11
+
+This file adds the necessary compatibility tricks to avoid symbols with
+version GLIBCXX_3.4.20 and bigger, keeping binary compatibility with
+libstdc++ 4.8.1.
+
+WARNING: all symbols from this file must be defined weak when they
+overlap with libstdc++.
+*/
+
+namespace std {
+
+/* We shouldn't be throwing exceptions at all, but it sadly turns out
+ we call STL (inline) functions that do. This avoids the GLIBCXX_3.4.20
+ symbol version. */
+void __attribute__((weak)) __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);
+}
+
+} // namespace std
+
+namespace __cxxabiv1 {
+/* Calls to this function are added by the compiler itself on `new Class[n]`
+ * calls. This avoids the GLIBCXX_3.4.20 symbol version. */
+extern "C" void __attribute__((weak)) __cxa_throw_bad_array_new_length() {
+ MOZ_CRASH();
+}
+} // namespace __cxxabiv1
+
+#if _GLIBCXX_RELEASE >= 11
+namespace std {
+
+/* This avoids the GLIBCXX_3.4.29 symbol version. */
+void __attribute__((weak)) __throw_bad_array_new_length() { MOZ_CRASH(); }
+
+} // namespace std
+#endif
+
+/* 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. This avoids the GLIBCXX_3.4.21 symbol version. */
+namespace std {
+__attribute__((weak)) runtime_error::runtime_error(char const* s)
+ : runtime_error(std::string(s)) {}
+__attribute__((weak)) out_of_range::out_of_range(char const* s)
+ : out_of_range(std::string(s)) {}
+__attribute__((weak)) invalid_argument::invalid_argument(char const* s)
+ : invalid_argument(std::string(s)) {}
+} // namespace std
+
+/* Expose the definitions for the old ABI, allowing us to call its functions */
+#define _GLIBCXX_THREAD_ABI_COMPAT 1
+#include <thread>
+
+namespace std {
+/* The old ABI has a thread::_M_start_thread(shared_ptr<_Impl_base>),
+ * while the new has thread::_M_start_thread(unique_ptr<_State>, void(*)()).
+ * There is an intermediate ABI at version 3.4.21, with
+ * thread::_M_start_thread(shared_ptr<_Impl_base>, void(*)()).
+ * The void(*)() parameter is only there to keep a reference to pthread_create
+ * on the caller side, and is unused in the implementation
+ * We're creating an entry point for the new and intermediate ABIs, and make
+ * them call the old ABI. This avoids the GLIBCXX_3.4.21 symbol version. */
+__attribute__((weak)) void thread::_M_start_thread(shared_ptr<_Impl_base> impl,
+ void (*)()) {
+ _M_start_thread(std::move(impl));
+}
+
+/* We need a _Impl_base-derived class wrapping a _State to call the old ABI
+ * from what we got by diverting the new API. This avoids the GLIBCXX_3.4.22
+ * symbol version. */
+struct StateWrapper : public thread::_Impl_base {
+ unique_ptr<thread::_State> mState;
+
+ StateWrapper(unique_ptr<thread::_State> aState) : mState(std::move(aState)) {}
+
+ void _M_run() override { mState->_M_run(); }
+};
+
+/* This avoids the GLIBCXX_3.4.22 symbol version. */
+__attribute__((weak)) void thread::_M_start_thread(unique_ptr<_State> aState,
+ void (*)()) {
+ auto impl = std::make_shared<StateWrapper>(std::move(aState));
+ _M_start_thread(std::move(impl));
+}
+
+/* For some reason this is a symbol exported by new versions of libstdc++,
+ * even though the destructor is default there too. This avoids the
+ * GLIBCXX_3.4.22 symbol version. */
+__attribute__((weak)) thread::_State::~_State() = default;
+
+#if _GLIBCXX_RELEASE >= 9
+// Ideally we'd define
+// bool _Sp_make_shared_tag::_S_eq(const type_info& ti) noexcept
+// but we wouldn't be able to change its visibility because of the existing
+// definition in C++ headers. We do need to change its visibility because we
+// don't want it to be shadowing the one provided by libstdc++ itself, because
+// it doesn't support RTTI. Not supporting RTTI doesn't matter for Firefox
+// itself because it's built with RTTI disabled.
+// So we define via the mangled symbol.
+// This avoids the GLIBCXX_3.4.26 symbol version.
+extern "C" __attribute__((visibility("hidden"))) bool
+_ZNSt19_Sp_make_shared_tag5_S_eqERKSt9type_info(const type_info*) noexcept {
+ return false;
+}
+#endif
+
+} // namespace std
+
+namespace std {
+/* Instantiate this template to avoid GLIBCXX_3.4.21 symbol versions
+ * depending on optimization level */
+template basic_ios<char, char_traits<char>>::operator bool() const;
+} // namespace std
+
+#if !defined(MOZ_ASAN) && !defined(MOZ_TSAN)
+/* operator delete with size is only available in CXXAPI_1.3.9, equivalent to
+ * GLIBCXX_3.4.21. */
+void operator delete(void* ptr, size_t size) noexcept(true) {
+ ::operator delete(ptr);
+}
+#endif
+
+namespace std {
+/* Instantiate this template to avoid GLIBCXX_3.4.23 symbol versions
+ * depending on optimization level */
+template basic_string<char, char_traits<char>, allocator<char>>::basic_string(
+ const basic_string&, size_t, const allocator<char>&);
+
+#if _GLIBCXX_RELEASE >= 9
+// This avoids the GLIBCXX_3.4.26 symbol version.
+template basic_stringstream<char, char_traits<char>,
+ allocator<char>>::basic_stringstream();
+
+template basic_ostringstream<char, char_traits<char>,
+ allocator<char>>::basic_ostringstream();
+#endif
+
+#if _GLIBCXX_RELEASE >= 11
+// This avoids the GLIBCXX_3.4.29 symbol version.
+template void basic_string<char, char_traits<char>, allocator<char>>::reserve();
+
+template void
+basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t>>::reserve();
+#endif
+
+} // namespace std
+
+/* The __cxa_thread_atexit_impl symbol is only available on GLIBC 2.18, but we
+ * want things to keep working on 2.17. It's not actually used directly from
+ * C++ code, but through __cxa_thead_atexit in libstdc++. The problem we have,
+ * though, is that rust's libstd also uses it, introducing a dependency we
+ * don't actually want. Fortunately, we can fall back to libstdc++'s wrapper
+ * (which, on systems without __cxa_thread_atexit_impl, has its own compatible
+ * implementation).
+ * The __cxa_thread_atexit symbol itself is marked CXXABI_1.3.7, which is
+ * equivalent to GLIBCXX_3.4.18.
+ */
+extern "C" int __cxa_thread_atexit_impl(void (*dtor)(void*), void* obj,
+ void* dso_handle) {
+ return __cxxabiv1::__cxa_thread_atexit(dtor, obj, dso_handle);
+}