| commit e156dabc766d6f6f99ce9402999eae380a3ec1f2 |
| Author: Szabolcs Nagy <szabolcs.nagy@arm.com> |
| Date: Mon Oct 26 15:48:01 2020 +0000 |
| |
| aarch64: Add variant PCS lazy binding test [BZ #26798] |
| |
| This test fails without bug 26798 fixed because some integer registers |
| likely get clobbered by lazy binding and variant PCS only allows x16 |
| and x17 to be clobbered at call time. |
| |
| The test requires binutils 2.32.1 or newer for handling variant PCS |
| symbols. SVE registers are not covered by this test, to avoid the |
| complexity of handling multiple compile- and runtime feature support |
| cases. |
| |
| (Trivial textual conflicts due to lack of PAC and BTI support) |
| |
| # Conflicts: |
| # sysdeps/aarch64/Makefile |
| # sysdeps/aarch64/configure |
| # sysdeps/aarch64/configure.ac |
| |
| diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile |
| index 94baaf52dda4b801..3ec78fefc6dd5797 100644 |
| |
| |
| @@ -3,6 +3,13 @@ long-double-fcts = yes |
| ifeq ($(subdir),elf) |
| sysdep-dl-routines += tlsdesc dl-tlsdesc |
| gen-as-const-headers += dl-link.sym |
| + |
| +ifeq (yes,$(aarch64-variant-pcs)) |
| +tests += tst-vpcs |
| +modules-names += tst-vpcs-mod |
| +LDFLAGS-tst-vpcs-mod.so = -Wl,-z,lazy |
| +$(objpfx)tst-vpcs: $(objpfx)tst-vpcs-mod.so |
| +endif |
| endif |
| |
| ifeq ($(subdir),csu) |
| diff --git a/sysdeps/aarch64/configure b/sysdeps/aarch64/configure |
| index 5bd355a6917df365..f78a79338aba1e34 100644 |
| |
| |
| @@ -172,3 +172,43 @@ else |
| config_vars="$config_vars |
| default-abi = lp64" |
| fi |
| + |
| +# Check if binutils supports variant PCS symbols. |
| +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for variant PCS support" >&5 |
| +$as_echo_n "checking for variant PCS support... " >&6; } |
| +if ${libc_cv_aarch64_variant_pcs+:} false; then : |
| + $as_echo_n "(cached) " >&6 |
| +else |
| + cat > conftest.S <<EOF |
| +.global foo |
| +.type foo, %function |
| +.variant_pcs foo |
| +foo: |
| + ret |
| +.global bar |
| +.type bar, %function |
| +bar: |
| + b foo |
| +EOF |
| + libc_cv_aarch64_variant_pcs=no |
| + if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS -nostdlib -nostartfiles $no_ssp -shared -fPIC -o conftest.so conftest.S' |
| + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 |
| + (eval $ac_try) 2>&5 |
| + ac_status=$? |
| + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 |
| + test $ac_status = 0; }; } \ |
| + && { ac_try='$READELF -dW conftest.so | grep -q AARCH64_VARIANT_PCS' |
| + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 |
| + (eval $ac_try) 2>&5 |
| + ac_status=$? |
| + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 |
| + test $ac_status = 0; }; } |
| + then |
| + libc_cv_aarch64_variant_pcs=yes |
| + fi |
| + rm -rf conftest.* |
| +fi |
| +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_aarch64_variant_pcs" >&5 |
| +$as_echo "$libc_cv_aarch64_variant_pcs" >&6; } |
| +config_vars="$config_vars |
| +aarch64-variant-pcs = $libc_cv_aarch64_variant_pcs" |
| diff --git a/sysdeps/aarch64/configure.ac b/sysdeps/aarch64/configure.ac |
| index 7851dd4dac345b2a..7f13bfb93b60bfd7 100644 |
| |
| |
| @@ -20,3 +20,25 @@ if test $libc_cv_aarch64_be = yes; then |
| else |
| LIBC_CONFIG_VAR([default-abi], [lp64]) |
| fi |
| + |
| +# Check if binutils supports variant PCS symbols. |
| +AC_CACHE_CHECK([for variant PCS support], [libc_cv_aarch64_variant_pcs], [dnl |
| + cat > conftest.S <<EOF |
| +.global foo |
| +.type foo, %function |
| +.variant_pcs foo |
| +foo: |
| + ret |
| +.global bar |
| +.type bar, %function |
| +bar: |
| + b foo |
| +EOF |
| + libc_cv_aarch64_variant_pcs=no |
| + if AC_TRY_COMMAND([${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS -nostdlib -nostartfiles $no_ssp -shared -fPIC -o conftest.so conftest.S]) \ |
| + && AC_TRY_COMMAND([$READELF -dW conftest.so | grep -q AARCH64_VARIANT_PCS]) |
| + then |
| + libc_cv_aarch64_variant_pcs=yes |
| + fi |
| + rm -rf conftest.*]) |
| +LIBC_CONFIG_VAR([aarch64-variant-pcs], [$libc_cv_aarch64_variant_pcs]) |
| diff --git a/sysdeps/aarch64/tst-vpcs-mod.S b/sysdeps/aarch64/tst-vpcs-mod.S |
| new file mode 100644 |
| index 0000000000000000..b2642ba030daaca7 |
| |
| |
| @@ -0,0 +1,141 @@ |
| +/* Record the register state before and after a variant PCS call. |
| + Copyright (C) 2020 Free Software Foundation, Inc. |
| + |
| + This file is part of the GNU C Library. |
| + |
| + The GNU C Library is free software; you can redistribute it and/or |
| + modify it under the terms of the GNU Lesser General Public License as |
| + published by the Free Software Foundation; either version 2.1 of the |
| + License, or (at your option) any later version. |
| + |
| + The GNU C Library is distributed in the hope that it will be useful, |
| + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| + Lesser General Public License for more details. |
| + |
| + You should have received a copy of the GNU Lesser General Public |
| + License along with the GNU C Library. If not, see |
| + <https://www.gnu.org/licenses/>. */ |
| + |
| + .variant_pcs vpcs_call |
| + .global vpcs_call |
| + .type vpcs_call, %function |
| +vpcs_call: |
| + .cfi_startproc |
| + hint 34 /* bti c. */ |
| + |
| + /* Save register state to *x0. */ |
| + stp x0, x1, [x0] |
| + stp x2, x3, [x0, 16] |
| + stp x4, x5, [x0, 32] |
| + stp x6, x7, [x0, 48] |
| + stp x8, x9, [x0, 64] |
| + stp x10, x11, [x0, 80] |
| + stp x12, x13, [x0, 96] |
| + stp x14, x15, [x0, 112] |
| + stp x16, x17, [x0, 128] |
| + stp x18, x19, [x0, 144] |
| + stp x20, x21, [x0, 160] |
| + stp x22, x23, [x0, 176] |
| + stp x24, x25, [x0, 192] |
| + stp x26, x27, [x0, 208] |
| + stp x28, x29, [x0, 224] |
| + mov x1, sp |
| + stp x30, x1, [x0, 240] |
| + stp q0, q1, [x0, 256] |
| + stp q2, q3, [x0, 288] |
| + stp q4, q5, [x0, 320] |
| + stp q6, q7, [x0, 352] |
| + stp q8, q9, [x0, 384] |
| + stp q10, q11, [x0, 416] |
| + stp q12, q13, [x0, 448] |
| + stp q14, q15, [x0, 480] |
| + stp q16, q17, [x0, 512] |
| + stp q18, q19, [x0, 544] |
| + stp q20, q21, [x0, 576] |
| + stp q22, q23, [x0, 608] |
| + stp q24, q25, [x0, 640] |
| + stp q26, q27, [x0, 672] |
| + stp q28, q29, [x0, 704] |
| + stp q30, q31, [x0, 736] |
| + ret |
| + .cfi_endproc |
| + .size vpcs_call, .-vpcs_call |
| + |
| + .global vpcs_call_regs |
| + .type vpcs_call_regs, %function |
| +vpcs_call_regs: |
| + .cfi_startproc |
| + hint 34 /* bti c. */ |
| + |
| + stp x29, x30, [sp, -160]! |
| + mov x29, sp |
| + |
| + /* Save callee-saved registers. */ |
| + stp x19, x20, [sp, 16] |
| + stp x21, x22, [sp, 32] |
| + stp x23, x24, [sp, 48] |
| + stp x25, x26, [sp, 64] |
| + stp x27, x28, [sp, 80] |
| + stp d8, d9, [sp, 96] |
| + stp d10, d11, [sp, 112] |
| + stp d12, d13, [sp, 128] |
| + stp d14, d15, [sp, 144] |
| + |
| + /* Initialize most registers from *x1, and save x0, x1, x29, x30, |
| + and sp (== x29), so *x1 contains the register state. */ |
| + stp x0, x1, [x1] |
| + str x29, [x1, 232] |
| + ldp x2, x3, [x1, 16] |
| + ldp x4, x5, [x1, 32] |
| + ldp x6, x7, [x1, 48] |
| + ldp x8, x9, [x1, 64] |
| + ldp x10, x11, [x1, 80] |
| + ldp x12, x13, [x1, 96] |
| + ldp x14, x15, [x1, 112] |
| + ldp x16, x17, [x1, 128] |
| + ldp x18, x19, [x1, 144] |
| + ldp x20, x21, [x1, 160] |
| + ldp x22, x23, [x1, 176] |
| + ldp x24, x25, [x1, 192] |
| + ldp x26, x27, [x1, 208] |
| + ldr x28, [x1, 224] |
| + /* Skip x29, x30, sp. */ |
| + ldp q0, q1, [x1, 256] |
| + ldp q2, q3, [x1, 288] |
| + ldp q4, q5, [x1, 320] |
| + ldp q6, q7, [x1, 352] |
| + ldp q8, q9, [x1, 384] |
| + ldp q10, q11, [x1, 416] |
| + ldp q12, q13, [x1, 448] |
| + ldp q14, q15, [x1, 480] |
| + ldp q16, q17, [x1, 512] |
| + ldp q18, q19, [x1, 544] |
| + ldp q20, q21, [x1, 576] |
| + ldp q22, q23, [x1, 608] |
| + ldp q24, q25, [x1, 640] |
| + ldp q26, q27, [x1, 672] |
| + ldp q28, q29, [x1, 704] |
| + ldp q30, q31, [x1, 736] |
| + |
| + /* Emulate a BL using B, but save x30 before the branch. */ |
| + adr x30, .L_return_addr |
| + stp x30, x29, [x1, 240] |
| + b vpcs_call |
| +.L_return_addr: |
| + |
| + /* Restore callee-saved registers. */ |
| + ldp x19, x20, [sp, 16] |
| + ldp x21, x22, [sp, 32] |
| + ldp x23, x24, [sp, 48] |
| + ldp x25, x26, [sp, 64] |
| + ldp x27, x28, [sp, 80] |
| + ldp d8, d9, [sp, 96] |
| + ldp d10, d11, [sp, 112] |
| + ldp d12, d13, [sp, 128] |
| + ldp d14, d15, [sp, 144] |
| + |
| + ldp x29, x30, [sp], 160 |
| + ret |
| + .cfi_endproc |
| + .size vpcs_call_regs, .-vpcs_call_regs |
| diff --git a/sysdeps/aarch64/tst-vpcs.c b/sysdeps/aarch64/tst-vpcs.c |
| new file mode 100644 |
| index 0000000000000000..92a701eb7cdea8ac |
| |
| |
| @@ -0,0 +1,78 @@ |
| +/* Test that variant PCS calls don't clobber registers with lazy binding. |
| + Copyright (C) 2020 Free Software Foundation, Inc. |
| + This file is part of the GNU C Library. |
| + |
| + The GNU C Library is free software; you can redistribute it and/or |
| + modify it under the terms of the GNU Lesser General Public |
| + License as published by the Free Software Foundation; either |
| + version 2.1 of the License, or (at your option) any later version. |
| + |
| + The GNU C Library is distributed in the hope that it will be useful, |
| + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| + Lesser General Public License for more details. |
| + |
| + You should have received a copy of the GNU Lesser General Public |
| + License along with the GNU C Library; if not, see |
| + <https://www.gnu.org/licenses/>. */ |
| + |
| +#include <stdint.h> |
| +#include <stdio.h> |
| +#include <support/check.h> |
| + |
| +struct regs |
| +{ |
| + uint64_t x[32]; |
| + union { |
| + long double q[32]; |
| + uint64_t u[64]; |
| + } v; |
| +}; |
| + |
| +/* Gives the registers in the caller and callee around a variant PCS call. |
| + Most registers are initialized from BEFORE in the caller so they can |
| + have values that likely show clobbers. Register state extensions such |
| + as SVE is not covered here, only the base registers. */ |
| +void vpcs_call_regs (struct regs *after, struct regs *before); |
| + |
| +static int |
| +do_test (void) |
| +{ |
| + struct regs before, after; |
| + int err = 0; |
| + |
| + unsigned char *p = (unsigned char *)&before; |
| + for (int i = 0; i < sizeof before; i++) |
| + p[i] = i & 0xff; |
| + |
| + vpcs_call_regs (&after, &before); |
| + |
| + for (int i = 0; i < 32; i++) |
| + if (before.x[i] != after.x[i]) |
| + { |
| + if (i == 16 || i == 17) |
| + /* Variant PCS allows clobbering x16 and x17. */ |
| + continue; |
| + err++; |
| + printf ("x%d: before: 0x%016llx after: 0x%016llx\n", |
| + i, |
| + (unsigned long long)before.x[i], |
| + (unsigned long long)after.x[i]); |
| + } |
| + for (int i = 0; i < 64; i++) |
| + if (before.v.u[i] != after.v.u[i]) |
| + { |
| + err++; |
| + printf ("v%d: before: 0x%016llx %016llx after: 0x%016llx %016llx\n", |
| + i/2, |
| + (unsigned long long)before.v.u[2*(i/2)+1], |
| + (unsigned long long)before.v.u[2*(i/2)], |
| + (unsigned long long)after.v.u[2*(i/2)+1], |
| + (unsigned long long)after.v.u[2*(i/2)]); |
| + } |
| + if (err) |
| + FAIL_EXIT1 ("The variant PCS call clobbered %d registers.\n", err); |
| + return 0; |
| +} |
| + |
| +#include <support/test-driver.c> |