cortex-m
cortex-m copied to clipboard
Any reason why the `#[exception]` macro disallows `extern "C" fn`s?
I have the following signature for my HardFault handler:
#[exception(trampoline = false)]
#[naked]
unsafe fn HardFault() -> ! {
...
This triggers the compiler warning
warning: Rust ABI is unsupported in naked functions note:
#[warn(undefined_naked_function_abi)]on by default
Which could be avoided by modifying the signature to be:
#[exception(trampoline = false)]
#[naked]
unsafe extern "C" fn HardFault() -> ! {
...
But this is not accepted by the macro:
HardFaulthandler must have signatureunsafe fn() -> !
Is there any reason for the macro to not allow specifying the ABI?
Also, could someone point me as to why does the following not produce the same warning?
#[pre_init]
#[naked]
unsafe fn pre_init() {
...
Do you still get this error on the latest nightly? I can compile the following OK:
#![no_std]
#![no_main]
#![feature(naked_functions)]
#[naked]
#[cortex_m_rt::pre_init]
unsafe fn pre_init() {
core::arch::naked_asm!("nop");
}
#[naked]
#[cortex_m_rt::exception(trampoline=false)]
unsafe fn HardFault() -> ! {
core::arch::naked_asm!("nop");
}
I don't think there's any reason to prohibit extern "C" functions here, but because the macro already generates the actual ISR and makes it extern "C" as appropriate, I don't think we should need to change the user interface. We added naked to the list of allowed attributes a while ago and what it means has changed as it moves towards stabilisation, so we should probably just make sure that the attribute does what's expected in this context.
You might find using cargo expand illuminating, for example the above expands to:
#[export_name = "__pre_init"]
#[allow(missing_docs)]
#[naked]
pub unsafe fn pre_init() {
asm!("nop");
}
#[doc(hidden)]
#[export_name = "_HardFault"]
unsafe extern "C" fn __cortex_m_rt_HardFault_trampoline() {}
#[export_name = "HardFault"]
#[link_section = ".HardFault.user"]
#[naked]
unsafe fn __cortex_m_rt_HardFault() -> ! {
asm!("nop");
}
in cortex-m-rt, there's a bl __pre_init which is how the pre-init function gets called, while HardFault is referred to from as extern "C" fn HardFault(); and placed into the vector table so will be called directly by hardware. The _HardFault is empty and just for diagnostics. It looks like this should all work as expected with naked functions.
Strange, with an empty project containing just the code from your test and rustc 1.86.0-nightly (a9730c3b5 2025-02-05) I still get:
error:
#[panic_handler]function required, but not foundwarning: Rust ABI is unsupported in naked functions --> src/main.rs:13:1 | 13 | unsafe fn HardFault() -> ! { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note:
#[warn(undefined_naked_function_abi)]on by default
Arguably it's an error that we accept non-extern "C" functions. Rust functions have no defined ABI, and yet the hardware assumes it is entering a function that preserves R4-R11 and returns to LR.
Generally the cortex-m-rt macros add extern "C" to the generated function/trampoline, which is why we don't require the user write it, but it looks like the pre_init macro doesn't add extern "C" (but should), probably a leftover from when it was called from Rust code instead of the startup assembly.
Additionally it looks like in latest stable Rust we do get the latest version of this error, because #[unsafe(naked)] ends up applied to the non-C functions:
#[export_name = "__pre_init"]
#[allow(missing_docs)]
#[unsafe(naked)]
pub unsafe fn pre_init() {
asm!("nop");
}
#[doc(hidden)]
#[export_name = "_HardFault"]
unsafe extern "C" fn __cortex_m_rt_HardFault_trampoline() {}
#[export_name = "HardFault"]
#[link_section = ".HardFault.user"]
#[unsafe(naked)]
unsafe fn __cortex_m_rt_HardFault() -> ! {
asm!("nop");
}
I think the fix is probably to have the macros generate pre_init and the HardFault exception as extern "C" too.