Skip to content

Conversation

@folkertdev
Copy link

@folkertdev folkertdev commented Nov 24, 2025

Support for the cmse-nonsecure-entry and cmse-nonsecure-call calling conventions on ArmV8m (thumbv8*) targets, and a lint preventing (partially) uninitialized values from crossing the security boundary.

extern "cmse-nonsecure-entry" fn entry(callback: extern "cmse-nonsecure-call" fn(_: u64)) { 
    callback(42);
}

The implementation is tracked in:

Important

When responding to RFCs, try to use inline review comments (it is possible to leave an inline review comment for the entire file at the top) instead of direct comments for normal comments and keep normal comments for procedural matters like starting FCPs.

This keeps the discussion more organized.

@rustbot label +T-lang

Rendered

@rustbot rustbot added the T-lang Relevant to the language team, which will review and decide on the RFC. label Nov 24, 2025
@folkertdev folkertdev changed the title RCF for CMSE calling conventions RFC for CMSE calling conventions Nov 24, 2025
@folkertdev folkertdev force-pushed the cmse-calling-conventions branch from f30d3be to 21f5b58 Compare November 24, 2025 12:14
@folkertdev folkertdev changed the title RFC for CMSE calling conventions CMSE calling conventions Nov 24, 2025
Copy link

@thejpster thejpster left a comment

Choose a reason for hiding this comment

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

I had one quibble with the architecture terminology, but everything else looks good to me.

Co-authored-by: Jonathan 'theJPster' Pallant <github@thejpster.org.uk>
@folkertdev
Copy link
Author

This has now been reviewed by several people from the rust embedded community and from Arm, so I think this is ready for T-lang to take a look.

@rustbot label +I-lang-nominated

@rustbot rustbot added the I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. label Nov 30, 2025
@traviscross traviscross added the P-lang-drag-2 Lang team prioritization drag level 2. label Dec 3, 2025
Co-authored-by: Jacob Lifshay <programmerjake@gmail.com>
@joshtriplett
Copy link
Member

This looks reasonable to me, including the lint.

@rfcbot merge lang

@rust-rfcbot
Copy link
Collaborator

rust-rfcbot commented Jan 21, 2026

Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
See this document for info about what commands tagged team members can give me.

