@@ -323,7 +323,7 @@ arc*-*-*)
arm*-*-*)
cpu_type=arm
extra_objs="arm-builtins.o aarch-common.o"
- extra_headers="mmintrin.h arm_neon.h arm_acle.h"
+ extra_headers="mmintrin.h arm_neon.h arm_acle.h arm_cmse.h"
target_type_format_char='%'
c_target_objs="arm-c.o"
cxx_target_objs="arm-c.o"
@@ -63,11 +63,11 @@ ARM_ARCH("armv8.1-a+crc",cortexa53, 8A,
ARM_FSET_MAKE (FL_CO_PROC | FL_CRC32 | FL_FOR_ARCH8A,
FL2_FOR_ARCH8_1A))
ARM_ARCH("armv8-m.base", cortexm0, 8M_BASE,
- ARM_FSET_MAKE_CPU1 ( FL_FOR_ARCH8M_BASE))
+ ARM_FSET_MAKE ( FL_FOR_ARCH8M_BASE, FL2_CMSE))
ARM_ARCH("armv8-m.main", cortexm7, 8M_MAIN,
- ARM_FSET_MAKE_CPU1(FL_CO_PROC | FL_FOR_ARCH8M_MAIN))
+ ARM_FSET_MAKE (FL_CO_PROC | FL_FOR_ARCH8M_MAIN, FL2_CMSE))
ARM_ARCH("armv8-m.main+dsp", cortexm7, 8M_MAIN,
- ARM_FSET_MAKE_CPU1(FL_CO_PROC | FL_ARCH7EM | FL_FOR_ARCH8M_MAIN))
+ ARM_FSET_MAKE (FL_CO_PROC | FL_ARCH7EM | FL_FOR_ARCH8M_MAIN, FL2_CMSE))
ARM_ARCH("iwmmxt", iwmmxt, 5TE, ARM_FSET_MAKE_CPU1 (FL_LDSCHED | FL_STRONG | FL_FOR_ARCH5TE | FL_XSCALE | FL_IWMMXT))
ARM_ARCH("iwmmxt2", iwmmxt2, 5TE, ARM_FSET_MAKE_CPU1 (FL_LDSCHED | FL_STRONG | FL_FOR_ARCH5TE | FL_XSCALE | FL_IWMMXT | FL_IWMMXT2))
@@ -76,6 +76,14 @@ arm_cpu_builtins (struct cpp_reader* pfile)
def_or_undef_macro (pfile, "__ARM_32BIT_STATE", TARGET_32BIT);
+ if (arm_arch8 && !arm_arch_notm)
+ {
+ if (arm_arch_cmse && use_cmse)
+ builtin_define_with_int_value ("__ARM_FEATURE_CMSE", 3);
+ else
+ builtin_define ("__ARM_FEATURE_CMSE");
+ }
+
if (TARGET_ARM_FEATURE_LDREX)
builtin_define_with_int_value ("__ARM_FEATURE_LDREX",
TARGET_ARM_FEATURE_LDREX);
@@ -388,6 +388,7 @@ extern bool arm_is_constant_pool_ref (rtx);
#define FL_ARCH6KZ (1 << 31) /* ARMv6KZ architecture. */
#define FL2_ARCH8_1 (1 << 0) /* Architecture 8.1. */
+#define FL2_CMSE (1 << 1) /* ARMv8-M Security Extensions. */
/* Flags that only effect tuning, not available instructions. */
#define FL_TUNE (FL_WBUF | FL_VFPV2 | FL_STRONG | FL_LDSCHED \
@@ -506,6 +506,9 @@ extern bool arm_disable_literal_pool;
/* Nonzero if chip supports the ARMv8 CRC instructions. */
extern int arm_arch_crc;
+/* Nonzero if chip support the ARMv8-M Security Extensions. */
+extern int arm_arch_cmse;
+
#ifndef TARGET_DEFAULT
#define TARGET_DEFAULT (MASK_APCS_FRAME)
#endif
@@ -892,6 +892,9 @@ int arm_condexec_masklen = 0;
/* Nonzero if chip supports the ARMv8 CRC instructions. */
int arm_arch_crc = 0;
+/* Nonzero if chip support the ARMv8-M security extensions. */
+int arm_arch_cmse = 0;
+
/* Nonzero if the core has a very small, high-latency, multiply unit. */
int arm_m_profile_small_mul = 0;
@@ -3179,6 +3182,7 @@ arm_option_override (void)
arm_arch_no_volatile_ce = ARM_FSET_HAS_CPU1 (insn_flags, FL_NO_VOLATILE_CE);
arm_tune_cortex_a9 = (arm_tune == cortexa9) != 0;
arm_arch_crc = ARM_FSET_HAS_CPU1 (insn_flags, FL_CRC32);
+ arm_arch_cmse = ARM_FSET_HAS_CPU2 (insn_flags, FL2_CMSE);
arm_m_profile_small_mul = ARM_FSET_HAS_CPU1 (insn_flags, FL_SMALLMUL);
/* V5 code we generate is completely interworking capable, so we turn off
@@ -3431,6 +3435,9 @@ arm_option_override (void)
if (target_slow_flash_data)
arm_disable_literal_pool = true;
+ if (use_cmse && !arm_arch_cmse)
+ error ("target CPU does not support ARMv8-M Security Extensions");
+
/* Disable scheduling fusion by default if it's not armv7 processor
or doesn't prefer ldrd/strd. */
if (flag_schedule_fusion == 2
@@ -109,6 +109,10 @@ mfloat-abi=
Target RejectNegative Joined Enum(float_abi_type) Var(arm_float_abi) Init(TARGET_DEFAULT_FLOAT_ABI)
Specify if floating point hardware should be used.
+mcmse
+Target RejectNegative Var(use_cmse)
+Specify that the compiler should target secure code as per ARMv8-M Security Extensions.
+
Enum
Name(float_abi_type) Type(enum float_abi_type)
Known floating-point ABIs (for use with the -mfloat-abi= option):
new file mode 100644
@@ -0,0 +1,199 @@
+/* ARMV8-M Secure Extensions intrinsics include file.
+
+ Copyright (C) 2015 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.
+
+ Under Section 7 of GPL version 3, you are granted additional
+ permissions described in the GCC Runtime Library Exception, version
+ 3.1, as published by the Free Software Foundation.
+
+ You should have received a copy of the GNU General Public License and
+ a copy of the GCC Runtime Library Exception along with this program;
+ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+
+#ifndef _GCC_ARM_CMSE_H
+#define _GCC_ARM_CMSE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __ARM_FEATURE_CMSE & 1
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __ARM_BIG_ENDIAN
+
+typedef union {
+ struct cmse_address_info {
+#if __ARM_FEATURE_CMSE & 2
+ unsigned idau_region:8;
+ unsigned idau_region_valid:1;
+ unsigned secure:1;
+ unsigned nonsecure_readwrite_ok:1;
+ unsigned nonsecure_read_ok:1;
+#else
+ unsigned :12;
+#endif
+ unsigned readwrite_ok:1;
+ unsigned read_ok:1;
+#if __ARM_FEATURE_CMSE & 2
+ unsigned sau_region_valid:1;
+#else
+ unsigned :1;
+#endif
+ unsigned mpu_region_valid:1;
+#if __ARM_FEATURE_CMSE & 2
+ unsigned sau_region:8;
+#else
+ unsigned :8;
+#endif
+ unsigned mpu_region:8;
+ } flags;
+ unsigned value;
+} cmse_address_info_t;
+
+#else
+
+typedef union {
+ struct cmse_address_info {
+ unsigned mpu_region:8;
+#if __ARM_FEATURE_CMSE & 2
+ unsigned sau_region:8;
+#else
+ unsigned :8;
+#endif
+ unsigned mpu_region_valid:1;
+#if __ARM_FEATURE_CMSE & 2
+ unsigned sau_region_valid:1;
+#else
+ unsigned :1;
+#endif
+ unsigned read_ok:1;
+ unsigned readwrite_ok:1;
+#if __ARM_FEATURE_CMSE & 2
+ unsigned nonsecure_read_ok:1;
+ unsigned nonsecure_readwrite_ok:1;
+ unsigned secure:1;
+ unsigned idau_region_valid:1;
+ unsigned idau_region:8;
+#else
+ unsigned :12;
+#endif
+ } flags;
+ unsigned value;
+} cmse_address_info_t;
+
+#endif
+
+#define cmse_TT_fptr(p) (cmse_TT_fptr_generic ((__cmse_fptr)p))
+
+typedef void (*__cmse_fptr)(void);
+
+#define CMSE_TT_ASM(flags) \
+{ \
+ cmse_address_info_t __result; \
+ __asm__ ("tt" # flags " %0,%1" \
+ : "=r"(__result) \
+ : "r"(__p) \
+ : "memory"); \
+ return __result; \
+}
+
+__extension__ static __inline __attribute__ ((__always_inline__))
+cmse_address_info_t
+cmse_TT_fptr_generic (__cmse_fptr __p)
+CMSE_TT_ASM ()
+
+__extension__ static __inline __attribute__ ((__always_inline__))
+cmse_address_info_t
+cmse_TT (void *__p)
+CMSE_TT_ASM ()
+
+#define cmse_TTT_fptr(p) (cmse_TTT_fptr_generic ((__cmse_fptr)p))
+
+__extension__ static __inline __attribute__ ((__always_inline__))
+cmse_address_info_t
+cmse_TTT_fptr_generic (__cmse_fptr __p)
+CMSE_TT_ASM (t)
+
+__extension__ static __inline __attribute__ ((__always_inline__))
+cmse_address_info_t
+cmse_TTT (void *__p)
+CMSE_TT_ASM (t)
+
+#if __ARM_FEATURE_CMSE & 2
+
+#define cmse_TTA_fptr(p) (cmse_TTA_fptr_generic ((__cmse_fptr)p))
+
+__extension__ static __inline __attribute__ ((__always_inline__))
+cmse_address_info_t
+cmse_TTA_fptr_generic (__cmse_fptr __p)
+CMSE_TT_ASM (a)
+
+__extension__ static __inline __attribute__ ((__always_inline__))
+cmse_address_info_t
+cmse_TTA (void *__p)
+CMSE_TT_ASM (a)
+
+#define cmse_TTAT_fptr(p) (cmse_TTAT_fptr_generic ((__cmse_fptr)p))
+
+__extension__ static __inline cmse_address_info_t
+__attribute__ ((__always_inline__))
+cmse_TTAT_fptr_generic (__cmse_fptr __p)
+CMSE_TT_ASM (at)
+
+__extension__ static __inline cmse_address_info_t
+__attribute__ ((__always_inline__))
+cmse_TTAT (void *__p)
+CMSE_TT_ASM (at)
+
+//TODO: diagnose use outside cmse_nonsecure_entry functions
+__extension__ static __inline int __attribute__ ((__always_inline__))
+cmse_nonsecure_caller (void)
+{
+ return __builtin_arm_cmse_nonsecure_caller ();
+}
+
+#define CMSE_AU_NONSECURE 2
+#define CMSE_MPU_NONSECURE 16
+#define CMSE_NONSECURE 18
+
+#endif
+
+#define CMSE_MPU_UNPRIV 4
+#define CMSE_MPU_READWRITE 1
+#define CMSE_MPU_READ 8
+
+__extension__ void *
+cmse_check_address_range (void *, size_t, int);
+
+#define cmse_check_pointed_object(p, f) \
+ ((typeof (p)) cmse_check_address_range (p, sizeof (*p), f))
+
+#define cmse_nsfptr_create(p) ((typeof (p)) ((intptr_t) p & ~1))
+
+#define cmse_is_nsfptr(p) (!((intptr_t) p & 1))
+
+#endif /* ifdef __ARM_FEATURE_CMSE. */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ifndef _GCC_ARM_CMSE_H. */
@@ -12132,6 +12132,11 @@ back ends support CRC32 intrinsics from @file{arm_acle.h}. The ARM back end's
AArch64's back end does not have support for 16-bit floating point Advanced SIMD
intrinsics yet.
+As a part of ACLE, GCC implements Security Extensions for ARMv8-M (CMSE) as
+described in ARMv8-M Security Extensions: Requirements on Development Tools,
+which can be found at
+@uref{http://infocenter.arm.com/help/topic/com.arm.doc.ecm0359818/ECM0359818_armv8m_security_extensions_reqs_on_dev_tools_1_0.pdf}.
+
See @ref{ARM Options} and @ref{AArch64 Options} for more information on the
availability of extensions.
@@ -631,7 +631,8 @@ Objective-C and Objective-C++ Dialects}.
-mneon-for-64bits @gol
-mslow-flash-data @gol
-masm-syntax-unified @gol
--mrestrict-it}
+-mrestrict-it
+-mcmse}
@emph{AVR Options}
@gccoptlist{-mmcu=@var{mcu} -maccumulate-args -mbranch-cost=@var{cost} @gol
@@ -14159,6 +14160,10 @@ Print CPU tuning information as comment in assembler file. This is
an option used only for regression testing of the compiler and not
intended for ordinary use in compiling code. This option is disabled
by default.
+
+@item -mcmse
+@opindex mcmse
+Generate secure code as per ARMv8-M Security Extensions.
@end table
@node AVR Options
new file mode 100644
@@ -0,0 +1,68 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_cmse_ok } */
+/* { dg-options "-Os -mcmse -fdump-rtl-expand" } */
+
+#include <arm_cmse.h>
+
+extern int a;
+extern int bar (void);
+
+int foo (char * p)
+{
+ cmse_address_info_t cait;
+
+ cait = cmse_TT (&a);
+ if (cait.flags.mpu_region)
+ a++;
+
+ cait = cmse_TT_fptr (&bar);
+ if (cait.flags.mpu_region)
+ a+= bar ();
+
+ cait = cmse_TTA (&a);
+ if (cait.flags.mpu_region)
+ a++;
+
+ cait = cmse_TTA_fptr (&bar);
+ if (cait.flags.mpu_region)
+ a+= bar ();
+
+ cait = cmse_TTT (&a);
+ if (cait.flags.mpu_region)
+ a++;
+
+ cait = cmse_TTT_fptr (&bar);
+ if (cait.flags.mpu_region)
+ a+= bar ();
+
+ cait = cmse_TTAT (&a);
+ if (cait.flags.mpu_region)
+ a++;
+
+ cait = cmse_TTAT_fptr (&bar);
+ if (cait.flags.mpu_region)
+ a+= bar ();
+
+ p = (char *) cmse_check_address_range ((void *) p, sizeof (char), 0);
+ p = (char *) cmse_check_address_range ((void *) p, sizeof (char),
+ CMSE_MPU_UNPRIV);
+ p = (char *) cmse_check_address_range ((void *) p, sizeof (char),
+ CMSE_MPU_READWRITE);
+ p = (char *) cmse_check_address_range ((void *) p, sizeof (char),
+ CMSE_MPU_UNPRIV | CMSE_MPU_READ);
+ p = (char *) cmse_check_address_range ((void *) p, sizeof (char),
+ CMSE_AU_NONSECURE
+ | CMSE_MPU_NONSECURE);
+ p = (char *) cmse_check_address_range ((void *) p, sizeof (char),
+ CMSE_NONSECURE | CMSE_MPU_UNPRIV);
+
+ p = (char *) cmse_check_pointed_object (p, CMSE_NONSECURE | CMSE_MPU_UNPRIV);
+
+ return a;
+}
+/* { dg-final { scan-assembler-times "\ttt " 2 } } */
+/* { dg-final { scan-assembler-times "ttt " 2 } } */
+/* { dg-final { scan-assembler-times "tta " 2 } } */
+/* { dg-final { scan-assembler-times "ttat " 2 } } */
+/* { dg-final { scan-assembler-times "bl.cmse_check_address_range" 7 } } */
+/* { dg-final { scan-assembler-not "cmse_check_pointed_object" } } */
new file mode 100644
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_cmse_ok } */
+/* { dg-options "-mcmse" } */
+#include <arm_cmse.h>
+
+char *
+foo (char * p)
+{
+ if (!cmse_is_nsfptr (p))
+ return cmse_nsfptr_create (p);
+}
+
+/* Checks for saving and clearing prior to function call. */
+/* { dg-final { scan-assembler-not "cmse_is_nsfptr" } } */
+/* { dg-final { scan-assembler-not "cmse_nsfptr_create" } } */
new file mode 100644
@@ -0,0 +1,45 @@
+# Copyright (C) 1997-2015 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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/>.
+
+# GCC testsuite for ARMv8-M Security Extensions using the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+ set DEFAULT_CFLAGS " -ansi -pedantic-errors"
+}
+
+# Initialize `dg'.
+dg-init
+
+set saved-dg-do-what-default ${dg-do-what-default}
+set dg-do-what-default "assemble"
+
+set saved-lto_torture_options ${LTO_TORTURE_OPTIONS}
+set LTO_TORTURE_OPTIONS ""
+
+# These are for both baseline and mainline.
+gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.c]] \
+ "" $DEFAULT_CFLAGS
+
+set LTO_TORTURE_OPTIONS ${saved-lto_torture_options}
+set dg-do-what-default ${saved-dg-do-what-default}
+
+# All done.
+dg-finish
@@ -3312,6 +3312,19 @@ proc check_effective_target_arm_thumb1_movt_ko {} {
}
}
+# Return 1 if this is an ARM target where ARMv8-M security extension is
+# available.
+
+proc check_effective_target_arm_cmse_ok {} {
+ return [check_no_compiler_messages arm_cmse object {
+ int
+ foo (void)
+ {
+ asm ("movt r0, #42");
+ }
+ } "-mcmse"];
+}
+
# Return 1 if this compilation turns on string_ops_prefer_neon on.
proc check_effective_target_arm_tune_string_ops_prefer_neon { } {
new file mode 100644
@@ -0,0 +1,110 @@
+/* ARMv8-M Security Extensions routines.
+ Copyright (C) 2015 Free Software Foundation, Inc.
+ Contributed by ARM Ltd.
+
+ This file 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.
+
+ This file 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.
+
+ Under Section 7 of GPL version 3, you are granted additional
+ permissions described in the GCC Runtime Library Exception, version
+ 3.1, as published by the Free Software Foundation.
+
+ You should have received a copy of the GNU General Public License and
+ a copy of the GCC Runtime Library Exception along with this program;
+ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+
+#if __ARM_FEATURE_CMSE & 1
+
+#include <arm_cmse.h>
+#include <stdint.h>
+
+
+/* ARM intrinsic function to perform a permission check on a given
+ address range. See ACLE changes for ARMv8-M. */
+
+void *
+cmse_check_address_range (void *p, size_t size, int flags)
+{
+ cmse_address_info_t permb, perme;
+ char *pb = (char *) p, *pe;
+
+ /* Check if the range wraps around. */
+ if (UINTPTR_MAX - (uintptr_t) p < size)
+ return NULL;
+
+ /* Check if an unknown flag is present. */
+ int known = CMSE_MPU_UNPRIV | CMSE_MPU_READWRITE | CMSE_MPU_READ;
+ int known_secure_level = CMSE_MPU_UNPRIV;
+#if __ARM_FEATURE_CMSE & 2
+ known |= CMSE_AU_NONSECURE | CMSE_MPU_NONSECURE;
+ known_secure_level |= CMSE_MPU_NONSECURE;
+#endif
+ if (flags & (~known))
+ return NULL;
+
+ /* Execute the right variant of the TT instructions. */
+ pe = pb + size - 1;
+ const int singleCheck = (((uintptr_t) pb ^ (uintptr_t) pe) < 32);
+ switch (flags & known_secure_level)
+ {
+ case 0:
+ permb = cmse_TT (pb);
+ perme = singleCheck ? permb : cmse_TT (pe);
+ break;
+ case CMSE_MPU_UNPRIV:
+ permb = cmse_TTT (pb);
+ perme = singleCheck ? permb : cmse_TTT (pe);
+ break;
+#if __ARM_FEATURE_CMSE & 2
+ case CMSE_MPU_NONSECURE:
+ permb = cmse_TTA (pb);
+ perme = singleCheck ? permb : cmse_TTA (pe);
+ break;
+ case CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE:
+ permb = cmse_TTAT (pb);
+ perme = singleCheck ? permb : cmse_TTAT (pe);
+ break;
+#endif
+ default:
+ /* Invalid flag, eg. CMSE_MPU_NONSECURE specified but
+ __ARM_FEATURE_CMSE & 2 == 0. */
+ return NULL;
+ }
+
+ /* Check that the range does not cross MPU, SAU, or IDAU boundaries. */
+ if (permb.value != perme.value)
+ return NULL;
+
+ /* Check the permissions on the range. */
+ switch (flags & (~known_secure_level))
+ {
+#if __ARM_FEATURE_CMSE & 2
+ case CMSE_MPU_READ | CMSE_MPU_READWRITE | CMSE_AU_NONSECURE:
+ case CMSE_MPU_READWRITE | CMSE_AU_NONSECURE:
+ return permb.flags.nonsecure_readwrite_ok ? p : NULL;
+ case CMSE_MPU_READ | CMSE_AU_NONSECURE:
+ return permb.flags.nonsecure_read_ok ? p : NULL;
+ case CMSE_AU_NONSECURE:
+ return permb.flags.secure ? NULL : p;
+#endif
+ case CMSE_MPU_READ | CMSE_MPU_READWRITE:
+ case CMSE_MPU_READWRITE:
+ return permb.flags.readwrite_ok ? p : NULL;
+ case CMSE_MPU_READ:
+ return permb.flags.read_ok ? p : NULL;
+ default:
+ return NULL;
+ }
+}
+
+
+#endif /* __ARM_FEATURE_CMSE & 1. */
@@ -1,3 +1,15 @@
LIB1ASMSRC = arm/lib1funcs.S
LIB1ASMFUNCS = _thumb1_case_sqi _thumb1_case_uqi _thumb1_case_shi \
_thumb1_case_uhi _thumb1_case_si
+
+HAVE_CMSE:=$(findstring __ARM_FEATURE_CMSE,$(shell $(gcc_compile_bare) -dM -E - </dev/null))
+ifneq ($(shell $(gcc_compile_bare) -E -mcmse - </dev/null 2>/dev/null),)
+CMSE_OPTS:=-mcmse
+endif
+
+ifdef HAVE_CMSE
+libgcc-objects += cmse.o cmse_nonsecure_call.o
+
+cmse.o: $(srcdir)/config/arm/cmse.c
+ $(gcc_compile) -c $(CMSE_OPTS) $<
+endif