diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /build/unix | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.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')
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----- + +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 +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----- + +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 +=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 = §ion->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); +} |