Skip to content

Commit ce9818f

Browse files
committed
Add a new ABI to support cmse_nonsecure_call
This commit adds a new ABI to be selected via `extern "C-cmse-nonsecure-call"` on function pointers in order for the compiler to apply the corresponding cmse_nonsecure_call callsite attribute. For Armv8-M targets supporting TrustZone-M, this will perform a non-secure function call by saving, clearing and calling a non-secure function pointer using the BLXNS instruction. See the page on the unstable book for details. Signed-off-by: Hugues de Valon <hugues.devalon@arm.com>
1 parent d60b29d commit ce9818f

File tree

35 files changed

+251
-3
lines changed

35 files changed

+251
-3
lines changed

compiler/rustc_ast_passes/src/feature_gate.rs

+8
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,14 @@ impl<'a> PostExpansionVisitor<'a> {
156156
"efiapi ABI is experimental and subject to change"
157157
);
158158
}
159+
"C-cmse-nonsecure-call" => {
160+
gate_feature_post!(
161+
&self,
162+
abi_c_cmse_nonsecure_call,
163+
span,
164+
"C-cmse-nonsecure-call ABI is experimental and subject to change"
165+
);
166+
}
159167
abi => self
160168
.sess
161169
.parse_sess

compiler/rustc_codegen_cranelift/src/abi/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ fn clif_sig_from_fn_abi<'tcx>(
2828
Conv::X86_64SysV => CallConv::SystemV,
2929
Conv::X86_64Win64 => CallConv::WindowsFastcall,
3030
Conv::ArmAapcs
31+
| Conv::CCmseNonSecureCall
3132
| Conv::Msp430Intr
3233
| Conv::PtxKernel
3334
| Conv::X86Fastcall

compiler/rustc_codegen_llvm/src/abi.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
389389

390390
fn llvm_cconv(&self) -> llvm::CallConv {
391391
match self.conv {
392-
Conv::C | Conv::Rust => llvm::CCallConv,
392+
Conv::C | Conv::Rust | Conv::CCmseNonSecureCall => llvm::CCallConv,
393393
Conv::AmdGpuKernel => llvm::AmdGpuKernel,
394394
Conv::AvrInterrupt => llvm::AvrInterrupt,
395395
Conv::AvrNonBlockingInterrupt => llvm::AvrNonBlockingInterrupt,
@@ -546,6 +546,18 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
546546
if cconv != llvm::CCallConv {
547547
llvm::SetInstructionCallConv(callsite, cconv);
548548
}
549+
550+
if self.conv == Conv::CCmseNonSecureCall {
551+
// This will probably get ignored on all targets but those supporting the TrustZone-M
552+
// extension (thumbv8m targets).
553+
unsafe {
554+
llvm::AddCallSiteAttrString(
555+
callsite,
556+
llvm::AttributePlace::Function,
557+
rustc_data_structures::const_cstr!("cmse_nonsecure_call"),
558+
);
559+
}
560+
}
549561
}
550562
}
551563

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,7 @@ extern "C" {
11001100
// Operations on call sites
11011101
pub fn LLVMSetInstructionCallConv(Instr: &Value, CC: c_uint);
11021102
pub fn LLVMRustAddCallSiteAttribute(Instr: &Value, index: c_uint, attr: Attribute);
1103+
pub fn LLVMRustAddCallSiteAttrString(Instr: &Value, index: c_uint, Name: *const c_char);
11031104
pub fn LLVMRustAddAlignmentCallSiteAttr(Instr: &Value, index: c_uint, bytes: u32);
11041105
pub fn LLVMRustAddDereferenceableCallSiteAttr(Instr: &Value, index: c_uint, bytes: u64);
11051106
pub fn LLVMRustAddDereferenceableOrNullCallSiteAttr(Instr: &Value, index: c_uint, bytes: u64);

compiler/rustc_codegen_llvm/src/llvm/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ pub fn AddFunctionAttrString(llfn: &'a Value, idx: AttributePlace, attr: &CStr)
4343
}
4444
}
4545

