@@ -73,4 +73,30 @@ enum aarch64_code_model {
AARCH64_CMODEL_LARGE
};
+/* AArch64 pointer authentication key indexes. "key_array" in
+ aarch64_output_sign_auth_reg depends on the order of this enum. */
+enum aarch64_pauth_key_index
+{
+ /* A key for instruction pointer. */
+ AARCH64_PAUTH_IKEY_A = 0,
+ /* B key for instruction pointer. */
+ AARCH64_PAUTH_IKEY_B,
+ /* A key for data pointer. */
+ AARCH64_PAUTH_DKEY_A,
+ /* B key for data pointer. */
+ AARCH64_PAUTH_DKEY_B,
+ /* A key for general pointer. */
+ AARCH64_PAUTH_GKEY_A
+};
+
+/* Function types -msign-return-address should sign. */
+enum aarch64_function_type {
+ /* Don't sign any function. */
+ AARCH64_FUNCTION_NONE,
+ /* Non-leaf functions. */
+ AARCH64_FUNCTION_NON_LEAF,
+ /* All functions. */
+ AARCH64_FUNCTION_ALL
+};
+
#endif
@@ -329,6 +329,7 @@ rtx aarch64_reverse_mask (enum machine_mode);
bool aarch64_offset_7bit_signed_scaled_p (machine_mode, HOST_WIDE_INT);
char *aarch64_output_scalar_simd_mov_immediate (rtx, machine_mode);
char *aarch64_output_simd_mov_immediate (rtx, machine_mode, unsigned);
+const char *aarch64_output_sign_auth_reg (rtx *, bool, bool);
bool aarch64_pad_arg_upward (machine_mode, const_tree);
bool aarch64_pad_reg_upward (machine_mode, const_tree, bool);
bool aarch64_regno_ok_for_base_p (int, bool);
@@ -3204,6 +3204,13 @@ aarch64_expand_prologue (void)
unsigned reg2 = cfun->machine->frame.wb_candidate2;
rtx_insn *insn;
+ if (AARCH64_ENABLE_RETURN_ADDRESS_SIGN)
+ emit_insn (gen_sign_reg (gen_rtx_REG (Pmode, LR_REGNUM),
+ gen_rtx_REG (Pmode, LR_REGNUM),
+ stack_pointer_rtx,
+ GEN_INT (aarch64_pauth_key),
+ const0_rtx));
+
if (flag_stack_usage_info)
current_function_static_stack_size = frame_size;
@@ -3340,6 +3347,20 @@ aarch64_expand_epilogue (bool for_sibcall)
RTX_FRAME_RELATED_P (insn) = 1;
}
+ /* sibcall won't generate normally return, therefore we need to authenticate
+ at here. TARGET_PAUTH will allow GCC to use combined authentication
+ instruction which we prefer, eh_return path can't do combined
+ authentication, as the following stack adjustment will update CFA to
+ handler's CFA while we want to use the CFA of the function which calls
+ __builtin_eh_return. */
+ if (AARCH64_ENABLE_RETURN_ADDRESS_SIGN
+ && (for_sibcall || !TARGET_PAUTH || crtl->calls_eh_return))
+ emit_insn (gen_auth_reg (gen_rtx_REG (Pmode, LR_REGNUM),
+ gen_rtx_REG (Pmode, LR_REGNUM),
+ stack_pointer_rtx,
+ GEN_INT (aarch64_pauth_key),
+ const0_rtx));
+
/* Stack adjustment for exception handler. */
if (crtl->calls_eh_return)
{
@@ -5434,6 +5455,74 @@ aarch64_output_casesi (rtx *operands)
return "";
}
+/* Output ARMv8.3-A pointer sign instructions when SIGN_P be TRUE or
+ authentication instructions when it's FALSE. X1716_P is TRUE if this
+ function is called from fixed register patterns, i.e sign_reg1716 or
+ auth_reg1716. */
+
+const char*
+aarch64_output_sign_auth_reg (rtx *operands, bool sign_p, bool x1716_p)
+{
+ char buf[64];
+ const char *action = sign_p ? "pac" : "aut";
+ /* Assembler suffix for key, this array should be synced with
+ "enum aarch64_pauth_key_index" in aarch64-opts.h. */
+ const char key_array[5] = {'a', 'b', 'a', 'b', 'a'};
+ char key_char
+ = key_array[x1716_p ? INTVAL (operands[0]) : INTVAL (operands[3])];
+ char type
+ = (x1716_p ? INTVAL (operands[1]) : INTVAL (operands[4])) ? 'd' : 'i';
+ unsigned FM = ((sign_p ? 0 : 1) << 2 /* F bit. */
+ | ((key_char == 'a' ? 0 : 1) << 1)); /* M bit. */
+
+ if (x1716_p
+ || (REGNO (operands[0]) == R17_REGNUM
+ && REG_P (operands[2]) && REGNO (operands[2]) == R16_REGNUM))
+ {
+ /* PAC*1716/AUT*1716 only support code pointer. */
+ gcc_assert (type == 'i');
+ unsigned CRm = 0x1;
+ unsigned FMS = (FM /* F and M bits. */
+ | 0); /* S bit. */
+ snprintf (buf, sizeof (buf), "hint\t%d %s%s%c1716", (CRm << 3) | FMS,
+ ASM_COMMENT_START, action, key_char);
+ }
+ else if (REGNO (operands[0]) == LR_REGNUM
+ && REG_P (operands[2]) && REGNO (operands[2]) == SP_REGNUM)
+ {
+ /* PAC*SP/AUT*SP only support code pointer. */
+ gcc_assert (type == 'i');
+ unsigned CRm = 0x3;
+ unsigned FMS = (FM /* F and M bits. */
+ | 1); /* S bit. */
+ snprintf (buf, sizeof (buf), "hint\t%d %s%si%csp", (CRm << 3) | FMS,
+ ASM_COMMENT_START, action, key_char);
+ }
+ else if (REGNO (operands[0]) == LR_REGNUM && operands[2] == const0_rtx)
+ {
+ /* PAC*Z/AUT*Z only support code pointer. */
+ gcc_assert (type == 'i');
+ unsigned CRm = 0x3;
+ unsigned FMS = (FM /* F and M bits. */
+ | 0); /* S bit. */
+ snprintf (buf, sizeof (buf), "hint\t%d %s%si%cz", (CRm << 3) | FMS,
+ ASM_COMMENT_START, action, key_char);
+ }
+ else if (operands[2] == const0_rtx)
+ {
+ /* General format PAC* Xd, Xn/AUT* Xd, Xn. */
+ snprintf (buf, sizeof (buf), "%s%cz%c %%0", action, type, key_char);
+ }
+ else
+ {
+ /* General format PAC*Z* Xd, Xn/AUT*Z* Xd, Xn. */
+ snprintf (buf, sizeof (buf), "%s%c%c %%0, %%2", action, type, key_char);
+ }
+
+ output_asm_insn (buf, operands);
+
+ return "";
+}
/* Return size in bits of an arithmetic operand which is shifted/scaled and
masked such that it is suitable for a UXTB, UXTH, or UXTW extend
@@ -8466,6 +8555,9 @@ aarch64_override_options (void)
error ("Assembler does not support -mabi=ilp32");
#endif
+ if (aarch64_ra_sign_scope != AARCH64_FUNCTION_NONE && TARGET_ILP32)
+ error ("Return address signing is only supported on LP64");
+
/* Make sure we properly set up the explicit options. */
if ((aarch64_cpu_string && valid_cpu)
|| (aarch64_tune_string && valid_tune))
@@ -8849,6 +8941,10 @@ static const struct aarch64_attribute_info aarch64_attributes[] =
{ "cpu", aarch64_attr_custom, false, aarch64_handle_attr_cpu, OPT_mcpu_ },
{ "tune", aarch64_attr_custom, false, aarch64_handle_attr_tune,
OPT_mtune_ },
+ { "sign-return-address", aarch64_attr_enum, false, NULL,
+ OPT_msign_return_address_ },
+ { "pauth-key", aarch64_attr_enum, false, NULL,
+ OPT_mpauth_key_ },
{ NULL, aarch64_attr_custom, false, NULL, OPT____ }
};
@@ -963,4 +963,11 @@ extern const char *host_detect_local_cpu (int argc, const char **argv);
extern tree aarch64_fp16_type_node;
extern tree aarch64_fp16_ptr_type_node;
+/* Return address signing is enabled for AARCH64_FUNCTION_ALL, or for
+ AARCH64_FUNCTION_NON_LEAF. */
+#define AARCH64_ENABLE_RETURN_ADDRESS_SIGN \
+ (aarch64_ra_sign_scope == AARCH64_FUNCTION_ALL \
+ || (aarch64_ra_sign_scope == AARCH64_FUNCTION_NON_LEAF \
+ && cfun->machine->frame.reg_offset[LR_REGNUM] >= 0))
+
#endif /* GCC_AARCH64_H */
@@ -66,6 +66,8 @@
)
(define_c_enum "unspec" [
+ UNSPEC_AUTH_REG
+ UNSPEC_AUTH_REG1716
UNSPEC_CASESI
UNSPEC_CRC32B
UNSPEC_CRC32CB
@@ -108,6 +110,8 @@
UNSPEC_PRLG_STK
UNSPEC_RBIT
UNSPEC_SCVTF
+ UNSPEC_SIGN_REG
+ UNSPEC_SIGN_REG1716
UNSPEC_SISD_NEG
UNSPEC_SISD_SSHL
UNSPEC_SISD_USHL
@@ -119,6 +123,8 @@
UNSPEC_ST2_LANE
UNSPEC_ST3_LANE
UNSPEC_ST4_LANE
+ UNSPEC_STRIP_REG_SIGN
+ UNSPEC_STRIP_X30_SIGN
UNSPEC_TLS
UNSPEC_TLSDESC
UNSPEC_TLSLE12
@@ -574,7 +580,21 @@
(define_insn "*do_return"
[(return)]
""
- "ret"
+ {
+ if (AARCH64_ENABLE_RETURN_ADDRESS_SIGN
+ && TARGET_PAUTH
+ && !crtl->calls_eh_return)
+ {
+ if (aarch64_pauth_key == AARCH64_PAUTH_IKEY_A)
+ return "retaa";
+ else if (aarch64_pauth_key == AARCH64_PAUTH_IKEY_B)
+ return "retab";
+
+ gcc_unreachable ();
+ }
+
+ return "ret";
+ }
[(set_attr "type" "branch")]
)
@@ -5210,6 +5230,114 @@
[(set_attr "length" "0")]
)
+;; ARMv8.3-A pointer authentication support
+;; OPERANDS[0] - Result.
+;; OPERANDS[1] - The value we want to sign or authenticate.
+;; OPERANDS[2] - The salt used for signing or authentication.
+;; OPERANDS[3] - Key index. See aarch64_pauth_key_type in aarch64-opts.h.
+;; OPERANDS[4] - Pointer type. 0 for code, 1 for data.
+;;
+;; These patterns are available for all architectures. For architectures
+;; without TARGET_PAUTH, they will become nop, this let the user write portable
+;; software which can get pointer authentication on new hardware while still
+;; runs OK on old hardware.
+
+(define_insn "sign_reg"
+ [(set (match_operand:DI 0 "register_operand" "=rk")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:DI 2 "aarch64_reg_or_zero" "rkZ")
+ (match_operand:DI 3 "aarch64_const0_const1" "i")
+ (match_operand:DI 4 "aarch64_const0_const1" "i")]
+ UNSPEC_SIGN_REG))]
+ ""
+ {
+ return aarch64_output_sign_auth_reg (operands, /* sign_p */true,
+ /* x1716_p */false);
+ }
+)
+
+(define_insn "sign_reg1716"
+ [(set (reg:DI R17_REGNUM)
+ (unspec:DI [(reg:DI R17_REGNUM)
+ (reg:DI R16_REGNUM)
+ (match_operand:DI 0 "aarch64_const0_const1" "i")
+ (match_operand:DI 1 "aarch64_const0_const1" "i")]
+ UNSPEC_SIGN_REG1716))]
+ ""
+ {
+ return aarch64_output_sign_auth_reg (operands, /* sign_p */true,
+ /* x1716_p */true);
+ }
+)
+
+(define_insn "auth_reg"
+ [(set (match_operand:DI 0 "register_operand" "=rk")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:DI 2 "aarch64_reg_or_zero" "rkZ")
+ (match_operand:DI 3 "aarch64_const0_const1" "i")
+ (match_operand:DI 4 "aarch64_const0_const1" "i")]
+ UNSPEC_AUTH_REG))]
+ ""
+ {
+ return aarch64_output_sign_auth_reg (operands, /* sign_p */false,
+ /* x1716_p */false);
+ }
+)
+
+(define_insn "auth_reg1716"
+ [(set (reg:DI R17_REGNUM)
+ (unspec:DI [(reg:DI R17_REGNUM)
+ (reg:DI R16_REGNUM)
+ (match_operand:DI 0 "aarch64_const0_const1" "i")
+ (match_operand:DI 1 "aarch64_const0_const1" "i")]
+ UNSPEC_AUTH_REG1716))]
+ ""
+ {
+ return aarch64_output_sign_auth_reg (operands, /* sign_p */false,
+ /* x1716_p */true);
+ }
+)
+
+(define_insn "strip_reg_sign"
+ [(set (match_operand:DI 0 "register_operand" "=rk")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:DI 2 "immediate_operand" "i")]
+ UNSPEC_STRIP_REG_SIGN))]
+ ""
+ {
+ if (REGNO (operands[1]) == LR_REGNUM)
+ {
+ if (INTVAL (operands[2]) == 0)
+ return "xpaclri";
+ else
+ /* LR can't be used for data strip. */
+ gcc_unreachable ();
+ }
+ else
+ {
+ if (INTVAL (operands[2]) == 0)
+ return "xpaci\t%0";
+ else
+ return "xpacd\t%0";
+ }
+ }
+)
+
+(define_insn "strip_lr_sign"
+ [(set (reg:DI R30_REGNUM)
+ (unspec:DI [(reg:DI R30_REGNUM)
+ (match_operand:DI 0 "immediate_operand" "i")]
+ UNSPEC_STRIP_X30_SIGN))]
+ ""
+ {
+ if (INTVAL (operands[0]) == 0)
+ return "xpaclri";
+ else
+ /* LR can't be used for data strip. */
+ gcc_unreachable ();
+ }
+)
+
;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
;; all of memory. This blocks insns from being moved across this point.
@@ -149,6 +149,37 @@ mpc-relative-literal-loads
Target Report Save Var(pcrelative_literal_loads) Init(2) Save
PC relative literal loads.
+msign-return-address=
+Target RejectNegative Report Joined Enum(aarch64_ra_sign_scope_t) Var(aarch64_ra_sign_scope) Init(AARCH64_FUNCTION_NONE) Save
+Select return address signing scope
+
+Enum
+Name(aarch64_ra_sign_scope_t) Type(enum aarch64_function_type)
+Supported AArch64 return address signing scope (for use with -msign-return-address= option):
+
+EnumValue
+Enum(aarch64_ra_sign_scope_t) String(none) Value(AARCH64_FUNCTION_NONE)
+
+EnumValue
+Enum(aarch64_ra_sign_scope_t) String(non-leaf) Value(AARCH64_FUNCTION_NON_LEAF)
+
+EnumValue
+Enum(aarch64_ra_sign_scope_t) String(all) Value(AARCH64_FUNCTION_ALL)
+
+mpauth-key=
+Target RejectNegative Report Joined Enum(aarch64_pauth_key_t) Var(aarch64_pauth_key) Init(AARCH64_PAUTH_IKEY_A) Save
+Select pointer authentication key
+
+Enum
+Name(aarch64_pauth_key_t) Type(enum aarch64_pauth_key_index)
+Known AArch64 pointer authentication keys (for use with the -mpauth-key= option):
+
+EnumValue
+Enum(aarch64_pauth_key_t) String(a_key) Value(AARCH64_PAUTH_IKEY_A)
+
+EnumValue
+Enum(aarch64_pauth_key_t) String(b_key) Value(AARCH64_PAUTH_IKEY_B)
+
mlow-precision-recip-sqrt
Common Var(flag_mrecip_low_precision_sqrt) Optimization
Enable the reciprocal square root approximation. Enabling this reduces
@@ -35,6 +35,10 @@
(and (match_code "const_int")
(match_test "op == CONST0_RTX (mode)")))
+(define_predicate "aarch64_const0_const1"
+ (ior (match_test "op == const0_rtx")
+ (match_test "op == const1_rtx")))
+
(define_predicate "aarch64_ccmp_immediate"
(and (match_code "const_int")
(match_test "IN_RANGE (INTVAL (op), -31, 31)")))
@@ -3509,6 +3509,18 @@ Specifies the core for which to tune the performance of this function and also
whose architectural features to use. The behavior and valid arguments are the
same as for the @option{-mcpu=} command-line option.
+@item sign-return-address
+@cindex @code{sign-return-address} function attribute, AArch64
+Select the function scope we want to do return address signing on. The behavior
+and permissible arguments are the same as for the command-line option
+@option{-msign-return-address=}. The default value is @code{none}
+
+@item pauth-key
+@cindex @code{pauth-key} function attribute, AArch64
+Specify the key used for return address signing for this function. The behavior
+and permissible arguments are the same as for the command-line option
+@option{-mpauth-key=}. The default key is @code{a_key}.
+
@end table
The above target attributes can be specified as follows:
@@ -13344,6 +13344,19 @@ accessed using a single instruction and emitted after each function. This
limits the maximum size of functions to 1MB. This is enabled by default for
@option{-mcmodel=tiny}.
+@item -msign-return-address=@var{scope}
+@opindex msign-return-address
+Select the function scope we want to do return address signing on. Permissible
+values are @samp{none}, @samp{none-leaf} and @samp{all}. @samp{none} means
+return address signing is disabled. @samp{non-leaf} enables it for non-leaf
+functions. @samp{all} for all functions and is the default value.
+
+@item -mpauth-key=@var{key_name}
+@opindex mpauth-key
+Select the key used for return address signing. Permissible values are
+@samp{a_key} for A key and @samp{b_key} for B key. @samp{a_key} is the default
+value.
+
@end table
@subsubsection @option{-march} and @option{-mcpu} Feature Modifiers
new file mode 100644
@@ -0,0 +1,57 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -msign-return-address=all" } */
+
+int foo (int);
+int bar (int, int);
+
+/* sibcall only. */
+int __attribute__ ((target ("arch=armv8.3-a")))
+func1 (int a, int b)
+{
+ /* paciasp */
+ return foo (a + b);
+ /* autiasp */
+}
+
+/* non-leaf function with sibcall. */
+int __attribute__ ((target ("arch=armv8.3-a")))
+func2 (int a, int b)
+{
+ /* paciasp */
+ if (a < b)
+ return b;
+
+ a = foo (b);
+
+ return foo (a);
+ /* autiasp */
+}
+
+/* non-leaf function, legacy arch. */
+int __attribute__ ((target ("arch=armv8.2-a")))
+func3 (int a, int b, int c)
+{
+ /* paciasp */
+ return a + foo (b) + c;
+ /* autiasp */
+}
+
+/* non-leaf function. */
+int __attribute__ ((target ("arch=armv8.3-a, pauth-key=b_key")))
+func4 (int a, int b, int c)
+{
+ /* paciasp */
+ return a + foo (b) + c;
+ /* retab */
+}
+
+int __attribute__ ((target ("arch=armv8.3-a, sign-return-address=none")))
+func4_disable (int a, int b, int c, int d)
+{
+ return c + bar (a, b) + d;
+}
+
+/* { dg-final { scan-assembler-times "autiasp" 3 } } */
+/* { dg-final { scan-assembler-times "paciasp" 3 } } */
+/* { dg-final { scan-assembler-times "pacibsp" 1 } } */
+/* { dg-final { scan-assembler-times "retab" 1 } } */
new file mode 100644
@@ -0,0 +1,57 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -msign-return-address=non-leaf" } */
+
+int foo (int);
+int bar (int, int);
+
+/* sibcall only. */
+int __attribute__ ((target ("arch=armv8.3-a")))
+func1 (int a, int b)
+{
+ /* No authentication. */
+ return foo (a + b);
+}
+
+/* non-leaf function with sibcall. */
+int __attribute__ ((target ("arch=armv8.3-a")))
+func2 (int a, int b)
+{
+ /* paciasp */
+ if (a < b)
+ return b;
+
+ a = foo (b);
+
+ return foo (a);
+ /* autiasp */
+}
+
+/* non-leaf function, legacy arch. */
+int __attribute__ ((target ("arch=armv8.2-a, pauth-key=b_key")))
+func3 (int a, int b, int c)
+{
+ /* pacibsp */
+ return a + foo (b) + c;
+ /* autibsp */
+}
+
+/* non-leaf function. */
+int __attribute__ ((target ("arch=armv8.3-a, pauth-key=b_key")))
+func4 (int a, int b, int c)
+{
+ /* pacibsp */
+ return a + foo (b) + c;
+ /* retab */
+}
+
+int __attribute__ ((target ("arch=armv8.3-a, sign-return-address=none")))
+func4_disable (int a, int b, int c, int d)
+{
+ return c + bar (a, b) + d;
+}
+
+/* { dg-final { scan-assembler-times "paciasp" 1 } } */
+/* { dg-final { scan-assembler-times "autiasp" 1 } } */
+/* { dg-final { scan-assembler-times "pacibsp" 2 } } */
+/* { dg-final { scan-assembler-times "autibsp" 1 } } */
+/* { dg-final { scan-assembler-times "retab" 1 } } */