Open hug-dev opened 3 years ago
What's the name of the actual underlying calling convention? Is it AAPCS? I think the extern name should contain it too somehow.
What's the name of the actual underlying calling convention?
It will use the C convention ultimately, the current implementation maps it to llvm::CCallConv
(I guess similar than AAPCS as this feature is only available on Arm processors?).
Do you mean that because it might be possible to have the cmse_nonsecure_call
feature available for other ABIs as well?
I think restricting it to only ever use the C ABI is not a bad thing: this is used to switch to functions that are defined in other executable files that could have been written in any programming language.
Well, what I really want is for the underlying ABI to be explicit in the ABI string, whatever it is, so that it is more obvious that a transmute as given in the example above is… valid. It would also, as you mention, enable us to add non-secure options for other calling conventions if necessary.
So a couple of proposals: C_cmse_nonsecure
or cmse_nonsecure_C
.
Ok makes sense! Will modify the implementation PR and this with the C
in front: extern "C-cmse-nonsecure-call"
.
We propose to stabilize the C-cmse-nonsecure-call
ABI. It can only be used in function pointer types, never in an actual *extern "C-cmse-nonsecure-call" {}
block. Such function pointers are typically received via FFI.
https://godbolt.org/z/KT6hc5Y7W
// `cargo build --target thumbv8m.main-none-eabi`
#![feature(abi_c_cmse_nonsecure_call)]
#![no_std]
#[no_mangle]
pub fn call_nonsecure(f: unsafe extern "C-cmse-nonsecure-call" fn(u8, u16, u32) -> f32) -> f32 {
unsafe { f(1, 2, 3) }
}
Which produces this LLVM IR:
; Function Attrs: nounwind
define dso_local float @call_nonsecure(ptr %f) unnamed_addr #0 !dbg !5 {
start:
%_0 = call float %f(i8 zeroext 1, i16 zeroext 2, i32 3) #1, !dbg !10
ret float %_0, !dbg !11
}
attributes #0 = { nounwind "frame-pointer"="all" "target-cpu"="generic" }
attributes #1 = { nounwind "cmse_nonsecure_call" }
Which produces this assembly:
call_nonsecure:
push {r7, lr}
mov r7, sp
mov r3, r0
movs r0, #1
movs r1, #2
movs r2, #3
push.w {r4, r5, r6, r7, r8, r9, r10, r11}
bic r3, r3, #1
sub sp, #136
vlstm sp
mov r4, r3 ; <- All unused registers get cleared, to not leak information
mov r5, r3
mov r6, r3
mov r7, r3
mov r8, r3
mov r9, r3
mov r10, r3
mov r11, r3
mov r12, r3
msr apsr_nzcvq, r3
blxns r3 ; <- the expected non-secure call instruction
vlldm sp
add sp, #136
pop.w {r4, r5, r6, r7, r8, r9, r10, r11}
pop {r7, pc}
The calling requirements for this ABI are checked within rustc to produce good error messsages.
#![feature(abi_c_cmse_nonsecure_call)]
#![no_std]
#[no_mangle]
pub fn call_nonsecure(f: unsafe extern "C-cmse-nonsecure-call" fn(u64, u64, u64) -> (u64, u64)) {
unsafe { f(1, 2, 3) };
}
Produces the following errors:
error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
--> <source>:5:77
|
5 | pub fn call_nonsecure(f: unsafe extern "C-cmse-nonsecure-call" fn(u64, u64, u64) -> (u64, u64)) {
| ^^^ this argument doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
--> <source>:5:85
|
5 | pub fn call_nonsecure(f: unsafe extern "C-cmse-nonsecure-call" fn(u64, u64, u64) -> (u64, u64)) {
| ^^^^^^^^^^ this type doesn't fit in the available registers
|
= note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
= note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0798`.
Test cases are in
https://github.com/rust-lang/rust/tree/master/tests/ui/cmse-nonsecure/cmse-nonsecure-call
extern
blocks or extern fn
definitions (and therefore is only allowed in function pointers)The tests in the (as yet unmerged) PR for cmse-nonsecure-entry validate the assembly output of this ABI.
Clang: https://godbolt.org/z/7ch3xcz96 GCC: https://godbolt.org/z/16arxab5x
This tracking issues seems to be missing some tags compared to #75835
@rustbot label +T-compiler +T-lang +A-codegen
Nominating for both T-lang and T-compiler discussion: there is a request for the stabilization of the C-cmse-nonsecure-call
ABI (posted above at https://github.com/rust-lang/rust/issues/81391#issuecomment-2258346708), there's also the seemingly related #[cmse_nonsecure_entry]
attribute (#75835). This is mostly nominated for several reasons stated below.
For T-compiler:
For T-lang:
For both T-compiler and T-lang:
@rustbot labels +I-compiler-nominated +I-lang-nominated
This is a tracking issue for the PR #81346. The feature gate for the issue is
#![feature(abi_c_cmse_nonsecure_call)]
.Description
The TrustZone-M feature is available for targets with the Armv8-M architecture profile (
thumbv8m
in their target name). LLVM, the Rust compiler and the linker are providing support for the TrustZone-M feature.One of the things provided, with this unstable feature, is the
C-cmse-nonsecure-call
function ABI. This ABI is used on function pointers to non-secure code to mark a non-secure function call (see section 5.5 for details).With this ABI, the compiler will do the following to perform the call:
To avoid using the non-secure stack, the compiler will constrain the number and type of parameters/return value.
The
extern "C-cmse-nonsecure-call"
ABI is otherwise equivalent to theextern "C"
ABI.Example
Steps