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 support for cmse_nonsecure_entry attribute #75810

Merged
merged 3 commits into from
Sep 30, 2020
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
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
Attribute::NoAlias.apply_llfn(llvm::AttributePlace::ReturnValue, llfn);
}
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) {
llvm::AddFunctionAttrString(llfn, Function, const_cstr!("cmse_nonsecure_entry"));
}
sanitize(cx, codegen_fn_attrs.no_sanitize, llfn);

// Always annotate functions with the target-cpu they are compiled for.
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_codegen_llvm/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,13 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void
.expect("non-UTF8 diagnostic");
diag_handler.warn(&msg);
}
llvm::diagnostic::Unsupported(diagnostic_ref) => {
let msg = llvm::build_string(|s| {
llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s)
})
.expect("non-UTF8 diagnostic");
diag_handler.err(&msg);
}
llvm::diagnostic::UnknownDiagnostic(..) => {}
}
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ pub enum Diagnostic<'ll> {
InlineAsm(InlineAsmDiagnostic<'ll>),
PGO(&'ll DiagnosticInfo),
Linker(&'ll DiagnosticInfo),
Unsupported(&'ll DiagnosticInfo),

/// LLVM has other types that we do not wrap here.
UnknownDiagnostic(&'ll DiagnosticInfo),
Expand Down Expand Up @@ -159,6 +160,7 @@ impl Diagnostic<'ll> {

Dk::PGOProfile => PGO(di),
Dk::Linker => Linker(di),
Dk::Unsupported => Unsupported(di),

_ => UnknownDiagnostic(di),
}
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 @@ -483,6 +483,7 @@ pub enum DiagnosticKind {
OptimizationFailure,
PGOProfile,
Linker,
Unsupported,
}

/// LLVMRustDiagnosticLevel
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ pub fn AddFunctionAttrStringValue(llfn: &'a Value, idx: AttributePlace, attr: &C
}
}

pub fn AddFunctionAttrString(llfn: &'a Value, idx: AttributePlace, attr: &CStr) {
unsafe {
LLVMRustAddFunctionAttrStringValue(llfn, idx.as_uint(), attr.as_ptr(), std::ptr::null())
}
}

#[derive(Copy, Clone)]
pub enum AttributePlace {
ReturnValue,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_error_codes/src/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,8 @@ E0770: include_str!("./error_codes/E0770.md"),
E0771: include_str!("./error_codes/E0771.md"),
E0773: include_str!("./error_codes/E0773.md"),
E0774: include_str!("./error_codes/E0774.md"),
E0775: include_str!("./error_codes/E0775.md"),
E0776: include_str!("./error_codes/E0776.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0775.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M
extension.

Erroneous code example:

```compile_fail,E0775
#![feature(cmse_nonsecure_entry)]

#[cmse_nonsecure_entry]
pub extern "C" fn entry_function() {}
```

To fix this error, compile your code for a Rust target that supports the
TrustZone-M extension. The current possible targets are:
* `thumbv8m.main-none-eabi`
* `thumbv8m.main-none-eabihf`
* `thumbv8m.base-none-eabi`
13 changes: 13 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0776.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
`#[cmse_nonsecure_entry]` functions require a C ABI

Erroneous code example:

```compile_fail,E0776
#![feature(cmse_nonsecure_entry)]

#[no_mangle]
#[cmse_nonsecure_entry]
pub fn entry_function(input: Vec<u32>) {}
```

To fix this error, declare your entry function with a C ABI, using `extern "C"`.
3 changes: 3 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,9 @@ declare_features! (
/// Allows using and casting function pointers in a `const fn`.
(active, const_fn_fn_ptr_basics, "1.48.0", Some(57563), None),

/// Allows to use the `#[cmse_nonsecure_entry]` attribute.
(active, cmse_nonsecure_entry, "1.48.0", Some(75835), None),

// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
experimental!(register_tool),
),

gated!(cmse_nonsecure_entry, AssumedUsed, template!(Word), experimental!(cmse_nonsecure_entry)),

// ==========================================================================
// Internal attributes: Stability, deprecation, and unsafe:
// ==========================================================================
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,7 @@ enum class LLVMRustDiagnosticKind {
OptimizationFailure,
PGOProfile,
Linker,
Unsupported,
};

static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) {
Expand All @@ -1197,6 +1198,8 @@ static LLVMRustDiagnosticKind toRust(DiagnosticKind Kind) {
return LLVMRustDiagnosticKind::PGOProfile;
case DK_Linker:
return LLVMRustDiagnosticKind::Linker;
case DK_Unsupported:
return LLVMRustDiagnosticKind::Unsupported;
default:
return (Kind >= DK_FirstRemark && Kind <= DK_LastRemark)
? LLVMRustDiagnosticKind::OptimizationRemarkOther
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ bitflags! {
/// #[ffi_const]: applies clang's `const` attribute to a foreign function
/// declaration.
const FFI_CONST = 1 << 13;
/// #[cmse_nonsecure_entry]: with a TrustZone-M extension, declare a
/// function as an entry function from Non-Secure code.
const CMSE_NONSECURE_ENTRY = 1 << 14;
}
}

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 @@ -337,6 +337,7 @@ symbols! {
closure_to_fn_coercion,
cmp,
cmpxchg16b_target_feature,
cmse_nonsecure_entry,
coerce_unsized,
cold,
column,
Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_typeck/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2543,6 +2543,21 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
} else if tcx.sess.check_name(attr, sym::used) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
} else if tcx.sess.check_name(attr, sym::cmse_nonsecure_entry) {
if tcx.fn_sig(id).abi() != abi::Abi::C {
struct_span_err!(
tcx.sess,
attr.span,
E0776,
"`#[cmse_nonsecure_entry]` requires C ABI"
)
.emit();
}
if !tcx.sess.target.target.llvm_target.contains("thumbv8m") {
struct_span_err!(tcx.sess, attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension")
.emit();
}
codegen_fn_attrs.flags |= CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY;
} else if tcx.sess.check_name(attr, sym::thread_local) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
} else if tcx.sess.check_name(attr, sym::track_caller) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# `cmse_nonsecure_entry`

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

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

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

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
`cmse_nonsecure_entry` attribute. This attribute marks a Secure function as an
entry function (see [section
5.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
With this attribute, the compiler will do the following:
* add a special symbol on the function which is the `__acle_se_` prefix and the
standard function name
Copy link
Contributor

Choose a reason for hiding this comment

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

Does that mean the function's name will be mangled differently or is this an aliased or additional symbol?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe this is an additional symbol. So that if you set this attribute on a function named toto, the compiler will generate the following assembly:

toto:
__acle_se_toto:
    INSTRUCTIONS

and the toto symbol will be set as weak. That way the linker can create the veneer like so:

toto:
    SG
    B.W __acle_se_toto

and the symbol toto and its address is passed to NS.

This is explained section 3.4.4 in here.

I don't think it modifies the mangling. However, as you can see in the examples in this PR, I believe you would always use the no_mangle and extern "C" ABI on the entry functions as they go through FFI.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I think we should definitely forbid using this attribute with non-"C" ABI functions then. Since the Rust ABI is unstable, it can pass arguments either directly in registers, or in memory, and that would be a problem here.

Copy link
Contributor Author

@hug-dev hug-dev Sep 30, 2020

Choose a reason for hiding this comment

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

Yes that makes sense, I am looking if there is a way to check what the ABI of the function compiled is.

[edit]: and add tests with and without the C ABI as well!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was easier than anticipated 😃 Added a new error code for that.

* constrain the number of parameters to avoid using the Non-Secure stack
jonas-schievink marked this conversation as resolved.
Show resolved Hide resolved
* before returning from the function, clear registers that might contain Secure
information
* use the `BXNS` instruction to return

Because the stack can not be used to pass parameters, there will be compilation
errors if:
* the total size of all parameters is too big (for example more than four 32
bits integers)
* the entry function is not using a C ABI

The special symbol `__acle_se_` will be used by the linker to generate a secure
gateway veneer.

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

``` rust,ignore
#![feature(cmse_nonsecure_entry)]

#[no_mangle]
#[cmse_nonsecure_entry]
pub extern "C" fn entry_function(input: u32) -> u32 {
input + 6
}
```

``` text
$ rustc --emit obj --crate-type lib --target thumbv8m.main-none-eabi function.rs
$ arm-none-eabi-objdump -D function.o

00000000 <entry_function>:
0: b580 push {r7, lr}
2: 466f mov r7, sp
4: b082 sub sp, #8
6: 9001 str r0, [sp, #4]
8: 1d81 adds r1, r0, #6
a: 460a mov r2, r1
c: 4281 cmp r1, r0
e: 9200 str r2, [sp, #0]
10: d30b bcc.n 2a <entry_function+0x2a>
12: e7ff b.n 14 <entry_function+0x14>
14: 9800 ldr r0, [sp, #0]
16: b002 add sp, #8
18: e8bd 4080 ldmia.w sp!, {r7, lr}
1c: 4671 mov r1, lr
1e: 4672 mov r2, lr
20: 4673 mov r3, lr
22: 46f4 mov ip, lr
24: f38e 8800 msr CPSR_f, lr
28: 4774 bxns lr
2a: f240 0000 movw r0, #0
2e: f2c0 0000 movt r0, #0
32: f240 0200 movw r2, #0
36: f2c0 0200 movt r2, #0
3a: 211c movs r1, #28
3c: f7ff fffe bl 0 <_ZN4core9panicking5panic17h5c028258ca2fb3f5E>
40: defe udf #254 ; 0xfe
```
11 changes: 11 additions & 0 deletions src/test/ui/cmse-nonsecure-entry/gate_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// gate-test-cmse_nonsecure_entry

#[no_mangle]
#[cmse_nonsecure_entry]
//~^ ERROR [E0775]
//~| ERROR [E0658]
pub extern "C" fn entry_function(input: u32) -> u32 {
input + 6
}

fn main() {}
19 changes: 19 additions & 0 deletions src/test/ui/cmse-nonsecure-entry/gate_test.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0658]: the `#[cmse_nonsecure_entry]` attribute is an experimental feature
--> $DIR/gate_test.rs:4:1
|
LL | #[cmse_nonsecure_entry]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #75835 <https://github.com/rust-lang/rust/issues/75835> for more information
= help: add `#![feature(cmse_nonsecure_entry)]` to the crate attributes to enable

error[E0775]: `#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension
--> $DIR/gate_test.rs:4:1
|
LL | #[cmse_nonsecure_entry]
| ^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0658, E0775.
For more information about an error, try `rustc --explain E0658`.
11 changes: 11 additions & 0 deletions src/test/ui/cmse-nonsecure-entry/params-on-registers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// build-pass
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
// only-thumbv8m.main-none-eabi
#![feature(cmse_nonsecure_entry)]
#![no_std]

#[no_mangle]
#[cmse_nonsecure_entry]
pub extern "C" fn entry_function(a: u32, b: u32, c: u32, d: u32) -> u32 {
a + b + c + d
}
10 changes: 10 additions & 0 deletions src/test/ui/cmse-nonsecure-entry/params-on-stack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
// only-thumbv8m.main-none-eabi
#![feature(cmse_nonsecure_entry)]
#![no_std]

#[no_mangle]
#[cmse_nonsecure_entry]
pub extern "C" fn entry_function(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 { //~ ERROR
a + b + c + d + e
}
5 changes: 5 additions & 0 deletions src/test/ui/cmse-nonsecure-entry/params-on-stack.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: <unknown>:0:0: in function entry_function i32 (i32, i32, i32, i32, i32): secure entry function requires arguments on stack
jonas-schievink marked this conversation as resolved.
Show resolved Hide resolved


error: aborting due to previous error

10 changes: 10 additions & 0 deletions src/test/ui/cmse-nonsecure-entry/trustzone-only.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// ignore-thumbv8m.main-none-eabi
#![feature(cmse_nonsecure_entry)]

#[no_mangle]
#[cmse_nonsecure_entry] //~ ERROR [E0775]
pub extern "C" fn entry_function(input: u32) -> u32 {
input + 6
}

fn main() {}
9 changes: 9 additions & 0 deletions src/test/ui/cmse-nonsecure-entry/trustzone-only.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0775]: `#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension
--> $DIR/trustzone-only.rs:5:1
|
LL | #[cmse_nonsecure_entry]
| ^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0775`.
10 changes: 10 additions & 0 deletions src/test/ui/cmse-nonsecure-entry/wrong-abi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
// only-thumbv8m.main-none-eabi
#![feature(cmse_nonsecure_entry)]
#![no_std]

#[no_mangle]
#[cmse_nonsecure_entry]
pub fn entry_function(a: u32, b: u32, c: u32, d: u32) -> u32 { //~ ERROR [E0776]
a + b + c + d
}
9 changes: 9 additions & 0 deletions src/test/ui/cmse-nonsecure-entry/wrong-abi.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0776]: `#[cmse_nonsecure_entry]` functions require C ABI
--> $DIR/wrong-abi.rs:7:1
|
LL | #[cmse_nonsecure_entry]
| ^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0776`.