[PATCH 1 of 1 v10] aarch64: Support PAC and BTI

Niels Möller nisse at lysator.liu.se
Fri Sep 18 21:46:59 CEST 2026


Bill Roberts <bill.roberts at arm.com> writes:

> Testing was done under the following CFLAGS and
> CXXFLAGS for all combinations:
>  1. -mbranch-protection=none
>  2. -mbranch-protection=standard
>  3. -mbranch-protection=pac-ret
>  4. -mbranch-protection=pac-ret+b-key
>  5. -mbranch-protection=bti

Question for Torbjörn: Which of the gmp machines (if any) would be
suitable for running tests?

> --- a/configure.ac	Sun Sep 06 19:15:00 2026 +0200
> +++ b/configure.ac	Wed Sep 16 15:00:02 2026 -0500
> @@ -3421,14 +3421,14 @@
>              CPUVEC_SETUP="$CPUVEC_SETUP    decided_cpuvec.$tmp_fbase = __gmpn_${tmp_fbase}_${tmp_suffix}; \\
>  "
>              # Ditto for any preinv variant (preinv_divrem_1, preinv_mod_1).
> -            if grep "^PROLOGUE(mpn_preinv_$tmp_fn)" $tmp_file >/dev/null; then
> +            if grep "^PROLOGUE_\(NONLEAF\)?(mpn_preinv_$tmp_fn)" $tmp_file >/dev/null; then
>                echo "DECL_preinv_$tmp_fbase (__gmpn_preinv_${tmp_fbase}_$tmp_suffix);" >>fat.h
>                CPUVEC_SETUP="$CPUVEC_SETUP    decided_cpuvec.preinv_$tmp_fbase = __gmpn_preinv_${tmp_fbase}_${tmp_suffix}; \\
>  "
>              fi
>  
>              # Ditto for any mod_1...cps variant
> -            if grep "^PROLOGUE(mpn_${tmp_fbase}_cps)" $tmp_file >/dev/null; then
> +            if grep "^PROLOGUE_\(_NONLEAF\)?(mpn_${tmp_fbase}_cps)" $tmp_file >/dev/null; then
>                echo "DECL_${tmp_fbase}_cps (__gmpn_${tmp_fbase}_cps_$tmp_suffix);" >>fat.h
>                CPUVEC_SETUP="$CPUVEC_SETUP    decided_cpuvec.${tmp_fbase}_cps = __gmpn_${tmp_fbase}_cps_${tmp_suffix}; \\
>  "
> @@ -3561,6 +3561,7 @@
>            gmp_ep=[`
>              sed -n 's/^[ 	]*MULFUNC_PROLOGUE(\(.*\))/\1/p' $tmp_file ;
>              sed -n 's/^[ 	]*PROLOGUE(\([^,]*\).*)/\1/p' $tmp_file
> +            sed -n 's/^[ 	]*NONLEAF_PROLOGUE(\([^,]*\).*)/\1/p' $tmp_file

There appears to be some confusion about NONLEAF_PROLOGUE (here) and
PROLOGUE_NONLEAF (above). Probably makes most sense to use
NONLEAF_PROLOGUE everywhere, for consistency with MULFUNC_PROLOGUE.

>            `]
>            for gmp_tmp in $gmp_ep; do
>              AC_DEFINE_UNQUOTED(HAVE_NATIVE_$gmp_tmp)
> @@ -3767,7 +3768,77 @@
>  	    *-*-darwin*)
>  	      GMP_INCLUDE_MPN(arm64/darwin.m4) ;;
>  	    *)
> -	      GMP_INCLUDE_MPN(arm64/arm64-defs.m4) ;;
> +	      GMP_INCLUDE_MPN(arm64/arm64-defs.m4)
> +
> +	      AC_CACHE_CHECK([if Armv8.5-A BTI is enabled],
> +	        [gmp_cv_asm_arm64_bti],
> +	        [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
> +	      #if !(defined __ARM_FEATURE_BTI_DEFAULT)
> +	        #error Armv8.5-A BTI is not enabled
> +	      #endif
> +	        ]], [[]])],
> +	        [gmp_cv_asm_arm64_bti=yes],
> +	        [gmp_cv_asm_arm64_bti=no])])
> +	      # Convert yes no values to BTI value, yes no is nice for the output message on the screen
> +	      # Example: checking if Armv8.5-A BTI is enabled... yes
> +	      AS_IF([test "$gmp_cv_asm_arm64_bti" = yes],
> +	        [ARM64_FEATURE_BTI_DEFAULT=1], [ARM64_FEATURE_BTI_DEFAULT=0])
> +
> +	      GMP_DEFINE_RAW(["define(<ARM64_FEATURE_BTI_DEFAULT>,$ARM64_FEATURE_BTI_DEFAULT)"])
> +	      AC_SUBST([ARM64_FEATURE_BTI_DEFAULT])

