@@ -7,11 +7,14 @@ extern crate proc_macro;
7
7
use proc_macro:: TokenStream ;
8
8
use proc_macro2:: Span ;
9
9
use quote:: quote;
10
- use std:: collections:: HashSet ;
11
10
use std:: iter;
11
+ use std:: { collections:: HashSet , fmt:: Display } ;
12
12
use 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 ,
15
18
} ;
16
19
17
20
#[ proc_macro_attribute]
@@ -113,21 +116,84 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
113
116
#[ derive( Debug , PartialEq ) ]
114
117
enum Exception {
115
118
DefaultHandler ,
116
- HardFault ,
119
+ HardFault ( HardFaultArgs ) ,
117
120
NonMaskableInt ,
118
121
Other ,
119
122
}
120
123
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
+
121
193
#[ proc_macro_attribute]
122
194
pub fn exception ( args : TokenStream , input : TokenStream ) -> TokenStream {
123
195
let mut f = parse_macro_input ! ( input as ItemFn ) ;
124
196
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
-
131
197
if let Err ( error) = check_attr_whitelist ( & f. attrs , WhiteListCaller :: Exception ) {
132
198
return error;
133
199
}
@@ -137,13 +203,35 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
137
203
138
204
let ident_s = ident. to_string ( ) ;
139
205
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
+ }
143
223
// NOTE that at this point we don't check if the exception is available on the target (e.g.
144
224
// MemoryManagement is not available on Cortex-M0)
145
225
"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
+ }
147
235
_ => {
148
236
return parse:: Error :: new ( ident. span ( ) , "This is not a valid exception name" )
149
237
. to_compile_error ( )
@@ -153,13 +241,9 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
153
241
154
242
if f. sig . unsafety . is_none ( ) {
155
243
match exn {
156
- Exception :: DefaultHandler | Exception :: HardFault | Exception :: NonMaskableInt => {
244
+ Exception :: DefaultHandler | Exception :: HardFault ( _ ) | Exception :: NonMaskableInt => {
157
245
// 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) ;
163
247
return parse:: Error :: new ( ident. span ( ) , format_args ! ( "defining a {} is unsafe and requires an `unsafe fn` (see the cortex-m-rt docs)" , name) )
164
248
. to_compile_error ( )
165
249
. into ( ) ;
@@ -232,17 +316,23 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
232
316
#f
233
317
)
234
318
}
235
- Exception :: HardFault => {
319
+ Exception :: HardFault ( args ) => {
236
320
let valid_signature = f. sig . constness . is_none ( )
237
321
&& f. vis == Visibility :: Inherited
238
322
&& 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 ( )
246
336
}
247
337
&& f. sig . generics . params . is_empty ( )
248
338
&& f. sig . generics . where_clause . is_none ( )
@@ -255,33 +345,75 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
255
345
if !valid_signature {
256
346
return parse:: Error :: new (
257
347
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
+ } ,
259
353
)
260
354
. to_compile_error ( )
261
355
. into ( ) ;
262
356
}
263
357
264
358
f. sig . ident = Ident :: new ( & format ! ( "__cortex_m_rt_{}" , f. sig. ident) , Span :: call_site ( ) ) ;
265
359
let tramp_ident = Ident :: new ( & format ! ( "{}_trampoline" , f. sig. ident) , Span :: call_site ( ) ) ;
266
- let ident = & f. sig . ident ;
267
360
268
- let ( ref cfgs, ref attrs) = extract_cfgs ( f. attrs . clone ( ) ) ;
361
+ if args. trampoline {
362
+ let ident = & f. sig . ident ;
269
363
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 ( ) ) ;
282
365
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
+ }
285
417
}
286
418
Exception :: NonMaskableInt | Exception :: Other => {
287
419
let valid_signature = f. sig . constness . is_none ( )
@@ -634,7 +766,7 @@ fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<
634
766
}
635
767
} ;
636
768
637
- return Err ( parse:: Error :: new ( attr. span ( ) , & err_str)
769
+ return Err ( parse:: Error :: new ( attr. span ( ) , err_str)
638
770
. to_compile_error ( )
639
771
. into ( ) ) ;
640
772
}
0 commit comments