about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--sysdeps/aarch64/Makefile7
-rw-r--r--sysdeps/aarch64/configure40
-rw-r--r--sysdeps/aarch64/configure.ac22
-rw-r--r--sysdeps/aarch64/tst-vpcs-mod.S141
-rw-r--r--sysdeps/aarch64/tst-vpcs.c78
5 files changed, 288 insertions, 0 deletions
diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
index 7f82fc055e..d8e06d27b2 100644
--- a/sysdeps/aarch64/Makefile
+++ b/sysdeps/aarch64/Makefile
@@ -17,6 +17,13 @@ sysdep-dl-routines += tlsdesc dl-tlsdesc
 gen-as-const-headers += dl-link.sym
 
 tests-internal += tst-ifunc-arg-1 tst-ifunc-arg-2
+
+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 ac3cf6fd36..1f8223725f 100644
--- a/sysdeps/aarch64/configure
+++ b/sysdeps/aarch64/configure
@@ -255,3 +255,43 @@ if test $libc_cv_aarch64_pac_ret = yes; then
   $as_echo "#define HAVE_AARCH64_PAC_RET 1" >>confdefs.h
 
 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 8b042d6d05..da2a8d81d0 100644
--- a/sysdeps/aarch64/configure.ac
+++ b/sysdeps/aarch64/configure.ac
@@ -61,3 +61,25 @@ EOF
 if test $libc_cv_aarch64_pac_ret = yes; then
   AC_DEFINE(HAVE_AARCH64_PAC_RET)
 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 0000000000..b2642ba030
--- /dev/null
+++ b/sysdeps/aarch64/tst-vpcs-mod.S
@@ -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 0000000000..92a701eb7c
--- /dev/null
+++ b/sysdeps/aarch64/tst-vpcs.c
@@ -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>