>From the docs, it seems GMP_DEFINE_RAW adds a definition to config.m4.
You combine with AC_SUBST, so you want ARM64_FEATURE_BTI_DEFAULT to go
into both config.m4 and other substituted files, e.g, Makefile? And same
for other variables.

> +	      AS_IF([test "$gmp_cv_asm_arm64_pac" = yes], [
> +	        AC_CACHE_CHECK([if PAC is using A or B key],
> +	          [gmp_cv_asm_arm64_pac_key],
> +	          [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
> +	        #if __ARM_FEATURE_PAC_DEFAULT & 1
> +	        // Armv8.3-A PAC A Key enabled else B Key enabled
> +	        #endif
> +	          ]], [[]])],
> +	          [gmp_cv_asm_arm64_pac_key=A],
> +	          [gmp_cv_asm_arm64_pac_key=B])])
> +	      ])

This test looks weird, should that C++-style comment be an #error
instead?

> +	      AC_CACHE_CHECK([whether __ELF__ is defined],
> +	        [gmp_cv_asm_arm64_have_elf],
> +	        [AC_PREPROC_IFELSE(
> +	           [AC_LANG_SOURCE([[
> +	      #ifdef __ELF__
> +	      int main(void) { return 0; }
> +	      #else
> +	      #error "__ELF__ not defined"
> +	      #endif
> +	      ]])],

If this is just a test running the preprocessor, having the main
function seems unnecessary. For consistency, I think it would make sense
to use AC_COMPILE_IFELSE and same style as the other tests above.

> +	        [gmp_cv_asm_arm64_have_elf=yes],
> +	        [gmp_cv_asm_arm64_have_elf=no])])
> +
> +	      if test "$gmp_cv_asm_arm64_have_elf" = yes; then
> +	        gmp_cv_asm_arm64_have_elf_value=1
> +	      else
> +	        gmp_cv_asm_arm64_have_elf_value=0
> +	      fi
> +
> +	      GMP_DEFINE_RAW(["define(<ARM64_ELF>,$gmp_cv_asm_arm64_have_elf_value)"])
> +	      ;;
>            esac
>  	  ;;
>        esac
> @@ -4058,6 +4129,9 @@
>  AC_PROG_YACC
>  AM_PROG_LEX
>  
> +AM_CONDITIONAL([ARM64_PAC_OR_BTI],
> +  [test $ARM64_FEATURE_BTI_DEFAULT -ne 0 || test $ARM64_FEATURE_BTI_DEFAULT])
> +

I think it would make sense to move this closer to the tests that set
these variables.

> +#define(`NONLEAF_PROLOGUE', `indir(`PROLOGUE', $1, true)')
> +define(`PROLOGUE_NONLEAF', `PROLOGUE($1, true)')

More naming.

> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/tests/mpn/t-arm64_bti.c	Wed Sep 16 15:00:02 2026 -0500
> +int
> +main (int argc, char **argv)
> +{
> +	unsigned long hwcap2 = getauxval(AT_HWCAP2);
> +	if (!(hwcap2 & HWCAP2_BTI)) {
> +		fprintf(stderr, "Hardware does not support BTI\n");
> +		return SKIP;
> +	}
> +
> +	/*
> +	 * 0x1001 is the value we will left shift by one, we expect 0x2002 as
> +	 * the valid result.
> +	 */
> +	mp_limb_t xp = 0x1001, wp;
> +
> +	fn_mpn_lshift fn = dlsym(RTLD_DEFAULT, "__gmpn_lshift");
> +	if (!fn) {
> +	  fprintf(stderr, "Could not find the symbol __gmpn_lshift\n");
> +	  return 0;
> +	}

I would expect this symbol lookup to fail in a static build. Maybe it
should be SKIP instead?

> +	/* should work as this will land on a BTI landing pad as expected */
> +	fn (&wp, &xp, (mp_size_t) 1, 1);
> +	/* And provide the proper result 0x1001 << 1 = 0x2002 */
> +	ASSERT_ALWAYS (wp == 0x2002);

Since this is an XFAIL test, hitting this assert will look like an
expected failure, right? I find XFAIL generally a bit confusing, it
would be nicer if the test could detect if an exception was raised, and
return success when things are as expected. E.g., installing a signal
handler for the expected signal just before running the code that is
expected to raise that exception, and have the exception handler just
call exit (0).

Regards,
/Niels

-- 
Niels Möller. PGP key CB4962D070D77D7FCB8BA36271D8F1FF368C6677.
Internet email is subject to wholesale government surveillance.


More information about the gmp-devel mailing list