new file mode 100644
@@ -0,0 +1,62 @@
+/*
+ * Branch target identification, basic notskip cases.
+ */
+
+#include "bti-crt.inc.c"
+
+static void skip2_sigill(int sig, siginfo_t *info, ucontext_t *uc)
+{
+ uc->uc_mcontext.pc += 8;
+ uc->uc_mcontext.pstate = 1;
+}
+
+#define NOP "nop"
+#define BTI_N "hint #32"
+#define BTI_C "hint #34"
+#define BTI_J "hint #36"
+#define BTI_JC "hint #38"
+
+#define BTYPE_1(DEST) \
+ asm("mov %0,#1; adr x16, 1f; br x16; 1: " DEST "; mov %0,#0" \
+ : "=r"(skipped) : : "x16")
+
+#define BTYPE_2(DEST) \
+ asm("mov %0,#1; adr x16, 1f; blr x16; 1: " DEST "; mov %0,#0" \
+ : "=r"(skipped) : : "x16", "x30")
+
+#define BTYPE_3(DEST) \
+ asm("mov %0,#1; adr x15, 1f; br x15; 1: " DEST "; mov %0,#0" \
+ : "=r"(skipped) : : "x15")
+
+#define TEST(WHICH, DEST, EXPECT) \
+ do { WHICH(DEST); fail += skipped ^ EXPECT; } while (0)
+
+
+int main()
+{
+ int fail = 0;
+ int skipped;
+
+ /* Signal-like with SA_SIGINFO. */
+ signal_info(SIGILL, skip2_sigill);
+
+ TEST(BTYPE_1, NOP, 1);
+ TEST(BTYPE_1, BTI_N, 1);
+ TEST(BTYPE_1, BTI_C, 0);
+ TEST(BTYPE_1, BTI_J, 0);
+ TEST(BTYPE_1, BTI_JC, 0);
+
+ TEST(BTYPE_2, NOP, 1);
+ TEST(BTYPE_2, BTI_N, 1);
+ TEST(BTYPE_2, BTI_C, 0);
+ TEST(BTYPE_2, BTI_J, 1);
+ TEST(BTYPE_2, BTI_JC, 0);
+
+ TEST(BTYPE_3, NOP, 1);
+ TEST(BTYPE_3, BTI_N, 1);
+ TEST(BTYPE_3, BTI_C, 1);
+ TEST(BTYPE_3, BTI_J, 0);
+ TEST(BTYPE_3, BTI_JC, 0);
+
+ return fail;
+}
new file mode 100644
@@ -0,0 +1,116 @@
+/*
+ * Branch target identification, basic notskip cases.
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#ifndef PROT_BTI
+#define PROT_BTI 0x10
+#endif
+
+static void skip2_sigill(int sig, siginfo_t *info, void *vuc)
+{
+ ucontext_t *uc = vuc;
+ uc->uc_mcontext.pc += 8;
+ uc->uc_mcontext.pstate = 1;
+}
+
+#define NOP "nop"
+#define BTI_N "hint #32"
+#define BTI_C "hint #34"
+#define BTI_J "hint #36"
+#define BTI_JC "hint #38"
+
+#define BTYPE_1(DEST) \
+ "mov x1, #1\n\t" \
+ "adr x16, 1f\n\t" \
+ "br x16\n" \
+"1: " DEST "\n\t" \
+ "mov x1, #0"
+
+#define BTYPE_2(DEST) \
+ "mov x1, #1\n\t" \
+ "adr x16, 1f\n\t" \
+ "blr x16\n" \
+"1: " DEST "\n\t" \
+ "mov x1, #0"
+
+#define BTYPE_3(DEST) \
+ "mov x1, #1\n\t" \
+ "adr x15, 1f\n\t" \
+ "br x15\n" \
+"1: " DEST "\n\t" \
+ "mov x1, #0"
+
+#define TEST(WHICH, DEST, EXPECT) \
+ WHICH(DEST) "\n" \
+ ".if " #EXPECT "\n\t" \
+ "eor x1, x1," #EXPECT "\n" \
+ ".endif\n\t" \
+ "add x0, x0, x1\n\t"
+
+asm("\n"
+"test_begin:\n\t"
+ BTI_C "\n\t"
+ "mov x2, x30\n\t"
+ "mov x0, #0\n\t"
+
+ TEST(BTYPE_1, NOP, 1)
+ TEST(BTYPE_1, BTI_N, 1)
+ TEST(BTYPE_1, BTI_C, 0)
+ TEST(BTYPE_1, BTI_J, 0)
+ TEST(BTYPE_1, BTI_JC, 0)
+
+ TEST(BTYPE_2, NOP, 1)
+ TEST(BTYPE_2, BTI_N, 1)
+ TEST(BTYPE_2, BTI_C, 0)
+ TEST(BTYPE_2, BTI_J, 1)
+ TEST(BTYPE_2, BTI_JC, 0)
+
+ TEST(BTYPE_3, NOP, 1)
+ TEST(BTYPE_3, BTI_N, 1)
+ TEST(BTYPE_3, BTI_C, 1)
+ TEST(BTYPE_3, BTI_J, 0)
+ TEST(BTYPE_3, BTI_JC, 0)
+
+ "ret x2\n"
+"test_end:"
+);
+
+int main()
+{
+ struct sigaction sa;
+ void *tb, *te;
+
+ void *p = mmap(0, getpagesize(),
+ PROT_EXEC | PROT_READ | PROT_WRITE | PROT_BTI,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (p == MAP_FAILED) {
+ perror("mmap");
+ return 1;
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = skip2_sigill;
+ sa.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGILL, &sa, NULL) < 0) {
+ perror("sigaction");
+ return 1;
+ }
+
+ /*
+ * ??? With "extern char test_begin[]", some compiler versions
+ * will use :got references, and some linker versions will
+ * resolve this reference to a static symbol incorrectly.
+ * Bypass this error by using a pc-relative reference directly.
+ */
+ asm("adr %0, test_begin; adr %1, test_end" : "=r"(tb), "=r"(te));
+
+ memcpy(p, tb, te - tb);
+
+ return ((int (*)(void))p)();
+}
new file mode 100644
@@ -0,0 +1,51 @@
+/*
+ * Minimal user-environment for testing BTI.
+ *
+ * Normal libc is not (yet) built with BTI support enabled,
+ * and so could generate a BTI TRAP before ever reaching main.
+ */
+
+#include <stdlib.h>
+#include <signal.h>
+#include <ucontext.h>
+#include <asm/unistd.h>
+
+int main(void);
+
+void _start(void)
+{
+ exit(main());
+}
+
+void exit(int ret)
+{
+ register int x0 __asm__("x0") = ret;
+ register int x8 __asm__("x8") = __NR_exit;
+
+ asm volatile("svc #0" : : "r"(x0), "r"(x8));
+ __builtin_unreachable();
+}
+
+/*
+ * Irritatingly, the user API struct sigaction does not match the
+ * kernel API struct sigaction. So for simplicity, isolate the
+ * kernel ABI here, and make this act like signal.
+ */
+void signal_info(int sig, void (*fn)(int, siginfo_t *, ucontext_t *))
+{
+ struct kernel_sigaction {
+ void (*handler)(int, siginfo_t *, ucontext_t *);
+ unsigned long flags;
+ unsigned long restorer;
+ unsigned long mask;
+ } sa = { fn, SA_SIGINFO, 0, 0 };
+
+ register int x0 __asm__("x0") = sig;
+ register void *x1 __asm__("x1") = &sa;
+ register void *x2 __asm__("x2") = 0;
+ register int x3 __asm__("x3") = sizeof(unsigned long);
+ register int x8 __asm__("x8") = __NR_rt_sigaction;
+
+ asm volatile("svc #0"
+ : : "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x8) : "memory");
+}
@@ -25,6 +25,16 @@ run-pauth-%: QEMU_OPTS += -cpu max
run-plugin-pauth-%: QEMU_OPTS += -cpu max
endif
+# BTI Tests
+# bti-1 tests the elf notes, so we require special compiler support.
+ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_BTI),)
+AARCH64_TESTS += bti-1
+bti-1: CFLAGS += -mbranch-protection=standard
+bti-1: LDFLAGS += -nostdlib
+endif
+# bti-2 tests PROT_BTI, so no special compiler support required.
+AARCH64_TESTS += bti-2
+
# Semihosting smoke test for linux-user
AARCH64_TESTS += semihosting
run-semihosting: semihosting
@@ -240,6 +240,10 @@ for target in $target_list; do
-march=armv8.3-a -o $TMPE $TMPC; then
echo "CROSS_CC_HAS_ARMV8_3=y" >> $config_target_mak
fi
+ if do_compiler "$target_compiler" $target_compiler_cflags \
+ -mbranch-protection=standard -o $TMPE $TMPC; then
+ echo "CROSS_CC_HAS_ARMV8_BTI=y" >> $config_target_mak
+ fi
;;
esac