@@ -7,11 +7,14 @@ extern crate proc_macro;
77use proc_macro:: TokenStream ;
88use proc_macro2:: Span ;
99use quote:: quote;
10- use std:: collections:: HashSet ;
1110use std:: iter;
11+ use std:: { collections:: HashSet , fmt:: Display } ;
1212use syn:: {
13- parse, parse_macro_input, spanned:: Spanned , AttrStyle , Attribute , FnArg , Ident , Item , ItemFn ,
14- ItemStatic , ReturnType , Stmt , Type , Visibility ,
13+ parse:: { self , Parse } ,
14+ parse_macro_input,
15+ spanned:: Spanned ,
16+ AttrStyle , Attribute , FnArg , Ident , Item , ItemFn , ItemStatic , ReturnType , Stmt , Type ,
17+ Visibility ,
1518} ;
1619
1720#[ proc_macro_attribute]
@@ -113,21 +116,84 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
113116#[ derive( Debug , PartialEq ) ]
114117enum Exception {
115118 DefaultHandler ,
116- HardFault ,
119+ HardFault ( HardFaultArgs ) ,
117120 NonMaskableInt ,
118121 Other ,
119122}
120123
124+ impl Display for Exception {
125+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
126+ match self {
127+ Exception :: DefaultHandler => write ! ( f, "`DefaultHandler`" ) ,
128+ Exception :: HardFault ( _) => write ! ( f, "`HardFault` handler" ) ,
129+ Exception :: NonMaskableInt => write ! ( f, "`NonMaskableInt` handler" ) ,
130+ Exception :: Other => write ! ( f, "Other exception handler" ) ,
131+ }
132+ }
133+ }
134+
135+ #[ derive( Debug , PartialEq ) ]
136+ struct HardFaultArgs {
137+ trampoline : bool ,
138+ }
139+
140+ impl Default for HardFaultArgs {
141+ fn default ( ) -> Self {
142+ Self { trampoline : true }
143+ }
144+ }
145+
146+ impl Parse for HardFaultArgs {
147+ fn parse ( input : parse:: ParseStream ) -> syn:: Result < Self > {
148+ let mut items = Vec :: new ( ) ;
149+ // Read a list of `ident = value,`
150+ loop {
151+ if input. is_empty ( ) {
152+ break ;
153+ }
154+
155+ let name = input. parse :: < Ident > ( ) ?;
156+ input. parse :: < syn:: Token !( =) > ( ) ?;
157+ let value = input. parse :: < syn:: Lit > ( ) ?;
158+
159+ items. push ( ( name, value) ) ;
160+
161+ if input. is_empty ( ) {
162+ break ;
163+ }
164+
165+ input. parse :: < syn:: Token !( , ) > ( ) ?;
166+ }
167+
168+ let mut args = Self :: default ( ) ;
169+
170+ for ( name, value) in items {
171+ match name. to_string ( ) . as_str ( ) {
172+ "trampoline" => match value {
173+ syn:: Lit :: Bool ( val) => {
174+ args. trampoline = val. value ( ) ;
175+ }
176+ _ => {
177+ return Err ( syn:: Error :: new_spanned (
178+ value,
179+ "Not a valid value. `trampoline` takes a boolean literal" ,
180+ ) )
181+ }
182+ } ,
183+ _ => {
184+ return Err ( syn:: Error :: new_spanned ( name, "Not a valid argument name" ) ) ;
185+ }
186+ }
187+ }
188+
189+ Ok ( args)
190+ }
191+ }
192+
121193#[ proc_macro_attribute]
122194pub fn exception ( args : TokenStream , input : TokenStream ) -> TokenStream {
123195 let mut f = parse_macro_input ! ( input as ItemFn ) ;
124196
125- if !args. is_empty ( ) {
126- return parse:: Error :: new ( Span :: call_site ( ) , "This attribute accepts no arguments" )
127- . to_compile_error ( )
128- . into ( ) ;
129- }
130-
131197 if let Err ( error) = check_attr_whitelist ( & f. attrs , WhiteListCaller :: Exception ) {
132198 return error;
133199 }
@@ -137,13 +203,35 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
137203
138204 let ident_s = ident. to_string ( ) ;
139205 let exn = match & * ident_s {
140- "DefaultHandler" => Exception :: DefaultHandler ,
141- "HardFault" => Exception :: HardFault ,
142- "NonMaskableInt" => Exception :: NonMaskableInt ,
206+ "DefaultHandler" => {
207+ if !args. is_empty ( ) {
208+ return parse:: Error :: new ( Span :: call_site ( ) , "This attribute accepts no arguments" )
209+ . to_compile_error ( )
210+ . into ( ) ;
211+ }
212+ Exception :: DefaultHandler
213+ }
214+ "HardFault" => Exception :: HardFault ( parse_macro_input ! ( args) ) ,
215+ "NonMaskableInt" => {
216+ if !args. is_empty ( ) {
217+ return parse:: Error :: new ( Span :: call_site ( ) , "This attribute accepts no arguments" )
218+ . to_compile_error ( )
219+ . into ( ) ;
220+ }
221+ Exception :: NonMaskableInt
222+ }
143223 // NOTE that at this point we don't check if the exception is available on the target (e.g.
144224 // MemoryManagement is not available on Cortex-M0)
145225 "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall"
146- | "DebugMonitor" | "PendSV" | "SysTick" => Exception :: Other ,
226+ | "DebugMonitor" | "PendSV" | "SysTick" => {
227+ if !args. is_empty ( ) {
228+ return parse:: Error :: new ( Span :: call_site ( ) , "This attribute accepts no arguments" )
229+ . to_compile_error ( )
230+ . into ( ) ;
231+ }
232+
233+ Exception :: Other
234+ }
147235 _ => {
148236 return parse:: Error :: new ( ident. span ( ) , "This is not a valid exception name" )
149237 . to_compile_error ( )
@@ -153,13 +241,9 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
153241
154242 if f. sig . unsafety . is_none ( ) {
155243 match exn {
156- Exception :: DefaultHandler | Exception :: HardFault | Exception :: NonMaskableInt => {
244+ Exception :: DefaultHandler | Exception :: HardFault ( _ ) | Exception :: NonMaskableInt => {
157245 // These are unsafe to define.
158- let name = if exn == Exception :: DefaultHandler {
159- "`DefaultHandler`" . to_string ( )
160- } else {
161- format ! ( "`{:?}` handler" , exn)
162- } ;
246+ let name = format ! ( "{}" , exn) ;
163247 return parse:: Error :: new ( ident. span ( ) , format_args ! ( "defining a {} is unsafe and requires an `unsafe fn` (see the cortex-m-rt docs)" , name) )
164248 . to_compile_error ( )
165249 . into ( ) ;
@@ -232,17 +316,23 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
232316 #f
233317 )
234318 }
235- Exception :: HardFault => {
319+ Exception :: HardFault ( args ) => {
236320 let valid_signature = f. sig . constness . is_none ( )
237321 && f. vis == Visibility :: Inherited
238322 && f. sig . abi . is_none ( )
239- && f. sig . inputs . len ( ) == 1
240- && match & f. sig . inputs [ 0 ] {
241- FnArg :: Typed ( arg) => match arg. ty . as_ref ( ) {
242- Type :: Reference ( r) => r. lifetime . is_none ( ) && r. mutability . is_none ( ) ,
243- _ => false ,
244- } ,
245- _ => false ,
323+ && if args. trampoline {
324+ f. sig . inputs . len ( ) == 1
325+ && match & f. sig . inputs [ 0 ] {
326+ FnArg :: Typed ( arg) => match arg. ty . as_ref ( ) {
327+ Type :: Reference ( r) => {
328+ r. lifetime . is_none ( ) && r. mutability . is_none ( )
329+ }
330+ _ => false ,
331+ } ,
332+ _ => false ,
333+ }
334+ } else {
335+ f. sig . inputs . is_empty ( )
246336 }
247337 && f. sig . generics . params . is_empty ( )
248338 && f. sig . generics . where_clause . is_none ( )
@@ -255,33 +345,75 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
255345 if !valid_signature {
256346 return parse:: Error :: new (
257347 fspan,
258- "`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`" ,
348+ if args. trampoline {
349+ "`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`"
350+ } else {
351+ "`HardFault` handler must have signature `unsafe fn() -> !`"
352+ } ,
259353 )
260354 . to_compile_error ( )
261355 . into ( ) ;
262356 }
263357
264358 f. sig . ident = Ident :: new ( & format ! ( "__cortex_m_rt_{}" , f. sig. ident) , Span :: call_site ( ) ) ;
265359 let tramp_ident = Ident :: new ( & format ! ( "{}_trampoline" , f. sig. ident) , Span :: call_site ( ) ) ;
266- let ident = & f. sig . ident ;
267360
268- let ( ref cfgs, ref attrs) = extract_cfgs ( f. attrs . clone ( ) ) ;
361+ if args. trampoline {
362+ let ident = & f. sig . ident ;
269363
270- quote ! (
271- #( #cfgs) *
272- #( #attrs) *
273- #[ doc( hidden) ]
274- #[ export_name = "HardFault" ]
275- // Only emit link_section when building for embedded targets,
276- // because some hosted platforms (used to check the build)
277- // cannot handle the long link section names.
278- #[ cfg_attr( target_os = "none" , link_section = ".HardFault.user" ) ]
279- pub unsafe extern "C" fn #tramp_ident( frame: & :: cortex_m_rt:: ExceptionFrame ) {
280- #ident( frame)
281- }
364+ let ( ref cfgs, ref attrs) = extract_cfgs ( f. attrs . clone ( ) ) ;
282365
283- #f
284- )
366+ quote ! (
367+ #( #cfgs) *
368+ #( #attrs) *
369+ #[ doc( hidden) ]
370+ #[ export_name = "_HardFault" ]
371+ unsafe extern "C" fn #tramp_ident( frame: & :: cortex_m_rt:: ExceptionFrame ) {
372+ #ident( frame)
373+ }
374+
375+ #f
376+
377+ // HardFault exceptions are bounced through this trampoline which grabs the stack pointer at
378+ // the time of the exception and passes it to the user's HardFault handler in r0.
379+ // Depending on the stack mode in EXC_RETURN, fetches stack from either MSP or PSP.
380+ core:: arch:: global_asm!(
381+ ".cfi_sections .debug_frame
382+ .section .HardFault.user, \" ax\"
383+ .global HardFault
384+ .type HardFault,%function
385+ .thumb_func
386+ .cfi_startproc
387+ HardFault:" ,
388+ "mov r0, lr
389+ movs r1, #4
390+ tst r0, r1
391+ bne 0f
392+ mrs r0, MSP
393+ b _HardFault
394+ 0:
395+ mrs r0, PSP
396+ b _HardFault" ,
397+ ".cfi_endproc
398+ .size HardFault, . - HardFault" ,
399+ ) ;
400+ )
401+ } else {
402+ quote ! (
403+ #[ doc( hidden) ]
404+ #[ export_name = "_HardFault" ]
405+ unsafe extern "C" fn #tramp_ident( ) {
406+ // This trampoline has no function except making the compiler diagnostics better.
407+ }
408+
409+ #[ export_name = "HardFault" ]
410+ // Only emit link_section when building for embedded targets,
411+ // because some hosted platforms (used to check the build)
412+ // cannot handle the long link section names.
413+ #[ cfg_attr( target_os = "none" , link_section = ".HardFault.user" ) ]
414+ #f
415+ )
416+ }
285417 }
286418 Exception :: NonMaskableInt | Exception :: Other => {
287419 let valid_signature = f. sig . constness . is_none ( )
@@ -634,7 +766,7 @@ fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<
634766 }
635767 } ;
636768
637- return Err ( parse:: Error :: new ( attr. span ( ) , & err_str)
769+ return Err ( parse:: Error :: new ( attr. span ( ) , err_str)
638770 . to_compile_error ( )
639771 . into ( ) ) ;
640772 }
0 commit comments