diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index 603849477063e8c70ece470404bdd34239fd6f19..784c93a8e9701a671004e3dfda463d726611c340 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -183,6 +183,12 @@ bool aarch64_pcrelative_literal_loads; /* Global flag for whether frame pointer is enabled. */ bool aarch64_use_frame_pointer; +#define BRANCH_PROTECT_STR_MAX 255 +char *accepted_branch_protection_string = NULL; + +static enum aarch64_parse_opt_result +aarch64_parse_branch_protection (const char*, char**); + /* Support for command line parsing of boolean flags in the tuning structures. */ struct aarch64_flag_desc @@ -1170,6 +1176,79 @@ aarch64_cc; #define AARCH64_INVERSE_CONDITION_CODE(X) ((aarch64_cc) (((int) X) ^ 1)) +struct aarch64_branch_protect_type +{ + /* The type's name that the user passes to the branch-protection option + string. */ + const char* name; + /* Function to handle the protection type and set global variables. + First argument is the string token corresponding with this type and the + second argument is the next token in the option string. + Return values: + * AARCH64_PARSE_OK: Handling was sucessful. + * AARCH64_INVALID_ARG: The type is invalid in this context and the caller + should print an error. + * AARCH64_INVALID_FEATURE: The type is invalid and the handler prints its + own error. */ + enum aarch64_parse_opt_result (*handler)(char*, char*); + /* A list of types that can follow this type in the option string. */ + const aarch64_branch_protect_type* subtypes; + unsigned int num_subtypes; +}; + +static enum aarch64_parse_opt_result +aarch64_handle_no_branch_protection (char* str, char* rest) +{ + aarch64_ra_sign_scope = AARCH64_FUNCTION_NONE; + if (rest) + { + error ("unexpected %<%s%> after %<%s%>", rest, str); + return AARCH64_PARSE_INVALID_FEATURE; + } + return AARCH64_PARSE_OK; +} + +static enum aarch64_parse_opt_result +aarch64_handle_standard_branch_protection (char* str, char* rest) +{ + aarch64_ra_sign_scope = AARCH64_FUNCTION_NON_LEAF; + if (rest) + { + error ("unexpected %<%s%> after %<%s%>", rest, str); + return AARCH64_PARSE_INVALID_FEATURE; + } + return AARCH64_PARSE_OK; +} + +static enum aarch64_parse_opt_result +aarch64_handle_pac_ret_protection (char* str ATTRIBUTE_UNUSED, + char* rest ATTRIBUTE_UNUSED) +{ + aarch64_ra_sign_scope = AARCH64_FUNCTION_NON_LEAF; + return AARCH64_PARSE_OK; +} + +static enum aarch64_parse_opt_result +aarch64_handle_pac_ret_leaf (char* str ATTRIBUTE_UNUSED, + char* rest ATTRIBUTE_UNUSED) +{ + aarch64_ra_sign_scope = AARCH64_FUNCTION_ALL; + return AARCH64_PARSE_OK; +} + +static const struct aarch64_branch_protect_type aarch64_pac_ret_subtypes[] = { + { "leaf", aarch64_handle_pac_ret_leaf, NULL, 0 }, + { NULL, NULL, NULL, 0 } +}; + +static const struct aarch64_branch_protect_type aarch64_branch_protect_types[] = { + { "none", aarch64_handle_no_branch_protection, NULL, 0 }, + { "standard", aarch64_handle_standard_branch_protection, NULL, 0 }, + { "pac-ret", aarch64_handle_pac_ret_protection, aarch64_pac_ret_subtypes, + ARRAY_SIZE (aarch64_pac_ret_subtypes) }, + { NULL, NULL, NULL, 0 } +}; + /* The condition codes of the processor, and the inverse function. */ static const char * const aarch64_condition_codes[] = { @@ -11099,6 +11178,12 @@ aarch64_parse_override_string (const char* input_string, static void aarch64_override_options_after_change_1 (struct gcc_options *opts) { + if (accepted_branch_protection_string) + { + opts->x_aarch64_branch_protection_string + = xstrdup (accepted_branch_protection_string); + } + /* PR 70044: We have to be careful about being called multiple times for the same function. This means all changes should be repeatable. */ @@ -11384,6 +11469,110 @@ aarch64_validate_mcpu (const char *str, const struct processor **res, return false; } +/* Parses CONST_STR for branch protection features specified in + aarch64_branch_protect_types, and set any global variables required. Returns + the parsing result and assigns LAST_STR to the last processed token from + CONST_STR so that it can be used for error reporting. */ + +static enum +aarch64_parse_opt_result aarch64_parse_branch_protection (const char *const_str, + char** last_str) +{ + char *str_root = xstrdup (const_str); + char* token_save = NULL; + char *str = strtok_r (str_root, "+", &token_save); + enum aarch64_parse_opt_result res = AARCH64_PARSE_OK; + if (!str) + res = AARCH64_PARSE_MISSING_ARG; + else + { + char *next_str = strtok_r (NULL, "+", &token_save); + /* Reset the branch protection features to their defaults. */ + aarch64_handle_no_branch_protection (NULL, NULL); + + while (str && res == AARCH64_PARSE_OK) + { + const aarch64_branch_protect_type* type = aarch64_branch_protect_types; + bool found = false; + /* Search for this type. */ + while (type && type->name && !found && res == AARCH64_PARSE_OK) + { + if (strcmp (str, type->name) == 0) + { + found = true; + res = type->handler (str, next_str); + str = next_str; + next_str = strtok_r (NULL, "+", &token_save); + } + else + type++; + } + if (found && res == AARCH64_PARSE_OK) + { + bool found_subtype = true; + /* Loop through each token until we find one that isn't a + subtype. */ + while (found_subtype) + { + found_subtype = false; + const aarch64_branch_protect_type *subtype = type->subtypes; + /* Search for the subtype. */ + while (str && subtype && subtype->name && !found_subtype + && res == AARCH64_PARSE_OK) + { + if (strcmp (str, subtype->name) == 0) + { + found_subtype = true; + res = subtype->handler (str, next_str); + str = next_str; + next_str = strtok_r (NULL, "+", &token_save); + } + else + subtype++; + } + } + } + else if (!found) + res = AARCH64_PARSE_INVALID_ARG; + } + } + /* Copy the last processed token into the argument to pass it back. + Used by option and attribute validation to print the offending token. */ + if (last_str) + { + if (str) strcpy (*last_str, str); + else *last_str = NULL; + } + if (res == AARCH64_PARSE_OK) + { + /* If needed, alloc the accepted string then copy in const_str. + Used by override_option_after_change_1. */ + if (!accepted_branch_protection_string) + accepted_branch_protection_string = (char *) xmalloc ( + BRANCH_PROTECT_STR_MAX + + 1); + strncpy (accepted_branch_protection_string, const_str, + BRANCH_PROTECT_STR_MAX + 1); + /* Forcibly null-terminate. */ + accepted_branch_protection_string[BRANCH_PROTECT_STR_MAX] = '\0'; + } + return res; +} + +static bool +aarch64_validate_mbranch_protection (const char *const_str) +{ + char *str = (char *) xmalloc (strlen (const_str)); + enum aarch64_parse_opt_result res = + aarch64_parse_branch_protection (const_str, &str); + if (res == AARCH64_PARSE_INVALID_ARG) + error ("invalid arg %<%s%> for %<-mbranch-protection=%>", str); + else if (res == AARCH64_PARSE_MISSING_ARG) + error ("missing arg for %<-mbranch-protection=%>"); + free (str); + return res == AARCH64_PARSE_OK; +} + /* Validate a command-line -march option. Parse the arch and extensions (if any) specified in STR and throw errors if appropriate. Put the results, if they are valid, in RES and ISA_FLAGS. Return whether the @@ -11518,6 +11707,9 @@ aarch64_override_options (void) selected_arch = NULL; selected_tune = NULL; + if (aarch64_branch_protection_string) + aarch64_validate_mbranch_protection (aarch64_branch_protection_string); + /* -mcpu=CPU is shorthand for -march=ARCH_FOR_CPU, -mtune=CPU. If either of -march or -mtune is given, they override their respective component of -mcpu. */ @@ -11690,6 +11882,8 @@ static void aarch64_option_save (struct cl_target_option *ptr, struct gcc_options *opts) { ptr->x_aarch64_override_tune_string = opts->x_aarch64_override_tune_string; + ptr->x_aarch64_branch_protection_string + = opts->x_aarch64_branch_protection_string; } /* Implements TARGET_OPTION_RESTORE. Restore the backend codegen decisions @@ -11703,6 +11897,13 @@ aarch64_option_restore (struct gcc_options *opts, struct cl_target_option *ptr) opts->x_explicit_arch = ptr->x_explicit_arch; selected_arch = aarch64_get_arch (ptr->x_explicit_arch); opts->x_aarch64_override_tune_string = ptr->x_aarch64_override_tune_string; + opts->x_aarch64_branch_protection_string + = ptr->x_aarch64_branch_protection_string; + if (opts->x_aarch64_branch_protection_string) + { + aarch64_parse_branch_protection (opts->x_aarch64_branch_protection_string, + NULL); + } aarch64_override_options_internal (opts); } @@ -11897,6 +12098,37 @@ aarch64_handle_attr_cpu (const char *str) return false; } +/* Handle the argument STR to the branch-protection= attribute. */ + + static bool + aarch64_handle_attr_branch_protection (const char* str) + { + char *err_str = (char *) xmalloc (strlen (str)); + enum aarch64_parse_opt_result res = aarch64_parse_branch_protection (str, + &err_str); + bool success = false; + switch (res) + { + case AARCH64_PARSE_MISSING_ARG: + error ("missing argument to % pragma or" + " attribute"); + break; + case AARCH64_PARSE_INVALID_ARG: + error ("invalid protection type (\"%s\") in % pragma or attribute", err_str); + break; + case AARCH64_PARSE_OK: + success = true; + /* Fall through. */ + case AARCH64_PARSE_INVALID_FEATURE: + break; + default: + gcc_unreachable (); + } + free (err_str); + return success; + } + /* Handle the argument STR to the tune= target attribute. */ static bool @@ -11995,6 +12227,8 @@ 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_ }, + { "branch-protection", aarch64_attr_custom, false, + aarch64_handle_attr_branch_protection, OPT_mbranch_protection_ }, { "sign-return-address", aarch64_attr_enum, false, NULL, OPT_msign_return_address_ }, { NULL, aarch64_attr_custom, false, NULL, OPT____ } diff --git a/gcc/config/aarch64/aarch64.opt b/gcc/config/aarch64/aarch64.opt index b2e80cbf6f1f9727c4309874b1122f975fb6b9be..9460636d93b67af1525f028176aa78e6fed4e45f 100644 --- a/gcc/config/aarch64/aarch64.opt +++ b/gcc/config/aarch64/aarch64.opt @@ -149,8 +149,12 @@ mpc-relative-literal-loads Target Report Save Var(pcrelative_literal_loads) Init(2) Save PC relative literal loads. +mbranch-protection= +Target RejectNegative Joined Var(aarch64_branch_protection_string) Save +Use branch-protection features. + msign-return-address= -Target RejectNegative Report Joined Enum(aarch64_ra_sign_scope_t) Var(aarch64_ra_sign_scope) Init(AARCH64_FUNCTION_NONE) Save +Target Deprecated RejectNegative Joined Enum(aarch64_ra_sign_scope_t) Var(aarch64_ra_sign_scope) Init(AARCH64_FUNCTION_NONE) Save Select return address signing scope. Enum diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index ac2ee59d92c3d5bd0e9deb854ba9fcb3dcee4ec9..99c67e3896dcf93c90384b08198523e94a754f7e 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -630,6 +630,7 @@ Objective-C and Objective-C++ Dialects}. -mlow-precision-recip-sqrt -mlow-precision-sqrt -mlow-precision-div @gol -mpc-relative-literal-loads @gol -msign-return-address=@var{scope} @gol +-mbranch-protection=@var{none}|@var{standard}|@var{pac-ret}[+@var{leaf}] @gol -march=@var{name} -mcpu=@var{name} -mtune=@var{name} @gol -moverride=@var{string} -mverbose-cost-dump -mtrack-speculation} @@ -15700,7 +15701,21 @@ Select the function scope on which return address signing will be applied. Permissible values are @samp{none}, which disables return address signing, @samp{non-leaf}, which enables pointer signing for functions which are not leaf functions, and @samp{all}, which enables pointer signing for all functions. The -default value is @samp{none}. +default value is @samp{none}. This option has been deprecated by +-mbranch-protection. + +@item -mbranch-protection=@var{none}|@var{standard}|@var{pac-ret}[+@var{leaf}] +@opindex mbranch-protection +Select the branch protection features to use. +@samp{none} is the default and turns off all types of branch protection. +@samp{standard} turns on all types of branch protection features. If a feature +has additional tuning options, then @samp{standard} sets it to its standard +level. +@samp{pac-ret[+@var{leaf}]} turns on return address signing to its standard +level: signing functions that save the return address to memory (non-leaf +functions will practically always do this) using the a-key. The optional +argument @samp{leaf} can be used to extend the signing to include leaf +functions. @item -msve-vector-bits=@var{bits} @opindex msve-vector-bits diff --git a/gcc/testsuite/gcc.target/aarch64/branch-protection-attr-2.c b/gcc/testsuite/gcc.target/aarch64/branch-protection-attr-2.c new file mode 100644 index 0000000000000000000000000000000000000000..19b3511ab6111a895383f5ddf93644110c3971c6 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/branch-protection-attr-2.c @@ -0,0 +1,16 @@ +/* { dg-do "compile" } */ + +void __attribute__ ((target("branch-protection=pac-ret+leaf,branch-protection=none"))) +foo () +{ +} + +void __attribute__ ((target("branch-protection=pac-ret,branch-protection=none"))) +foo2 () +{ + /* Function call here to make this a non-leaf function, so that it is covered by pac-ret. */ + foo (); +} + +/* { dg-final { scan-assembler-not "\tautiasp\t" } } */ +/* { dg-final { scan-assembler-not "\tpaciasp\t" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/branch-protection-attr.c b/gcc/testsuite/gcc.target/aarch64/branch-protection-attr.c new file mode 100644 index 0000000000000000000000000000000000000000..229ce1ca7bebce060204946682af175389527193 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/branch-protection-attr.c @@ -0,0 +1,22 @@ +/* { dg-do "compile" } */ + +void __attribute__ ((target("branch-protection=leaf"))) +foo1 () +{ +} +/* { dg-error {invalid protection type \("leaf"\) in 'target\("branch-protection="\)' pragma or attribute} "" { target *-*-* } 5 } */ +/* { dg-error {pragma or attribute 'target\("branch-protection=leaf"\)' is not valid} "" { target *-*-* } 5 } */ + +void __attribute__ ((target("branch-protection=none+pac-ret"))) +foo2 () +{ +} +/* { dg-error "unexpected 'pac-ret' after 'none'" "" { target *-*-* } 12 } */ +/* { dg-error {pragma or attribute 'target\("branch-protection=none\+pac-ret"\)' is not valid} "" { target *-*-* } 12 } */ + +void __attribute__ ((target("branch-protection="))) +foo3 () +{ +} +/* { dg-error {missing argument to 'target\("branch-protection="\)' pragma or attribute} "" { target *-*-* } 19 } */ +/* { dg-error {pragma or attribute 'target\("branch-protection="\)' is not valid} "" { target *-*-* } 19 } */ diff --git a/gcc/testsuite/gcc.target/aarch64/branch-protection-option-2.c b/gcc/testsuite/gcc.target/aarch64/branch-protection-option-2.c new file mode 100644 index 0000000000000000000000000000000000000000..281851136520ece286186fd18c1a64b984f0b3e8 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/branch-protection-option-2.c @@ -0,0 +1,9 @@ +/* { dg-do "compile" } */ +/* { dg-options "-mbranch-protection=pac-ret+leaf -mbranch-protection=none" } */ + +void foo2 () +{ +} + +/* { dg-final { scan-assembler-not "\tautiasp\t" } } */ +/* { dg-final { scan-assembler-not "\tpaciasp\t" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/branch-protection-option.c b/gcc/testsuite/gcc.target/aarch64/branch-protection-option.c new file mode 100644 index 0000000000000000000000000000000000000000..1b3bf4ee2b88a6e89d78f99766889904849a89db --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/branch-protection-option.c @@ -0,0 +1,4 @@ +/* { dg-do "compile" } */ +/* { dg-options "-mbranch-protection=leaf -mbranch-protection=none+pac-ret" } */ + +/* { dg-error "unexpected 'pac-ret' after 'none'" "" { target *-*-* } 0 } */ diff --git a/gcc/testsuite/gcc.target/aarch64/return_address_sign_1.c b/gcc/testsuite/gcc.target/aarch64/return_address_sign_1.c index f87c3d28d1edff473a787a39a436e57076f97508..0140bee194f5a3ec53e794984c2f9b0e96bdbb63 100644 --- a/gcc/testsuite/gcc.target/aarch64/return_address_sign_1.c +++ b/gcc/testsuite/gcc.target/aarch64/return_address_sign_1.c @@ -1,6 +1,6 @@ /* Testing return address signing where no combined instructions used. */ /* { dg-do compile } */ -/* { dg-options "-O2 -msign-return-address=all" } */ +/* { dg-options "-O2 -mbranch-protection=pac-ret+leaf" } */ /* { dg-require-effective-target lp64 } */ int foo (int); diff --git a/gcc/testsuite/gcc.target/aarch64/return_address_sign_2.c b/gcc/testsuite/gcc.target/aarch64/return_address_sign_2.c index c5c1439b92e6637f85c47c6161cd797c0d68df25..a4bc5b4533382d3d21085a763028576f72531ed7 100644 --- a/gcc/testsuite/gcc.target/aarch64/return_address_sign_2.c +++ b/gcc/testsuite/gcc.target/aarch64/return_address_sign_2.c @@ -1,6 +1,6 @@ /* Testing return address signing where combined instructions used. */ /* { dg-do compile } */ -/* { dg-options "-O2 -msign-return-address=all" } */ +/* { dg-options "-O2 -mbranch-protection=pac-ret+leaf" } */ /* { dg-require-effective-target lp64 } */ int foo (int); diff --git a/gcc/testsuite/gcc.target/aarch64/return_address_sign_3.c b/gcc/testsuite/gcc.target/aarch64/return_address_sign_3.c index 7d9ec6eebd1ce452013d2895a551671c59e98f0c..e39ed34ab1c13d1368f4a625b4a466eb76354ef8 100644 --- a/gcc/testsuite/gcc.target/aarch64/return_address_sign_3.c +++ b/gcc/testsuite/gcc.target/aarch64/return_address_sign_3.c @@ -1,17 +1,17 @@ /* Testing the disable of return address signing. */ /* { dg-do compile } */ -/* { dg-options "-O2 -msign-return-address=all" } */ +/* { dg-options "-O2 -mbranch-protection=pac-ret+leaf" } */ /* { dg-require-effective-target lp64 } */ int bar (int, int); -int __attribute__ ((target ("arch=armv8.3-a, sign-return-address=non-leaf"))) +int __attribute__ ((target ("arch=armv8.3-a, branch-protection=pac-ret"))) func1_leaf (int a, int b, int c, int d) { return a + b + c + d; } -int __attribute__ ((target ("arch=armv8.3-a, sign-return-address=none"))) +int __attribute__ ((target ("arch=armv8.3-a, branch-protection=none"))) func2_none (int a, int b, int c, int d) { return c + bar (a, b) + d;