tree aarch64_resolve_overloaded_builtin_general (location_t, tree, void *);
+const char *aarch64_sls_barrier (int);
extern bool aarch64_harden_sls_retbr_p (void);
extern bool aarch64_harden_sls_blr_p (void);
if (TARGET_ILP32)
{
- asm_fprintf (f, "\tldr\tw%d, .+12\n", IP1_REGNUM - R0_REGNUM);
- asm_fprintf (f, "\tldr\tw%d, .+12\n", STATIC_CHAIN_REGNUM - R0_REGNUM);
+ asm_fprintf (f, "\tldr\tw%d, .+20\n", IP1_REGNUM - R0_REGNUM);
+ asm_fprintf (f, "\tldr\tw%d, .+20\n", STATIC_CHAIN_REGNUM - R0_REGNUM);
}
else
{
- asm_fprintf (f, "\tldr\t%s, .+12\n", reg_names [IP1_REGNUM]);
- asm_fprintf (f, "\tldr\t%s, .+16\n", reg_names [STATIC_CHAIN_REGNUM]);
+ asm_fprintf (f, "\tldr\t%s, .+20\n", reg_names [IP1_REGNUM]);
+ asm_fprintf (f, "\tldr\t%s, .+24\n", reg_names [STATIC_CHAIN_REGNUM]);
}
asm_fprintf (f, "\tbr\t%s\n", reg_names [IP1_REGNUM]);
+ /* We always emit a speculation barrier.
+ This is because the same trampoline template is used for every nested
+ function. Since nested functions are not particularly common or
+ performant we don't worry too much about the extra instructions to copy
+ around.
+ This is not yet a problem, since we have not yet implemented function
+ specific attributes to choose between hardening against straight line
+ speculation or not, but such function specific attributes are likely to
+ happen in the future. */
+ asm_fprintf (f, "\tdsb\tsy\n\tisb\n");
+
assemble_aligned_integer (POINTER_BYTES, const0_rtx);
assemble_aligned_integer (POINTER_BYTES, const0_rtx);
}
aarch64_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
{
rtx fnaddr, mem, a_tramp;
- const int tramp_code_sz = 16;
+ const int tramp_code_sz = 24;
/* Don't need to copy the trailing D-words, we fill those in below. */
- emit_block_move (m_tramp, assemble_trampoline_template (),
+ /* We create our own memory address in Pmode so that `emit_block_move` can
+ use parts of the backend which expect Pmode addresses. */
+ rtx temp = convert_memory_address (Pmode, XEXP (m_tramp, 0));
+ emit_block_move (gen_rtx_MEM (BLKmode, temp),
+ assemble_trampoline_template (),
GEN_INT (tramp_code_sz), BLOCK_OP_NORMAL);
mem = adjust_address (m_tramp, ptr_mode, tramp_code_sz);
fnaddr = XEXP (DECL_RTL (fndecl), 0);
output_asm_insn (buf, operands);
output_asm_insn (patterns[index][1], operands);
output_asm_insn ("br\t%3", operands);
+ output_asm_insn (aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()),
+ operands);
assemble_label (asm_out_file, label);
return "";
}
#undef GNU_PROPERTY_AARCH64_FEATURE_1_BTI
#undef GNU_PROPERTY_AARCH64_FEATURE_1_AND
+/* Helper function for straight line speculation.
+ Return what barrier should be emitted for straight line speculation
+ mitigation.
+ When not mitigating against straight line speculation this function returns
+ an empty string.
+ When mitigating against straight line speculation, use:
+ * SB when the v8.5-A SB extension is enabled.
+ * DSB+ISB otherwise. */
+const char *
+aarch64_sls_barrier (int mitigation_required)
+{
+ return mitigation_required
+ ? (TARGET_SB ? "sb" : "dsb\tsy\n\tisb")
+ : "";
+}
+
/* Target-specific selftests. */
#if CHECKING_P
#define AARCH64_ISA_F32MM (aarch64_isa_flags & AARCH64_FL_F32MM)
#define AARCH64_ISA_F64MM (aarch64_isa_flags & AARCH64_FL_F64MM)
#define AARCH64_ISA_BF16 (aarch64_isa_flags & AARCH64_FL_BF16)
+#define AARCH64_ISA_SB (aarch64_isa_flags & AARCH64_FL_SB)
/* Crypto is an optional extension to AdvSIMD. */
#define TARGET_CRYPTO (TARGET_SIMD && AARCH64_ISA_CRYPTO)
#define TARGET_FIX_ERR_A53_835769_DEFAULT 1
#endif
+/* SB instruction is enabled through +sb. */
+#define TARGET_SB (AARCH64_ISA_SB)
+
/* Apply the workaround for Cortex-A53 erratum 835769. */
#define TARGET_FIX_ERR_A53_835769 \
((aarch64_fix_a53_err835769 == 2) \
#define RETURN_ADDR_RTX aarch64_return_addr
-/* BTI c + 3 insns + 2 pointer-sized entries. */
-#define TRAMPOLINE_SIZE (TARGET_ILP32 ? 24 : 32)
+/* BTI c + 3 insns
+ + sls barrier of DSB + ISB.
+ + 2 pointer-sized entries. */
+#define TRAMPOLINE_SIZE (24 + (TARGET_ILP32 ? 8 : 16))
/* Trampolines contain dwords, so must be dword aligned. */
#define TRAMPOLINE_ALIGNMENT 64
;; Attribute that specifies whether the alternative uses MOVPRFX.
(define_attr "movprfx" "no,yes" (const_string "no"))
+;; Attribute to specify that an alternative has the length of a single
+;; instruction plus a speculation barrier.
+(define_attr "sls_length" "none,retbr,casesi" (const_string "none"))
+
(define_attr "length" ""
(cond [(eq_attr "movprfx" "yes")
(const_int 8)
- ] (const_int 4)))
+
+ (eq_attr "sls_length" "retbr")
+ (cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 4)
+ (match_test "TARGET_SB") (const_int 8)]
+ (const_int 12))
+
+ (eq_attr "sls_length" "casesi")
+ (cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 16)
+ (match_test "TARGET_SB") (const_int 20)]
+ (const_int 24))
+ ]
+ (const_int 4)))
;; Strictly for compatibility with AArch32 in pipeline models, since AArch64 has
;; no predicated insns.
(define_insn "indirect_jump"
[(set (pc) (match_operand:DI 0 "register_operand" "r"))]
""
- "br\\t%0"
- [(set_attr "type" "branch")]
+ {
+ output_asm_insn ("br\\t%0", operands);
+ return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ());
+ }
+ [(set_attr "type" "branch")
+ (set_attr "sls_length" "retbr")]
)
(define_insn "jump"
"*
return aarch64_output_casesi (operands);
"
- [(set_attr "length" "16")
+ [(set_attr "sls_length" "casesi")
(set_attr "type" "branch")]
)
[(return)]
""
{
+ const char *ret = NULL;
if (aarch64_return_address_signing_enabled ()
&& TARGET_ARMV8_3
&& !crtl->calls_eh_return)
{
if (aarch64_ra_sign_key == AARCH64_KEY_B)
- return "retab";
+ ret = "retab";
else
- return "retaa";
+ ret = "retaa";
}
- return "ret";
+ else
+ ret = "ret";
+ output_asm_insn (ret, operands);
+ return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ());
}
- [(set_attr "type" "branch")]
+ [(set_attr "type" "branch")
+ (set_attr "sls_length" "retbr")]
)
(define_expand "return"
(define_insn "simple_return"
[(simple_return)]
""
- "ret"
- [(set_attr "type" "branch")]
+ {
+ output_asm_insn ("ret", operands);
+ return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ());
+ }
+ [(set_attr "type" "branch")
+ (set_attr "sls_length" "retbr")]
)
(define_insn "*cb<optab><mode>1"
(unspec:DI [(match_operand:DI 2 "const_int_operand")] UNSPEC_CALLEE_ABI)
(return)]
"SIBLING_CALL_P (insn)"
- "@
- br\\t%0
- b\\t%c0"
- [(set_attr "type" "branch, branch")]
+ {
+ if (which_alternative == 0)
+ {
+ output_asm_insn ("br\\t%0", operands);
+ return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ());
+ }
+ return "b\\t%c0";
+ }
+ [(set_attr "type" "branch, branch")
+ (set_attr "sls_length" "retbr,none")]
)
(define_insn "*sibcall_value_insn"
(unspec:DI [(match_operand:DI 3 "const_int_operand")] UNSPEC_CALLEE_ABI)
(return)]
"SIBLING_CALL_P (insn)"
- "@
- br\\t%1
- b\\t%c1"
- [(set_attr "type" "branch, branch")]
+ {
+ if (which_alternative == 0)
+ {
+ output_asm_insn ("br\\t%1", operands);
+ return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ());
+ }
+ return "b\\t%c1";
+ }
+ [(set_attr "type" "branch, branch")
+ (set_attr "sls_length" "retbr,none")]
)
;; Call subroutine returning any type.
--- /dev/null
+/* Avoid ILP32 since pacret is only available for LP64 */
+/* { dg-do compile { target { ! ilp32 } } } */
+/* { dg-additional-options "-mharden-sls=retbr -mbranch-protection=pac-ret -march=armv8.3-a" } */
+
+/* Testing the do_return pattern for retaa and retab. */
+long retbr_subcall(void);
+long retbr_do_return_retaa(void)
+{
+ return retbr_subcall()+1;
+}
+
+__attribute__((target("branch-protection=pac-ret+b-key")))
+long retbr_do_return_retab(void)
+{
+ return retbr_subcall()+1;
+}
+
+/* Ensure there are no BR or RET instructions which are not directly followed
+ by a speculation barrier. */
+/* { dg-final { scan-assembler-not {\t(br|ret|retaa|retab)\tx[0-9][0-9]?\n\t(?!dsb\tsy\n\tisb)} } } */
+/* { dg-final { scan-assembler-not {ret\t} } } */
--- /dev/null
+/* We ensure that -Wpedantic is off since it complains about the trampolines
+ we explicitly want to test. */
+/* { dg-additional-options "-mharden-sls=retbr -Wno-pedantic " } */
+/*
+ Ensure that the SLS hardening of RET and BR leaves no unprotected RET/BR
+ instructions.
+ */
+typedef int (foo) (int, int);
+typedef void (bar) (int, int);
+struct sls_testclass {
+ foo *x;
+ bar *y;
+ int left;
+ int right;
+};
+
+int
+retbr_sibcall_value_insn (struct sls_testclass x)
+{
+ return x.x(x.left, x.right);
+}
+
+void
+retbr_sibcall_insn (struct sls_testclass x)
+{
+ x.y(x.left, x.right);
+}
+
+/* Aim to test two different returns.
+ One that introduces a tail call in the middle of the function, and one that
+ has a normal return. */
+int
+retbr_multiple_returns (struct sls_testclass x)
+{
+ int temp;
+ if (x.left % 10)
+ return x.x(x.left, 100);
+ else if (x.right % 20)
+ {
+ return x.x(x.left * x.right, 100);
+ }
+ temp = x.left % x.right;
+ temp *= 100;
+ temp /= 2;
+ return temp % 3;
+}
+
+void
+retbr_multiple_returns_void (struct sls_testclass x)
+{
+ if (x.left % 10)
+ {
+ x.y(x.left, 100);
+ }
+ else if (x.right % 20)
+ {
+ x.y(x.left * x.right, 100);
+ }
+ return;
+}
+
+/* Testing the casesi jump via register. */
+__attribute__ ((optimize ("Os")))
+int
+retbr_casesi_dispatch (struct sls_testclass x)
+{
+ switch (x.left)
+ {
+ case -5:
+ return -2;
+ case -3:
+ return -1;
+ case 0:
+ return 0;
+ case 3:
+ return 1;
+ case 5:
+ break;
+ default:
+ __builtin_unreachable ();
+ }
+ return x.right;
+}
+
+/* Testing the BR in trampolines is mitigated against. */
+void f1 (void *);
+void f3 (void *, void (*)(void *));
+void f2 (void *);
+
+int
+retbr_trampolines (void *a, int b)
+{
+ if (!b)
+ {
+ f1 (a);
+ return 1;
+ }
+ if (b)
+ {
+ void retbr_tramp_internal (void *c)
+ {
+ if (c == a)
+ f2 (c);
+ }
+ f3 (a, retbr_tramp_internal);
+ }
+ return 0;
+}
+
+/* Testing the indirect_jump pattern. */
+void
+retbr_indirect_jump (int *buf)
+{
+ __builtin_longjmp(buf, 1);
+}
+
+/* Ensure there are no BR or RET instructions which are not directly followed
+ by a speculation barrier. */
+/* { dg-final { scan-assembler-not {\t(br|ret|retaa|retab)\tx[0-9][0-9]?\n\t(?!dsb\tsy\n\tisb|sb)} } } */
--- /dev/null
+# Regression driver for SLS mitigation on AArch64.
+# Copyright (C) 2020 Free Software Foundation, Inc.
+# Contributed by ARM Ltd.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>. */
+
+# Exit immediately if this isn't an AArch64 target.
+if {![istarget aarch64*-*-*] } then {
+ return
+}
+
+# Load support procs.
+load_lib gcc-dg.exp
+load_lib torture-options.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+ set DEFAULT_CFLAGS " "
+}
+
+# Initialize `dg'.
+dg-init
+torture-init
+
+# Use different architectures as well as the normal optimisation options.
+# (i.e. use both SB and DSB+ISB barriers).
+
+set save-dg-do-what-default ${dg-do-what-default}
+# Main loop.
+# Run with torture tests (i.e. a bunch of different optimisation levels) just
+# to increase test coverage.
+set dg-do-what-default assemble
+gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \
+ "-save-temps" $DEFAULT_CFLAGS
+
+# Run the same tests but this time with SB extension.
+# Since not all supported assemblers will support that extension we decide
+# whether to assemble or just compile based on whether the extension is
+# supported for the available assembler.
+
+set templist {}
+foreach x $DG_TORTURE_OPTIONS {
+ lappend templist "$x -march=armv8.3-a+sb "
+ lappend templist "$x -march=armv8-a+sb "
+}
+set-torture-options $templist
+if { [check_effective_target_aarch64_asm_sb_ok] } {
+ set dg-do-what-default assemble
+} else {
+ set dg-do-what-default compile
+}
+gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \
+ "-save-temps" $DEFAULT_CFLAGS
+set dg-do-what-default ${save-dg-do-what-default}
+
+# All done.
+torture-finish
+dg-finish
# various architecture extensions via the .arch_extension pseudo-op.
foreach { aarch64_ext } { "fp" "simd" "crypto" "crc" "lse" "dotprod" "sve"
- "i8mm" "f32mm" "f64mm" "bf16" } {
+ "i8mm" "f32mm" "f64mm" "bf16" "sb" } {
eval [string map [list FUNC $aarch64_ext] {
proc check_effective_target_aarch64_asm_FUNC_ok { } {
if { [istarget aarch64*-*-*] } {