Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new ABI to support cmse_nonsecure_call #81346

Merged
merged 1 commit into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ impl<'a> PostExpansionVisitor<'a> {
"efiapi ABI is experimental and subject to change"
);
}
"C-cmse-nonsecure-call" => {
gate_feature_post!(
&self,
abi_c_cmse_nonsecure_call,
span,
"C-cmse-nonsecure-call ABI is experimental and subject to change"
);
}
abi => self
.sess
.parse_sess
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_cranelift/src/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ fn clif_sig_from_fn_abi<'tcx>(
Conv::X86_64SysV => CallConv::SystemV,
Conv::X86_64Win64 => CallConv::WindowsFastcall,
Conv::ArmAapcs
| Conv::CCmseNonSecureCall
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased and added this to fix the error

| Conv::Msp430Intr
| Conv::PtxKernel
| Conv::X86Fastcall
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_codegen_llvm/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {

fn llvm_cconv(&self) -> llvm::CallConv {
match self.conv {
Conv::C | Conv::Rust => llvm::CCallConv,
Conv::C | Conv::Rust | Conv::CCmseNonSecureCall => llvm::CCallConv,
Conv::AmdGpuKernel => llvm::AmdGpuKernel,
Conv::AvrInterrupt => llvm::AvrInterrupt,
Conv::AvrNonBlockingInterrupt => llvm::AvrNonBlockingInterrupt,
Expand Down Expand Up @@ -546,6 +546,18 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
if cconv != llvm::CCallConv {
llvm::SetInstructionCallConv(callsite, cconv);
}

if self.conv == Conv::CCmseNonSecureCall {
// This will probably get ignored on all targets but those supporting the TrustZone-M
// extension (thumbv8m targets).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should reject the ABI when using a --target that doesn't support it (hmm, do we not do that for #[cmse_non_secure_entry]?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True! Initially I was looking how to check the target from this file but I am not sure how to get the target information from there. It looks like it would be possible to do it in the change file compiler/rustc_ast_passes/src/feature_gate.rs as there is access to the rustc_session::session::Session structure which contains the target.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I do, can I maybe re-use and modify the same error code used for cmse_nonsecure_entry?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that should be fine

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feature_gate.rs is not the right place to check this though. We already have per-target lists of supported ABIs, this should just plug in there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, sorry I mixed up the two things and thought we were talking about the target check. Cool I can move the bare_function check there.

Just wondering how I would do it 😅 In the feature_gate.rs I can have the information that the compiler is currently checking a bare function type but I don't see how I can check that in the check.rs file. Or did you mean that I should call the check_abi function of check.rs (with the bare_function info) from the feature_gate.rs file?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, feature_gate.rs should not be involved in checking ABI-specific restrictions. I'm not sure where the best way to put this is, perhaps you have to write your own visitor, similar to what inline asm uses?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, got it.

I noticed that check_abi on check.rs is only called to check the ABI of function declarations and external blocks which are the exact two cases where the CCmseNonSecureCall ABI is forbidden. I believe the other only use of extern is for function pointer and that one is allowed.

So one easy way would be to add a check for CCmseNonSecureCall inside check_abi and emit an error if that's equal. I tested it and it works as expected.

Maybe it's a bit risky if check_abi is ever used for function pointers but we can add tests covering all the cases?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, seems fine if there are tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok done, there should be:

  • one test checking that the ABI on a function pointer compiles successfully
  • one test checking that the ABI on on function definition fails
  • one test checking that the ABI on an external block fails

unsafe {
llvm::AddCallSiteAttrString(
callsite,
llvm::AttributePlace::Function,
rustc_data_structures::const_cstr!("cmse_nonsecure_call"),
);
}
}
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,7 @@ extern "C" {
// Operations on call sites
pub fn LLVMSetInstructionCallConv(Instr: &Value, CC: c_uint);
pub fn LLVMRustAddCallSiteAttribute(Instr: &Value, index: c_uint, attr: Attribute);
pub fn LLVMRustAddCallSiteAttrString(Instr: &Value, index: c_uint, Name: *const c_char);
pub fn LLVMRustAddAlignmentCallSiteAttr(Instr: &Value, index: c_uint, bytes: u32);
pub fn LLVMRustAddDereferenceableCallSiteAttr(Instr: &Value, index: c_uint, bytes: u64);
pub fn LLVMRustAddDereferenceableOrNullCallSiteAttr(Instr: &Value, index: c_uint, bytes: u64);
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ pub fn AddFunctionAttrString(llfn: &'a Value, idx: AttributePlace, attr: &CStr)
}
}

pub fn AddCallSiteAttrString(callsite: &Value, idx: AttributePlace, attr: &CStr) {
unsafe { LLVMRustAddCallSiteAttrString(callsite, idx.as_uint(), attr.as_ptr()) }
}

#[derive(Copy, Clone)]
pub enum AttributePlace {
ReturnValue,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ E0777: include_str!("./error_codes/E0777.md"),
E0778: include_str!("./error_codes/E0778.md"),
E0779: include_str!("./error_codes/E0779.md"),
E0780: include_str!("./error_codes/E0780.md"),
E0781: include_str!("./error_codes/E0781.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0781.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
The `C-cmse-nonsecure-call` ABI can only be used with function pointers.

Erroneous code example:

```compile_fail,E0781
#![feature(abi_c_cmse_nonsecure_call)]

pub extern "C-cmse-nonsecure-call" fn test() {}
```

The `C-cmse-nonsecure-call` ABI should be used by casting function pointers to
specific addresses.
3 changes: 3 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,9 @@ declare_features! (

/// Allows using `pointer` and `reference` in intra-doc links
(active, intra_doc_pointers, "1.51.0", Some(80896), None),

/// Allows `extern "C-cmse-nonsecure-call" fn()`.
(active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None),
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,14 @@ extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index,
Call->addAttribute(Index, Attr);
}

extern "C" void LLVMRustAddCallSiteAttrString(LLVMValueRef Instr, unsigned Index,
const char *Name) {
CallBase *Call = unwrap<CallBase>(Instr);
Attribute Attr = Attribute::get(Call->getContext(), Name);
Call->addAttribute(Index, Attr);
}


extern "C" void LLVMRustAddAlignmentCallSiteAttr(LLVMValueRef Instr,
unsigned Index,
uint32_t Bytes) {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2650,6 +2650,7 @@ where
Win64 => Conv::X86_64Win64,
SysV64 => Conv::X86_64SysV,
Aapcs => Conv::ArmAapcs,
CCmseNonSecureCall => Conv::CCmseNonSecureCall,
PtxKernel => Conv::PtxKernel,
Msp430Interrupt => Conv::Msp430Intr,
X86Interrupt => Conv::X86Intr,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ symbols! {
abi,
abi_amdgpu_kernel,
abi_avr_interrupt,
abi_c_cmse_nonsecure_call,
abi_efiapi,
abi_msp430_interrupt,
abi_ptx,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/abi/call/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ pub enum Conv {

// Target-specific calling conventions.
ArmAapcs,
CCmseNonSecureCall,

Msp430Intr,

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_target/src/spec/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub enum Abi {
EfiApi,
AvrInterrupt,
AvrNonBlockingInterrupt,
CCmseNonSecureCall,

// Multiplatform / generic ABIs
System,
Expand Down Expand Up @@ -81,6 +82,7 @@ const AbiDatas: &[AbiData] = &[
name: "avr-non-blocking-interrupt",
generic: false,
},
AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call", generic: false },
// Cross-platform ABIs
AbiData { abi: Abi::System, name: "system", generic: true },
AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic", generic: true },
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_typeck/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ pub(super) fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) {
)
.emit()
}

// This ABI is only allowed on function pointers
if abi == Abi::CCmseNonSecureCall {
struct_span_err!(
tcx.sess,
span,
E0781,
"the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers."
)
.emit()
}
}

/// Helper used for fns and closures. Does the grungy work of checking a function
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# `abi_c_cmse_nonsecure_call`

The tracking issue for this feature is: [#81391]

[#81391]: https://github.com/rust-lang/rust/issues/81391

------------------------

The [TrustZone-M
feature](https://developer.arm.com/documentation/100690/latest/) 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](https://developer.arm.com/documentation/ecm0359818/latest/) 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](https://developer.arm.com/documentation/ecm0359818/latest/) for details).

With this ABI, the compiler will do the following to perform the call:
* save registers needed after the call to Secure memory
* clear all registers that might contain confidential information
* clear the Least Significant Bit of the function address
* branches using the BLXNS instruction

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 the
`extern "C"` ABI.

<!-- NOTE(ignore) this example is specific to thumbv8m targets -->

``` rust,ignore
#![no_std]
#![feature(abi_c_cmse_nonsecure_call)]

#[no_mangle]
pub fn call_nonsecure_function(addr: usize) -> u32 {
let non_secure_function =
unsafe { core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn() -> u32>(addr) };
non_secure_function()
}
```

``` text
$ rustc --emit asm --crate-type lib --target thumbv8m.main-none-eabi function.rs

call_nonsecure_function:
.fnstart
.save {r7, lr}
push {r7, lr}
.setfp r7, sp
mov r7, sp
.pad #16
sub sp, #16
str r0, [sp, #12]
ldr r0, [sp, #12]
str r0, [sp, #8]
b .LBB0_1
.LBB0_1:
ldr r0, [sp, #8]
push.w {r4, r5, r6, r7, r8, r9, r10, r11}
bic r0, r0, #1
mov r1, r0
mov r2, r0
mov r3, r0
mov r4, r0
mov r5, r0
mov r6, r0
mov r7, r0
mov r8, r0
mov r9, r0
mov r10, r0
mov r11, r0
mov r12, r0
msr apsr_nzcvq, r0
blxns r0
pop.w {r4, r5, r6, r7, r8, r9, r10, r11}
str r0, [sp, #4]
b .LBB0_2
.LBB0_2:
ldr r0, [sp, #4]
add sp, #16
pop {r7, pc}
```
11 changes: 11 additions & 0 deletions src/test/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// gate-test-abi_c_cmse_nonsecure_call
fn main() {
let non_secure_function = unsafe {
core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
//~^ ERROR [E0658]
0x10000004,
)
};
let mut toto = 5;
toto += non_secure_function(toto, 2, 3, 5);
}
12 changes: 12 additions & 0 deletions src/test/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0658]: C-cmse-nonsecure-call ABI is experimental and subject to change
--> $DIR/gate_test.rs:4:46
|
LL | core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #81391 <https://github.com/rust-lang/rust/issues/81391> for more information
= help: add `#![feature(abi_c_cmse_nonsecure_call)]` to the crate attributes to enable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0658`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// build-pass
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
// only-thumbv8m.main-none-eabi
#![feature(abi_c_cmse_nonsecure_call)]
#![no_std]

#[no_mangle]
pub fn test(a: u32, b: u32, c: u32, d: u32) -> u32 {
let non_secure_function = unsafe {
core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32) -> u32>(
0x10000004,
)
};
non_secure_function(a, b, c, d)
}
17 changes: 17 additions & 0 deletions src/test/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
// only-thumbv8m.main-none-eabi
#![feature(abi_c_cmse_nonsecure_call)]
#![no_std]

#[no_mangle]
pub fn test(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 {
let non_secure_function = unsafe {
core::mem::transmute::<
usize,
extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32>
(
0x10000004,
)
};
non_secure_function(a, b, c, d, e)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: <unknown>:0:0: in function test i32 (i32, i32, i32, i32, i32): call to non-secure function would require passing arguments on stack


error: aborting due to previous error

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
// only-thumbv8m.main-none-eabi
#![feature(abi_c_cmse_nonsecure_call)]
#![no_std]

pub extern "C-cmse-nonsecure-call" fn test() {} //~ ERROR [E0781]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0781]: the `"cmse-nonsecure-call"` ABI is only allowed on function pointers.
--> $DIR/wrong-abi-location-1.rs:6:1
|
LL | pub extern "C-cmse-nonsecure-call" fn test() {} //~ ERROR [E0781]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0781`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
// only-thumbv8m.main-none-eabi
#![feature(abi_c_cmse_nonsecure_call)]
#![no_std]

extern "C-cmse-nonsecure-call" { //~ ERROR [E0781]
fn test();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0781]: the `"C-cmse-nonsecure-call"` ABI is only allowed on function pointers.
--> $DIR/wrong-abi-location-2.rs:6:1
|
LL | / extern "C-cmse-nonsecure-call" {
LL | | fn test(); //~ ERROR [E0781]
LL | | }
| |_^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0781`.
2 changes: 1 addition & 1 deletion src/test/ui/codemap_tests/unicode.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `路濫狼á́́`
LL | extern "路濫狼á́́" fn foo() {}
| ^^^^^^^^^ invalid ABI
|
= 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
= 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

error: aborting due to previous error

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/parser/issue-8537.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `invalid-ab_isize`
LL | "invalid-ab_isize"
| ^^^^^^^^^^^^^^^^^^ invalid ABI
|
= 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
= 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

error: aborting due to previous error

Expand Down