aboutsummaryrefslogtreecommitdiff
path: root/bolt/test
diff options
context:
space:
mode:
Diffstat (limited to 'bolt/test')
-rw-r--r--bolt/test/AArch64/negate-ra-state-disallow.s25
-rw-r--r--bolt/test/AArch64/negate-ra-state-incorrect.s78
-rw-r--r--bolt/test/AArch64/negate-ra-state-reorder.s73
-rw-r--r--bolt/test/AArch64/negate-ra-state.s76
-rw-r--r--bolt/test/AArch64/pacret-split-funcs.s54
-rw-r--r--bolt/test/X86/fragment-alias.s13
-rw-r--r--bolt/test/perf2bolt/AArch64/perf2bolt-spe.test3
-rw-r--r--bolt/test/runtime/AArch64/negate-ra-state.cpp26
-rw-r--r--bolt/test/runtime/AArch64/pacret-function-split.cpp42
9 files changed, 388 insertions, 2 deletions
diff --git a/bolt/test/AArch64/negate-ra-state-disallow.s b/bolt/test/AArch64/negate-ra-state-disallow.s
new file mode 100644
index 0000000..95adb71
--- /dev/null
+++ b/bolt/test/AArch64/negate-ra-state-disallow.s
@@ -0,0 +1,25 @@
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q
+# RUN: not llvm-bolt %t.exe -o %t.exe.bolt --update-branch-protection=false 2>&1 | FileCheck %s
+
+# CHECK: BOLT-ERROR: --update-branch-protection is set to false, but foo contains .cfi-negate-ra-state
+
+ .text
+ .globl foo
+ .p2align 2
+ .type foo,@function
+foo:
+ .cfi_startproc
+ hint #25
+ .cfi_negate_ra_state
+ mov x1, #0
+ hint #29
+ .cfi_negate_ra_state
+ ret
+ .cfi_endproc
+ .size foo, .-foo
+
+ .global _start
+ .type _start, %function
+_start:
+ b foo
diff --git a/bolt/test/AArch64/negate-ra-state-incorrect.s b/bolt/test/AArch64/negate-ra-state-incorrect.s
new file mode 100644
index 0000000..14d2c38
--- /dev/null
+++ b/bolt/test/AArch64/negate-ra-state-incorrect.s
@@ -0,0 +1,78 @@
+# This test checks that MarkRAStates pass ignores functions with
+# malformed .cfi_negate_ra_state sequences in the input binary.
+
+# The cases checked are:
+# - extra .cfi_negate_ra_state in Signed state: checked in foo,
+# - extra .cfi_negate_ra_state in Unsigned state: checked in bar,
+# - missing .cfi_negate_ra_state from PSign or PAuth instructions: checked in baz.
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt %t.exe -o %t.exe.bolt --no-threads | FileCheck %s --check-prefix=CHECK-BOLT
+
+# CHECK-BOLT: BOLT-INFO: inconsistent RAStates in function foo: ptr authenticating inst encountered in Unsigned RA state
+# CHECK-BOLT: BOLT-INFO: inconsistent RAStates in function bar: ptr signing inst encountered in Signed RA state
+# CHECK-BOLT: BOLT-INFO: inconsistent RAStates in function baz: ptr sign/auth inst without .cfi_negate_ra_state
+
+# Check that the incorrect functions got ignored, so they are not in the new .text section
+# RUN: llvm-objdump %t.exe.bolt -d -j .text | FileCheck %s --check-prefix=CHECK-OBJDUMP
+# CHECK-OBJDUMP-NOT: <foo>:
+# CHECK-OBJDUMP-NOT: <bar>:
+# CHECK-OBJDUMP-NOT: <baz>:
+
+
+ .text
+ .globl foo
+ .p2align 2
+ .type foo,@function
+foo:
+ .cfi_startproc
+ hint #25
+ .cfi_negate_ra_state
+ mov x1, #0
+ .cfi_negate_ra_state // Incorrect CFI in signed state
+ hint #29
+ .cfi_negate_ra_state
+ ret
+ .cfi_endproc
+ .size foo, .-foo
+
+ .text
+ .globl bar
+ .p2align 2
+ .type bar,@function
+bar:
+ .cfi_startproc
+ mov x1, #0
+ .cfi_negate_ra_state // Incorrect CFI in unsigned state
+ hint #25
+ .cfi_negate_ra_state
+ mov x1, #0
+ hint #29
+ .cfi_negate_ra_state
+ ret
+ .cfi_endproc
+ .size bar, .-bar
+
+ .text
+ .globl baz
+ .p2align 2
+ .type baz,@function
+baz:
+ .cfi_startproc
+ mov x1, #0
+ hint #25
+ .cfi_negate_ra_state
+ mov x1, #0
+ hint #29
+ // Missing .cfi_negate_ra_state
+ ret
+ .cfi_endproc
+ .size baz, .-baz
+
+ .global _start
+ .type _start, %function
+_start:
+ b foo
+ b bar
+ b baz
diff --git a/bolt/test/AArch64/negate-ra-state-reorder.s b/bolt/test/AArch64/negate-ra-state-reorder.s
new file mode 100644
index 0000000..2659f75
--- /dev/null
+++ b/bolt/test/AArch64/negate-ra-state-reorder.s
@@ -0,0 +1,73 @@
+# Checking that after reordering BasicBlocks, the generated OpNegateRAState instructions
+# are placed where the RA state is different between two consecutive instructions.
+# This case demonstrates, that the input might have a different amount than the output:
+# input has 4, but output only has 3.
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt %t.exe -o %t.exe.bolt --no-threads --reorder-blocks=reverse \
+# RUN: --print-cfg --print-after-lowering --print-only foo | FileCheck %s
+
+# Check that the reordering succeeded.
+# CHECK: Binary Function "foo" after building cfg {
+# CHECK: BB Layout : .LBB00, .Ltmp2, .Ltmp0, .Ltmp1
+# CHECK: Binary Function "foo" after inst-lowering {
+# CHECK: BB Layout : .LBB00, .Ltmp1, .Ltmp0, .Ltmp2
+
+
+# Check the generated CFIs.
+# CHECK: OpNegateRAState
+# CHECK-NEXT: mov x2, #0x6
+
+# CHECK: autiasp
+# CHECK-NEXT: OpNegateRAState
+# CHECK-NEXT: ret
+
+# CHECK: paciasp
+# CHECK-NEXT: OpNegateRAState
+
+# CHECK: DWARF CFI Instructions:
+# CHECK-NEXT: 0: OpNegateRAState
+# CHECK-NEXT: 1: OpNegateRAState
+# CHECK-NEXT: 2: OpNegateRAState
+# CHECK-NEXT: End of Function "foo"
+
+ .text
+ .globl foo
+ .p2align 2
+ .type foo,@function
+foo:
+ .cfi_startproc
+ // RA is unsigned
+ mov x1, #0
+ mov x1, #1
+ mov x1, #2
+ // jump into the signed "range"
+ b .Lmiddle
+.Lback:
+// sign RA
+ paciasp
+ .cfi_negate_ra_state
+ mov x2, #3
+ mov x2, #4
+ // skip unsigned instructions
+ b .Lcont
+ .cfi_negate_ra_state
+.Lmiddle:
+// RA is unsigned
+ mov x4, #5
+ b .Lback
+ .cfi_negate_ra_state
+.Lcont:
+// continue in signed state
+ mov x2, #6
+ autiasp
+ .cfi_negate_ra_state
+ ret
+ .cfi_endproc
+ .size foo, .-foo
+
+ .global _start
+ .type _start, %function
+_start:
+ b foo
diff --git a/bolt/test/AArch64/negate-ra-state.s b/bolt/test/AArch64/negate-ra-state.s
new file mode 100644
index 0000000..30786d4
--- /dev/null
+++ b/bolt/test/AArch64/negate-ra-state.s
@@ -0,0 +1,76 @@
+# Checking that .cfi-negate_ra_state directives are emitted in the same location as in the input in the case of no optimizations.
+
+# The foo and bar functions are a pair, with the first signing the return address,
+# and the second authenticating it. We have a tailcall between the two.
+# This is testing that BOLT can handle functions starting in signed RA state.
+
+# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q
+# RUN: llvm-bolt %t.exe -o %t.exe.bolt --no-threads --print-all | FileCheck %s --check-prefix=CHECK-BOLT
+
+# Check that the negate-ra-state at the start of bar is not discarded.
+# If it was discarded, MarkRAState would report bar as having inconsistent RAStates.
+# This is testing the handling of initialRAState on the BinaryFunction.
+# CHECK-BOLT-NOT: BOLT-INFO: inconsistent RAStates in function foo
+# CHECK-BOLT-NOT: BOLT-INFO: inconsistent RAStates in function bar
+
+# Check that OpNegateRAState CFIs are generated correctly.
+# CHECK-BOLT: Binary Function "foo" after insert-negate-ra-state-pass {
+# CHECK-BOLT: paciasp
+# CHECK-BOLT-NEXT: OpNegateRAState
+
+# CHECK-BOLT: DWARF CFI Instructions:
+# CHECK-BOLT-NEXT: 0: OpNegateRAState
+# CHECK-BOLT-NEXT: End of Function "foo"
+
+# CHECK-BOLT: Binary Function "bar" after insert-negate-ra-state-pass {
+# CHECK-BOLT: OpNegateRAState
+# CHECK-BOLT-NEXT: mov x1, #0x0
+# CHECK-BOLT-NEXT: mov x1, #0x1
+# CHECK-BOLT-NEXT: autiasp
+# CHECK-BOLT-NEXT: OpNegateRAState
+# CHECK-BOLT-NEXT: ret
+
+# CHECK-BOLT: DWARF CFI Instructions:
+# CHECK-BOLT-NEXT: 0: OpNegateRAState
+# CHECK-BOLT-NEXT: 1: OpNegateRAState
+# CHECK-BOLT-NEXT: End of Function "bar"
+
+# End of negate-ra-state insertion logs for foo and bar.
+# CHECK: Binary Function "_start" after insert-negate-ra-state-pass {
+
+# Check that the functions are in the new .text section
+# RUN: llvm-objdump %t.exe.bolt -d -j .text | FileCheck %s --check-prefix=CHECK-OBJDUMP
+# CHECK-OBJDUMP: <foo>:
+# CHECK-OBJDUMP: <bar>:
+
+
+ .text
+ .globl foo
+ .p2align 2
+ .type foo,@function
+foo:
+ .cfi_startproc
+ paciasp
+ .cfi_negate_ra_state
+ mov x1, #0
+ b bar
+ .cfi_endproc
+ .size foo, .-foo
+
+
+
+ .text
+ .globl bar
+ .p2align 2
+ .type bar,@function
+bar:
+ .cfi_startproc
+ .cfi_negate_ra_state // Indicating that RA is signed from the start of bar.
+ mov x1, #0
+ mov x1, #1
+ autiasp
+ .cfi_negate_ra_state
+ ret
+ .cfi_endproc
+ .size bar, .-bar
diff --git a/bolt/test/AArch64/pacret-split-funcs.s b/bolt/test/AArch64/pacret-split-funcs.s
new file mode 100644
index 0000000..27b34710
--- /dev/null
+++ b/bolt/test/AArch64/pacret-split-funcs.s
@@ -0,0 +1,54 @@
+# Checking that we generate an OpNegateRAState CFI after the split point,
+# when splitting a region with signed RA state.
+# We split at the fallthrough label.
+
+# REQUIRES: system-linux
+
+# RUN: %clang %s %cflags -march=armv8.3-a -Wl,-q -o %t
+# RUN: link_fdata --no-lbr %s %t %t.fdata
+# RUN: llvm-bolt %t -o %t.bolt --data %t.fdata -split-functions \
+# RUN: --print-only foo --print-split --print-all 2>&1 | FileCheck %s
+
+# Checking that we don't see any OpNegateRAState CFIs before the insertion pass.
+# CHECK-NOT: OpNegateRAState
+# CHECK: Binary Function "foo" after insert-negate-ra-state-pass
+
+# CHECK: paciasp
+# CHECK-NEXT: OpNegateRAState
+
+# CHECK: ------- HOT-COLD SPLIT POINT -------
+
+# CHECK: OpNegateRAState
+# CHECK-NEXT: mov x0, #0x1
+# CHECK-NEXT: autiasp
+# CHECK-NEXT: OpNegateRAState
+# CHECK-NEXT: ret
+
+# End of the insert-negate-ra-state-pass logs
+# CHECK: Binary Function "foo" after finalize-functions
+
+ .text
+ .globl foo
+ .type foo, %function
+foo:
+.cfi_startproc
+.entry_bb:
+# FDATA: 1 foo #.entry_bb# 10
+ paciasp
+ .cfi_negate_ra_state // indicating that paciasp changed the RA state to signed
+ cmp x0, #0
+ b.eq .Lcold_bb1
+.Lfallthrough: // split point
+ mov x0, #1
+ autiasp
+ .cfi_negate_ra_state // indicating that autiasp changed the RA state to unsigned
+ ret
+.Lcold_bb1: // Instructions below are not important, they are just here so the cold block is not empty.
+ .cfi_negate_ra_state // ret has unsigned RA state, but the next inst (autiasp) has signed RA state
+ mov x0, #2
+ retaa
+.cfi_endproc
+ .size foo, .-foo
+
+## Force relocation mode.
+.reloc 0, R_AARCH64_NONE
diff --git a/bolt/test/X86/fragment-alias.s b/bolt/test/X86/fragment-alias.s
new file mode 100644
index 0000000..3392dd5
--- /dev/null
+++ b/bolt/test/X86/fragment-alias.s
@@ -0,0 +1,13 @@
+## This test reproduces the issue where a fragment has the same address as
+## parent function.
+# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %s -o %t.o
+# RUN: %clang %cflags %t.o -o %t
+# RUN: llvm-bolt %t -o %t.out 2>&1 | FileCheck %s
+# CHECK: BOLT-WARNING: fragment maps to the same function as parent: main/1(*2)
+.type main, @function
+.type main.cold, @function
+main.cold:
+main:
+ ret
+.size main, .-main
+.size main.cold, .-main.cold
diff --git a/bolt/test/perf2bolt/AArch64/perf2bolt-spe.test b/bolt/test/perf2bolt/AArch64/perf2bolt-spe.test
index 91f5c85..1f44f75 100644
--- a/bolt/test/perf2bolt/AArch64/perf2bolt-spe.test
+++ b/bolt/test/perf2bolt/AArch64/perf2bolt-spe.test
@@ -6,7 +6,6 @@ RUN: %clang %cflags %p/../../Inputs/asm_foo.s %p/../../Inputs/asm_main.c -o %t.e
RUN: perf record -e cycles -q -o %t.perf.data -- %t.exe 2> /dev/null
-RUN: (perf2bolt -p %t.perf.data -o %t.perf.boltdata --spe %t.exe 2> /dev/null; exit 0) | FileCheck %s --check-prefix=CHECK-SPE-LBR
+RUN: perf2bolt -p %t.perf.data -o %t.perf.boltdata --spe %t.exe | FileCheck %s --check-prefix=CHECK-SPE-LBR
CHECK-SPE-LBR: PERF2BOLT: parse SPE branch events in LBR-format
-
diff --git a/bolt/test/runtime/AArch64/negate-ra-state.cpp b/bolt/test/runtime/AArch64/negate-ra-state.cpp
new file mode 100644
index 0000000..60b0b08
--- /dev/null
+++ b/bolt/test/runtime/AArch64/negate-ra-state.cpp
@@ -0,0 +1,26 @@
+// REQUIRES: system-linux,bolt-runtime
+
+// RUN: %clangxx --target=aarch64-unknown-linux-gnu \
+// RUN: -mbranch-protection=pac-ret -Wl,-q %s -o %t.exe
+// RUN: llvm-bolt %t.exe -o %t.bolt.exe
+// RUN: %t.bolt.exe | FileCheck %s
+
+// CHECK: Exception caught: Exception from bar().
+
+#include <cstdio>
+#include <stdexcept>
+
+void bar() { throw std::runtime_error("Exception from bar()."); }
+
+void foo() {
+ try {
+ bar();
+ } catch (const std::exception &e) {
+ printf("Exception caught: %s\n", e.what());
+ }
+}
+
+int main() {
+ foo();
+ return 0;
+}
diff --git a/bolt/test/runtime/AArch64/pacret-function-split.cpp b/bolt/test/runtime/AArch64/pacret-function-split.cpp
new file mode 100644
index 0000000..208fc5c
--- /dev/null
+++ b/bolt/test/runtime/AArch64/pacret-function-split.cpp
@@ -0,0 +1,42 @@
+/* This test check that the negate-ra-state CFIs are properly emitted in case of
+ function splitting. The test checks two things:
+ - we split at the correct location: to test the feature,
+ we need to split *before* the bl __cxa_throw@PLT call is made,
+ so the unwinder has to unwind from the split (cold) part.
+
+ - the BOLTed binary runs, and returns the string from foo.
+
+# REQUIRES: system-linux,bolt-runtime
+
+# FDATA: 1 main #split# 1 _Z3foov 0 0 1
+
+# RUN: %clangxx --target=aarch64-unknown-linux-gnu \
+# RUN: -mbranch-protection=pac-ret %s -o %t.exe -Wl,-q
+# RUN: link_fdata %s %t.exe %t.fdata
+# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-eh \
+# RUN: --split-strategy=profile2 --split-all-cold --print-split \
+# RUN: --print-only=_Z3foov --data=%t.fdata 2>&1 | FileCheck \
+# RUN: --check-prefix=BOLT-CHECK %s
+# RUN: %t.bolt | FileCheck %s --check-prefix=RUN-CHECK
+
+# BOLT-CHECK-NOT: bl __cxa_throw@PLT
+# BOLT-CHECK: ------- HOT-COLD SPLIT POINT -------
+# BOLT-CHECK: bl __cxa_throw@PLT
+
+# RUN-CHECK: Exception caught: Exception from foo().
+*/
+
+#include <cstdio>
+#include <stdexcept>
+
+void foo() { throw std::runtime_error("Exception from foo()."); }
+
+int main() {
+ try {
+ __asm__ __volatile__("split:");
+ foo();
+ } catch (const std::exception &e) {
+ printf("Exception caught: %s\n", e.what());
+ }
+ return 0;
+}