46+
pub fn AddCallSiteAttrString(callsite: &Value, idx: AttributePlace, attr: &CStr) {
47+
unsafe { LLVMRustAddCallSiteAttrString(callsite, idx.as_uint(), attr.as_ptr()) }
48+
}
49+
4650
#[derive(Copy, Clone)]
4751
pub enum AttributePlace {
4852
ReturnValue,

compiler/rustc_error_codes/src/error_codes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ E0777: include_str!("./error_codes/E0777.md"),
465465
E0778: include_str!("./error_codes/E0778.md"),
466466
E0779: include_str!("./error_codes/E0779.md"),
467467
E0780: include_str!("./error_codes/E0780.md"),
468+
E0781: include_str!("./error_codes/E0781.md"),
468469
;
469470
// E0006, // merged with E0005
470471
// E0008, // cannot bind by-move into a pattern guard
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
The `C-cmse-nonsecure-call` ABI can only be used with function pointers.
2+
3+
Erroneous code example:
4+
5+
```compile_fail,E0781
6+
#![feature(abi_c_cmse_nonsecure_call)]
7+
8+
pub extern "C-cmse-nonsecure-call" fn test() {}
9+
```
10+
11+
The `C-cmse-nonsecure-call` ABI should be used by casting function pointers to
12+
specific addresses.

compiler/rustc_feature/src/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ declare_features! (
628628

629629
/// Allows using `pointer` and `reference` in intra-doc links
630630
(active, intra_doc_pointers, "1.51.0", Some(80896), None),
631+
632+
/// Allows `extern "C-cmse-nonsecure-call" fn()`.
633+
(active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None),
631634
// -------------------------------------------------------------------------
632635
// feature-group-end: actual feature gates
633636
// -------------------------------------------------------------------------

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,14 @@ extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index,
216216
Call->addAttribute(Index, Attr);
217217
}
218218

219+
extern "C" void LLVMRustAddCallSiteAttrString(LLVMValueRef Instr, unsigned Index,
220+
const char *Name) {
221+
CallBase *Call = unwrap<CallBase>(Instr);
222+
Attribute Attr = Attribute::get(Call->getContext(), Name);
223+
Call->addAttribute(Index, Attr);
224+
}
225+
226+
219227
extern "C" void LLVMRustAddAlignmentCallSiteAttr(LLVMValueRef Instr,
220228
unsigned Index,
221229
uint32_t Bytes) {

compiler/rustc_middle/src/ty/layout.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2650,6 +2650,7 @@ where
26502650
Win64 => Conv::X86_64Win64,
26512651
SysV64 => Conv::X86_64SysV,
26522652
Aapcs => Conv::ArmAapcs,
2653+
CCmseNonSecureCall => Conv::CCmseNonSecureCall,
26532654
PtxKernel => Conv::PtxKernel,
26542655
Msp430Interrupt => Conv::Msp430Intr,
26552656
X86Interrupt => Conv::X86Intr,

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ symbols! {
218218
abi,
219219
abi_amdgpu_kernel,
220220
abi_avr_interrupt,
221+
abi_c_cmse_nonsecure_call,
221222
abi_efiapi,
222223
abi_msp430_interrupt,
223224
abi_ptx,

compiler/rustc_target/src/abi/call/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ pub enum Conv {
551551

552552
// Target-specific calling conventions.
553553
ArmAapcs,
554+
CCmseNonSecureCall,
554555

555556
Msp430Intr,
556557

compiler/rustc_target/src/spec/abi.rs

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub enum Abi {
3636
EfiApi,
3737
AvrInterrupt,
3838
AvrNonBlockingInterrupt,
39+
CCmseNonSecureCall,
3940

4041
// Multiplatform / generic ABIs
4142
System,
@@ -81,6 +82,7 @@ const AbiDatas: &[AbiData] = &[
8182
name: "avr-non-blocking-interrupt",
8283
generic: false,
8384
},
85+
AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call", generic: false },
8486
// Cross-platform ABIs
8587
AbiData { abi: Abi::System, name: "system", generic: true },
8688
AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic", generic: true },

compiler/rustc_typeck/src/check/check.rs

+11
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ pub(super) fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) {
4242
)
4343
.emit()
4444
}
45+
46+
// This ABI is only allowed on function pointers
47+
if abi == Abi::CCmseNonSecureCall {
48+
struct_span_err!(
49+
tcx.sess,
50+
span,
51+
E0781,
52+
"the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers."
53+
)
54+
.emit()
55+
}
4556
}
4657

4758
/// Helper used for fns and closures. Does the grungy work of checking a function
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# `abi_c_cmse_nonsecure_call`
2+
3+
The tracking issue for this feature is: [#81391]
4+
5+
[#81391]: https://github.com/rust-lang/rust/issues/81391
6+
7+
------------------------
8+
9+
The [TrustZone-M
10+
feature](https://developer.arm.com/documentation/100690/latest/) is available
11+
for targets with the Armv8-M architecture profile (`thumbv8m` in their target
12+
name).
13+
LLVM, the Rust compiler and the linker are providing
14+
[support](https://developer.arm.com/documentation/ecm0359818/latest/) for the
15+
TrustZone-M feature.
16+
17+
One of the things provided, with this unstable feature, is the
18+
`C-cmse-nonsecure-call` function ABI. This ABI is used on function pointers to
19+
non-secure code to mark a non-secure function call (see [section
20+
5.5](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
21+
22+
With this ABI, the compiler will do the following to perform the call:
23+
* save registers needed after the call to Secure memory
24+
* clear all registers that might contain confidential information
25+
* clear the Least Significant Bit of the function address
26+
* branches using the BLXNS instruction
27+
28+
To avoid using the non-secure stack, the compiler will constrain the number and
29+
type of parameters/return value.
30+
31+
The `extern "C-cmse-nonsecure-call"` ABI is otherwise equivalent to the
32+
`extern "C"` ABI.
33+
34+
<!-- NOTE(ignore) this example is specific to thumbv8m targets -->
35+
36+
``` rust,ignore
37+
#![no_std]
38+
#![feature(abi_c_cmse_nonsecure_call)]
39+
40+
#[no_mangle]
41+
pub fn call_nonsecure_function(addr: usize) -> u32 {
42+
let non_secure_function =
43+
unsafe { core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn() -> u32>(addr) };
44+
non_secure_function()
45+
}
46+
```
47+
48+
``` text
49+
$ rustc --emit asm --crate-type lib --target thumbv8m.main-none-eabi function.rs
50+
51+
call_nonsecure_function:
52+
.fnstart
53+
.save {r7, lr}
54+
push {r7, lr}
55+
.setfp r7, sp
56+
mov r7, sp
57+
.pad #16
58+
sub sp, #16
59+
str r0, [sp, #12]
60+
ldr r0, [sp, #12]
61+
str r0, [sp, #8]
62+
b .LBB0_1
63+
.LBB0_1:
64+
ldr r0, [sp, #8]
65+
push.w {r4, r5, r6, r7, r8, r9, r10, r11}
66+
bic r0, r0, #1
67+
mov r1, r0
68+
mov r2, r0
69+
mov r3, r0
70+
mov r4, r0
71+
mov r5, r0
72+
mov r6, r0
73+
mov r7, r0
74+
mov r8, r0
75+
mov r9, r0
76+
mov r10, r0
77+
mov r11, r0
78+
mov r12, r0
79+
msr apsr_nzcvq, r0
80+
blxns r0
81+
pop.w {r4, r5, r6, r7, r8, r9, r10, r11}
82+
str r0, [sp, #4]
83+
b .LBB0_2
84+
.LBB0_2:
85+
ldr r0, [sp, #4]
86+
add sp, #16
87+
pop {r7, pc}
88+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// gate-test-abi_c_cmse_nonsecure_call
2+
fn main() {
3+
let non_secure_function = unsafe {
4+
core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
5+
//~^ ERROR [E0658]
6+
0x10000004,
7+
)
8+
};
9+
let mut toto = 5;
10+
toto += non_secure_function(toto, 2, 3, 5);
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0658]: C-cmse-nonsecure-call ABI is experimental and subject to change
2+
--> $DIR/gate_test.rs:4:46
3+
|
4+
LL | core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
5+
| ^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #81391 <https://github.com/rust-lang/rust/issues/81391> for more information
8+
= help: add `#![feature(abi_c_cmse_nonsecure_call)]` to the crate attributes to enable
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// build-pass
2+
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
3+
// only-thumbv8m.main-none-eabi
4+
#![feature(abi_c_cmse_nonsecure_call)]
5+
#![no_std]
6+
7+
#[no_mangle]
8+
pub fn test(a: u32, b: u32, c: u32, d: u32) -> u32 {
9+
let non_secure_function = unsafe {
10+
core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32) -> u32>(
11+
0x10000004,
12+
)
13+
};
14+
non_secure_function(a, b, c, d)
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
2+
// only-thumbv8m.main-none-eabi
3+
#![feature(abi_c_cmse_nonsecure_call)]
4+
#![no_std]
5+
6+
#[no_mangle]
7+
pub fn test(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 {
8+
let non_secure_function = unsafe {
9+
core::mem::transmute::<
10+
usize,
11+
extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32>
12+
(
13+
0x10000004,
14+
)
15+
};
16+
non_secure_function(a, b, c, d, e)
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: <unknown>:0:0: in function test i32 (i32, i32, i32, i32, i32): call to non-secure function would require passing arguments on stack
2+
3+
4+
error: aborting due to previous error
5+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
2+
// only-thumbv8m.main-none-eabi
3+
#![feature(abi_c_cmse_nonsecure_call)]
4+
#![no_std]
5+
6+
pub extern "C-cmse-nonsecure-call" fn test() {} //~ ERROR [E0781]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0781]: the `"cmse-nonsecure-call"` ABI is only allowed on function pointers.
2+
--> $DIR/wrong-abi-location-1.rs:6:1
3+
|
4+
LL | pub extern "C-cmse-nonsecure-call" fn test() {} //~ ERROR [E0781]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0781`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
2+
// only-thumbv8m.main-none-eabi
3+
#![feature(abi_c_cmse_nonsecure_call)]
4+
#![no_std]
5+
6+
extern "C-cmse-nonsecure-call" { //~ ERROR [E0781]
7+
fn test();
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0781]: the `"C-cmse-nonsecure-call"` ABI is only allowed on function pointers.
2+
--> $DIR/wrong-abi-location-2.rs:6:1
3+
|
4+
LL | / extern "C-cmse-nonsecure-call" {
5+
LL | | fn test(); //~ ERROR [E0781]
6+
LL | | }
7+
| |_^
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0781`.

src/test/ui/codemap_tests/unicode.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `路濫狼á́́`
44
LL | extern "路濫狼á́́" fn foo() {}
55
| ^^^^^^^^^ invalid ABI
66
|
7-
= help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
7+
= help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
88

99
error: aborting due to previous error
1010

src/test/ui/parser/issue-8537.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `invalid-ab_isize`
44
LL | "invalid-ab_isize"
55
| ^^^^^^^^^^^^^^^^^^ invalid ABI
66
|
7-
= help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
7+
= help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
88

99
error: aborting due to previous error
1010

0 commit comments

Comments
 (0)