aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bors[bot] <bors[bot]@users.noreply.github.com> 2018-09-06 00:44:19 +0000
committerGravatar bors[bot] <bors[bot]@users.noreply.github.com> 2018-09-06 00:44:19 +0000
commit7854e96f69f98570504c701ae860175efc7a25d9 (patch)
treeeaf897d43263606e24b6cca9f5f6620498648dc6
parent0fb051055a0340ad6c5b59d18183c260468e455f (diff)
parent31713aba3f4a4aacd55d12b8a3435334ae61986a (diff)
downloadcortex-m-7854e96f69f98570504c701ae860175efc7a25d9.tar.gz
cortex-m-7854e96f69f98570504c701ae860175efc7a25d9.tar.zst
cortex-m-7854e96f69f98570504c701ae860175efc7a25d9.zip
Merge #90
90: turn macros into attributes r=adamgreig a=japaric This is a PoC implementation of RFC #82. Assuming that we are OK with this implementation we should add some compile fail tests (e.g. using a function with the wrong signature as the entry point) before landing this. Look at the diff of the examples to get an overview of the changes. cc @rust-embedded/cortex-m Co-authored-by: Jorge Aparicio <jorge@japaric.io>
-rw-r--r--cortex-m-rt/.gitignore1
-rw-r--r--cortex-m-rt/.travis.yml37
-rw-r--r--cortex-m-rt/Cargo.toml8
-rw-r--r--cortex-m-rt/ci/script.sh23
-rw-r--r--cortex-m-rt/examples/alignment.rs4
-rw-r--r--cortex-m-rt/examples/data_overflow.rs4
-rw-r--r--cortex-m-rt/examples/device.rs5
-rw-r--r--cortex-m-rt/examples/divergent-default-handler.rs19
-rw-r--r--cortex-m-rt/examples/divergent-exception.rs18
-rw-r--r--cortex-m-rt/examples/entry-static.rs20
-rw-r--r--cortex-m-rt/examples/minimal.rs6
-rw-r--r--cortex-m-rt/examples/override-exception.rs17
-rw-r--r--cortex-m-rt/examples/pre_init.rs8
-rw-r--r--cortex-m-rt/examples/state.rs12
-rw-r--r--cortex-m-rt/examples/unsafe-default-handler.rs16
-rw-r--r--cortex-m-rt/examples/unsafe-entry.rs13
-rw-r--r--cortex-m-rt/examples/unsafe-exception.rs16
-rw-r--r--cortex-m-rt/examples/unsafe-hard-fault.rs18
-rw-r--r--cortex-m-rt/macros/Cargo.toml19
-rw-r--r--cortex-m-rt/macros/src/lib.rs534
-rw-r--r--cortex-m-rt/src/lib.rs292
-rw-r--r--cortex-m-rt/tests/compile-fail/default-handler-bad-signature-1.rs16
-rw-r--r--cortex-m-rt/tests/compile-fail/default-handler-bad-signature-2.rs18
-rw-r--r--cortex-m-rt/tests/compile-fail/default-handler-hidden.rs22
-rw-r--r--cortex-m-rt/tests/compile-fail/default-handler-twice.rs22
-rw-r--r--cortex-m-rt/tests/compile-fail/entry-args.rs13
-rw-r--r--cortex-m-rt/tests/compile-fail/entry-bad-signature-1.rs11
-rw-r--r--cortex-m-rt/tests/compile-fail/entry-bad-signature-2.rs11
-rw-r--r--cortex-m-rt/tests/compile-fail/entry-bad-signature-3.rs13
-rw-r--r--cortex-m-rt/tests/compile-fail/entry-hidden.rs19
-rw-r--r--cortex-m-rt/tests/compile-fail/entry-twice.rs17
-rw-r--r--cortex-m-rt/tests/compile-fail/exception-args.rs16
-rw-r--r--cortex-m-rt/tests/compile-fail/exception-bad-signature-1.rs16
-rw-r--r--cortex-m-rt/tests/compile-fail/exception-bad-signature-2.rs18
-rw-r--r--cortex-m-rt/tests/compile-fail/exception-hidden.rs22
-rw-r--r--cortex-m-rt/tests/compile-fail/exception-twice.rs22
-rw-r--r--cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-1.rs18
-rw-r--r--cortex-m-rt/tests/compile-fail/hard-fault-hidden.rs24
-rw-r--r--cortex-m-rt/tests/compile-fail/hard-fault-twice.rs26
-rw-r--r--cortex-m-rt/tests/compile-fail/pre-init-args.rs16
-rw-r--r--cortex-m-rt/tests/compile-fail/pre-init-bad-signature-1.rs16
-rw-r--r--cortex-m-rt/tests/compile-fail/pre-init-bad-signature-2.rs16
-rw-r--r--cortex-m-rt/tests/compile-fail/pre-init-hidden.rs23
-rw-r--r--cortex-m-rt/tests/compile-fail/pre-init-twice.rs18
-rw-r--r--cortex-m-rt/tests/compiletest.rs21
45 files changed, 1205 insertions, 319 deletions
diff --git a/cortex-m-rt/.gitignore b/cortex-m-rt/.gitignore
index c71d6db..d8748d6 100644
--- a/cortex-m-rt/.gitignore
+++ b/cortex-m-rt/.gitignore
@@ -1,4 +1,5 @@
**/*.rs.bk
+.#*
Cargo.lock
bin/*.after
bin/*.before
diff --git a/cortex-m-rt/.travis.yml b/cortex-m-rt/.travis.yml
index caa29ef..fd45f63 100644
--- a/cortex-m-rt/.travis.yml
+++ b/cortex-m-rt/.travis.yml
@@ -3,24 +3,29 @@ language: rust
matrix:
include:
- env: TARGET=x86_64-unknown-linux-gnu
- rust: stable
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- - env: TARGET=thumbv6m-none-eabi
- rust: stable
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- - env: TARGET=thumbv7m-none-eabi
- rust: stable
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- - env: TARGET=thumbv7em-none-eabi
- rust: stable
+ # TODO switch to 1.30-beta
+ rust: nightly
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
- - env: TARGET=thumbv7em-none-eabihf
- rust: stable
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
+ # TODO enable when 1.30-beta is out
+ # - env: TARGET=thumbv6m-none-eabi
+ # rust: beta
+ # if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
+
+ # TODO enable when 1.30-beta is out
+ # - env: TARGET=thumbv7m-none-eabi
+ # rust: beta
+ # if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
+
+ # TODO enable when 1.30-beta is out
+ # - env: TARGET=thumbv7em-none-eabi
+ # rust: beta
+ # if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
+
+ # TODO enable when 1.30-beta is out
+ # - env: TARGET=thumbv7em-none-eabihf
+ # rust: beta
+ # if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
- env: TARGET=thumbv6m-none-eabi
rust: nightly
diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml
index 0dfbaf7..9cf2c14 100644
--- a/cortex-m-rt/Cargo.toml
+++ b/cortex-m-rt/Cargo.toml
@@ -12,11 +12,15 @@ version = "0.5.3"
[dependencies]
r0 = "0.2.1"
+cortex-m-rt-macros = { path = "macros", version = "0.1.0" }
[dev-dependencies]
-panic-semihosting = "0.3.0"
-panic-abort = "0.2.0"
cortex-m = "0.5.4"
+panic-abort = "0.3.0"
+panic-semihosting = "0.4.0"
+
+[target.'cfg(not(target_os = "none"))'.dev-dependencies]
+compiletest_rs = "0.3.14"
[features]
device = []
diff --git a/cortex-m-rt/ci/script.sh b/cortex-m-rt/ci/script.sh
index 7d1cc67..cecb975 100644
--- a/cortex-m-rt/ci/script.sh
+++ b/cortex-m-rt/ci/script.sh
@@ -5,18 +5,31 @@ main() {
cargo check --target $TARGET --features device
+ if [ $TARGET = x86_64-unknown-linux-gnu ]; then
+ ( cd macros && cargo check && cargo test )
+
+ cargo test --test compiletest
+ fi
+
local examples=(
alignment
- minimal
+ divergent-default-handler
+ divergent-exception
+ entry-static
main
+ minimal
override-exception
pre_init
state
+ unsafe-default-handler
+ unsafe-hard-fault
+ unsafe-entry
+ unsafe-exception
)
local fail_examples=(
data_overflow
)
- if [ $TRAVIS_RUST_VERSION = nightly ]; then
+ if [ $TARGET != x86_64-unknown-linux-gnu ]; then
# linking with GNU LD
for ex in "${examples[@]}"; do
cargo rustc --target $TARGET --example $ex -- \
@@ -48,29 +61,23 @@ main() {
# linking with rustc's LLD
for ex in "${examples[@]}"; do
cargo rustc --target $TARGET --example $ex -- \
- -C linker=rust-lld \
-C link-arg=-Tlink.x
cargo rustc --target $TARGET --example $ex --release -- \
- -C linker=rust-lld \
-C link-arg=-Tlink.x
done
for ex in "${fail_examples[@]}"; do
! cargo rustc --target $TARGET --example $ex -- \
- -C linker=rust-lld \
-C link-arg=-Tlink.x
! cargo rustc --target $TARGET --example $ex --release -- \
- -C linker=rust-lld \
-C link-arg=-Tlink.x
done
cargo rustc --target $TARGET --example device --features device -- \
- -C linker=rust-lld \
-C link-arg=-Tlink.x
cargo rustc --target $TARGET --example device --features device --release -- \
- -C linker=rust-lld \
-C link-arg=-Tlink.x
fi
diff --git a/cortex-m-rt/examples/alignment.rs b/cortex-m-rt/examples/alignment.rs
index 5635851..25d755d 100644
--- a/cortex-m-rt/examples/alignment.rs
+++ b/cortex-m-rt/examples/alignment.rs
@@ -4,13 +4,12 @@
#![no_main]
#![no_std]
-#[macro_use(entry)]
extern crate cortex_m_rt as rt;
extern crate panic_abort;
use core::ptr;
-entry!(main);
+use rt::entry;
static mut BSS1: u16 = 0;
static mut BSS2: u8 = 0;
@@ -19,6 +18,7 @@ static mut DATA2: u16 = 1;
static RODATA1: &[u8; 3] = b"012";
static RODATA2: &[u8; 2] = b"34";
+#[entry]
fn main() -> ! {
unsafe {
let _bss1 = ptr::read_volatile(&BSS1);
diff --git a/cortex-m-rt/examples/data_overflow.rs b/cortex-m-rt/examples/data_overflow.rs
index 396f1c8..ceec18b 100644
--- a/cortex-m-rt/examples/data_overflow.rs
+++ b/cortex-m-rt/examples/data_overflow.rs
@@ -5,13 +5,12 @@
#![no_main]
#![no_std]
-#[macro_use(entry)]
extern crate cortex_m_rt as rt;
extern crate panic_abort;
use core::ptr;
-entry!(main);
+use rt::entry;
// This large static array uses most of .rodata
static RODATA: [u8; 48*1024] = [1u8; 48*1024];
@@ -20,6 +19,7 @@ static RODATA: [u8; 48*1024] = [1u8; 48*1024];
// without also overflowing RAM.
static mut DATA: [u8; 16*1024] = [1u8; 16*1024];
+#[entry]
fn main() -> ! {
unsafe {
let _bigdata = ptr::read_volatile(&RODATA as *const u8);
diff --git a/cortex-m-rt/examples/device.rs b/cortex-m-rt/examples/device.rs
index 4395db2..950a564 100644
--- a/cortex-m-rt/examples/device.rs
+++ b/cortex-m-rt/examples/device.rs
@@ -5,13 +5,12 @@
#![no_main]
#![no_std]
-#[macro_use(entry)]
extern crate cortex_m_rt as rt;
extern crate panic_semihosting;
-// the program entry point
-entry!(main);
+use rt::entry;
+#[entry]
fn main() -> ! {
loop {}
}
diff --git a/cortex-m-rt/examples/divergent-default-handler.rs b/cortex-m-rt/examples/divergent-default-handler.rs
new file mode 100644
index 0000000..cbb8bb1
--- /dev/null
+++ b/cortex-m-rt/examples/divergent-default-handler.rs
@@ -0,0 +1,19 @@
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception]
+fn DefaultHandler(_irqn: i16) -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/examples/divergent-exception.rs b/cortex-m-rt/examples/divergent-exception.rs
new file mode 100644
index 0000000..9998884
--- /dev/null
+++ b/cortex-m-rt/examples/divergent-exception.rs
@@ -0,0 +1,18 @@
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception]
+fn SysTick() -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/examples/entry-static.rs b/cortex-m-rt/examples/entry-static.rs
new file mode 100644
index 0000000..1b2e118
--- /dev/null
+++ b/cortex-m-rt/examples/entry-static.rs
@@ -0,0 +1,20 @@
+//! `static mut` variables local to the entry point are safe to use
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt as rt;
+extern crate panic_semihosting;
+
+use rt::entry;
+
+#[entry]
+fn main() -> ! {
+ static mut COUNT: u32 = 0;
+
+ loop {
+ *COUNT += 1;
+ }
+}
diff --git a/cortex-m-rt/examples/minimal.rs b/cortex-m-rt/examples/minimal.rs
index a036046..6f60180 100644
--- a/cortex-m-rt/examples/minimal.rs
+++ b/cortex-m-rt/examples/minimal.rs
@@ -5,13 +5,13 @@
#![no_main]
#![no_std]
-#[macro_use(entry)]
extern crate cortex_m_rt as rt;
extern crate panic_semihosting;
-// the program entry point
-entry!(main);
+use rt::entry;
+// the program entry point
+#[entry]
fn main() -> ! {
loop {}
}
diff --git a/cortex-m-rt/examples/override-exception.rs b/cortex-m-rt/examples/override-exception.rs
index 2f100a2..3e0af25 100644
--- a/cortex-m-rt/examples/override-exception.rs
+++ b/cortex-m-rt/examples/override-exception.rs
@@ -6,29 +6,24 @@
#![no_std]
extern crate cortex_m;
-#[macro_use(entry, exception)]
extern crate cortex_m_rt as rt;
extern crate panic_semihosting;
use cortex_m::asm;
-use rt::ExceptionFrame;
-
-// the program entry point
-entry!(main);
+use rt::{entry, exception, ExceptionFrame};
+#[entry]
fn main() -> ! {
loop {}
}
-exception!(*, default_handler);
-
-fn default_handler(_irqn: i16) {
+#[exception]
+fn DefaultHandler(_irqn: i16) {
asm::bkpt();
}
-exception!(HardFault, hard_fault);
-
-fn hard_fault(_ef: &ExceptionFrame) -> ! {
+#[exception]
+fn HardFault(_ef: &ExceptionFrame) -> ! {
asm::bkpt();
loop {}
diff --git a/cortex-m-rt/examples/pre_init.rs b/cortex-m-rt/examples/pre_init.rs
index 7258936..00e2f2c 100644
--- a/cortex-m-rt/examples/pre_init.rs
+++ b/cortex-m-rt/examples/pre_init.rs
@@ -4,19 +4,17 @@
#![no_main]
#![no_std]
-#[macro_use(entry, pre_init)]
extern crate cortex_m_rt as rt;
extern crate panic_semihosting;
-pre_init!(disable_watchdog);
+use rt::{entry, pre_init};
+#[pre_init]
unsafe fn disable_watchdog() {
// Do what you need to disable the watchdog.
}
-// the program entry point
-entry!(main);
-
+#[entry]
fn main() -> ! {
loop {}
}
diff --git a/cortex-m-rt/examples/state.rs b/cortex-m-rt/examples/state.rs
index dbacdaf..573914f 100644
--- a/cortex-m-rt/examples/state.rs
+++ b/cortex-m-rt/examples/state.rs
@@ -5,20 +5,20 @@
#![no_main]
#![no_std]
-#[macro_use(entry, exception)]
extern crate cortex_m_rt as rt;
extern crate panic_semihosting;
-// the program entry point
-entry!(main);
+use rt::{entry, exception};
+#[entry]
fn main() -> ! {
loop {}
}
// exception handler with state
-exception!(SysTick, sys_tick, state: u32 = 0);
+#[exception]
+fn SysTick() {
+ static mut STATE: u32 = 0;
-fn sys_tick(state: &mut u32) {
- *state += 1;
+ *STATE += 1;
}
diff --git a/cortex-m-rt/examples/unsafe-default-handler.rs b/cortex-m-rt/examples/unsafe-default-handler.rs
new file mode 100644
index 0000000..48bd31e
--- /dev/null
+++ b/cortex-m-rt/examples/unsafe-default-handler.rs
@@ -0,0 +1,16 @@
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception]
+unsafe fn DefaultHandler(_irqn: i16) {}
diff --git a/cortex-m-rt/examples/unsafe-entry.rs b/cortex-m-rt/examples/unsafe-entry.rs
new file mode 100644
index 0000000..feb6f44
--- /dev/null
+++ b/cortex-m-rt/examples/unsafe-entry.rs
@@ -0,0 +1,13 @@
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::entry;
+
+#[entry]
+unsafe fn foo() -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/examples/unsafe-exception.rs b/cortex-m-rt/examples/unsafe-exception.rs
new file mode 100644
index 0000000..d67f06f
--- /dev/null
+++ b/cortex-m-rt/examples/unsafe-exception.rs
@@ -0,0 +1,16 @@
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception]
+unsafe fn SysTick() {}
diff --git a/cortex-m-rt/examples/unsafe-hard-fault.rs b/cortex-m-rt/examples/unsafe-hard-fault.rs
new file mode 100644
index 0000000..b091d47
--- /dev/null
+++ b/cortex-m-rt/examples/unsafe-hard-fault.rs
@@ -0,0 +1,18 @@
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception]
+unsafe fn HardFault(_ef: &ExceptionFrame) -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/macros/Cargo.toml b/cortex-m-rt/macros/Cargo.toml
new file mode 100644
index 0000000..d0644ee
--- /dev/null
+++ b/cortex-m-rt/macros/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "cortex-m-rt-macros"
+version = "0.1.0"
+authors = ["Jorge Aparicio <jorge@japaric.io>"]
+
+[lib]
+proc-macro = true
+
+[dependencies]
+quote = "0.6.6"
+rand = "0.5.5"
+proc-macro2 = "0.4.15"
+
+[dependencies.syn]
+features = ["extra-traits", "full"]
+version = "0.14.8"
+
+[dev-dependencies]
+cortex-m-rt = { path = ".." }
diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs
new file mode 100644
index 0000000..bd34b04
--- /dev/null
+++ b/cortex-m-rt/macros/src/lib.rs
@@ -0,0 +1,534 @@
+#![deny(warnings)]
+
+extern crate proc_macro;
+extern crate rand;
+#[macro_use]
+extern crate quote;
+extern crate proc_macro2;
+extern crate syn;
+
+use proc_macro2::Span;
+use rand::Rng;
+use syn::{FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, Visibility};
+
+use proc_macro::TokenStream;
+
+/// Attribute to declare the entry point of the program
+///
+/// **IMPORTANT**: This attribute must be used once in the dependency graph and must be used on a
+/// reachable item (i.e. there must be no private modules between the item and the root of the
+/// crate). If the item is in the root of the crate you'll be fine.
+///
+/// The specified function will be called by the reset handler *after* RAM has been initialized. In
+/// the case of the `thumbv7em-none-eabihf` target the FPU will also be enabled before the function
+/// is called.
+///
+/// The type of the specified function must be `[unsafe] fn() -> !` (never ending function)
+///
+/// # Properties
+///
+/// The entry point will be called by the reset handler. The program can't reference to the entry
+/// point, much less invoke it.
+///
+/// `static mut` variables declared within the entry point are safe to access. The compiler can't
+/// prove this is safe so the attribute will help by making a transformation to the source code: for
+/// this reason a variable like `static mut FOO: u32` will become `let FOO: &'static mut u32;`. Note
+/// that `&'static mut` references have move semantics.
+///
+/// # Examples
+///
+/// - Simple entry point
+///
+/// ``` no_run
+/// # #![no_main]
+/// # use cortex_m_rt_macros::entry;
+/// #[entry]
+/// fn main() -> ! {
+/// loop {
+/// /* .. */
+/// }
+/// }
+/// ```
+///
+/// - `static mut` variables local to the entry point are safe to modify.
+///
+/// ``` no_run
+/// # #![no_main]
+/// # use cortex_m_rt_macros::entry;
+/// #[entry]
+/// fn main() -> ! {
+/// static mut FOO: u32 = 0;
+///
+/// let foo: &'static mut u32 = FOO;
+/// assert_eq!(*foo, 0);
+/// *foo = 1;
+/// assert_eq!(*foo, 1);
+///
+/// loop {
+/// /* .. */
+/// }
+/// }
+/// ```
+#[proc_macro_attribute]
+pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
+ let f: ItemFn = syn::parse(input).expect("`#[entry]` must be applied to a function");
+
+ // check the function signature
+ assert!(
+ f.constness.is_none()
+ && f.vis == Visibility::Inherited
+ && f.abi.is_none()
+ && f.decl.inputs.is_empty()
+ && f.decl.generics.params.is_empty()
+ && f.decl.generics.where_clause.is_none()
+ && f.decl.variadic.is_none()
+ && match f.decl.output {
+ ReturnType::Default => false,
+ ReturnType::Type(_, ref ty) => match **ty {
+ Type::Never(_) => true,
+ _ => false,
+ },
+ },
+ "`#[entry]` function must have signature `[unsafe] fn() -> !`"
+ );
+
+ assert!(
+ args.to_string() == "",
+ "`entry` attribute must have no arguments"
+ );
+
+ // XXX should we blacklist other attributes?
+ let attrs = f.attrs;
+ let ident = f.ident;
+ let (statics, stmts) = extract_static_muts(f.block.stmts);
+
+ let vars = statics
+ .into_iter()
+ .map(|var| {
+ let ident = var.ident;
+ // `let` can't shadow a `static mut` so we must give the `static` a different
+ // name. We'll create a new name by appending an underscore to the original name
+ // of the `static`.
+ let mut ident_ = ident.to_string();
+ ident_.push('_');
+ let ident_ = Ident::new(&ident_, Span::call_site());
+ let ty = var.ty;
+ let expr = var.expr;
+
+ quote!(
+ static mut #ident_: #ty = #expr;
+ #[allow(non_snake_case)]
+ let #ident: &'static mut #ty = unsafe { &mut #ident_ };
+ )
+ }).collect::<Vec<_>>();
+
+ quote!(
+ // TODO(forbid) see tests/compile-fail/entry-hidden.rs
+ // #[forbid(dead_code)]
+ #[export_name = "main"]
+ #(#attrs)*
+ pub fn #ident() -> ! {
+ #(#vars)*
+
+ #(#stmts)*
+ }
+ ).into()
+}
+
+/// Attribute to declare an exception handler
+///
+/// **IMPORTANT**: This attribute must be used on reachable items (i.e. there must be no private
+/// modules between the item and the root of the crate). If the item is in the root of the crate
+/// you'll be fine.
+///
+/// # Syntax
+///
+/// ```
+/// # use cortex_m_rt_macros::exception;
+/// #[exception]
+/// fn SysTick() {
+/// // ..
+/// }
+///
+/// # fn main() {}
+/// ```
+///
+/// where the name of the function must be one of:
+///
+/// - `DefaultHandler`
+/// - `NonMaskableInt`
+/// - `HardFault`
+/// - `MemoryManagement` (a)
+/// - `BusFault` (a)
+/// - `UsageFault` (a)
+/// - `SecureFault` (b)
+/// - `SVCall`
+/// - `DebugMonitor` (a)
+/// - `PendSV`
+/// - `SysTick`
+///
+/// (a) Not available on Cortex-M0 variants (`thumbv6m-none-eabi`)
+///
+/// (b) Only available on ARMv8-M
+///
+/// # Usage
+///
+/// `#[exception] fn HardFault(..` sets the hard fault handler. The handler must have signature
+/// `[unsafe] fn(&ExceptionFrame) -> !`. This handler is not allowed to return as that can cause
+/// undefined behavior.
+///
+/// `#[exception] fn DefaultHandler(..` sets the *default* handler. All exceptions which have not
+/// been assigned a handler will be serviced by this handler. This handler must have signature
+/// `[unsafe] fn(irqn: i16) [-> !]`. `irqn` is the IRQ number (See CMSIS); `irqn` will be a negative
+/// number when the handler is servicing a core exception; `irqn` will be a positive number when the
+/// handler is servicing a device specific exception (interrupt).
+///
+/// `#[exception] fn Name(..` overrides the default handler for the exception with the given `Name`.
+/// These handlers must have signature `[unsafe] fn() [-> !]`. When overriding these other exception
+/// it's possible to add state to them by declaring `static mut` variables at the beginning of the
+/// body of the function. These variables will be safe to access from the function body.
+///
+/// # Properties
+///
+/// Exception handlers can only be called by the hardware. Other parts of the program can't refer to
+/// the exception handlers, much less invoke them as if they were functions.
+///
+/// `static mut` variables declared within an exception handler are safe to access and can be used
+/// to preserve state across invocations of the handler. The compiler can't prove this is safe so
+/// the attribute will help by making a transformation to the source code: for this reason a
+/// variable like `static mut FOO: u32` will become `let FOO: &mut u32;`.
+///
+/// # Examples
+///
+/// - Setting the `HardFault` handler
+///
+/// ```
+/// # extern crate cortex_m_rt;
+/// # extern crate cortex_m_rt_macros;
+/// # use cortex_m_rt_macros::exception;
+/// #[exception]
+/// fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! {
+/// // prints the exception frame as a panic message
+/// panic!("{:#?}", ef);
+/// }
+///
+/// # fn main() {}
+/// ```
+///
+/// - Setting the default handler
+///
+/// ```
+/// # use cortex_m_rt_macros::exception;
+/// #[exception]
+/// fn DefaultHandler(irqn: i16) {
+/// println!("IRQn = {}", irqn);
+/// }
+///
+/// # fn main() {}
+/// ```
+///
+/// - Overriding the `SysTick` handler
+///
+/// ```
+/// extern crate cortex_m_rt as rt;
+///
+/// use rt::exception;
+///
+/// #[exception]
+/// fn SysTick() {
+/// static mut COUNT: i32 = 0;
+///
+/// // `COUNT` is safe to access and has type `&mut i32`
+/// *COUNT += 1;
+///
+/// println!("{}", COUNT);
+/// }
+///
+/// # fn main() {}
+/// ```
+#[proc_macro_attribute]
+pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
+ let f: ItemFn = syn::parse(input).expect("`#[exception]` must be applied to a function");
+
+ assert!(
+ args.to_string() == "",
+ "`exception` attribute must have no arguments"
+ );
+
+ let ident = f.ident;
+
+ enum Exception {
+ DefaultHandler,
+ HardFault,
+ Other,
+ }
+
+ let ident_s = ident.to_string();
+ let exn = match &*ident_s {
+ "DefaultHandler" => Exception::DefaultHandler,
+ "HardFault" => Exception::HardFault,
+ // NOTE that at this point we don't check if the exception is available on the target (e.g.
+ // MemoryManagement is not available on Cortex-M0)
+ "NonMaskableInt" | "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault"
+ | "SVCall" | "DebugMonitor" | "PendSV" | "SysTick" => Exception::Other,
+ _ => panic!("{} is not a valid exception name", ident_s),
+ };
+
+ // XXX should we blacklist other attributes?
+ let attrs = f.attrs;
+ let block = f.block;
+ let stmts = block.stmts;
+
+ let hash = random_ident();
+ match exn {
+ Exception::DefaultHandler => {
+ assert!(
+ f.constness.is_none()
+ && f.vis == Visibility::Inherited
+ && f.abi.is_none()
+ && f.decl.inputs.len() == 1
+ && f.decl.generics.params.is_empty()
+ && f.decl.generics.where_clause.is_none()
+ && f.decl.variadic.is_none()
+ && match f.decl.output {
+ ReturnType::Default => true,
+ ReturnType::Type(_, ref ty) => match **ty {
+ Type::Tuple(ref tuple) => tuple.elems.is_empty(),
+ Type::Never(..) => true,
+ _ => false,
+ },
+ },
+ "`DefaultHandler` exception must have signature `[unsafe] fn(i16) [-> !]`"
+ );
+
+ let arg = match f.decl.inputs[0] {
+ FnArg::Captured(ref arg) => arg,
+ _ => unreachable!(),
+ };
+
+ quote!(
+ #[export_name = #ident_s]
+ #(#attrs)*
+ pub extern "C" fn #hash() {
+ extern crate core;
+
+ const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;
+
+ let #arg = unsafe { core::ptr::read(SCB_ICSR) as u8 as i16 - 16 };
+
+ #(#stmts)*
+ }
+ ).into()
+ }
+ Exception::HardFault => {
+ assert!(
+ f.constness.is_none()
+ && f.vis == Visibility::Inherited
+ && f.abi.is_none()
+ && f.decl.inputs.len() == 1
+ && match f.decl.inputs[0] {
+ FnArg::Captured(ref arg) => match arg.ty {
+ Type::Reference(ref r) => {
+ r.lifetime.is_none() && r.mutability.is_none()
+ }
+ _ => false,
+ },
+ _ => false,
+ }
+ && f.decl.generics.params.is_empty()
+ && f.decl.generics.where_clause.is_none()
+ && f.decl.variadic.is_none()
+ && match f.decl.output {
+ ReturnType::Default => false,
+ ReturnType::Type(_, ref ty) => match **ty {
+ Type::Never(_) => true,
+ _ => false,
+ },
+ },
+ "`HardFault` exception must have signature `[unsafe] fn(&ExceptionFrame) -> !`"
+ );
+
+ let arg = match f.decl.inputs[0] {
+ FnArg::Captured(ref arg) => arg,
+ _ => unreachable!(),
+ };
+
+ let pat = &arg.pat;
+
+ quote!(
+ #[export_name = "UserHardFault"]
+ #(#attrs)*
+ pub extern "C" fn #hash(#arg) -> ! {
+ extern crate cortex_m_rt;
+
+ // further type check of the input argument
+ let #pat: &cortex_m_rt::ExceptionFrame = #pat;
+
+ #(#stmts)*
+ }
+ ).into()
+ }
+ Exception::Other => {
+ assert!(
+ f.constness.is_none()
+ && f.vis == Visibility::Inherited
+ && f.abi.is_none()
+ && f.decl.inputs.is_empty()
+ && f.decl.generics.params.is_empty()
+ && f.decl.generics.where_clause.is_none()
+ && f.decl.variadic.is_none()
+ && match f.decl.output {
+ ReturnType::Default => true,
+ ReturnType::Type(_, ref ty) => match **ty {
+ Type::Tuple(ref tuple) => tuple.elems.is_empty(),
+ Type::Never(..) => true,
+ _ => false,
+ },
+ },
+ "`#[exception]` functions other than `DefaultHandler` and `HardFault` must \
+ have signature `[unsafe] fn() [-> !]`"
+ );
+
+ let (statics, stmts) = extract_static_muts(stmts);
+
+ let vars = statics
+ .into_iter()
+ .map(|var| {
+ let ident = var.ident;
+ // `let` can't shadow a `static mut` so we must give the `static` a different
+ // name. We'll create a new name by appending an underscore to the original name
+ // of the `static`.
+ let mut ident_ = ident.to_string();
+ ident_.push('_');
+ let ident_ = Ident::new(&ident_, Span::call_site());
+ let ty = var.ty;
+ let expr = var.expr;
+
+ quote!(
+ static mut #ident_: #ty = #expr;
+ #[allow(non_snake_case)]
+ let #ident: &mut #ty = unsafe { &mut #ident_ };
+ )
+ }).collect::<Vec<_>>();
+
+ quote!(
+ #[export_name = #ident_s]
+ #(#attrs)*
+ pub fn #hash() {
+ extern crate cortex_m_rt;
+
+ // check that this exception actually exists
+ cortex_m_rt::Exception::#ident;
+
+ #(#vars)*
+
+ #(#stmts)*
+ }
+ ).into()
+ }
+ }
+}
+
+/// Attribute to mark which function will be called at the beginning of the reset handler.
+///
+/// **IMPORTANT**: This attribute must be used once in the dependency graph and must be used on a
+/// reachable item (i.e. there must be no private modules between the item and the root of the
+/// crate). If the item is in the root of the crate you'll be fine.
+///
+/// The function must have the signature of `unsafe fn()`.
+///
+/// The function passed will be called before static variables are initialized. Any access of static
+/// variables will result in undefined behavior.
+///
+/// # Examples
+///
+/// ```
+/// # use cortex_m_rt_macros::pre_init;
+/// #[pre_init]
+/// unsafe fn before_main() {
+/// // do something here
+/// }
+///
+/// # fn main() {}
+/// ```
+#[proc_macro_attribute]
+pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream {
+ let f: ItemFn = syn::parse(input).expect("`#[pre_init]` must be applied to a function");
+
+ // check the function signature
+ assert!(
+ f.constness.is_none()
+ && f.vis == Visibility::Inherited
+ && f.unsafety.is_some()
+ && f.abi.is_none()
+ && f.decl.inputs.is_empty()
+ && f.decl.generics.params.is_empty()
+ && f.decl.generics.where_clause.is_none()
+ && f.decl.variadic.is_none()
+ && match f.decl.output {
+ ReturnType::Default => true,
+ ReturnType::Type(_, ref ty) => match **ty {
+ Type::Tuple(ref tuple) => tuple.elems.is_empty(),
+ _ => false,
+ },
+ },
+ "`#[pre_init]` function must have signature `unsafe fn()`"
+ );
+
+ assert!(
+ args.to_string() == "",
+ "`pre_init` attribute must have no arguments"
+ );
+
+ // XXX should we blacklist other attributes?
+ let attrs = f.attrs;
+ let ident = f.ident;
+ let block = f.block;
+
+ quote!(
+ #[export_name = "__pre_init"]
+ #(#attrs)*
+ pub unsafe fn #ident() #block
+ ).into()
+}
+
+// Creates a random identifier
+fn random_ident() -> Ident {
+ let mut rng = rand::thread_rng();
+ Ident::new(
+ &(0..16)
+ .map(|i| {
+ if i == 0 || rng.gen() {
+ ('a' as u8 + rng.gen::<u8>() % 25) as char
+ } else {
+ ('0' as u8 + rng.gen::<u8>() % 10) as char
+ }
+ }).collect::<String>(),
+ Span::call_site(),
+ )
+}
+
+/// Extracts `static mut` vars from the beginning of the given statements
+fn extract_static_muts(stmts: Vec<Stmt>) -> (Vec<ItemStatic>, Vec<Stmt>) {
+ let mut istmts = stmts.into_iter();
+
+ let mut statics = vec![];
+ let mut stmts = vec![];
+ while let Some(stmt) = istmts.next() {
+ match stmt {
+ Stmt::Item(Item::Static(var)) => if var.mutability.is_some() {
+ statics.push(var);
+ } else {
+ stmts.push(Stmt::Item(Item::Static(var)));
+ },
+ _ => {
+ stmts.push(stmt);
+ break;
+ }
+ }
+ }
+
+ stmts.extend(istmts);
+
+ (statics, stmts)
+}
diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs
index 10f60f6..17c4a63 100644
--- a/cortex-m-rt/src/lib.rs
+++ b/cortex-m-rt/src/lib.rs
@@ -14,9 +14,16 @@
//!
//! - Enabling the FPU before the program entry point if the target is `thumbv7em-none-eabihf`.
//!
-//! This crate also provides a mechanism to set exception handlers: see the [`exception!`] macro.
+//! This crate also provides the following attributes:
//!
-//! [`exception!`]: macro.exception.html
+//! - [`#[entry]`] to declare the entry point of the program
+//! - [`#[exception]`] to override an exception handler. If not overridden all exception handlers
+//! default to an infinite loop.
+//! - [`#[pre_init]`] to run code *before* `static` variables are initialized
+//!
+//! [`#[entry]`]: ../cortex_m_rt_macros/fn.entry.html
+//! [`#[exception]`]: ../cortex_m_rt_macros/fn.exception.html
+//! [`#[pre_init]`]: ../cortex_m_rt_macros/fn.pre_init.html
//!
//! # Requirements
//!
@@ -87,25 +94,23 @@
//! This section presents a minimal application built on top of `cortex-m-rt`. Apart from the
//! mandatory `memory.x` linker script describing the memory layout of the device, the hard fault
//! handler and the default exception handler must also be defined somewhere in the dependency
-//! graph (cf. [`exception!`]). In this example we define them in the binary crate:
+//! graph (see [`#[exception]`]). In this example we define them in the binary crate:
//!
//! ``` ignore
//! // IMPORTANT the standard `main` interface is not used because it requires nightly
//! #![no_main]
//! #![no_std]
//!
-//! #[macro_use(entry, exception)]
//! extern crate cortex_m_rt as rt;
//!
//! // makes `panic!` print messages to the host stderr using semihosting
//! extern crate panic_semihosting;
//!
-//! use rt::ExceptionFrame;
+//! use rt::entry;
//!
//! // use `main` as the entry point of this application
-//! entry!(main);
-//!
//! // `main` is not allowed to return
+//! #[entry]
//! fn main() -> ! {
//! // initialization
//!
@@ -113,20 +118,6 @@
//! // application logic
//! }
//! }
-//!
-//! // define the hard fault handler
-//! exception!(HardFault, hard_fault);
-//!
-//! fn hard_fault(ef: &ExceptionFrame) -> ! {
-//! panic!("{:#?}", ef);
-//! }
-//!
-//! // define the default exception handler
-//! exception!(*, default_handler);
-//!
-//! fn default_handler(irqn: i16) {
-//! panic!("unhandled exception (IRQn={})", irqn);
-//! }
//! ```
//!
//! To actually build this program you need to place a `memory.x` linker script somewhere the linker
@@ -207,15 +198,15 @@
//!
//! [`entry!`]: macro.entry.html
//!
-//! - `DefaultHandler`. This is the default handler. This function will contain, or call, the
-//! function you declared in the second argument of `exception!(*, ..)`.
+//! - `DefaultHandler`. This is the default handler. If not overridden using `#[exception] fn
+//! DefaultHandler(..` this will be an infinite loop.
//!
//! - `HardFault`. This is the hard fault handler. This function is simply a trampoline that jumps
-//! into the user defined hard fault handler: `UserHardFault`. The trampoline is required to set up
-//! the pointer to the stacked exception frame.
+//! into the user defined hard fault handler named `UserHardFault`. The trampoline is required to
+//! set up the pointer to the stacked exception frame.
//!
-//! - `UserHardFault`. This is the user defined hard fault handler. This function will contain, or
-//! call, the function you declared in the second argument of `exception!(HardFault, ..)`
+//! - `UserHardFault`. This is the user defined hard fault handler. If not overridden using
+//! `#[exception] fn HardFault(..` this will be an infinite loop.
//!
//! - `__STACK_START`. This is the first entry in the `.vector_table` section. This symbol contains
//! the initial value of the stack pointer; this is where the stack will be located -- the stack
@@ -356,8 +347,8 @@
//! PROVIDE(Bar = DefaultHandler);
//! ```
//!
-//! This weakly aliases both `Foo` and `Bar`. `DefaultHandler` is the default exception handler that
-//! the user provides via `exception!(*, ..)` and that the core exceptions use unless overridden.
+//! This weakly aliases both `Foo` and `Bar`. `DefaultHandler` is the default exception handler and
+//! that the core exceptions use unless overridden.
//!
//! Because this linker script is provided by a dependency of the final application the dependency
//! must contain build script that puts `device.x` somewhere the linker can find. An example of such
@@ -397,11 +388,14 @@
#![deny(warnings)]
#![no_std]
+extern crate cortex_m_rt_macros as macros;
extern crate r0;
use core::fmt;
use core::sync::atomic::{self, Ordering};
+pub use macros::{entry, exception, pre_init};
+
/// Registers stacked (pushed into the stack) during an exception
#[derive(Clone, Copy)]
#[repr(C)]
@@ -474,8 +468,6 @@ pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
#[no_mangle]
pub unsafe extern "C" fn Reset() -> ! {
extern "C" {
- // This symbol will be provided by the user via the `entry!` macro
- fn main() -> !;
// These symbols come from `link.x`
static mut __sbss: u32;
@@ -485,11 +477,17 @@ pub unsafe extern "C" fn Reset() -> ! {
static mut __edata: u32;
static __sidata: u32;
+ }
+
+ extern "Rust" {
+ // This symbol will be provided by the user via `#[entry]`
+ fn main() -> !;
+
+ // This symbol will be provided by the user via `#[pre_init]`
fn __pre_init();
}
- let pre_init: unsafe extern "C" fn() = __pre_init;
- pre_init();
+ __pre_init();
// Initialize RAM
r0::zero_bss(&mut __sbss, &mut __ebss);
@@ -551,31 +549,6 @@ pub unsafe extern "C" fn DefaultHandler_() -> ! {
#[no_mangle]
pub unsafe extern "C" fn DefaultPreInit() {}
-/// Macro to define the entry point of the program
-///
-/// **NOTE** This macro must be invoked once and must be invoked from an accessible module, ideally
-/// from the root of the crate.
-///
-/// Usage: `entry!(path::to::entry::point)`
-///
-/// The specified function will be called by the reset handler *after* RAM has been initialized. In
-/// the case of the `thumbv7em-none-eabihf` target the FPU will also be enabled before the function
-/// is called.
-///
-/// The signature of the specified function must be `fn() -> !` (never ending function)
-#[macro_export]
-macro_rules! entry {
- ($path:expr) => {
- #[export_name = "main"]
- pub extern "C" fn __impl_main() -> ! {
- // validate the signature of the program entry point
- let f: fn() -> ! = $path;
-
- f()
- }
- };
-}
-
/* Exceptions */
#[doc(hidden)]
pub enum Exception {
@@ -721,206 +694,3 @@ pub static __INTERRUPTS: [unsafe extern "C" fn(); 32] = [{
DefaultHandler
}; 32];
-
-/// Macro to set or override a processor core exception handler
-///
-/// **NOTE** This macro must be invoked from an accessible module, ideally from the root of the
-/// crate.
-///
-/// # Syntax
-///
-/// ``` ignore
-/// exception!(
-/// // Name of the exception
-/// $Name:ident,
-///
-/// // Path to the exception handler (a function)
-/// $handler:expr,
-///
-/// // Optional, state preserved across invocations of the handler
-/// state: $State:ty = $initial_state:expr,
-/// );
-/// ```
-///
-/// where `$Name` can be one of:
-///
-/// - `*`
-/// - `NonMaskableInt`
-/// - `HardFault`
-/// - `MemoryManagement` (a)
-/// - `BusFault` (a)
-/// - `UsageFault` (a)
-/// - `SecureFault` (b)
-/// - `SVCall`
-/// - `DebugMonitor` (a)
-/// - `PendSV`
-/// - `SysTick`
-///
-/// (a) Not available on Cortex-M0 variants (`thumbv6m-none-eabi`)
-///
-/// (b) Only available on ARMv8-M
-///
-/// # Usage
-///
-/// `exception!(HardFault, ..)` sets the hard fault handler. The handler must have signature
-/// `fn(&ExceptionFrame) -> !`. This handler is not allowed to return as that can cause undefined
-/// behavior. It's mandatory to set the `HardFault` handler somewhere in the dependency graph of an
-/// application.
-///
-/// `exception!(*, ..)` sets the *default* handler. All exceptions which have not been assigned a
-/// handler will be serviced by this handler. This handler must have signature `fn(irqn: i16)`.
-/// `irqn` is the IRQ number (cf. CMSIS); `irqn` will be a negative number when the handler is
-/// servicing a core exception; `irqn` will be a positive number when the handler is servicing a
-/// device specific exception (interrupt). It's mandatory to set the default handler somewhere
-/// in the dependency graph of an application.
-///
-/// `exception!($Exception, ..)` overrides the default handler for `$Exception`. All exceptions,
-/// except for `HardFault`, can be assigned some `$State`.
-///
-/// # Examples
-///
-/// - Setting the `HardFault` handler
-///
-/// ```
-/// #[macro_use(exception)]
-/// extern crate cortex_m_rt as rt;
-///
-/// use rt::ExceptionFrame;
-///
-/// exception!(HardFault, hard_fault);
-///
-/// fn hard_fault(ef: &ExceptionFrame) -> ! {
-/// // prints the exception frame as a panic message
-/// panic!("{:#?}", ef);
-/// }
-///
-/// # fn main() {}
-/// ```
-///
-/// - Setting the default handler
-///
-/// ```
-/// #[macro_use(exception)]
-/// extern crate cortex_m_rt as rt;
-///
-/// exception!(*, default_handler);
-///
-/// fn default_handler(irqn: i16) {
-/// println!("IRQn = {}", irqn);
-/// }
-///
-/// # fn main() {}
-/// ```
-///
-/// - Overriding the `SysTick` handler
-///
-/// ```
-/// #[macro_use(exception)]
-/// extern crate cortex_m_rt as rt;
-///
-/// exception!(SysTick, sys_tick, state: u32 = 0);
-///
-/// fn sys_tick(count: &mut u32) {
-/// println!("count = {}", *count);
-///
-/// *count += 1;
-/// }
-///
-/// # fn main() {}
-/// ```
-#[macro_export]
-macro_rules! exception {
- (* , $handler:expr) => {
- #[allow(unsafe_code)]
- #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible
- #[no_mangle]
- pub unsafe extern "C" fn DefaultHandler() {
- extern crate core;
-
- // validate the signature of the user provided handler
- let f: fn(i16) = $handler;
-
- const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;
-
- // NOTE not volatile so the compiler can opt the load operation away if the value is
- // unused
- f(core::ptr::read(SCB_ICSR) as u8 as i16 - 16)
- }
- };
-
- (HardFault, $handler:expr) => {
- #[allow(unsafe_code)]
- #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible
- #[no_mangle]
- pub unsafe extern "C" fn UserHardFault(ef: &$crate::ExceptionFrame) {
- // validate the signature of the user provided handler
- let f: fn(&$crate::ExceptionFrame) -> ! = $handler;
-
- f(ef)
- }
- };
-
- ($Name:ident, $handler:expr,state: $State:ty = $initial_state:expr) => {
- #[allow(unsafe_code)]
- #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible
- #[no_mangle]
- pub unsafe extern "C" fn $Name() {
- static mut STATE: $State = $initial_state;
-
- // check that this exception exists
- let _ = $crate::Exception::$Name;
-
- // validate the signature of the user provided handler
- let f: fn(&mut $State) = $handler;
-
- f(&mut STATE)
- }
- };
-
- ($Name:ident, $handler:expr) => {
- #[allow(unsafe_code)]
- #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible
- #[no_mangle]
- pub unsafe extern "C" fn $Name() {
- // check that this exception exists
- let _ = $crate::Exception::$Name;
-
- // validate the signature of the user provided handler
- let f: fn() = $handler;
-
- f()
- }
- };
-}
-
-/// Macro to set the function to be called at the beginning of the reset handler.
-///
-/// The function must have the signature of `unsafe fn()`.
-///
-/// The function passed will be called before static variables are initialized. Any access of static
-/// variables will result in undefined behavior.
-///
-/// # Examples
-///
-/// ``` ignore
-/// pre_init!(foo::bar);
-///
-/// mod foo {
-/// pub unsafe fn bar() {
-/// // do something here
-/// }
-/// }
-/// ```
-#[macro_export]
-macro_rules! pre_init {
- ($handler:path) => {
- #[allow(unsafe_code)]
- #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible
- #[no_mangle]
- pub unsafe extern "C" fn __pre_init() {
- // validate user handler
- let f: unsafe fn() = $handler;
- f();
- }
- };
-}
diff --git a/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-1.rs
new file mode 100644
index 0000000..037e9c8
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-1.rs
@@ -0,0 +1,16 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception] //~ ERROR custom attribute panicked
+//~^ HELP `DefaultHandler` exception must have signature `[unsafe] fn(i16) [-> !]`
+fn DefaultHandler(_irqn: i16, undef: u32) {}
diff --git a/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-2.rs
new file mode 100644
index 0000000..8fc4a7e
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-2.rs
@@ -0,0 +1,18 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception] //~ ERROR custom attribute panicked
+//~^ HELP `DefaultHandler` exception must have signature `[unsafe] fn(i16) [-> !]`
+fn DefaultHandler(_irqn: i16) -> u32 {
+ 0
+}
diff --git a/cortex-m-rt/tests/compile-fail/default-handler-hidden.rs b/cortex-m-rt/tests/compile-fail/default-handler-hidden.rs
new file mode 100644
index 0000000..059c10b
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/default-handler-hidden.rs
@@ -0,0 +1,22 @@
+// ignore-test :sadface: it's not possible to prevent this user error at compile time
+// see rust-lang/rust#53975 for details
+
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+mod hidden {
+ use cortex_m_rt::exception;
+
+ #[exception]
+ fn DefaultHandler(_irqn: i16) {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/default-handler-twice.rs b/cortex-m-rt/tests/compile-fail/default-handler-twice.rs
new file mode 100644
index 0000000..7d6ad98
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/default-handler-twice.rs
@@ -0,0 +1,22 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception]
+fn DefaultHandler(_irqn: i16) {}
+
+pub mod reachable {
+ use cortex_m_rt::exception;
+
+ #[exception] //~ ERROR symbol `DefaultHandler` is already defined
+ fn DefaultHandler(_irqn: i16) {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/entry-args.rs b/cortex-m-rt/tests/compile-fail/entry-args.rs
new file mode 100644
index 0000000..07cb4bd
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/entry-args.rs
@@ -0,0 +1,13 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::entry;
+
+#[entry(foo)] //~ ERROR custom attribute panicked
+//~^ HELP `entry` attribute must have no arguments
+fn foo() -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/entry-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/entry-bad-signature-1.rs
new file mode 100644
index 0000000..5eeb49f
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/entry-bad-signature-1.rs
@@ -0,0 +1,11 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::entry;
+
+#[entry] //~ ERROR custom attribute panicked
+//~^ HELP `#[entry]` function must have signature `[unsafe] fn() -> !`
+fn foo() {}
diff --git a/cortex-m-rt/tests/compile-fail/entry-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/entry-bad-signature-2.rs
new file mode 100644
index 0000000..18bbaed
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/entry-bad-signature-2.rs
@@ -0,0 +1,11 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::entry;
+
+#[entry] //~ ERROR custom attribute panicked
+//~^ HELP `#[entry]` function must have signature `[unsafe] fn() -> !`
+fn foo(undef: i32) -> ! {}
diff --git a/cortex-m-rt/tests/compile-fail/entry-bad-signature-3.rs b/cortex-m-rt/tests/compile-fail/entry-bad-signature-3.rs
new file mode 100644
index 0000000..09b75e9
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/entry-bad-signature-3.rs
@@ -0,0 +1,13 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::entry;
+
+#[entry] //~ ERROR custom attribute panicked
+//~^ HELP `#[entry]` function must have signature `[unsafe] fn() -> !`
+extern "C" fn foo() -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/entry-hidden.rs b/cortex-m-rt/tests/compile-fail/entry-hidden.rs
new file mode 100644
index 0000000..7d74063
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/entry-hidden.rs
@@ -0,0 +1,19 @@
+// ignore-test :sadface: it's not possible to prevent this user error at compile time
+// see rust-lang/rust#53975 for details
+
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+mod hidden {
+ use cortex_m_rt::entry;
+
+ // this function needs to be "reachable" (all modules between it and the crate root must be
+ // `pub`) or linking will fail
+ #[entry]
+ fn foo() -> ! { //~ ERROR function is never used
+ loop {}
+ }
+}
diff --git a/cortex-m-rt/tests/compile-fail/entry-twice.rs b/cortex-m-rt/tests/compile-fail/entry-twice.rs
new file mode 100644
index 0000000..b2819f6
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/entry-twice.rs
@@ -0,0 +1,17 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::entry;
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[entry] //~ ERROR symbol `main` is already defined
+fn bar() -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/exception-args.rs b/cortex-m-rt/tests/compile-fail/exception-args.rs
new file mode 100644
index 0000000..85613ff
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/exception-args.rs
@@ -0,0 +1,16 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception(SysTick)] //~ ERROR custom attribute panicked
+//~^ HELP `exception` attribute must have no arguments
+fn SysTick() {}
diff --git a/cortex-m-rt/tests/compile-fail/exception-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/exception-bad-signature-1.rs
new file mode 100644
index 0000000..966493e
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/exception-bad-signature-1.rs
@@ -0,0 +1,16 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception] //~ ERROR custom attribute panicked
+//~^ HELP `#[exception]` functions other than `DefaultHandler` and `HardFault` must have signature `[unsafe] fn() [-> !]`
+fn SysTick(undef: u32) {}
diff --git a/cortex-m-rt/tests/compile-fail/exception-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/exception-bad-signature-2.rs
new file mode 100644
index 0000000..8504771
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/exception-bad-signature-2.rs
@@ -0,0 +1,18 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception] //~ ERROR custom attribute panicked
+//~^ HELP `#[exception]` functions other than `DefaultHandler` and `HardFault` must have signature `[unsafe] fn() [-> !]`
+fn SysTick() -> u32 {
+ 0
+}
diff --git a/cortex-m-rt/tests/compile-fail/exception-hidden.rs b/cortex-m-rt/tests/compile-fail/exception-hidden.rs
new file mode 100644
index 0000000..053c81c
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/exception-hidden.rs
@@ -0,0 +1,22 @@
+// ignore-test :sadface: it's not possible to prevent this user error at compile time
+// see rust-lang/rust#53975 for details
+
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+mod hidden {
+ use cortex_m_rt::exception;
+
+ #[exception]
+ fn SysTick() {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/exception-twice.rs b/cortex-m-rt/tests/compile-fail/exception-twice.rs
new file mode 100644
index 0000000..5377fce
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/exception-twice.rs
@@ -0,0 +1,22 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception]
+fn SysTick() {}
+
+pub mod reachable {
+ use cortex_m_rt::exception;
+
+ #[exception] //~ ERROR symbol `SysTick` is already defined
+ fn SysTick() {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-1.rs
new file mode 100644
index 0000000..83fda5f
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-1.rs
@@ -0,0 +1,18 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception] //~ ERROR custom attribute panicked
+//~^ HELP `HardFault` exception must have signature `[unsafe] fn(&ExceptionFrame) -> !`
+fn HardFault(_ef: &ExceptionFrame, undef: u32) -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/hard-fault-hidden.rs b/cortex-m-rt/tests/compile-fail/hard-fault-hidden.rs
new file mode 100644
index 0000000..31237c4
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/hard-fault-hidden.rs
@@ -0,0 +1,24 @@
+// ignore-test :sadface: it's not possible to prevent this user error at compile time
+// see rust-lang/rust#53975 for details
+
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+mod hidden {
+ use cortex_m_rt::{exception, ExceptionFrame};
+
+ #[exception]
+ fn HardFault(_ef: &ExceptionFrame) -> ! {
+ loop {}
+ }
+}
diff --git a/cortex-m-rt/tests/compile-fail/hard-fault-twice.rs b/cortex-m-rt/tests/compile-fail/hard-fault-twice.rs
new file mode 100644
index 0000000..90270e5
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/hard-fault-twice.rs
@@ -0,0 +1,26 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, exception, ExceptionFrame};
+
+#[entry]
+fn foo() -> ! {
+ loop {}
+}
+
+#[exception]
+fn HardFault(_ef: &ExceptionFrame) -> ! {
+ loop {}
+}
+
+pub mod reachable {
+ use cortex_m_rt::{exception, ExceptionFrame};
+
+ #[exception] //~ ERROR symbol `UserHardFault` is already defined
+ fn HardFault(_ef: &ExceptionFrame) -> ! {
+ loop {}
+ }
+}
diff --git a/cortex-m-rt/tests/compile-fail/pre-init-args.rs b/cortex-m-rt/tests/compile-fail/pre-init-args.rs
new file mode 100644
index 0000000..716b211
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/pre-init-args.rs
@@ -0,0 +1,16 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, pre_init};
+
+#[pre_init(foo)] //~ ERROR custom attribute panicked
+//~^ HELP `pre_init` attribute must have no arguments
+unsafe fn foo() {}
+
+#[entry]
+fn baz() -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-1.rs
new file mode 100644
index 0000000..58d3022
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-1.rs
@@ -0,0 +1,16 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, pre_init};
+
+#[pre_init] //~ ERROR custom attribute panicked
+//~^ HELP `#[pre_init]` function must have signature `unsafe fn()`
+fn foo() {}
+
+#[entry]
+fn bar() -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-2.rs
new file mode 100644
index 0000000..e47ed59
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/pre-init-bad-signature-2.rs
@@ -0,0 +1,16 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, pre_init};
+
+#[pre_init] //~ ERROR custom attribute panicked
+//~^ HELP `#[pre_init]` function must have signature `unsafe fn()`
+unsafe fn foo(undef: i32) {}
+
+#[entry]
+fn bar() -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/pre-init-hidden.rs b/cortex-m-rt/tests/compile-fail/pre-init-hidden.rs
new file mode 100644
index 0000000..f512d62
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/pre-init-hidden.rs
@@ -0,0 +1,23 @@
+// ignore-test :sadface: it's not possible to prevent this user error at compile time
+// see rust-lang/rust#53975 for details
+
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+mod hidden {
+ use cortex_m_rt::pre_init;
+
+ // this function needs to be "reachable" (all modules between it and the crate root must be
+ // `pub`) or the function will be ignored
+ #[entry]
+ unsafe fn pre_init() {} //~ ERROR function is never used
+}
+
+#[entry]
+fn foo() -> ! {
+ //~ ERROR function is never used
+ loop {}
+}
diff --git a/cortex-m-rt/tests/compile-fail/pre-init-twice.rs b/cortex-m-rt/tests/compile-fail/pre-init-twice.rs
new file mode 100644
index 0000000..5fb1ade
--- /dev/null
+++ b/cortex-m-rt/tests/compile-fail/pre-init-twice.rs
@@ -0,0 +1,18 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+extern crate panic_semihosting;
+
+use cortex_m_rt::{entry, pre_init};
+
+#[pre_init]
+unsafe fn foo() {}
+
+#[pre_init] //~ ERROR symbol `__pre_init` is already defined
+unsafe fn bar() {}
+
+#[entry]
+fn baz() -> ! {
+ loop {}
+}
diff --git a/cortex-m-rt/tests/compiletest.rs b/cortex-m-rt/tests/compiletest.rs
new file mode 100644
index 0000000..6cea3ac
--- /dev/null
+++ b/cortex-m-rt/tests/compiletest.rs
@@ -0,0 +1,21 @@
+extern crate compiletest_rs as compiletest;
+
+use std::path::PathBuf;
+
+fn run_mode(mode: &'static str) {
+ let mut config = compiletest::Config::default();
+
+ config.mode = mode.parse().expect("Invalid mode");
+ config.src_base = PathBuf::from(format!("tests/{}", mode));
+ // config.link_deps(); // Populate config.target_rustcflags with dependencies on the path
+ config.target_rustcflags =
+ Some("-L target/debug -L target/debug/deps -C panic=abort".to_owned());
+ // config.clean_rmeta(); // If your tests import the parent crate, this helps with E0464
+
+ compiletest::run_tests(&config);
+}
+
+#[test]
+fn compile_test() {
+ run_mode("compile-fail");
+}