@rust-rfcbot rust-rfcbot added proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. labels Jan 21, 2026
type T5 = extern "cmse-nonsecure-call" fn(_: i64, _: i64) -> WrappedI64;
```

An error is emitted when the program contains a signature that violates the calling convention's constraints:
Copy link
Member

Choose a reason for hiding this comment

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

Please explain the exact algorithm used for this check. There are potential semver concerns here so it'd be good to be clear about what is being checked here.

For instance, if I have a single-field struct like U64 without the repr(transparent), is that accepted or not?

Copy link
Author

Choose a reason for hiding this comment

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

Sure, I'll clarify that.

The current implementation is to look at the BackendRepr, and accept anything passed as Primitive::Int(Integer::I64, _) | Primitive::Float(Float::F64). Because we're working with a subset of AAPCS, I don't think how values are passed can ever change?

https://github.com/rust-lang/rust/blob/b765963267a390d817d0b519b15cfa0d6311d0dc/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs#L161-L181

We test for the case you mention, it emits an error (clang would emit an error in this case too):

https://github.com/rust-lang/rust/blob/b765963267a390d817d0b519b15cfa0d6311d0dc/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs#L13-L27

Copy link
Member

@RalfJung RalfJung Jan 22, 2026

Choose a reason for hiding this comment

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

BackendRepr is an internal implementation detail of rustc and prone to changes. It seems risky to get ourselves into a corner where changing unspecified layout computation details is a breaking change.

(We do rely on BackendRepr for the SIMD vector ABI check, but that was a case of fixing a previous oversight which caused soundness trouble, so we had to minimize breakage. I wouldn't take that as a template for a new feature where we have the chance to do it right. Also, we don't rely just on BackendRepr there, we also check the PassMode, so really what we are checking there is the de-facto underlying ABI, at least on the level of detail that is represented in LLVM IR.)

Copy link
Member

@RalfJung RalfJung Jan 22, 2026

Choose a reason for hiding this comment

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

We test for the case you mention

That is not the case I mentioned. I suggested to remove repr(transparent), but I didn't suggest adding repr(C). (That will probably trigger an FFI lint, but since we are talking about hard errors here, whether or not there is a lint is largely irrelevant.)

Also, the fact that repr(C) inhibits BackendRepr::Scalar is a limitation we'd like to lift (rust-lang/rust#119183). We should not add new logic that relies on how repr(C) and BackendRepr correlate.

Copy link
Author

Choose a reason for hiding this comment

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

So would a check based on the type (peeling off layers of repr(transparent) work better?

Copy link
Author

Choose a reason for hiding this comment

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

Presumably, there can be at most 4 arguments.

module zero-sized types, yes.


The check is based just on the size of the arguments, each rounded up to the next multiple of 4. If that exceeds 16, the arguments do not fit.

I've updated the text.

Copy link
Member

Choose a reason for hiding this comment

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

module zero-sized types, yes.

The fact that zero-sized arguments are skipped in some ABIs is an implementation detail. I would be hesistant to enshrine it in the language.

Choose a reason for hiding this comment

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

ZSTs are used a lot in embedded Rust. It would be very nice to be able to pass ZSTs as an argument without it taking up the register budget.

Often hardware peripherals or certain state conditions are modeled using ZSTs.

For example, say we want a secure memcpy, that could look like:

fn secure_memcpy(dma: DMA, src: *const u8, src_len: usize, target: *mut u8, target_len: usize) -> DMA

If the DMA ZST were counted as one of the 4 possible arguments we couldn't make this work, even when there's not technical reason for it.

Copy link
Author

Choose a reason for hiding this comment

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

You could store the arguments into a repr(C) struct...

But that is just a weird workaround. Given that the calling convention inherits from aapcs, and it does ignore zero-sized types, it feels like it should be OK to allow them here?

Copy link
Member

@RalfJung RalfJung Jan 28, 2026

Choose a reason for hiding this comment

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

it does ignore zero-sized types

Is that documented in the aapcs ABI description? C does not have zero-sized types, so this would surprise me. So it would be more correct to say that rustc happens to ignore ZST on aapcs calls at the moment, but that's not a deliberate choice (as in, t-lang approved or so) or a stable promise by any means. Generally we should be careful whenever we pass things over an ABI that do not exist in C, since if we make a stable choice here and C later makes a different choice, we are in trouble.

Our general ABI rules do not allow ignoring ZST, and in fact there are ABIs where they currently are not being ignored (specifically on Windows). That might be partially because we make some types ZST that shouldn't be ZST, but last time I checked it looked like we could not get away with a blanket "ZST are ignored" rule. We'd have to start making ABI-specific promises, which is a rabbit hole that we avoided so far.

@folkertdev folkertdev force-pushed the cmse-calling-conventions branch from f795ce0 to ae12919 Compare January 28, 2026 13:30
An error is emitted when the program contains a signature that violates the calling convention's constraints:
The arguments fit if:

- the sum of their sizes, each rounded up to the next multiple of 4, is 16 bytes or less
Copy link
Member

Choose a reason for hiding this comment

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

So, a single argument of type (i32, (i8, i16), i64) is allowed?

That's... very strange and not at all what I expected. In the end I don't care that strongly as long as it is clearly specified, but implementation-wise this seems like it could become quite messy.

Is this our ABI to define or is it a standard ABI we have to follow? If it is the latter, allowing repr(Rust) types (this includes tuples) might be a mistake -- it would be the first time we make any kind of ABI guarantees for them.

Copy link
Author

Choose a reason for hiding this comment

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

It is really just aapcs, so the 32-bit arm C calling convention. It already lints on passing repr(Rust) types, I'd be fine with actually disallowing them though. This is meant to be an FFI boundary, the user is meant to think about the constraints.

Copy link
Member

Choose a reason for hiding this comment

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

The thing is that for normal aapcs, if we change repr(Rust) things won't fail to build. They might "just" stop working properly if you ignored the lints. This RFC turns things into a hard error, which typically has a different par for the lang team. But this feels worth explicitly calling out as an open question to them to get their vibes on it. After all, changing the size of a type can already lead to compilation failures due to repr(transparent).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. P-lang-drag-2 Lang team prioritization drag level 2. proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. T-lang Relevant to the language team, which will review and decide on the RFC.

Projects

None yet

Development

Successfully merging this pull request may close these issues.