Skip to content
This repository was archived by the owner on Jan 24, 2022. It is now read-only.

Commit 4974583

Browse files
committed
make static mut variables safe to access in the entry point
extend / update documentation
1 parent 0e6f310 commit 4974583

File tree

2 files changed

+128
-47
lines changed

2 files changed

+128
-47
lines changed

macros/src/lib.rs

Lines changed: 110 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ extern crate syn;
99

1010
use proc_macro2::Span;
1111
use rand::Rng;
12-
use syn::{FnArg, Ident, Item, ItemFn, ReturnType, Stmt, Type, Visibility};
12+
use syn::{FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, Visibility};
1313

1414
use proc_macro::TokenStream;
1515

@@ -24,13 +24,45 @@ use proc_macro::TokenStream;
2424
///
2525
/// The type of the specified function must be `fn() -> !` (never ending function)
2626
///
27+
/// # Properties
28+
///
29+
/// The entry point will be called by the reset handler. The program can't reference to the entry
30+
/// point, much less invoke it.
31+
///
32+
/// `static mut` variables declared within the entry point are safe to access. The compiler can't
33+
/// prove this is safe so the attribute will help by making a transformation to the source code: for
34+
/// this reason a variable like `static mut FOO: u32` will become `let FOO: &'static mut u32;`. Note
35+
/// that `&'static mut` references have move semantics.
36+
///
2737
/// # Examples
2838
///
39+
/// - Simple entry point
40+
///
41+
/// ``` no_run
42+
/// # #![no_main]
43+
/// # use cortex_m_rt_macros::entry;
44+
/// #[entry]
45+
/// fn main() -> ! {
46+
/// loop {
47+
/// /* .. */
48+
/// }
49+
/// }
50+
/// ```
51+
///
52+
/// - `static mut` variables local to the entry point are safe to modify.
53+
///
2954
/// ``` no_run
3055
/// # #![no_main]
3156
/// # use cortex_m_rt_macros::entry;
3257
/// #[entry]
3358
/// fn main() -> ! {
59+
/// static mut FOO: u32 = 0;
60+
///
61+
/// let foo: &'static mut u32 = FOO;
62+
/// assert_eq!(*foo, 0);
63+
/// *foo = 1;
64+
/// assert_eq!(*foo, 1);
65+
///
3466
/// loop {
3567
/// /* .. */
3668
/// }
@@ -69,12 +101,36 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
69101
// XXX should we blacklist other attributes?
70102
let attrs = f.attrs;
71103
let ident = f.ident;
72-
let block = f.block;
104+
let (statics, stmts) = extract_static_muts(f.block.stmts);
105+
106+
let vars = statics
107+
.into_iter()
108+
.map(|var| {
109+
let ident = var.ident;
110+
// `let` can't shadow a `static mut` so we must give the `static` a different
111+
// name. We'll create a new name by appending an underscore to the original name
112+
// of the `static`.
113+
let mut ident_ = ident.to_string();
114+
ident_.push('_');
115+
let ident_ = Ident::new(&ident_, Span::call_site());
116+
let ty = var.ty;
117+
let expr = var.expr;
118+
119+
quote!(
120+
static mut #ident_: #ty = #expr;
121+
#[allow(non_snake_case, unsafe_code)]
122+
let #ident: &'static mut #ty = unsafe { &mut #ident_ };
123+
)
124+
}).collect::<Vec<_>>();
73125

74126
quote!(
75127
#[export_name = "main"]
76128
#(#attrs)*
77-
pub fn #ident() -> ! #block
129+
pub fn #ident() -> ! {
130+
#(#vars)*
131+
132+
#(#stmts)*
133+
}
78134
).into()
79135
}
80136

@@ -130,8 +186,15 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
130186
/// mut` variables at the beginning of the body of the function. These variables will be safe to
131187
/// access from the function body.
132188
///
189+
/// # Properties
190+
///
133191
/// Exception handlers can only be called by the hardware. Other parts of the program can't refer to
134-
/// the exception handler much less invoke them as if they were functions.
192+
/// the exception handlers, much less invoke them as if they were functions.
193+
///
194+
/// `static mut` variables declared within an exception handler are safe to access and can be used
195+
/// to preserve state across invocations of the handler. The compiler can't prove this is safe so
196+
/// the attribute will help by making a transformation to the source code: for this reason a
197+
/// variable like `static mut FOO: u32` will become `let FOO: &mut u32;`.
135198
///
136199
/// # Examples
137200
///
@@ -215,17 +278,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
215278
let block = f.block;
216279
let stmts = block.stmts;
217280

