@@ -10,8 +10,11 @@ use quote::quote;
10
10
use std:: collections:: HashSet ;
11
11
use std:: iter;
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,73 @@ 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
+ #[ derive( Debug , PartialEq ) ]
125
+ struct HardFaultArgs {
126
+ trampoline : bool ,
127
+ }
128
+
129
+ impl Default for HardFaultArgs {
130
+ fn default ( ) -> Self {
131
+ Self { trampoline : true }
132
+ }
133
+ }
134
+
135
+ impl Parse for HardFaultArgs {
136
+ fn parse ( input : parse:: ParseStream ) -> syn:: Result < Self > {
137
+ let mut items = Vec :: new ( ) ;
138
+ // Read a list of `ident = value,`
139
+ loop {
140
+ if input. is_empty ( ) {
141
+ break ;
142
+ }
143
+
144
+ let name = input. parse :: < Ident > ( ) ?;
145
+ input. parse :: < syn:: Token !( =) > ( ) ?;
146
+ let value = input. parse :: < syn:: Lit > ( ) ?;
147
+
148
+ items. push ( ( name, value) ) ;
149
+
150
+ if input. is_empty ( ) {
151
+ break ;
152
+ }
153
+
154
+ input. parse :: < syn:: Token !( , ) > ( ) ?;
155
+ }
156
+
157
+ let mut args = Self :: default ( ) ;
158
+
159
+ for ( name, value) in items {
160
+ match name. to_string ( ) . as_str ( ) {
161
+ "trampoline" => match value {
162
+ syn:: Lit :: Bool ( val) => {
163
+ args. trampoline = val. value ( ) ;
164
+ }
165
+ _ => {
166
+ return Err ( syn:: Error :: new_spanned (
167
+ value,
168
+ "Not a valid value. `trampoline` takes a boolean literal" ,
169
+ ) )
170
+ }
171
+ } ,
172
+ _ => {
173
+ return Err ( syn:: Error :: new_spanned ( name, "Not a valid argument name" ) ) ;
174
+ }
175
+ }
176
+ }
177
+
178
+ Ok ( args)
179
+ }
180
+ }
181
+
121
182
#[ proc_macro_attribute]
122
183
pub fn exception ( args : TokenStream , input : TokenStream ) -> TokenStream {
123
184
let mut f = parse_macro_input ! ( input as ItemFn ) ;
124
185
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
186
if let Err ( error) = check_attr_whitelist ( & f. attrs , WhiteListCaller :: Exception ) {
132
187
return error;
133
188
}
@@ -137,14 +192,34 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
137
192
138
193
let ident_s = ident. to_string ( ) ;
139
194
let exn = match & * ident_s {
140
- "DefaultHandler" => Exception :: DefaultHandler ,
141
- "HardFault" => Exception :: HardFault ,
142
- "NonMaskableInt" => Exception :: NonMaskableInt ,
195
+ "DefaultHandler" => {
196
+ if !args. is_empty ( ) {
197
+ return parse:: Error :: new ( Span :: call_site ( ) , "This attribute accepts no arguments" )
198
+ . to_compile_error ( )
199
+ . into ( ) ;
200
+ }
201
+ Exception :: DefaultHandler
202
+ }
203
+ "HardFault" => Exception :: HardFault ( parse_macro_input ! ( args) ) ,
204
+ "NonMaskableInt" => {
205
+ if !args. is_empty ( ) {
206
+ return parse:: Error :: new ( Span :: call_site ( ) , "This attribute accepts no arguments" )
207
+ . to_compile_error ( )
208
+ . into ( ) ;
209
+ }
210
+ Exception :: NonMaskableInt
211
+ }
143
212
// NOTE that at this point we don't check if the exception is available on the target (e.g.
144
213
// MemoryManagement is not available on Cortex-M0)
145
214
"MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall"
146
215
| "DebugMonitor" | "PendSV" | "SysTick" => Exception :: Other ,
147
216
_ => {
217
+ if !args. is_empty ( ) {
218
+ return parse:: Error :: new ( Span :: call_site ( ) , "This attribute accepts no arguments" )
219
+ . to_compile_error ( )
220
+ . into ( ) ;
221
+ }
222
+
148
223
return parse:: Error :: new ( ident. span ( ) , "This is not a valid exception name" )
149
224
. to_compile_error ( )
150
225
. into ( ) ;
@@ -153,7 +228,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
153
228
154
229
if f. sig . unsafety . is_none ( ) {
155
230
match exn {
156
- Exception :: DefaultHandler | Exception :: HardFault | Exception :: NonMaskableInt => {
231
+ Exception :: DefaultHandler | Exception :: HardFault ( _ ) | Exception :: NonMaskableInt => {
157
232
// These are unsafe to define.
158
233
let name = if exn == Exception :: DefaultHandler {
159
234
"`DefaultHandler`" . to_string ( )
@@ -232,17 +307,24 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
232
307
#f
233
308
)
234
309
}
235
- Exception :: HardFault if cfg ! ( feature = "hardfault-trampoline" ) => {
310
+ Exception :: HardFault ( args ) => {
236
311
let valid_signature = f. sig . constness . is_none ( )
237
312
&& f. vis == Visibility :: Inherited
238
313
&& 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 ( ) ,
314
+ && if args. trampoline {
315
+ match & f. sig . inputs [ 0 ] {
316
+ FnArg :: Typed ( arg) => match arg. ty . as_ref ( ) {
317
+ Type :: Reference ( r) => {
318
+ r. lifetime . is_none ( )
319
+ && r. mutability . is_none ( )
320
+ && f. sig . inputs . len ( ) == 1
321
+ }
322
+ _ => false ,
323
+ } ,
243
324
_ => false ,
244
- } ,
245
- _ => false ,
325
+ }
326
+ } else {
327
+ f. sig . inputs . is_empty ( )
246
328
}
247
329
&& f. sig . generics . params . is_empty ( )
248
330
&& f. sig . generics . where_clause . is_none ( )
@@ -255,66 +337,74 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
255
337
if !valid_signature {
256
338
return parse:: Error :: new (
257
339
fspan,
258
- "`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`" ,
340
+ if args. trampoline {
341
+ "`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`"
342
+ } else {
343
+ "`HardFault` handler must have signature `unsafe fn() -> !`"
344
+ } ,
259
345
)
260
346
. to_compile_error ( )
261
347
. into ( ) ;
262
348
}
263
349
264
350
f. sig . ident = Ident :: new ( & format ! ( "__cortex_m_rt_{}" , f. sig. ident) , Span :: call_site ( ) ) ;
265
- let tramp_ident = Ident :: new ( & format ! ( "{}_trampoline" , f. sig. ident) , Span :: call_site ( ) ) ;
266
- let ident = & f. sig . ident ;
267
351
268
- let ( ref cfgs, ref attrs) = extract_cfgs ( f. attrs . clone ( ) ) ;
352
+ if args. trampoline {
353
+ let tramp_ident =
354
+ Ident :: new ( & format ! ( "{}_trampoline" , f. sig. ident) , Span :: call_site ( ) ) ;
355
+ let ident = & f. sig . ident ;
269
356
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
- }
357
+ let ( ref cfgs, ref attrs) = extract_cfgs ( f. attrs . clone ( ) ) ;
282
358
283
- #f
284
- )
285
- }
286
- Exception :: HardFault => {
287
- let valid_signature = f. sig . constness . is_none ( )
288
- && f. vis == Visibility :: Inherited
289
- && f. sig . abi . is_none ( )
290
- && f. sig . inputs . is_empty ( )
291
- && f. sig . generics . params . is_empty ( )
292
- && f. sig . generics . where_clause . is_none ( )
293
- && f. sig . variadic . is_none ( )
294
- && match f. sig . output {
295
- ReturnType :: Default => false ,
296
- ReturnType :: Type ( _, ref ty) => matches ! ( * * ty, Type :: Never ( _) ) ,
297
- } ;
359
+ quote ! (
360
+ #( #cfgs) *
361
+ #( #attrs) *
362
+ #[ doc( hidden) ]
363
+ #[ export_name = "HardFault" ]
364
+ // Only emit link_section when building for embedded targets,
365
+ // because some hosted platforms (used to check the build)
366
+ // cannot handle the long link section names.
367
+ #[ cfg_attr( target_os = "none" , link_section = ".HardFault.user" ) ]
368
+ pub unsafe extern "C" fn #tramp_ident( frame: & :: cortex_m_rt:: ExceptionFrame ) {
369
+ #ident( frame)
370
+ }
298
371
299
- if !valid_signature {
300
- return parse:: Error :: new (
301
- fspan,
302
- "`HardFault` handler must have signature `unsafe fn() -> !`" ,
372
+ #f
373
+
374
+ // HardFault exceptions are bounced through this trampoline which grabs the stack pointer at
375
+ // the time of the exception and passes it to the user's HardFault handler in r0.
376
+ // Depending on the stack mode in EXC_RETURN, fetches stack from either MSP or PSP.
377
+ core:: arch:: global_asm!(
378
+ ".cfi_sections .debug_frame
379
+ .section .HardFaultTrampoline, \" ax\"
380
+ .global HardFaultTrampline
381
+ .type HardFaultTrampline,%function
382
+ .thumb_func
383
+ .cfi_startproc
384
+ HardFaultTrampoline:" ,
385
+ "mov r0, lr
386
+ movs r1, #4
387
+ tst r0, r1
388
+ bne 0f
389
+ mrs r0, MSP
390
+ b HardFault
391
+ 0:
392
+ mrs r0, PSP
393
+ b HardFault" ,
394
+ ".cfi_endproc
395
+ .size HardFaultTrampoline, . - HardFaultTrampoline" ,
396
+ ) ;
397
+ )
398
+ } else {
399
+ quote ! (
400
+ #[ export_name = "HardFault" ]
401
+ // Only emit link_section when building for embedded targets,
402
+ // because some hosted platforms (used to check the build)
403
+ // cannot handle the long link section names.
404
+ #[ cfg_attr( target_os = "none" , link_section = ".HardFault.user" ) ]
405
+ #f
303
406
)
304
- . to_compile_error ( )
305
- . into ( ) ;
306
407
}
307
-
308
- f. sig . ident = Ident :: new ( & format ! ( "__cortex_m_rt_{}" , f. sig. ident) , Span :: call_site ( ) ) ;
309
-
310
- quote ! (
311
- #[ export_name = "HardFault" ]
312
- // Only emit link_section when building for embedded targets,
313
- // because some hosted platforms (used to check the build)
314
- // cannot handle the long link section names.
315
- #[ cfg_attr( target_os = "none" , link_section = ".HardFault.user" ) ]
316
- #f
317
- )
318
408
}
319
409
Exception :: NonMaskableInt | Exception :: Other => {
320
410
let valid_signature = f. sig . constness . is_none ( )
0 commit comments