Enable Pointer Authentication Codes (PAC) and Branch Target Identification (BTI) support for ARM 64 targets.
PAC works by signing the LR with either an A key or B key and verifying the return address. There are quite a few instructions capable of doing this, however, the Linux ARM ABI is to use hint compatible instructions that can be safely NOP'd on older hardware and can be assembled and linked with older binutils. This limits the instruction set to paciasp, pacibsp, autiasp and autibsp. Instructions prefixed with pac are for signing and instructions prefixed with aut are for signing. Both instructions are then followed with an a or b to indicate which signing key they are using. The keys can be controlled using -mbranch-protection=pac-ret for the A key and -mbranch-protection=pac-ret+b-key for the B key. BTI works by marking all call and jump positions with bti c and bti j instructions. If execution control transfers to an instruction other than a BTI instruction, the execution is killed via SIGILL. Note that to remove one instruction, the aforementioned pac instructions will also work as a BTI landing pad for bti c usages. For BTI to work, all object files linked for a unit of execution, whether an executable or a library must have the GNU Notes section of the ELF file marked to indicate BTI support. This is so loader/linkers can apply the proper permission bits (PROT_BRI) on the memory region. PAC can also be annotated in the GNU ELF notes section, but it's not required for enablement, as interleaved PAC and non-pac code works as expected since it's the callee that performs all the checking. The linker follows the same rules as BTI for discarding the PAC flag from the GNU Notes section. 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 Add tests that get skipped on non-pac and bti enabled systems, so this safely limits the tests to aarch64 platforms with support. One test dynamically tests that an mpn assembly routine supports BTI when the binary is enabled AND the system has support by calling that routine and verifying it's functionality and then by calling it one instruction past the correct entry point, and thus missing the landing pad. The other test added, tests that the ELF binary has the proper GNU Notes section for the set of build flags. Signed-off-by: Bill Roberts <bill.robe...@arm.com> --- acinclude.m4 | 33 ++++++++++++ configure.ac | 20 ++++++- mpn/Makeasm.am | 3 +- mpn/arm64/aors_n.asm | 1 + mpn/arm64/aorsmul_1.asm | 1 + mpn/arm64/aorsorrlsh1_n.asm | 1 + mpn/arm64/aorsorrlsh2_n.asm | 1 + mpn/arm64/arm64-defs.m4 | 95 +++++++++++++++++++++++++++++++++ mpn/arm64/bdiv_dbm1c.asm | 1 + mpn/arm64/bdiv_q_1.asm | 1 + mpn/arm64/cnd_aors_n.asm | 1 + mpn/arm64/com.asm | 1 + mpn/arm64/copyd.asm | 1 + mpn/arm64/copyi.asm | 1 + mpn/arm64/divrem_1.asm | 9 +++- mpn/arm64/gcd_11.asm | 1 + mpn/arm64/gcd_22.asm | 1 + mpn/arm64/hamdist.asm | 1 + mpn/arm64/invert_limb.asm | 1 + mpn/arm64/logops_n.asm | 1 + mpn/arm64/lshift.asm | 1 + mpn/arm64/lshiftc.asm | 1 + mpn/arm64/mod_34lsub1.asm | 1 + mpn/arm64/mul_1.asm | 1 + mpn/arm64/popcount.asm | 1 + mpn/arm64/rsh1aors_n.asm | 1 + mpn/arm64/rshift.asm | 1 + mpn/arm64/sec_tabselect.asm | 1 + mpn/arm64/sqr_diag_addlsh1.asm | 1 + tests/mpn/Makefile.am | 43 ++++++++++----- tests/mpn/log-compiler.sh | 21 ++++++++ tests/mpn/t-arm64_bti.c | 86 ++++++++++++++++++++++++++++++ tests/mpn/t-arm64_elf_check.sh | 96 ++++++++++++++++++++++++++++++++++ 33 files changed, 412 insertions(+), 18 deletions(-) create mode 100755 tests/mpn/log-compiler.sh create mode 100644 tests/mpn/t-arm64_bti.c create mode 100755 tests/mpn/t-arm64_elf_check.sh diff --git a/acinclude.m4 b/acinclude.m4 index fddb5fb07..e89ab19cd 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -3992,3 +3992,36 @@ case $gmp_cv_check_libm_for_build in *) LIBM_FOR_BUILD=$gmp_cv_check_libm_for_build ;; esac ]) + +# Define GET_CPP_VALUE to capture the value of a C preprocessor symbol via compilation. +# This is useful when something like AC_EGREP_CPP doesn't have the correct environment. +# Arg 1 - The name of the macro to check in the compiled program. +# Arg 2 - The variable name to define the value of the macro to. +# Arg 3 - The default value if not defined. +# +# Example: GMP_GET_MACRO_VALUE([FOO], [BAR], [0]) +# This will check for macro FOO and define in MR a new macro as BAR with the value +# as derived from compilation or 0. +# +AC_DEFUN([GMP_GET_MACRO_VALUE], [ + AC_MSG_CHECKING([value of $1]) + + # Run a test to capture the value of the given macro + AC_CACHE_CHECK([for value of $1], [ac_cv_value_$2], [ + AC_TRY_RUN([ + /* Start of a C Program */ + #include <stdio.h> + #ifndef $1 + #define $1 $3 /* Set to a default if not defined */ + #endif + + int main() { + printf("%d", $1); + return 0; + } + /* End of a C Program */ + ], + [ac_cv_value_$2=`./conftest`; $2=$ac_cv_value_$2], + [ac_cv_value_$2="undefined"; $2=$ac_cv_value_$2]) + ]) +]) diff --git a/configure.ac b/configure.ac index c3a4a9bf8..c691e91e1 100644 --- a/configure.ac +++ b/configure.ac @@ -3760,7 +3760,17 @@ if test "$gmp_asm_syntax_testing" != no; then *-*-darwin*) GMP_INCLUDE_MPN(arm64/darwin.m4) ;; *) - GMP_INCLUDE_MPN(arm64/arm64-defs.m4) ;; + GMP_INCLUDE_MPN(arm64/arm64-defs.m4) + GMP_GET_MACRO_VALUE([__ARM_FEATURE_BTI_DEFAULT], [ARM64_FEATURE_BTI_DEFAULT], [0]) + GMP_DEFINE_RAW(["define(<ARM64_FEATURE_BTI_DEFAULT>,<$ARM64_FEATURE_BTI_DEFAULT>)"]) + AC_SUBST([ARM64_FEATURE_BTI_DEFAULT]) + + GMP_GET_MACRO_VALUE([__ARM_FEATURE_PAC_DEFAULT], [ARM64_FEATURE_PAC_DEFAULT], [0]) + GMP_DEFINE_RAW(["define(<ARM64_FEATURE_PAC_DEFAULT>,<$ARM64_FEATURE_PAC_DEFAULT>)"]) + AC_SUBST([ARM64_FEATURE_PAC_DEFAULT]) + + GMP_GET_MACRO_VALUE([__ELF__], [ARM64_ELF], [0]) + GMP_DEFINE_RAW(["define(<ARM64_ELF>,<$ARM64_ELF>)"]) esac ;; esac @@ -4051,6 +4061,12 @@ fi AC_PROG_YACC AM_PROG_LEX +AC_CHECK_TOOL([HAVE_BASH], [bash], [no]) +AM_CONDITIONAL([HAVE_BASH], [test "$HAVE_BASH" != "no"]) + +AC_CHECK_TOOL([HAVE_READELF], [readelf], [no]) +AM_CONDITIONAL([HAVE_READELF], [test "$HAVE_READELF" != "no"]) + # Create config.m4. GMP_FINISH @@ -4062,7 +4078,7 @@ AC_CONFIG_FILES([Makefile \ tests/Makefile tests/devel/Makefile \ tests/mpf/Makefile tests/mpn/Makefile tests/mpq/Makefile \ tests/mpz/Makefile tests/rand/Makefile tests/misc/Makefile \ - tests/cxx/Makefile \ + tests/cxx/Makefile \ doc/Makefile tune/Makefile \ demos/Makefile demos/calc/Makefile demos/expr/Makefile \ gmp.h:gmp-h.in gmp.pc:gmp.pc.in gmpxx.pc:gmpxx.pc.in]) diff --git a/mpn/Makeasm.am b/mpn/Makeasm.am index 5d7306c22..527bf41cf 100644 --- a/mpn/Makeasm.am +++ b/mpn/Makeasm.am @@ -115,4 +115,5 @@ RM_TMP = rm -f $(CCAS) $(COMPILE_FLAGS) tmp-$*.s -o $@ $(RM_TMP) tmp-$*.s .asm.lo: - $(LIBTOOL) --mode=compile --tag=CC $(top_srcdir)/mpn/m4-ccas --m4="$(M4)" $(CCAS) $(COMPILE_FLAGS) `test -f '$<' || echo '$(srcdir)/'`$< + $(LIBTOOL) --mode=compile --tag=CC $(top_srcdir)/mpn/m4-ccas --m4="$(M4)" \ + $(CCAS) $(COMPILE_FLAGS) `test -f '$<' || echo '$(srcdir)/'`$< diff --git a/mpn/arm64/aors_n.asm b/mpn/arm64/aors_n.asm index b4a6da6ff..c2b388356 100644 --- a/mpn/arm64/aors_n.asm +++ b/mpn/arm64/aors_n.asm @@ -123,3 +123,4 @@ L(end): ADDSUBC x12, x6, x10 L(ret): RETVAL ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/aorsmul_1.asm b/mpn/arm64/aorsmul_1.asm index 81ec1dabb..14bb28220 100644 --- a/mpn/arm64/aorsmul_1.asm +++ b/mpn/arm64/aorsmul_1.asm @@ -143,3 +143,4 @@ L(mid): sub n, n, #1 csinc x0, x15, x15, COND ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/aorsorrlsh1_n.asm b/mpn/arm64/aorsorrlsh1_n.asm index c617a67a9..29ea17885 100644 --- a/mpn/arm64/aorsorrlsh1_n.asm +++ b/mpn/arm64/aorsorrlsh1_n.asm @@ -41,3 +41,4 @@ ifdef(`OPERATION_rsblsh1_n',`define(`DO_rsb')') MULFUNC_PROLOGUE(mpn_addlsh1_n mpn_sublsh1_n mpn_rsblsh1_n) include_mpn(`arm64/aorsorrlshC_n.asm') +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/aorsorrlsh2_n.asm b/mpn/arm64/aorsorrlsh2_n.asm index 852d11720..524cfa0ff 100644 --- a/mpn/arm64/aorsorrlsh2_n.asm +++ b/mpn/arm64/aorsorrlsh2_n.asm @@ -41,3 +41,4 @@ ifdef(`OPERATION_rsblsh2_n',`define(`DO_rsb')') MULFUNC_PROLOGUE(mpn_addlsh2_n mpn_sublsh2_n mpn_rsblsh2_n) include_mpn(`arm64/aorsorrlshC_n.asm') +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/arm64-defs.m4 b/mpn/arm64/arm64-defs.m4 index 46149f7bf..067722fdc 100644 --- a/mpn/arm64/arm64-defs.m4 +++ b/mpn/arm64/arm64-defs.m4 @@ -36,6 +36,101 @@ dnl don't want to disable macro expansions in or after them. changecom +dnl use the hint instructions so they NOP on older machines. +dnl Add comments so the assembly is notated with the instruction + + +define(`PACIASP', `hint #25 /* paciasp */') +define(`AUTIASP', `hint #29 /* autiasp */') +define(`PACIBSP', `hint #27 /* pacibsp */') +define(`AUTIBSP', `hint #31 /* autibsp */') + +dnl if BTI is enabled we want the SIGN_LR to be a valid +dnl landing pad, we don't need VERIFY_LR and we need to +dnl indicate the valid BTI support for gnu notes. + + +ifelse(ARM64_FEATURE_BTI_DEFAULT, `1', + `define(`BTI_C', `hint #34 /* bti c */') + define(`SIGN_LR', `BTI_C') + define(`GNU_PROPERTY_AARCH64_BTI', `1') + define(`PAC_OR_BTI')', ` + define(`BTI_C', `') + define(`GNU_PROPERTY_AARCH64_BTI', `0')' +') + +dnl define instructions for PAC, which can use the A +dnl or the B key. PAC instructions are also valid BTI +dnl landing pads, so we re-define SIGN_LR if BTI is +dnl enabled. + + +ifelse(ARM64_FEATURE_PAC_DEFAULT, `1', + `define(`SIGN_LR', `PACIASP') + define(`VERIFY_LR', `AUTIASP') + define(`GNU_PROPERTY_AARCH64_POINTER_AUTH', `2') + define(`PAC_OR_BTI')', + ARM64_FEATURE_PAC_DEFAULT, `2', + `define(`SIGN_LR', `PACIBSP') + define(`VERIFY_LR', `AUTIBSP') + define(`GNU_PROPERTY_AARCH64_POINTER_AUTH', `2') + define(`PAC_OR_BTI')', + `ifdef(`SIGN_LR', , `define(`SIGN_LR', `')') + define(`VERIFY_LR', `') + define(`GNU_PROPERTY_AARCH64_POINTER_AUTH', `0')' +') + +dnl NOTE OVERRIDES asm-defs.m4 definition for arch specific functionality +dnl +dnl Usage: PROLOGUE_cpu(GSYM_PREFIX`'foo[,param]) +dnl EPILOGUE_cpu(GSYM_PREFIX`'foo) +dnl +dnl These macros hold the CPU-specific parts of PROLOGUE and is called +dnl with the function name, with GSYM_PREFIX already prepended. +dnl +dnl By default, it marks entry points with a bti c instruction unless +dnl the second argument is true and it marks it using SIGN_LR which expands +dnl to the proper paci instruction OR bti c instruction depending on +dnl compilation flags. In the case of an instruction that uses paci, this +dnl provides a one instruction advantage over having a bti c followed by +dnl a paci instruction. + +define(`PROLOGUE_cpu', +m4_assert_numargs_range(1,2) +` TEXT + ALIGN(8) + GLOBL `$1' GLOBL_ATTR + TYPE(`$1',`function') +`$1'LABEL_SUFFIX + ifelse(`$2',`true', + `SIGN_LR', + `BTI_C') +') + +dnl ADD_GNU_NOTES_IF_NEEDED +dnl +dnl Conditionally add into ELF assembly files the GNU notes indicating if +dnl BTI or PAC is support. BTI is required by the linkers and loaders, however +dnl PAC is a nice to have for auditing. Use readelf -n to display. + + +define(`ADD_GNU_NOTES_IF_NEEDED', ` + ifdef(`ARM64_ELF', ` + ifdef(`PAC_OR_BTI', ` + .pushsection .note.gnu.property, "a"; + .balign 8; + .long 4; + .long 0x10; + .long 0x5; + .asciz "GNU"; + .long 0xc0000000; /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ + .long 4; + .long eval(indir(`GNU_PROPERTY_AARCH64_POINTER_AUTH') + indir(`GNU_PROPERTY_AARCH64_BTI')); + .long 0; + .popsection; + ') + ') +') dnl LEA_HI(reg,gmp_symbol), LEA_LO(reg,gmp_symbol) dnl diff --git a/mpn/arm64/bdiv_dbm1c.asm b/mpn/arm64/bdiv_dbm1c.asm index 78984b426..fb35cfdd5 100644 --- a/mpn/arm64/bdiv_dbm1c.asm +++ b/mpn/arm64/bdiv_dbm1c.asm @@ -109,3 +109,4 @@ L(wd1): subs x4, x4, x12 sbc x0, x4, x13 ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/bdiv_q_1.asm b/mpn/arm64/bdiv_q_1.asm index 7fffc9369..976226ae1 100644 --- a/mpn/arm64/bdiv_q_1.asm +++ b/mpn/arm64/bdiv_q_1.asm @@ -120,3 +120,4 @@ L(tpn): ldr x9, [up],#8 L(en1): ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/cnd_aors_n.asm b/mpn/arm64/cnd_aors_n.asm index 397aa5100..7193e9fd6 100644 --- a/mpn/arm64/cnd_aors_n.asm +++ b/mpn/arm64/cnd_aors_n.asm @@ -127,3 +127,4 @@ L(end): bic x6, x12, cnd L(rt): RETVAL ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/com.asm b/mpn/arm64/com.asm index d59494380..0cc48b455 100644 --- a/mpn/arm64/com.asm +++ b/mpn/arm64/com.asm @@ -90,3 +90,4 @@ L(tl1): tbz n, #0, L(tl2) str x4, [rp] L(tl2): ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/copyd.asm b/mpn/arm64/copyd.asm index d542970b7..aee84fc7f 100644 --- a/mpn/arm64/copyd.asm +++ b/mpn/arm64/copyd.asm @@ -83,3 +83,4 @@ L(tl1): tbz n, #0, L(tl2) str x4, [rp,#-8] L(tl2): ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/copyi.asm b/mpn/arm64/copyi.asm index 0de40c5d7..edf937f5f 100644 --- a/mpn/arm64/copyi.asm +++ b/mpn/arm64/copyi.asm @@ -80,3 +80,4 @@ L(tl1): tbz n, #0, L(tl2) str x4, [rp] L(tl2): ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/divrem_1.asm b/mpn/arm64/divrem_1.asm index 9d5bb5959..9cb382cf1 100644 --- a/mpn/arm64/divrem_1.asm +++ b/mpn/arm64/divrem_1.asm @@ -65,7 +65,7 @@ dnl mp_limb_t d_unnorm, mp_limb_t dinv, int cnt) ASM_START() -PROLOGUE(mpn_preinv_divrem_1) +PROLOGUE(mpn_preinv_divrem_1, true) cbz n_arg, L(fz) stp x29, x30, [sp, #-80]! mov x29, sp @@ -85,7 +85,7 @@ PROLOGUE(mpn_preinv_divrem_1) b L(uentry) EPILOGUE() -PROLOGUE(mpn_divrem_1) +PROLOGUE(mpn_divrem_1, true) cbz n_arg, L(fz) stp x29, x30, [sp, #-80]! mov x29, sp @@ -154,6 +154,7 @@ L(uend):add x2, x11, #1 ldp x21, x22, [sp, #32] ldp x23, x24, [sp, #48] ldp x29, x30, [sp], #80 + VERIFY_LR ret L(ufx): add x2, x2, #1 @@ -194,6 +195,7 @@ L(nend):cbnz fn, L(frac) ldp x21, x22, [sp, #32] ldp x23, x24, [sp, #48] ldp x29, x30, [sp], #80 + VERIFY_LR ret L(nfx): add x2, x2, #1 @@ -219,6 +221,7 @@ L(ftop):add x2, x11, #1 ldp x21, x22, [sp, #32] ldp x23, x24, [sp, #48] ldp x29, x30, [sp], #80 + VERIFY_LR ret C Block zero. We need this for the degenerated case of n = 0, fn != 0. @@ -227,5 +230,7 @@ L(ztop):str xzr, [qp_arg], #8 sub fn_arg, fn_arg, #1 cbnz fn_arg, L(ztop) L(zend):mov x0, #0 + VERIFY_LR ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/gcd_11.asm b/mpn/arm64/gcd_11.asm index d8cc3e2cf..9e80d58e1 100644 --- a/mpn/arm64/gcd_11.asm +++ b/mpn/arm64/gcd_11.asm @@ -68,3 +68,4 @@ L(top): rbit x12, x3 C 1,5 L(end): ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/gcd_22.asm b/mpn/arm64/gcd_22.asm index 5367fea02..e3379cf35 100644 --- a/mpn/arm64/gcd_22.asm +++ b/mpn/arm64/gcd_22.asm @@ -110,3 +110,4 @@ L(end): mov x0, v0 mov x1, v1 ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/hamdist.asm b/mpn/arm64/hamdist.asm index c72ca55b3..cec16f512 100644 --- a/mpn/arm64/hamdist.asm +++ b/mpn/arm64/hamdist.asm @@ -179,3 +179,4 @@ L(gt8k): mov x30, x8 ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/invert_limb.asm b/mpn/arm64/invert_limb.asm index 6a99bf002..38e0521a5 100644 --- a/mpn/arm64/invert_limb.asm +++ b/mpn/arm64/invert_limb.asm @@ -81,3 +81,4 @@ approx_tab: forloop(i,256,512-1,dnl ` .hword eval(0x7fd00/i) ')dnl +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/logops_n.asm b/mpn/arm64/logops_n.asm index e959abc71..adf3ac286 100644 --- a/mpn/arm64/logops_n.asm +++ b/mpn/arm64/logops_n.asm @@ -137,3 +137,4 @@ L(end): LOGOP( x12, x6, x10) stp x12, x13, [rp] L(ret): ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/lshift.asm b/mpn/arm64/lshift.asm index fe8a1aa18..0aa16a445 100644 --- a/mpn/arm64/lshift.asm +++ b/mpn/arm64/lshift.asm @@ -136,3 +136,4 @@ L(end): orr x10, x10, x13 str x2, [rp,#-24] ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/lshiftc.asm b/mpn/arm64/lshiftc.asm index 6bf584400..9cd94e700 100644 --- a/mpn/arm64/lshiftc.asm +++ b/mpn/arm64/lshiftc.asm @@ -139,3 +139,4 @@ L(end): eon x10, x10, x13 str x2, [rp,#-24] ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/mod_34lsub1.asm b/mpn/arm64/mod_34lsub1.asm index 7945fe72c..779e82b84 100644 --- a/mpn/arm64/mod_34lsub1.asm +++ b/mpn/arm64/mod_34lsub1.asm @@ -122,3 +122,4 @@ L(1): ldr x2, [ap] add x0, x0, x2, lsr #48 ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/mul_1.asm b/mpn/arm64/mul_1.asm index fb965efff..53fcc3d96 100644 --- a/mpn/arm64/mul_1.asm +++ b/mpn/arm64/mul_1.asm @@ -126,3 +126,4 @@ L(2e): adcs x12, x8, x11 L(1): adc x0, x11, xzr ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/popcount.asm b/mpn/arm64/popcount.asm index 74de3fc01..68a993dfa 100644 --- a/mpn/arm64/popcount.asm +++ b/mpn/arm64/popcount.asm @@ -155,3 +155,4 @@ L(gt8k): mov x30, x8 ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/rsh1aors_n.asm b/mpn/arm64/rsh1aors_n.asm index afd3d5be4..80ae914ce 100644 --- a/mpn/arm64/rsh1aors_n.asm +++ b/mpn/arm64/rsh1aors_n.asm @@ -166,3 +166,4 @@ L(2): cset x14, COND L(ret): mov x0, x10 ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/rshift.asm b/mpn/arm64/rshift.asm index 90187ad51..ba0bc7734 100644 --- a/mpn/arm64/rshift.asm +++ b/mpn/arm64/rshift.asm @@ -134,3 +134,4 @@ L(end): orr x10, x10, x13 str x2, [rp,#32] ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/sec_tabselect.asm b/mpn/arm64/sec_tabselect.asm index 18a268ace..901e17526 100644 --- a/mpn/arm64/sec_tabselect.asm +++ b/mpn/arm64/sec_tabselect.asm @@ -120,3 +120,4 @@ L(tp1): cmeq maskq.2d, v5.2d, v7.2d L(b00): ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/mpn/arm64/sqr_diag_addlsh1.asm b/mpn/arm64/sqr_diag_addlsh1.asm index 39f1cb1bc..1ea46716e 100644 --- a/mpn/arm64/sqr_diag_addlsh1.asm +++ b/mpn/arm64/sqr_diag_addlsh1.asm @@ -100,3 +100,4 @@ L(end): extr x9, x6, x5, #63 ret EPILOGUE() +ADD_GNU_NOTES_IF_NEEDED diff --git a/tests/mpn/Makefile.am b/tests/mpn/Makefile.am index 0e979a3ad..16d4d2dc6 100644 --- a/tests/mpn/Makefile.am +++ b/tests/mpn/Makefile.am @@ -22,19 +22,36 @@ AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/tests AM_LDFLAGS = -no-install LDADD = $(top_builddir)/tests/libtests.la $(top_builddir)/libgmp.la -check_PROGRAMS = t-asmtype t-aors_1 t-divrem_1 t-mod_1 t-fat t-get_d \ - t-instrument t-iord_u t-mp_bases t-perfsqr t-scan logic \ - t-toom22 t-toom32 t-toom33 t-toom42 t-toom43 t-toom44 \ - t-toom52 t-toom53 t-toom54 t-toom62 t-toom63 t-toom6h t-toom8h \ - t-toom2-sqr t-toom3-sqr t-toom4-sqr t-toom6-sqr t-toom8-sqr \ - t-div t-mul t-mullo t-sqrlo t-mulmod_bnm1 t-sqrmod_bnm1 t-mulmid \ - t-mulmod_bknp1 t-sqrmod_bknp1 \ - t-addaddmul t-hgcd t-hgcd_appr t-matrix22 t-invert t-bdiv t-fib2m \ - t-broot t-brootinv t-minvert t-sizeinbase t-gcd_11 t-gcd_22 t-gcdext_1 - -EXTRA_DIST = toom-shared.h toom-sqr-shared.h - -TESTS = $(check_PROGRAMS) +TEST_EXTENSIONS = .sh +AM_SH_LOG_FLAGS = --enable-pac=@ARM64_FEATURE_PAC_DEFAULT@ \ + --enable-bti=@ARM64_FEATURE_BTI_DEFAULT@ \ + $(top_builddir)/.libs/libgmp.so +SH_LOG_COMPILER = $(srcdir)/log-compiler.sh + +check_PROGRAMS = t-asmtype t-aors_1 t-divrem_1 t-mod_1 t-fat t-get_d \ + t-instrument t-iord_u t-mp_bases t-perfsqr t-scan logic \ + t-toom22 t-toom32 t-toom33 t-toom42 t-toom43 t-toom44 \ + t-toom52 t-toom53 t-toom54 t-toom62 t-toom63 t-toom6h t-toom8h \ + t-toom2-sqr t-toom3-sqr t-toom4-sqr t-toom6-sqr t-toom8-sqr \ + t-div t-mul t-mullo t-sqrlo t-mulmod_bnm1 t-sqrmod_bnm1 t-mulmid \ + t-mulmod_bknp1 t-sqrmod_bknp1 \ + t-addaddmul t-hgcd t-hgcd_appr t-matrix22 t-invert t-bdiv t-fib2m \ + t-broot t-brootinv t-minvert t-sizeinbase t-gcd_11 t-gcd_22 t-gcdext_1 \ + t-arm64_bti + +test_scripts = +if HAVE_BASH +if HAVE_READELF + test_scripts += t-arm64_elf_check.sh +endif +endif +check_SCRIPTS = $(test_scripts) + +EXTRA_DIST = toom-shared.h toom-sqr-shared.h t-arm64_elf_check.sh + +TESTS = $(check_PROGRAMS) $(check_SCRIPTS) + +XFAIL_TESTS = t-arm64_bti $(top_builddir)/tests/libtests.la: cd $(top_builddir)/tests; $(MAKE) $(AM_MAKEFLAGS) libtests.la diff --git a/tests/mpn/log-compiler.sh b/tests/mpn/log-compiler.sh new file mode 100755 index 000000000..092b21b33 --- /dev/null +++ b/tests/mpn/log-compiler.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +echo "Log Compiler: $@" + +# Flip <options> command to command <options> by swaping the +# first and last elements of the argv array +# Convert "$@" to an array for easy manipulation +args=("$@") + +# Get the indices for the first and last elements +first=0 +last=$((${#args[@]} - 1)) + +# Swap the first and last elements +temp="${args[$first]}" +args[$first]="${args[$last]}" +args[$last]="$temp" + +# Run the script +./${args[@]} +exit $? diff --git a/tests/mpn/t-arm64_bti.c b/tests/mpn/t-arm64_bti.c new file mode 100644 index 000000000..6c36da2d5 --- /dev/null +++ b/tests/mpn/t-arm64_bti.c @@ -0,0 +1,86 @@ +/* +Copyright 2024 Free Software Foundation, Inc. + +This file is part of the GNU MP Library test suite. + +The GNU MP Library test suite 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 of the License, +or (at your option) any later version. + +The GNU MP Library test suite 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 +the GNU MP Library test suite. If not, see https://www.gnu.org/licenses/. */ + +/* + * Test if if BTI is working within the GMP assembly stubs for AArch64 aka arm64 + * within GMP. This test gets a function pointer to mpn_lshift avoiding the PLT + * using dlsym and calls the function and checks for a valid return. It then + * advances the function pointer by 2, which points us to the next instruction, + * and calls. The following scenarios are possible: + * | Binary BTI Enabled | Hardware BTI Enabled | Executable Outcome | Test Outcome | + * | 0 | 0 | Works returning 77 | SKIP | + * | 0 | 1 | Works returning 77 | SKIP | + * | 1 | 0 | Works returning 77 | SKIP | + * | 1 | 1 | BTI Exception | PASS | + * Note: 77 is the magic value for autotools to indicate to skip a test. + * Note: You MUST run this test when enabled on a BTI enabled hardware setup. + * Note: That for non-aarch64 platforms, this also just skips. + */ + +#define SKIP 77 + +/* AArch64 BTI Binary enabled code ONLY */ +#ifdef __ARM_FEATURE_BTI_DEFAULT + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dlfcn.h> +#include <sys/auxv.h> +#include <asm/hwcap.h> + +#include "gmp-impl.h" +#include "tests.h" + +typedef mp_limb_t (*fn_mpn_lshift)(mp_ptr, mp_srcptr, mp_size_t, unsigned int); + +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; + } + + 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; + } + + /* should work as this will land on a BTI landing pad as expected */ + fn (&wp, &xp, (mp_size_t) 1, 1); + ASSERT_ALWAYS (wp == 0x2002); + + /* this should fail as it's off 1 instruction */ + fn = (fn_mpn_lshift)((uintptr_t)fn + 4); + fn(&wp, &xp, (mp_size_t) 1, 1); + fprintf(stderr, "This should cause an exception, does your system support BTI?\n"); + return 0; +} +#else +/* No binary support for BTI or another arch, just skips */ +int +main (int argc, char **argv) { + return SKIP; +} +#endif diff --git a/tests/mpn/t-arm64_elf_check.sh b/tests/mpn/t-arm64_elf_check.sh new file mode 100755 index 000000000..f3a8e7e6a --- /dev/null +++ b/tests/mpn/t-arm64_elf_check.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash + +set -e -o pipefail + +check_val() { + + local grep_flags="-qi" + local not_msg="" + # invertt he grep match if it SHOULDN'T be found int he flags. + # ie BTI 0 means BTI should not be int he notes. + if [ "${2}" -eq 0 ]; then + grep_flags+="v" + not_msg="Not " + fi + + printf 'Checking for %s in "%s". Expecting "%sPresent", ' "${1}" "${ELF_BINARY}" "${not_msg}" + + set +e + readelf -n "${ELF_BINARY}" | grep $grep_flags -- "${1}" + local r="${?}" + set -e + # Possible states we care about, which grep will fail under: + # - State 1: Not expecting and Found + # - State 2: Expecting and not Found + if [[ "${r}" -ne 0 ]]; then + # Flipt he not message + if [ -z "${not_msg}" ]; then + not_msg="Not " + else + not_msg="" + fi + fi + + # print found or not found + printf 'got "%sPresent."\n' "${not_msg}" + + # The grep result means we returnt he rct hrought he named variable + #t his way consumers can just add allt he valuest o determine if its + # a failure. + eval "${1}=\"${r}\"" +} + +# Initialize variables +BTI="0" +PAC="0" +ELF_BINARY="" + +# Loopt hrought he arguments +while [[ "${#}" -gt 0 ]]; do + case "${1}" in + --enable-bti=*) + BTI="${1#*=}" + shift + ;; + --enable-pac=*) + PAC="${1#*=}" + shift + ;; + --enable-bti | --enable-pac) + # Ift he argument is int he form --enable-bti value (without =) + printf 'Error: Option %s requires a value, like --enable-bti=value' "${1}" + exit 1 + ;; + *) + # Handlet he non-option argument + if [[ -z "${ELF_BINARY}" ]]; then + ELF_BINARY="${1}" + else + printf 'Error: Moret han one non-option argument provided: %s\n' "${1}" + exit 1 + fi + shift + ;; + esac +done + +if [ -z "${ELF_BINARY}" ]; then + printf "Must specifyt he ELF binary ast he ONLY script argument" + exit 1 +fi + +# Skip if nothing is enabled, 77 is automake magic for SKIPt hist est. +# For non-supporting architectures and ABIs both oft hese will be 0 +# andt hus skip. +if [[ "${BTI}" -eq 0 && "${PAC}" -eq 0 ]]; then + printf "PAC and BTI disabled...skipping\n" + exit 77 +fi + +check_val "BTI" "${BTI}" +check_val "PAC" "${PAC}" + +# don't use expr as it returns non-zero whent he addition result it non-zero +# and causest he set -e scriptt o fail. +rc=$((BTI + PAC)) +exit ${rc} -- 2.46.0 _______________________________________________ gmp-devel mailing list gmp-devel@gmplib.org https://gmplib.org/mailman/listinfo/gmp-devel