218-
let mut rng = rand::thread_rng();
219-
let hash = (0..16)
220-
.map(|i| {
221-
if i == 0 || rng.gen() {
222-
('a' as u8 + rng.gen::<u8>() % 25) as char
223-
} else {
224-
('0' as u8 + rng.gen::<u8>() % 10) as char
225-
}
226-
}).collect::<String>();
227-
let hash = Ident::new(&hash, Span::call_site());
228-
281+
let hash = random_ident();
229282
match exn {
230283
Exception::DefaultHandler => {
231284
assert!(
@@ -336,27 +389,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
336389
have signature `fn()`"
337390
);
338391

339-
// Collect all the `static mut` at the beginning of the function body. We'll make them
340-
// safe
341-
let mut istmts = stmts.into_iter();
342-
343-
let mut statics = vec![];
344-
let mut stmts = vec![];
345-
while let Some(stmt) = istmts.next() {
346-
match stmt {
347-
Stmt::Item(Item::Static(var)) => if var.mutability.is_some() {
348-
statics.push(var);
349-
} else {
350-
stmts.push(Stmt::Item(Item::Static(var)));
351-
},
352-
_ => {
353-
stmts.push(stmt);
354-
break;
355-
}
356-
}
357-
}
358-
359-
stmts.extend(istmts);
392+
let (statics, stmts) = extract_static_muts(stmts);
360393

361394
let vars = statics
362395
.into_iter()
@@ -455,3 +488,44 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream {
455488
pub unsafe fn #ident() #block
456489
).into()
457490
}
491+
492+
// Creates a random identifier
493+
fn random_ident() -> Ident {
494+
let mut rng = rand::thread_rng();
495+
Ident::new(
496+
&(0..16)
497+
.map(|i| {
498+
if i == 0 || rng.gen() {
499+
('a' as u8 + rng.gen::<u8>() % 25) as char
500+
} else {
501+
('0' as u8 + rng.gen::<u8>() % 10) as char
502+
}
503+
}).collect::<String>(),
504+
Span::call_site(),
505+
)
506+
}
507+
508+
/// Extracts `static mut` vars from the beginning of the given statements
509+
fn extract_static_muts(stmts: Vec<Stmt>) -> (Vec<ItemStatic>, Vec<Stmt>) {
510+
let mut istmts = stmts.into_iter();
511+
512+
let mut statics = vec![];
513+
let mut stmts = vec![];
514+
while let Some(stmt) = istmts.next() {
515+
match stmt {
516+
Stmt::Item(Item::Static(var)) => if var.mutability.is_some() {
517+
statics.push(var);
518+
} else {
519+
stmts.push(Stmt::Item(Item::Static(var)));
520+
},
521+
_ => {
522+
stmts.push(stmt);
523+
break;
524+
}
525+
}
526+
}
527+
528+
stmts.extend(istmts);
529+
530+
(statics, stmts)
531+
}

src/lib.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,16 @@
1414
//!
1515
//! - Enabling the FPU before the program entry point if the target is `thumbv7em-none-eabihf`.
1616
//!
17-
//! This crate also provides a mechanism to set exception handlers: see the [`exception!`] macro.
17+
//! This crate also provides the following attributes:
1818
//!
19-
//! [`exception!`]: macro.exception.html
19+
//! - [`#[entry]`] to declare the entry point of the program
20+
//! - [`#[exception]`] to override an exception handler. If not overridden all exception handlers
21+
//! default to an infinite loop.
22+
//! - [`#[pre_init]`] to run code *before* `static` variables are initialized
23+
//!
24+
//! [`#[entry]`]: ../cortex_m_rt_macros/fn.entry.html
25+
//! [`#[exception]`]: ../cortex_m_rt_macros/fn.exception.html
26+
//! [`#[pre_init]`]: ../cortex_m_rt_macros/fn.pre_init.html
2027
//!
2128
//! # Requirements
2229
//!
@@ -87,7 +94,7 @@
8794
//! This section presents a minimal application built on top of `cortex-m-rt`. Apart from the
8895
//! mandatory `memory.x` linker script describing the memory layout of the device, the hard fault
8996
//! handler and the default exception handler must also be defined somewhere in the dependency
90-
//! graph (cf. [`exception!`]). In this example we define them in the binary crate:
97+
//! graph (see [`#[exception]`]). In this example we define them in the binary crate:
9198
//!
9299
//! ``` ignore
93100
//! // IMPORTANT the standard `main` interface is not used because it requires nightly
@@ -191,15 +198,15 @@
191198
//!
192199
//! [`entry!`]: macro.entry.html
193200
//!
194-
//! - `DefaultHandler`. This is the default handler. This function will contain, or call, the
195-
//! function you declared in the second argument of `exception!(*, ..)`.
201+
//! - `DefaultHandler`. This is the default handler. If not overridden using `#[exception] fn
202+
//! DefaultHandler(..` this will be an infinite loop.
196203
//!
197204
//! - `HardFault`. This is the hard fault handler. This function is simply a trampoline that jumps
198-
//! into the user defined hard fault handler: `UserHardFault`. The trampoline is required to set up
199-
//! the pointer to the stacked exception frame.
205+
//! into the user defined hard fault handler named `UserHardFault`. The trampoline is required to
206+
//! set up the pointer to the stacked exception frame.
200207
//!
201-
//! - `UserHardFault`. This is the user defined hard fault handler. This function will contain, or
202-
//! call, the function you declared in the second argument of `exception!(HardFault, ..)`
208+
//! - `UserHardFault`. This is the user defined hard fault handler. If not overridden using
209+
//! `#[exception] fn HardFault(..` this will be an infinite loop.
203210
//!
204211
//! - `__STACK_START`. This is the first entry in the `.vector_table` section. This symbol contains
205212
//! the initial value of the stack pointer; this is where the stack will be located -- the stack
@@ -340,8 +347,8 @@
340347
//! PROVIDE(Bar = DefaultHandler);
341348
//! ```
342349
//!
343-
//! This weakly aliases both `Foo` and `Bar`. `DefaultHandler` is the default exception handler that
344-
//! the user provides via `exception!(*, ..)` and that the core exceptions use unless overridden.
350+
//! This weakly aliases both `Foo` and `Bar`. `DefaultHandler` is the default exception handler and
351+
//! that the core exceptions use unless overridden.
345352
//!
346353
//! Because this linker script is provided by a dependency of the final application the dependency
347354
//! must contain build script that puts `device.x` somewhere the linker can find. An example of such

0 commit comments

Comments
 (0)