[10/12,V2] arm: Implement cortex-M return signing address codegen
Commit Message
Hi all,
second version of this patch enabling address return signature and
verification based on Armv8.1-M Pointer Authentication [1].
To sign the return address, we use the PAC R12, LR, SP instruction
upon function entry. This is signing LR using SP and storing the
result in R12. R12 will be pushed into the stack.
During function epilogue R12 will be popped and AUT R12, LR, SP will
be used to verify that the content of LR is still valid before return.
Here an example of PAC instrumented function prologue and epilogue:
void foo (void);
int main()
{
foo ();
return 0;
}
Compiled with '-march=armv8.1-m.main -mbranch-protection=pac-ret
-mthumb' translates into:
main:
pac ip, lr, sp
push {r3, r7, ip, lr}
add r7, sp, #0
bl foo
movs r3, #0
mov r0, r3
pop {r3, r7, ip, lr}
aut ip, lr, sp
bx lr
The patch also takes care of generating a PACBTI instruction in place
of the sequence BTI+PAC when Branch Target Identification is enabled
contextually.
Ex. the previous example compiled with '-march=armv8.1-m.main
-mbranch-protection=pac-ret+bti -mthumb' translates into:
main:
pacbti ip, lr, sp
push {r3, r7, ip, lr}
add r7, sp, #0
bl foo
movs r3, #0
mov r0, r3
pop {r3, r7, ip, lr}
aut ip, lr, sp
bx lr
As part of previous upstream suggestions a test for varargs has been
added and '-mtpcs-frame' is deemed being incompatible with this return
signing address feature being introduced.
[1] <https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/armv8-1-m-pointer-authentication-and-branch-target-identification-extension>
gcc/Changelog
* config/arm/arm.c: (arm_compute_frame_layout)
(arm_expand_prologue, thumb2_expand_return, arm_expand_epilogue)
(arm_conditional_register_usage): Update for pac codegen.
(arm_current_function_pac_enabled_p): New function.
* config/arm/arm.md (pac_ip_lr_sp, pacbti_ip_lr_sp, aut_ip_lr_sp):
Add new patterns.
* config/arm/unspecs.md (UNSPEC_PAC_IP_LR_SP)
(UNSPEC_PACBTI_IP_LR_SP, UNSPEC_AUT_IP_LR_SP): Add unspecs.
gcc/testsuite/Changelog
* gcc.target/arm/pac.h : New file.
* gcc.target/arm/pac-1.c : New test case.
* gcc.target/arm/pac-2.c : Likewise.
* gcc.target/arm/pac-3.c : Likewise.
* gcc.target/arm/pac-4.c : Likewise.
* gcc.target/arm/pac-5.c : Likewise.
* gcc.target/arm/pac-6.c : Likewise.
* gcc.target/arm/pac-7.c : Likewise.
* gcc.target/arm/pac-8.c : Likewise.
Comments
On 28/06/2022 10:17, Andrea Corallo via Gcc-patches wrote:
> Hi all,
>
> second version of this patch enabling address return signature and
> verification based on Armv8.1-M Pointer Authentication [1].
>
> To sign the return address, we use the PAC R12, LR, SP instruction
> upon function entry. This is signing LR using SP and storing the
> result in R12. R12 will be pushed into the stack.
>
> During function epilogue R12 will be popped and AUT R12, LR, SP will
> be used to verify that the content of LR is still valid before return.
>
> Here an example of PAC instrumented function prologue and epilogue:
>
> void foo (void);
>
> int main()
> {
> foo ();
> return 0;
> }
>
> Compiled with '-march=armv8.1-m.main -mbranch-protection=pac-ret
> -mthumb' translates into:
>
> main:
> pac ip, lr, sp
> push {r3, r7, ip, lr}
> add r7, sp, #0
> bl foo
> movs r3, #0
> mov r0, r3
> pop {r3, r7, ip, lr}
> aut ip, lr, sp
> bx lr
>
> The patch also takes care of generating a PACBTI instruction in place
> of the sequence BTI+PAC when Branch Target Identification is enabled
> contextually.
>
> Ex. the previous example compiled with '-march=armv8.1-m.main
> -mbranch-protection=pac-ret+bti -mthumb' translates into:
>
> main:
> pacbti ip, lr, sp
> push {r3, r7, ip, lr}
> add r7, sp, #0
> bl foo
> movs r3, #0
> mov r0, r3
> pop {r3, r7, ip, lr}
> aut ip, lr, sp
> bx lr
>
> As part of previous upstream suggestions a test for varargs has been
> added and '-mtpcs-frame' is deemed being incompatible with this return
> signing address feature being introduced.
>
> [1] <https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/armv8-1-m-pointer-authentication-and-branch-target-identification-extension>
>
> gcc/Changelog
>
> * config/arm/arm.c: (arm_compute_frame_layout)
> (arm_expand_prologue, thumb2_expand_return, arm_expand_epilogue)
> (arm_conditional_register_usage): Update for pac codegen.
> (arm_current_function_pac_enabled_p): New function.
> * config/arm/arm.md (pac_ip_lr_sp, pacbti_ip_lr_sp, aut_ip_lr_sp):
> Add new patterns.
> * config/arm/unspecs.md (UNSPEC_PAC_IP_LR_SP)
> (UNSPEC_PACBTI_IP_LR_SP, UNSPEC_AUT_IP_LR_SP): Add unspecs.
>
> gcc/testsuite/Changelog
>
> * gcc.target/arm/pac.h : New file.
> * gcc.target/arm/pac-1.c : New test case.
> * gcc.target/arm/pac-2.c : Likewise.
> * gcc.target/arm/pac-3.c : Likewise.
> * gcc.target/arm/pac-4.c : Likewise.
> * gcc.target/arm/pac-5.c : Likewise.
> * gcc.target/arm/pac-6.c : Likewise.
> * gcc.target/arm/pac-7.c : Likewise.
> * gcc.target/arm/pac-8.c : Likewise.
>
@@ -21139,6 +21139,14 @@ arm_compute_save_core_reg_mask (void)
save_reg_mask |= arm_compute_save_reg0_reg12_mask ();
+ if (arm_current_function_pac_enabled_p ())
+ {
+ if (TARGET_TPCS_FRAME
+ || (TARGET_TPCS_LEAF_FRAME && crtl->is_leaf))
+ error ("TPCS incompatible with return address signing.");
+ save_reg_mask |= 1 << IP_REGNUM;
+ }
+
This is the wrong place for a test like this as it will be generated
every time this function is called (which might be more than once per
compiled function).
However, TPCS frames are only supported for 'thumb-1' code and PACBTI
needs armv8-m.main (ie Thumb-2), so the test is really pretty pointless
at the moment. I think we should just drop the error.
@@ -22302,7 +22310,7 @@ arm_emit_multi_reg_pop (unsigned long
saved_regs_mask)
par = emit_insn (par);
REG_NOTES (par) = dwarf;
- if (!return_in_pc)
+ if (!return_in_pc && !frame_pointer_needed)
arm_add_cfa_adjust_cfa_note (par, UNITS_PER_WORD * num_regs,
stack_pointer_rtx, stack_pointer_rtx);
}
What's this hunk for? It doesn't seem related to the PAC changes. Is
this some generic bug? If so, it should be pulled out into a separate
patch. If not, it needs some comment as to why we do it this way.
@@ -23352,6 +23360,11 @@ output_probe_stack_range (rtx reg1, rtx reg2)
return "";
}
+static bool aarch_bti_enabled ()
+{
+ return false;
+}
+
GNU style requires the function name to be in the first column:
static bool
aarch_bti_enabled ()
{
...
@@ -23431,11 +23444,12 @@ arm_expand_prologue (void)
/* The static chain register is the same as the IP register. If it is
clobbered when creating the frame, we need to save and restore
it. */
clobber_ip = IS_NESTED (func_type)
- && ((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
- || ((flag_stack_check == STATIC_BUILTIN_STACK_CHECK
- || flag_stack_clash_protection)
- && !df_regs_ever_live_p (LR_REGNUM)
- && arm_r3_live_at_start_p ()));
+ && (((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
+ || ((flag_stack_check == STATIC_BUILTIN_STACK_CHECK
+ || flag_stack_clash_protection)
+ && !df_regs_ever_live_p (LR_REGNUM)
+ && arm_r3_live_at_start_p ()))
+ || (arm_current_function_pac_enabled_p ()));
This whole statement needs parenthesis around it so that auto-indent
will work properly:
clobber_ip
= (IS_NESTED (func_type)
&& (....
|| arm_current_function_pac_enabled_p ()));
@@ -23511,6 +23525,14 @@ arm_expand_prologue (void)
}
}
+ if (arm_current_function_pac_enabled_p ())
+ {
+ if (aarch_bti_enabled ())
+ emit_insn (gen_pacbti_nop ());
+ else
+ emit_insn (gen_pac_nop ());
+ }
What about BTI enabled but PAC not?
@@ -27299,7 +27321,8 @@ thumb2_expand_return (bool simple_return)
to assert it for now to ensure that future code changes do not silently
change this behavior. */
gcc_assert (!IS_CMSE_ENTRY (arm_current_func_type ()));
- if (num_regs == 1)
+ if (num_regs == 1
+ && !(arm_current_function_pac_enabled_p ()))
Redundant parenthesis.
@@ -27314,10 +27337,20 @@ thumb2_expand_return (bool simple_return)
}
else
{
- saved_regs_mask &= ~ (1 << LR_REGNUM);
- saved_regs_mask |= (1 << PC_REGNUM);
- arm_emit_multi_reg_pop (saved_regs_mask);
- }
+ if (arm_current_function_pac_enabled_p ())
+ {
+ saved_regs_mask &= ~ (1 << PC_REGNUM);
Is that really needed? The other cases are changing LR to PC, but I
don't think PC should already be set as otherwise our calculation of the
frame size will be incorrect. Please try it as a gcc_assert() and
validate this assertion.
+ arm_emit_multi_reg_pop (saved_regs_mask);
+ emit_insn (gen_aut_nop ());
+ emit_jump_insn (simple_return_rtx);
@@ -30541,6 +30578,9 @@ arm_conditional_register_usage (void)
global_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1;
}
+ if (TARGET_HAVE_PACBTI)
+ call_used_regs[IP_REGNUM] = 1;
+
Why is this needed? CALL_USED_REGISTERS already defines IP (r12) as
call-used.
+++ b/gcc/config/arm/arm.md
@@ -11514,11 +11514,17 @@
(define_expand "prologue"
[(clobber (const_int 0))]
"TARGET_EITHER"
- "if (TARGET_32BIT)
+ "if (arm_current_function_pac_enabled_p () && !arm_arch8)
+ {
+ error (\"This architecture does not support branch protection
instructions\");
+ DONE;
+ }
No, this is the wrong place for a test like this. A check should be
placed in arm_option_override_internal instead (and normally we warn and
tweak the options to be something sensible if they conflict).
+(define_insn "pac_nop"
+ [(set (reg:SI IP_REGNUM)
+ (unspec:SI [(reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
+ UNSPEC_PAC_NOP))]
+ "TARGET_THUMB2"
+ "pac\t%|ip, %|lr, %|sp"
+ [(set_attr "length" "2")])
This pattern is missing a type. The length is also incorrect as the
instruction is 32-bits (4 bytes). Similarly for the other instructions
below. Also, you need to mark them as incompatible with conditional
execution (they're constrained-unpredictable in IT blocks).
All of the tests lack checks that the target board can run PAC/BTI.
R.
Richard Earnshaw <Richard.Earnshaw@foss.arm.com> writes:
[...]
> +(define_insn "pac_nop"
> + [(set (reg:SI IP_REGNUM)
> + (unspec:SI [(reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
> + UNSPEC_PAC_NOP))]
> + "TARGET_THUMB2"
> + "pac\t%|ip, %|lr, %|sp"
> + [(set_attr "length" "2")])
>
> This pattern is missing a type.
Which type do you think is missing?
> The length is also incorrect as the
> instruction is 32-bits (4 bytes).
Ack.
> Similarly for the other
> instructions below. Also, you need to mark them as incompatible with
> conditional execution (they're constrained-unpredictable in IT
> blocks).
I guess this would translate in setting it with '(set_attr "predicable" "no")'
But isn't this already the default?
Thanks
Andrea
(Sorry, this is a very late reply)
> -----Original Message-----
> From: Gcc-patches <gcc-patches-
> bounces+kyrylo.tkachov=arm.com@gcc.gnu.org> On Behalf Of Andrea
> Corallo via Gcc-patches
> Sent: Monday, August 8, 2022 10:34 AM
> To: Richard Earnshaw <Richard.Earnshaw@foss.arm.com>
> Cc: Richard Earnshaw <Richard.Earnshaw@arm.com>; nd <nd@arm.com>;
> Andrea Corallo via Gcc-patches <gcc-patches@gcc.gnu.org>
> Subject: Re: [PATCH 10/12 V2] arm: Implement cortex-M return signing
> address codegen
>
> Richard Earnshaw <Richard.Earnshaw@foss.arm.com> writes:
>
> [...]
>
> > +(define_insn "pac_nop"
> > + [(set (reg:SI IP_REGNUM)
> > + (unspec:SI [(reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
> > + UNSPEC_PAC_NOP))]
> > + "TARGET_THUMB2"
> > + "pac\t%|ip, %|lr, %|sp"
> > + [(set_attr "length" "2")])
> >
> > This pattern is missing a type.
>
> Which type do you think is missing?
>
> > The length is also incorrect as the
> > instruction is 32-bits (4 bytes).
>
> Ack.
>
> > Similarly for the other
> > instructions below. Also, you need to mark them as incompatible with
> > conditional execution (they're constrained-unpredictable in IT
> > blocks).
>
> I guess this would translate in setting it with '(set_attr "predicable" "no")'
>
> But isn't this already the default?
I think Richard means the "conds" attribute. It's something I'd like to see cleaned from the arm backend eventually, but for now there's a (very) late condexec pass that can generate conditional instructions based on that attribute.
Basically, it needs to be set to "undconditional" for these instructions.
Thanks,
Kyrill
>
> Thanks
>
> Andrea
@@ -379,6 +379,7 @@ extern int vfp3_const_double_for_bits (rtx);
extern void arm_emit_coreregs_64bit_shift (enum rtx_code, rtx, rtx, rtx, rtx,
rtx);
extern bool arm_fusion_enabled_p (tune_params::fuse_ops);
+extern bool arm_current_function_pac_enabled_p (void);
extern bool arm_valid_symbolic_address_p (rtx);
extern bool arm_validize_comparison (rtx *, rtx *, rtx *);
extern bool arm_expand_vector_compare (rtx, rtx_code, rtx, rtx, bool);
@@ -21139,6 +21139,14 @@ arm_compute_save_core_reg_mask (void)
save_reg_mask |= arm_compute_save_reg0_reg12_mask ();
+ if (arm_current_function_pac_enabled_p ())
+ {
+ if (TARGET_TPCS_FRAME
+ || (TARGET_TPCS_LEAF_FRAME && crtl->is_leaf))
+ error ("TPCS incompatible with return address signing.");
+ save_reg_mask |= 1 << IP_REGNUM;
+ }
+
/* Decide if we need to save the link register.
Interrupt routines have their own banked link register,
so they never need to save it.
@@ -22302,7 +22310,7 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
par = emit_insn (par);
REG_NOTES (par) = dwarf;
- if (!return_in_pc)
+ if (!return_in_pc && !frame_pointer_needed)
arm_add_cfa_adjust_cfa_note (par, UNITS_PER_WORD * num_regs,
stack_pointer_rtx, stack_pointer_rtx);
}
@@ -23352,6 +23360,11 @@ output_probe_stack_range (rtx reg1, rtx reg2)
return "";
}
+static bool aarch_bti_enabled ()
+{
+ return false;
+}
+
/* Generate the prologue instructions for entry into an ARM or Thumb-2
function. */
void
@@ -23431,11 +23444,12 @@ arm_expand_prologue (void)
/* The static chain register is the same as the IP register. If it is
clobbered when creating the frame, we need to save and restore it. */
clobber_ip = IS_NESTED (func_type)
- && ((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
- || ((flag_stack_check == STATIC_BUILTIN_STACK_CHECK
- || flag_stack_clash_protection)
- && !df_regs_ever_live_p (LR_REGNUM)
- && arm_r3_live_at_start_p ()));
+ && (((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
+ || ((flag_stack_check == STATIC_BUILTIN_STACK_CHECK
+ || flag_stack_clash_protection)
+ && !df_regs_ever_live_p (LR_REGNUM)
+ && arm_r3_live_at_start_p ()))
+ || (arm_current_function_pac_enabled_p ()));
/* Find somewhere to store IP whilst the frame is being created.
We try the following places in order:
@@ -23511,6 +23525,14 @@ arm_expand_prologue (void)
}
}
+ if (arm_current_function_pac_enabled_p ())
+ {
+ if (aarch_bti_enabled ())
+ emit_insn (gen_pacbti_nop ());
+ else
+ emit_insn (gen_pac_nop ());
+ }
+
if (TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
{
if (IS_INTERRUPT (func_type))
@@ -27299,7 +27321,8 @@ thumb2_expand_return (bool simple_return)
to assert it for now to ensure that future code changes do not silently
change this behavior. */
gcc_assert (!IS_CMSE_ENTRY (arm_current_func_type ()));
- if (num_regs == 1)
+ if (num_regs == 1
+ && !(arm_current_function_pac_enabled_p ()))
{
rtx par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (2));
rtx reg = gen_rtx_REG (SImode, PC_REGNUM);
@@ -27314,10 +27337,20 @@ thumb2_expand_return (bool simple_return)
}
else
{
- saved_regs_mask &= ~ (1 << LR_REGNUM);
- saved_regs_mask |= (1 << PC_REGNUM);
- arm_emit_multi_reg_pop (saved_regs_mask);
- }
+ if (arm_current_function_pac_enabled_p ())
+ {
+ saved_regs_mask &= ~ (1 << PC_REGNUM);
+ arm_emit_multi_reg_pop (saved_regs_mask);
+ emit_insn (gen_aut_nop ());
+ emit_jump_insn (simple_return_rtx);
+ }
+ else
+ {
+ saved_regs_mask &= ~ (1 << LR_REGNUM);
+ saved_regs_mask |= (1 << PC_REGNUM);
+ arm_emit_multi_reg_pop (saved_regs_mask);
+ }
+ }
}
else
{
@@ -27723,7 +27756,8 @@ arm_expand_epilogue (bool really_return)
&& really_return
&& crtl->args.pretend_args_size == 0
&& saved_regs_mask & (1 << LR_REGNUM)
- && !crtl->calls_eh_return)
+ && !crtl->calls_eh_return
+ && !arm_current_function_pac_enabled_p ())
{
saved_regs_mask &= ~(1 << LR_REGNUM);
saved_regs_mask |= (1 << PC_REGNUM);
@@ -27837,6 +27871,9 @@ arm_expand_epilogue (bool really_return)
}
}
+ if (arm_current_function_pac_enabled_p ())
+ emit_insn (gen_aut_nop ());
+
if (!really_return)
return;
@@ -30541,6 +30578,9 @@ arm_conditional_register_usage (void)
global_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1;
}
+ if (TARGET_HAVE_PACBTI)
+ call_used_regs[IP_REGNUM] = 1;
+
/* The Q and GE bits are only accessed via special ACLE patterns. */
CLEAR_HARD_REG_BIT (operand_reg_set, APSRQ_REGNUM);
CLEAR_HARD_REG_BIT (operand_reg_set, APSRGE_REGNUM);
@@ -32931,6 +32971,15 @@ arm_fusion_enabled_p (tune_params::fuse_ops op)
return current_tune->fusible_ops & op;
}
+/* Return TRUE if return address signing mechanism is enabled. */
+bool
+arm_current_function_pac_enabled_p (void)
+{
+ return aarch_ra_sign_scope == AARCH_FUNCTION_ALL
+ || (aarch_ra_sign_scope == AARCH_FUNCTION_NON_LEAF
+ && !crtl->is_leaf);
+}
+
/* Implement TARGET_SCHED_CAN_SPECULATE_INSN. Return true if INSN can be
scheduled for speculative execution. Reject the long-running division
and square-root instructions. */
@@ -11514,11 +11514,17 @@
(define_expand "prologue"
[(clobber (const_int 0))]
"TARGET_EITHER"
- "if (TARGET_32BIT)
+ "if (arm_current_function_pac_enabled_p () && !arm_arch8)
+ {
+ error (\"This architecture does not support branch protection instructions\");
+ DONE;
+ }
+
+ if (TARGET_32BIT)
arm_expand_prologue ();
else
thumb1_expand_prologue ();
- DONE;
+ DONE;
"
)
@@ -12890,6 +12896,29 @@
(set_attr "length" "8")]
)
+(define_insn "pac_nop"
+ [(set (reg:SI IP_REGNUM)
+ (unspec:SI [(reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
+ UNSPEC_PAC_NOP))]
+ "TARGET_THUMB2"
+ "pac\t%|ip, %|lr, %|sp"
+ [(set_attr "length" "2")])
+
+(define_insn "pacbti_nop"
+ [(set (reg:SI IP_REGNUM)
+ (unspec:SI [(reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
+ UNSPEC_PACBTI_NOP))]
+ "TARGET_THUMB2"
+ "pacbti\t%|ip, %|lr, %|sp"
+ [(set_attr "length" "2")])
+
+(define_insn "aut_nop"
+ [(unspec:SI [(reg:SI IP_REGNUM) (reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
+ UNSPEC_AUT_NOP)]
+ "TARGET_THUMB2"
+ "aut\t%|ip, %|lr, %|sp"
+ [(set_attr "length" "2")])
+
;; Vector bits common to IWMMXT, Neon and MVE
(include "vec-common.md")
;; Load the Intel Wireless Multimedia Extension patterns
@@ -159,6 +159,9 @@
UNSPEC_VCDE ; Custom Datapath Extension instruction.
UNSPEC_VCDEA ; Custom Datapath Extension instruction.
UNSPEC_DLS ; Used for DLS (Do Loop Start), Armv8.1-M Mainline instruction
+ UNSPEC_PAC_NOP ; Represents PAC signing LR
+ UNSPEC_PACBTI_NOP ; Represents PAC signing LR + valid landing pad
+ UNSPEC_AUT_NOP ; Represents PAC verifying LR
])
new file mode 100644
@@ -0,0 +1,9 @@
+/* Testing return address signing. */
+/* { dg-do run } */
+/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret+leaf -mthumb --save-temps -O0" } */
+
+#include "pac.h"
+
+/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-not "bti" } } */
new file mode 100644
@@ -0,0 +1,9 @@
+/* Testing return address signing. */
+/* { dg-do run } */
+/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret -mthumb --save-temps -O0" } */
+
+#include "pac.h"
+
+/* { dg-final { scan-assembler "pac\tip, lr, sp" } } */
+/* { dg-final { scan-assembler "aut\tip, lr, sp" } } */
+/* { dg-final { scan-assembler-not "bti" } } */
new file mode 100644
@@ -0,0 +1,9 @@
+/* Testing return address signing. */
+/* { dg-do run } */
+/* { dg-options "-march=armv8.1-m.main -mbranch-protection=bti+pac-ret+leaf -mthumb --save-temps -O2" } */
+
+#include "pac.h"
+
+/* { dg-final { scan-assembler-times "pacbti\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-not "\tbti\t" } } */
new file mode 100644
@@ -0,0 +1,9 @@
+/* Testing return address signing. */
+/* { dg-do compile } */
+/* { dg-options "-march=armv8.1-m.main+pacbti -mthumb --save-temps -O2" } */
+
+#include "pac.h"
+
+/* { dg-final { scan-assembler-not "\tbti\t" } } */
+/* { dg-final { scan-assembler-not "\tpac\t" } } */
+/* { dg-final { scan-assembler-not "\tpacbti\t" } } */
new file mode 100644
@@ -0,0 +1,26 @@
+/* Testing return address signing. */
+/* { dg-do run } */
+/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret+leaf -mthumb --save-temps -O0" } */
+
+#include <stdlib.h>
+
+int
+__attribute__((noinline))
+foo1 (int a, int b)
+{
+ int square (int z) { return z * z; }
+ return square (a) + square (b);
+}
+
+int
+main (void)
+{
+ if (foo1 (1, 2) != 5)
+ abort ();
+
+ return 0;
+}
+
+/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 3 } } */
+/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 3 } } */
+/* { dg-final { scan-assembler-not "bti" } } */
new file mode 100644
@@ -0,0 +1,18 @@
+/* Check that GCC does .save and .cfi_offset directives with RA_AUTH_CODE pseudo hard-register. */
+/* { dg-do compile } */
+/* { dg-skip-if "avoid conflicting multilib options" { *-*-* } { "-marm" "-mcpu=*" } } */
+/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret+leaf -mthumb --save-temps -O0 -g" } */
+
+int i;
+
+void foo (int);
+
+int bar()
+{
+ foo (i);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "pac\tip, lr, sp" } } */
+/* { dg-final { scan-assembler "aut\tip, lr, sp" } } */
+/* { dg-final { scan-assembler-not "bti" } } */
new file mode 100644
@@ -0,0 +1,30 @@
+/* Testing return address signing. */
+/* { dg-do run } */
+/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret+leaf -mthumb --save-temps -O0" } */
+
+#include <stdlib.h>
+
+int
+__attribute__((noinline))
+foo1 (int a, int b)
+{
+ int x = 4;
+ int foo2 (int a, int b)
+ {
+ return a + b + x;
+ }
+ return foo2 (a, b);
+}
+
+int
+main (void)
+{
+ if (foo1 (1, 2) != 7)
+ abort ();
+
+ return 0;
+}
+
+/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 3 } } */
+/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 3 } } */
+/* { dg-final { scan-assembler-not "bti" } } */
new file mode 100644
@@ -0,0 +1,32 @@
+/* Testing return address signing. */
+/* { dg-do run } */
+/* { dg-options "-march=armv8.1-m.main -mbranch-protection=pac-ret+leaf -mthumb --save-temps -O0" } */
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+int acc (int n, ...)
+{
+ int sum = 0;
+ va_list ptr;
+
+ va_start (ptr, n);
+
+ for (int i = 0; i < n; i++)
+ sum += va_arg (ptr, int);
+ va_end (ptr);
+
+ return sum;
+}
+
+int main()
+{
+ if (acc (3, 1, 2, 3) != 6)
+ abort ();
+
+ return 0;
+}
+
+/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 2 } } */
+/* { dg-final { scan-assembler-not "bti" } } */
new file mode 100644
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+
+int
+__attribute__((noinline))
+foo1 (int a, int b)
+{
+ return a + b;
+}
+
+int
+main (void)
+{
+ if (foo1 (1, 2) != 3)
+ abort ();
+
+ return 0;
+}