From f2a155a0715cb99cbace2eca7ab5fcfa93d106d2 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 18 Aug 2018 22:45:13 +0200 Subject: turn macros into attributes --- cortex-m-rt/macros/src/lib.rs | 462 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 cortex-m-rt/macros/src/lib.rs (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs new file mode 100644 index 0000000..3aa494d --- /dev/null +++ b/cortex-m-rt/macros/src/lib.rs @@ -0,0 +1,462 @@ +#![deny(warnings)] + +extern crate proc_macro; +#[macro_use] +extern crate quote; +#[macro_use] +extern crate syn; + +use syn::synom::Synom; +use syn::token::{Colon, Comma, Eq, Static}; +use syn::{Expr, FnArg, Ident, ItemFn, ReturnType, Type, Visibility}; + +use proc_macro::TokenStream; + +/// Attribute to declare 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. +/// +/// 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 `fn() -> !` (never ending function) +/// +/// # Examples +/// +/// ``` no_run +/// # #![no_main] +/// # use cortex_m_rt_macros::entry; +/// #[entry] +/// fn main() -> ! { +/// 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.unsafety.is_none() + && 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 `fn() -> !`" + ); + + assert_eq!( + args.to_string(), + "", + "`entry` 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 = "main"] + #(#attrs)* + pub fn #ident() -> ! #block + ).into() +} + +struct ExceptionArgs { + first: Ident, + second: Option, +} + +impl Synom for ExceptionArgs { + named!(parse -> Self, do_parse!( + first: syn!(Ident) >> + second: option!(syn!(State)) >> ( + ExceptionArgs { first, second } + ) + )); +} + +struct State { + _comma: Comma, + _static: Static, + ident: Ident, + _colon: Colon, + ty: Type, + _eq: Eq, + expr: Expr, +} + +impl Synom for State { + named!(parse -> Self, do_parse!( + _comma: punct!(,) >> + _static: syn!(Static) >> + ident: syn!(Ident) >> + _colon: punct!(:) >> + ty: syn!(Type) >> + _eq: punct!(=) >> + expr: syn!(Expr) >> ( + State { _comma, _static, ident, _colon, ty, _eq, expr } + ) + )); +} + +/// Attribute to declare an exception handler +/// +/// **NOTE** This macro must be invoked from an accessible module, ideally from the root of the +/// crate. +/// +/// # Syntax +/// +/// ``` +/// # use cortex_m_rt_macros::exception; +/// #[exception(SysTick, static COUNT: u32 = 0)] +/// fn handler() { +/// // .. +/// } +/// +/// # fn main() {} +/// ``` +/// +/// where the first argument can be one of: +/// +/// - `DefaultHandler` +/// - `NonMaskableInt` +/// - `HardFault` +/// - `MemoryManagement` (a) +/// - `BusFault` (a) +/// - `UsageFault` (a) +/// - `SecureFault` (b) +/// - `SVCall` +/// - `DebugMonitor` (a) +/// - `PendSV` +/// - `SysTick` +/// +/// and the second is optional. +/// +/// (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. +/// +/// `#[exception(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 `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(Name)]` overrides the default handler for the exception with the given `Name`. +/// +/// # Examples +/// +/// - Setting the `HardFault` handler +/// +/// ``` +/// # extern crate cortex_m_rt; +/// # extern crate cortex_m_rt_macros; +/// # use cortex_m_rt_macros::exception; +/// #[exception(HardFault)] +/// fn hard_fault(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(DefaultHandler)] +/// fn default_handler(irqn: i16) { +/// println!("IRQn = {}", irqn); +/// } +/// +/// # fn main() {} +/// ``` +/// +/// - Overriding the `SysTick` handler +/// +/// ``` +/// extern crate cortex_m_rt as rt; +/// +/// use rt::exception; +/// +/// #[exception(SysTick, static COUNT: i32 = 0)] +/// fn sys_tick() { +/// *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"); + let args: ExceptionArgs = syn::parse(args).expect( + "`exception` attribute expects the exception name as its argument. \ + e.g. `#[exception(HardFault)]`", + ); + let name = args.first; + let name_s = name.to_string(); + + enum Exception { + DefaultHandler, + HardFault, + Other, + } + + // first validation of the exception name + let exn = match &*name_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", name_s), + }; + + // XXX should we blacklist other attributes? + let attrs = f.attrs; + let ident = f.ident; + let block = f.block; + let stmts = &block.stmts; + + match exn { + Exception::DefaultHandler => { + assert!( + f.constness.is_none() + && f.vis == Visibility::Inherited + && f.unsafety.is_none() + && 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(), + _ => false, + }, + }, + "`#[exception(DefaultHandler)]` function must have signature `fn(i16)`" + ); + + assert!( + args.second.is_none(), + "`#[exception(DefaultHandler)]` takes no additional arguments" + ); + + let arg = match f.decl.inputs[0] { + FnArg::Captured(ref arg) => arg, + _ => unreachable!(), + }; + + quote!( + #[export_name = #name_s] + #(#attrs)* + pub fn #ident() { + 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.unsafety.is_none() + && 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, + }, + }, + "`#[exception(HardFault)]` function must have signature `fn(&ExceptionFrame) -> !`" + ); + + assert!( + args.second.is_none(), + "`#[exception(HardFault)]` takes no additional arguments" + ); + + let arg = match f.decl.inputs[0] { + FnArg::Captured(ref arg) => arg, + _ => unreachable!(), + }; + + let pat = &arg.pat; + + quote!( + #[export_name = "UserHardFault"] + #(#attrs)* + pub unsafe extern "C" fn #ident(#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.unsafety.is_none() + && 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, + }, + }, + "`#[exception]` functions must have signature `fn()`" + ); + + if let Some(second) = args.second { + let ty = second.ty; + let expr = second.expr; + let state = second.ident; + + quote!( + #[export_name = #name_s] + #(#attrs)* + pub fn #ident() { + extern crate cortex_m_rt; + + cortex_m_rt::Exception::#name; + + static mut __STATE__: #ty = #expr; + + #[allow(non_snake_case)] + let #state: &mut #ty = unsafe { &mut __STATE__ }; + + #(#stmts)* + } + ).into() + } else { + quote!( + #[export_name = #name_s] + #(#attrs)* + pub fn #ident() { + extern crate cortex_m_rt; + + cortex_m_rt::Exception::#name; + + #(#stmts)* + } + ).into() + } + } + } +} + +/// Attribute to mark which function will 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 +/// +/// ``` +/// # 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_eq!( + 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() +} -- cgit v1.2.3 From f68e65af8fa0bf915209ae7b3d75303c317ea109 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 31 Aug 2018 22:02:29 +0200 Subject: fix soundness issue; change the syntax of the `exception` attribute --- cortex-m-rt/examples/override-exception.rs | 8 +- cortex-m-rt/examples/state.rs | 6 +- cortex-m-rt/macros/Cargo.toml | 2 + cortex-m-rt/macros/src/lib.rs | 185 +++++++++++++++++------------ 4 files changed, 121 insertions(+), 80 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/examples/override-exception.rs b/cortex-m-rt/examples/override-exception.rs index dca31e6..3e0af25 100644 --- a/cortex-m-rt/examples/override-exception.rs +++ b/cortex-m-rt/examples/override-exception.rs @@ -17,13 +17,13 @@ fn main() -> ! { loop {} } -#[exception(DefaultHandler)] -fn default_handler(_irqn: i16) { +#[exception] +fn DefaultHandler(_irqn: i16) { asm::bkpt(); } -#[exception(HardFault)] -fn hard_fault(_ef: &ExceptionFrame) -> ! { +#[exception] +fn HardFault(_ef: &ExceptionFrame) -> ! { asm::bkpt(); loop {} diff --git a/cortex-m-rt/examples/state.rs b/cortex-m-rt/examples/state.rs index 72ca194..573914f 100644 --- a/cortex-m-rt/examples/state.rs +++ b/cortex-m-rt/examples/state.rs @@ -16,7 +16,9 @@ fn main() -> ! { } // exception handler with state -#[exception(SysTick, static STATE: u32 = 0)] -fn sys_tick() { +#[exception] +fn SysTick() { + static mut STATE: u32 = 0; + *STATE += 1; } diff --git a/cortex-m-rt/macros/Cargo.toml b/cortex-m-rt/macros/Cargo.toml index 85e8cd1..d0644ee 100644 --- a/cortex-m-rt/macros/Cargo.toml +++ b/cortex-m-rt/macros/Cargo.toml @@ -8,6 +8,8 @@ proc-macro = true [dependencies] quote = "0.6.6" +rand = "0.5.5" +proc-macro2 = "0.4.15" [dependencies.syn] features = ["extra-traits", "full"] diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 3aa494d..5cba510 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -1,14 +1,19 @@ -#![deny(warnings)] +// #![deny(warnings)] +#![allow(warnings)] extern crate proc_macro; +extern crate rand; #[macro_use] extern crate quote; #[macro_use] extern crate syn; +extern crate proc_macro2; +use proc_macro2::Span; +use rand::Rng; use syn::synom::Synom; use syn::token::{Colon, Comma, Eq, Static}; -use syn::{Expr, FnArg, Ident, ItemFn, ReturnType, Type, Visibility}; +use syn::{Expr, FnArg, Ident, Item, ItemFn, ReturnType, Stmt, Type, Visibility}; use proc_macro::TokenStream; @@ -124,15 +129,15 @@ impl Synom for State { /// /// ``` /// # use cortex_m_rt_macros::exception; -/// #[exception(SysTick, static COUNT: u32 = 0)] -/// fn handler() { +/// #[exception] +/// fn SysTick() { /// // .. /// } /// /// # fn main() {} /// ``` /// -/// where the first argument can be one of: +/// where the name of the function must be one of: /// /// - `DefaultHandler` /// - `NonMaskableInt` @@ -146,25 +151,29 @@ impl Synom for State { /// - `PendSV` /// - `SysTick` /// -/// and the second is optional. -/// /// (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 +/// `#[exception] fn 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. /// -/// `#[exception(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 `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 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 +/// `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`. +/// 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. /// -/// `#[exception(Name)]` overrides the default handler for the exception with the given `Name`. +/// Exception handlers can only be called by the hardware. Other parts of the program can't refer to +/// the exception handler much less invoke them as if they were functions. /// /// # Examples /// @@ -174,8 +183,8 @@ impl Synom for State { /// # extern crate cortex_m_rt; /// # extern crate cortex_m_rt_macros; /// # use cortex_m_rt_macros::exception; -/// #[exception(HardFault)] -/// fn hard_fault(ef: &cortex_m_rt::ExceptionFrame) -> ! { +/// #[exception] +/// fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! { /// // prints the exception frame as a panic message /// panic!("{:#?}", ef); /// } @@ -187,8 +196,8 @@ impl Synom for State { /// /// ``` /// # use cortex_m_rt_macros::exception; -/// #[exception(DefaultHandler)] -/// fn default_handler(irqn: i16) { +/// #[exception] +/// fn DefaultHandler(irqn: i16) { /// println!("IRQn = {}", irqn); /// } /// @@ -202,8 +211,11 @@ impl Synom for State { /// /// use rt::exception; /// -/// #[exception(SysTick, static COUNT: i32 = 0)] -/// fn sys_tick() { +/// #[exception] +/// fn SysTick() { +/// static mut COUNT: i32 = 0; +/// +/// // `COUNT` is safe to access and has type `&mut i32` /// *COUNT += 1; /// /// println!("{}", COUNT); @@ -214,12 +226,14 @@ impl Synom for State { #[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"); - let args: ExceptionArgs = syn::parse(args).expect( - "`exception` attribute expects the exception name as its argument. \ - e.g. `#[exception(HardFault)]`", + + assert_eq!( + args.to_string(), + "", + "`exception` attribute must have no arguments" ); - let name = args.first; - let name_s = name.to_string(); + + let ident = f.ident; enum Exception { DefaultHandler, @@ -227,22 +241,32 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { Other, } - // first validation of the exception name - let exn = match &*name_s { + 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", name_s), + _ => panic!("{} is not a valid exception name", ident_s), }; // XXX should we blacklist other attributes? let attrs = f.attrs; - let ident = f.ident; let block = f.block; - let stmts = &block.stmts; + let stmts = block.stmts; + + let mut rng = rand::thread_rng(); + let hash = (0..16) + .map(|i| { + if i == 0 || rng.gen() { + ('a' as u8 + rng.gen::() % 25) as char + } else { + ('0' as u8 + rng.gen::() % 10) as char + } + }).collect::(); + let hash = Ident::new(&hash, Span::call_site()); match exn { Exception::DefaultHandler => { @@ -262,12 +286,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { _ => false, }, }, - "`#[exception(DefaultHandler)]` function must have signature `fn(i16)`" - ); - - assert!( - args.second.is_none(), - "`#[exception(DefaultHandler)]` takes no additional arguments" + "`#DefaultHandler` function must have signature `fn(i16)`" ); let arg = match f.decl.inputs[0] { @@ -276,9 +295,9 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { }; quote!( - #[export_name = #name_s] + #[export_name = #ident_s] #(#attrs)* - pub fn #ident() { + pub extern "C" fn #hash() { extern crate core; const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; @@ -318,11 +337,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { "`#[exception(HardFault)]` function must have signature `fn(&ExceptionFrame) -> !`" ); - assert!( - args.second.is_none(), - "`#[exception(HardFault)]` takes no additional arguments" - ); - let arg = match f.decl.inputs[0] { FnArg::Captured(ref arg) => arg, _ => unreachable!(), @@ -333,7 +347,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[export_name = "UserHardFault"] #(#attrs)* - pub unsafe extern "C" fn #ident(#arg) -> ! { + pub extern "C" fn #hash(#arg) -> ! { extern crate cortex_m_rt; // further type check of the input argument @@ -360,43 +374,66 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { _ => false, }, }, - "`#[exception]` functions must have signature `fn()`" + "`#[exception]` functions other than `DefaultHandler` and `HardFault` must \ + have signature `fn()`" ); - if let Some(second) = args.second { - let ty = second.ty; - let expr = second.expr; - let state = second.ident; - - quote!( - #[export_name = #name_s] - #(#attrs)* - pub fn #ident() { - extern crate cortex_m_rt; - - cortex_m_rt::Exception::#name; + // Collect all the `static mut` at the beginning of the function body. We'll make them + // safe + 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; + } + } + } - static mut __STATE__: #ty = #expr; + stmts.extend(istmts); + + 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, unsafe_code)] + let #ident: &mut #ty = unsafe { &mut #ident_ }; + ) + }).collect::>(); - #[allow(non_snake_case)] - let #state: &mut #ty = unsafe { &mut __STATE__ }; + quote!( + #[export_name = #ident_s] + #(#attrs)* + pub fn #hash() { + extern crate cortex_m_rt; - #(#stmts)* - } - ).into() - } else { - quote!( - #[export_name = #name_s] - #(#attrs)* - pub fn #ident() { - extern crate cortex_m_rt; + // check that this exception actually exists + cortex_m_rt::Exception::#ident; - cortex_m_rt::Exception::#name; + #(#vars)* - #(#stmts)* - } - ).into() - } + #(#stmts)* + } + ).into() } } } -- cgit v1.2.3 From 48c1af844ef99772446850eb0288bef61d663afb Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 31 Aug 2018 22:13:06 +0200 Subject: remove dead code --- cortex-m-rt/macros/src/lib.rs | 48 +++---------------------------------------- 1 file changed, 3 insertions(+), 45 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 5cba510..82457f1 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -1,19 +1,15 @@ -// #![deny(warnings)] -#![allow(warnings)] +#![deny(warnings)] extern crate proc_macro; extern crate rand; #[macro_use] extern crate quote; -#[macro_use] -extern crate syn; extern crate proc_macro2; +extern crate syn; use proc_macro2::Span; use rand::Rng; -use syn::synom::Synom; -use syn::token::{Colon, Comma, Eq, Static}; -use syn::{Expr, FnArg, Ident, Item, ItemFn, ReturnType, Stmt, Type, Visibility}; +use syn::{FnArg, Ident, Item, ItemFn, ReturnType, Stmt, Type, Visibility}; use proc_macro::TokenStream; @@ -82,44 +78,6 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { ).into() } -struct ExceptionArgs { - first: Ident, - second: Option, -} - -impl Synom for ExceptionArgs { - named!(parse -> Self, do_parse!( - first: syn!(Ident) >> - second: option!(syn!(State)) >> ( - ExceptionArgs { first, second } - ) - )); -} - -struct State { - _comma: Comma, - _static: Static, - ident: Ident, - _colon: Colon, - ty: Type, - _eq: Eq, - expr: Expr, -} - -impl Synom for State { - named!(parse -> Self, do_parse!( - _comma: punct!(,) >> - _static: syn!(Static) >> - ident: syn!(Ident) >> - _colon: punct!(:) >> - ty: syn!(Type) >> - _eq: punct!(=) >> - expr: syn!(Expr) >> ( - State { _comma, _static, ident, _colon, ty, _eq, expr } - ) - )); -} - /// Attribute to declare an exception handler /// /// **NOTE** This macro must be invoked from an accessible module, ideally from the root of the -- cgit v1.2.3 From fc592e4960d99ca8f33c6414ca9903bf0cb6df73 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 3 Sep 2018 20:41:41 +0200 Subject: make `static mut` variables safe to access in the entry point extend / update documentation --- cortex-m-rt/macros/src/lib.rs | 146 +++++++++++++++++++++++++++++++----------- cortex-m-rt/src/lib.rs | 29 +++++---- 2 files changed, 128 insertions(+), 47 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 82457f1..5d6a603 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -9,7 +9,7 @@ extern crate syn; use proc_macro2::Span; use rand::Rng; -use syn::{FnArg, Ident, Item, ItemFn, ReturnType, Stmt, Type, Visibility}; +use syn::{FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, Visibility}; use proc_macro::TokenStream; @@ -24,13 +24,45 @@ use proc_macro::TokenStream; /// /// The type of the specified function must be `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 { /// /* .. */ /// } @@ -69,12 +101,36 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { // XXX should we blacklist other attributes? let attrs = f.attrs; let ident = f.ident; - let block = f.block; + 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, unsafe_code)] + let #ident: &'static mut #ty = unsafe { &mut #ident_ }; + ) + }).collect::>(); quote!( #[export_name = "main"] #(#attrs)* - pub fn #ident() -> ! #block + pub fn #ident() -> ! { + #(#vars)* + + #(#stmts)* + } ).into() } @@ -130,8 +186,15 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { /// 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 handler much less invoke them as if they were functions. +/// 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 /// @@ -215,17 +278,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let block = f.block; let stmts = block.stmts; - let mut rng = rand::thread_rng(); - let hash = (0..16) - .map(|i| { - if i == 0 || rng.gen() { - ('a' as u8 + rng.gen::() % 25) as char - } else { - ('0' as u8 + rng.gen::() % 10) as char - } - }).collect::(); - let hash = Ident::new(&hash, Span::call_site()); - + let hash = random_ident(); match exn { Exception::DefaultHandler => { assert!( @@ -336,27 +389,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { have signature `fn()`" ); - // Collect all the `static mut` at the beginning of the function body. We'll make them - // safe - 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); + let (statics, stmts) = extract_static_muts(stmts); let vars = statics .into_iter() @@ -455,3 +488,44 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { 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::() % 25) as char + } else { + ('0' as u8 + rng.gen::() % 10) as char + } + }).collect::(), + Span::call_site(), + ) +} + +/// Extracts `static mut` vars from the beginning of the given statements +fn extract_static_muts(stmts: Vec) -> (Vec, Vec) { + 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 526b28b..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,7 +94,7 @@ //! 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 @@ -191,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 @@ -340,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 -- cgit v1.2.3 From fbd29fc7fe75ec9e906d8e961ab08cf66794d305 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 6 Sep 2018 00:25:45 +0200 Subject: add compile-fail tests; test only on nightly we'll test on beta when 1.30-beta is out --- cortex-m-rt/.travis.yml | 37 ++++++++++++---------- cortex-m-rt/Cargo.toml | 7 ++-- cortex-m-rt/ci/script.sh | 8 +++-- cortex-m-rt/macros/src/lib.rs | 6 ++-- .../default-handler-bad-signature-1.rs | 16 ++++++++++ .../default-handler-bad-signature-2.rs | 16 ++++++++++ .../default-handler-bad-signature-3.rs | 18 +++++++++++ .../tests/compile-fail/default-handler-hidden.rs | 22 +++++++++++++ .../tests/compile-fail/default-handler-twice.rs | 22 +++++++++++++ .../tests/compile-fail/entry-bad-signature-1.rs | 11 +++++++ .../tests/compile-fail/entry-bad-signature-2.rs | 11 +++++++ .../tests/compile-fail/entry-bad-signature-3.rs | 13 ++++++++ .../tests/compile-fail/entry-bad-signature-4.rs | 13 ++++++++ cortex-m-rt/tests/compile-fail/entry-hidden.rs | 19 +++++++++++ cortex-m-rt/tests/compile-fail/entry-twice.rs | 17 ++++++++++ .../compile-fail/exception-bad-signature-1.rs | 16 ++++++++++ .../compile-fail/exception-bad-signature-2.rs | 16 ++++++++++ .../compile-fail/exception-bad-signature-3.rs | 18 +++++++++++ cortex-m-rt/tests/compile-fail/exception-hidden.rs | 22 +++++++++++++ cortex-m-rt/tests/compile-fail/exception-twice.rs | 22 +++++++++++++ .../compile-fail/hard-fault-bad-signature-1.rs | 18 +++++++++++ .../compile-fail/hard-fault-bad-signature-2.rs | 18 +++++++++++ .../tests/compile-fail/hard-fault-hidden.rs | 24 ++++++++++++++ cortex-m-rt/tests/compile-fail/hard-fault-twice.rs | 26 +++++++++++++++ .../tests/compile-fail/pre-init-bad-signature-1.rs | 16 ++++++++++ .../tests/compile-fail/pre-init-bad-signature-2.rs | 16 ++++++++++ cortex-m-rt/tests/compile-fail/pre-init-hidden.rs | 23 ++++++++++++++ cortex-m-rt/tests/compile-fail/pre-init-twice.rs | 18 +++++++++++ cortex-m-rt/tests/compiletest.rs | 21 ++++++++++++ 29 files changed, 488 insertions(+), 22 deletions(-) create mode 100644 cortex-m-rt/tests/compile-fail/default-handler-bad-signature-1.rs create mode 100644 cortex-m-rt/tests/compile-fail/default-handler-bad-signature-2.rs create mode 100644 cortex-m-rt/tests/compile-fail/default-handler-bad-signature-3.rs create mode 100644 cortex-m-rt/tests/compile-fail/default-handler-hidden.rs create mode 100644 cortex-m-rt/tests/compile-fail/default-handler-twice.rs create mode 100644 cortex-m-rt/tests/compile-fail/entry-bad-signature-1.rs create mode 100644 cortex-m-rt/tests/compile-fail/entry-bad-signature-2.rs create mode 100644 cortex-m-rt/tests/compile-fail/entry-bad-signature-3.rs create mode 100644 cortex-m-rt/tests/compile-fail/entry-bad-signature-4.rs create mode 100644 cortex-m-rt/tests/compile-fail/entry-hidden.rs create mode 100644 cortex-m-rt/tests/compile-fail/entry-twice.rs create mode 100644 cortex-m-rt/tests/compile-fail/exception-bad-signature-1.rs create mode 100644 cortex-m-rt/tests/compile-fail/exception-bad-signature-2.rs create mode 100644 cortex-m-rt/tests/compile-fail/exception-bad-signature-3.rs create mode 100644 cortex-m-rt/tests/compile-fail/exception-hidden.rs create mode 100644 cortex-m-rt/tests/compile-fail/exception-twice.rs create mode 100644 cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-1.rs create mode 100644 cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-2.rs create mode 100644 cortex-m-rt/tests/compile-fail/hard-fault-hidden.rs create mode 100644 cortex-m-rt/tests/compile-fail/hard-fault-twice.rs create mode 100644 cortex-m-rt/tests/compile-fail/pre-init-bad-signature-1.rs create mode 100644 cortex-m-rt/tests/compile-fail/pre-init-bad-signature-2.rs create mode 100644 cortex-m-rt/tests/compile-fail/pre-init-hidden.rs create mode 100644 cortex-m-rt/tests/compile-fail/pre-init-twice.rs create mode 100644 cortex-m-rt/tests/compiletest.rs (limited to 'cortex-m-rt/macros/src') 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 016cdc9..9cf2c14 100644 --- a/cortex-m-rt/Cargo.toml +++ b/cortex-m-rt/Cargo.toml @@ -15,9 +15,12 @@ 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 8443046..0a28641 100644 --- a/cortex-m-rt/ci/script.sh +++ b/cortex-m-rt/ci/script.sh @@ -5,7 +5,11 @@ main() { cargo check --target $TARGET --features device - ( cd macros && cargo check && cargo test ) + if [ $TARGET = x86_64-unknown-linux-gnu ]; then + ( cd macros && cargo check && cargo test ) + + cargo test --test compiletest + fi local examples=( alignment @@ -18,7 +22,7 @@ main() { 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 -- \ diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 5d6a603..7d3c61a 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -124,6 +124,8 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { }).collect::>(); quote!( + // TODO(forbid) see tests/compile-fail/entry-hidden.rs + // #[forbid(dead_code)] #[export_name = "main"] #(#attrs)* pub fn #ident() -> ! { @@ -297,7 +299,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { _ => false, }, }, - "`#DefaultHandler` function must have signature `fn(i16)`" + "`DefaultHandler` exception must have signature `fn(i16)`" ); let arg = match f.decl.inputs[0] { @@ -345,7 +347,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { _ => false, }, }, - "`#[exception(HardFault)]` function must have signature `fn(&ExceptionFrame) -> !`" + "`HardFault` exception must have signature `fn(&ExceptionFrame) -> !`" ); let arg = match f.decl.inputs[0] { 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..a2c16c1 --- /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 `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..ec74993 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/default-handler-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, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] //~ ERROR custom attribute panicked +//~^ HELP `DefaultHandler` exception must have signature `fn(i16)` +unsafe fn DefaultHandler(_irqn: i16) {} diff --git a/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-3.rs b/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-3.rs new file mode 100644 index 0000000..c827b6a --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-3.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 `fn(i16)` +fn DefaultHandler(_irqn: i16) -> ! { + loop {} +} 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-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/entry-bad-signature-1.rs new file mode 100644 index 0000000..c4914db --- /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 `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..88ffd34 --- /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 `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..0f2ddca --- /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 `fn() -> !` +unsafe fn foo() -> ! { + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/entry-bad-signature-4.rs b/cortex-m-rt/tests/compile-fail/entry-bad-signature-4.rs new file mode 100644 index 0000000..2077f41 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/entry-bad-signature-4.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 `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-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/exception-bad-signature-1.rs new file mode 100644 index 0000000..1f4b707 --- /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 `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..8b1011d --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-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, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] //~ ERROR custom attribute panicked +//~^ HELP `#[exception]` functions other than `DefaultHandler` and `HardFault` must have signature `fn()` +unsafe fn SysTick() {} diff --git a/cortex-m-rt/tests/compile-fail/exception-bad-signature-3.rs b/cortex-m-rt/tests/compile-fail/exception-bad-signature-3.rs new file mode 100644 index 0000000..546ef90 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-bad-signature-3.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 `fn()` +fn SysTick() -> ! { + loop {} +} 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..7c61240 --- /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 `fn(&ExceptionFrame) -> !` +fn HardFault(_ef: &ExceptionFrame, undef: u32) -> ! { + loop {} +} diff --git a/cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-2.rs new file mode 100644 index 0000000..add6943 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/hard-fault-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, ExceptionFrame}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] //~ ERROR custom attribute panicked +//~^ HELP `HardFault` exception must have signature `fn(&ExceptionFrame) -> !` +unsafe fn HardFault(_ef: &ExceptionFrame) -> ! { + 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-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"); +} -- cgit v1.2.3 From 2f162e2f09d662e2bcb245000a5514335106fe84 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 6 Sep 2018 00:26:23 +0200 Subject: remove `#[allow]`s they are no longer required due to rust-lang/rust#52467 --- cortex-m-rt/macros/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 7d3c61a..afc7600 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -118,7 +118,6 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { quote!( static mut #ident_: #ty = #expr; - #[allow(non_snake_case, unsafe_code)] let #ident: &'static mut #ty = unsafe { &mut #ident_ }; ) }).collect::>(); @@ -408,7 +407,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { quote!( static mut #ident_: #ty = #expr; - #[allow(non_snake_case, unsafe_code)] let #ident: &mut #ty = unsafe { &mut #ident_ }; ) }).collect::>(); -- cgit v1.2.3 From 8ee2e58fc6eb843b9c751bc5f45e414efc325e62 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 6 Sep 2018 00:37:05 +0200 Subject: whoops, it seems like we do need some `#[allow]`s after all --- cortex-m-rt/macros/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index afc7600..e37eb9c 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -118,6 +118,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { quote!( static mut #ident_: #ty = #expr; + #[allow(non_snake_case)] let #ident: &'static mut #ty = unsafe { &mut #ident_ }; ) }).collect::>(); @@ -407,6 +408,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { quote!( static mut #ident_: #ty = #expr; + #[allow(non_snake_case)] let #ident: &mut #ty = unsafe { &mut #ident_ }; ) }).collect::>(); -- cgit v1.2.3 From bc526b8d74cb548c9da8d5c10d6013008e164ba5 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 6 Sep 2018 01:04:39 +0200 Subject: add compile-fail tests for passing arguments to the attributes --- cortex-m-rt/macros/src/lib.rs | 15 ++++++--------- cortex-m-rt/tests/compile-fail/entry-args.rs | 13 +++++++++++++ cortex-m-rt/tests/compile-fail/exception-args.rs | 16 ++++++++++++++++ cortex-m-rt/tests/compile-fail/pre-init-args.rs | 16 ++++++++++++++++ 4 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 cortex-m-rt/tests/compile-fail/entry-args.rs create mode 100644 cortex-m-rt/tests/compile-fail/exception-args.rs create mode 100644 cortex-m-rt/tests/compile-fail/pre-init-args.rs (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index e37eb9c..da21954 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -92,9 +92,8 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { "`#[entry]` function must have signature `fn() -> !`" ); - assert_eq!( - args.to_string(), - "", + assert!( + args.to_string() == "", "`entry` attribute must have no arguments" ); @@ -250,9 +249,8 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let f: ItemFn = syn::parse(input).expect("`#[exception]` must be applied to a function"); - assert_eq!( - args.to_string(), - "", + assert!( + args.to_string() == "", "`exception` attribute must have no arguments" ); @@ -473,9 +471,8 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { "`#[pre_init]` function must have signature `unsafe fn()`" ); - assert_eq!( - args.to_string(), - "", + assert!( + args.to_string() == "", "`pre_init` attribute must have no arguments" ); 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/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/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 {} +} -- cgit v1.2.3 From 19fcb13d22a56e01fd2fa5d34051196f59c9511b Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 6 Sep 2018 01:24:01 +0200 Subject: relax checks of the signatures of `entry` and the exceptions --- cortex-m-rt/ci/script.sh | 6 ++++ cortex-m-rt/examples/divergent-default-handler.rs | 19 +++++++++++++ cortex-m-rt/examples/divergent-exception.rs | 18 ++++++++++++ cortex-m-rt/examples/unsafe-default-handler.rs | 16 +++++++++++ cortex-m-rt/examples/unsafe-entry.rs | 13 +++++++++ cortex-m-rt/examples/unsafe-exception.rs | 16 +++++++++++ cortex-m-rt/examples/unsafe-hard-fault.rs | 18 ++++++++++++ cortex-m-rt/macros/src/lib.rs | 32 ++++++++++------------ .../default-handler-bad-signature-1.rs | 2 +- .../default-handler-bad-signature-2.rs | 16 ----------- .../default-handler-bad-signature-3.rs | 18 ------------ .../tests/compile-fail/entry-bad-signature-1.rs | 2 +- .../tests/compile-fail/entry-bad-signature-2.rs | 2 +- .../tests/compile-fail/entry-bad-signature-3.rs | 4 +-- .../tests/compile-fail/entry-bad-signature-4.rs | 13 --------- .../compile-fail/exception-bad-signature-1.rs | 2 +- .../compile-fail/exception-bad-signature-2.rs | 16 ----------- .../compile-fail/exception-bad-signature-3.rs | 18 ------------ .../compile-fail/hard-fault-bad-signature-1.rs | 2 +- .../compile-fail/hard-fault-bad-signature-2.rs | 18 ------------ 20 files changed, 128 insertions(+), 123 deletions(-) create mode 100644 cortex-m-rt/examples/divergent-default-handler.rs create mode 100644 cortex-m-rt/examples/divergent-exception.rs create mode 100644 cortex-m-rt/examples/unsafe-default-handler.rs create mode 100644 cortex-m-rt/examples/unsafe-entry.rs create mode 100644 cortex-m-rt/examples/unsafe-exception.rs create mode 100644 cortex-m-rt/examples/unsafe-hard-fault.rs delete mode 100644 cortex-m-rt/tests/compile-fail/default-handler-bad-signature-2.rs delete mode 100644 cortex-m-rt/tests/compile-fail/default-handler-bad-signature-3.rs delete mode 100644 cortex-m-rt/tests/compile-fail/entry-bad-signature-4.rs delete mode 100644 cortex-m-rt/tests/compile-fail/exception-bad-signature-2.rs delete mode 100644 cortex-m-rt/tests/compile-fail/exception-bad-signature-3.rs delete mode 100644 cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-2.rs (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/ci/script.sh b/cortex-m-rt/ci/script.sh index ce1aec8..cecb975 100644 --- a/cortex-m-rt/ci/script.sh +++ b/cortex-m-rt/ci/script.sh @@ -13,12 +13,18 @@ main() { local examples=( alignment + 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 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/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/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index da21954..2619a44 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -22,7 +22,7 @@ use proc_macro::TokenStream; /// 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 `fn() -> !` (never ending function) +/// The type of the specified function must be `[unsafe] fn() -> !` (never ending function) /// /// # Properties /// @@ -76,7 +76,6 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { assert!( f.constness.is_none() && f.vis == Visibility::Inherited - && f.unsafety.is_none() && f.abi.is_none() && f.decl.inputs.is_empty() && f.decl.generics.params.is_empty() @@ -89,7 +88,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { _ => false, }, }, - "`#[entry]` function must have signature `fn() -> !`" + "`#[entry]` function must have signature `[unsafe] fn() -> !`" ); assert!( @@ -173,19 +172,19 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { /// # Usage /// /// `#[exception] fn 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. +/// `[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 -/// `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). +/// `[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`. -/// 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. +/// 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 /// @@ -284,7 +283,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { assert!( f.constness.is_none() && f.vis == Visibility::Inherited - && f.unsafety.is_none() && f.abi.is_none() && f.decl.inputs.len() == 1 && f.decl.generics.params.is_empty() @@ -294,10 +292,11 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { 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 `fn(i16)`" + "`DefaultHandler` exception must have signature `[unsafe] fn(i16) [-> !]`" ); let arg = match f.decl.inputs[0] { @@ -323,7 +322,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { assert!( f.constness.is_none() && f.vis == Visibility::Inherited - && f.unsafety.is_none() && f.abi.is_none() && f.decl.inputs.len() == 1 && match f.decl.inputs[0] { @@ -345,7 +343,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { _ => false, }, }, - "`HardFault` exception must have signature `fn(&ExceptionFrame) -> !`" + "`HardFault` exception must have signature `[unsafe] fn(&ExceptionFrame) -> !`" ); let arg = match f.decl.inputs[0] { @@ -372,7 +370,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { assert!( f.constness.is_none() && f.vis == Visibility::Inherited - && f.unsafety.is_none() && f.abi.is_none() && f.decl.inputs.is_empty() && f.decl.generics.params.is_empty() @@ -382,11 +379,12 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { 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 `fn()`" + have signature `[unsafe] fn() [-> !]`" ); let (statics, stmts) = extract_static_muts(stmts); 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 index a2c16c1..037e9c8 100644 --- 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 @@ -12,5 +12,5 @@ fn foo() -> ! { } #[exception] //~ ERROR custom attribute panicked -//~^ HELP `DefaultHandler` exception must have signature `fn(i16)` +//~^ 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 deleted file mode 100644 index ec74993..0000000 --- a/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-2.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![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 `fn(i16)` -unsafe fn DefaultHandler(_irqn: i16) {} diff --git a/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-3.rs b/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-3.rs deleted file mode 100644 index c827b6a..0000000 --- a/cortex-m-rt/tests/compile-fail/default-handler-bad-signature-3.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![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 `fn(i16)` -fn DefaultHandler(_irqn: i16) -> ! { - 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 index c4914db..5eeb49f 100644 --- a/cortex-m-rt/tests/compile-fail/entry-bad-signature-1.rs +++ b/cortex-m-rt/tests/compile-fail/entry-bad-signature-1.rs @@ -7,5 +7,5 @@ extern crate panic_semihosting; use cortex_m_rt::entry; #[entry] //~ ERROR custom attribute panicked -//~^ HELP `#[entry]` function must have signature `fn() -> !` +//~^ 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 index 88ffd34..18bbaed 100644 --- a/cortex-m-rt/tests/compile-fail/entry-bad-signature-2.rs +++ b/cortex-m-rt/tests/compile-fail/entry-bad-signature-2.rs @@ -7,5 +7,5 @@ extern crate panic_semihosting; use cortex_m_rt::entry; #[entry] //~ ERROR custom attribute panicked -//~^ HELP `#[entry]` function must have signature `fn() -> !` +//~^ 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 index 0f2ddca..09b75e9 100644 --- a/cortex-m-rt/tests/compile-fail/entry-bad-signature-3.rs +++ b/cortex-m-rt/tests/compile-fail/entry-bad-signature-3.rs @@ -7,7 +7,7 @@ extern crate panic_semihosting; use cortex_m_rt::entry; #[entry] //~ ERROR custom attribute panicked -//~^ HELP `#[entry]` function must have signature `fn() -> !` -unsafe fn foo() -> ! { +//~^ HELP `#[entry]` function must have signature `[unsafe] fn() -> !` +extern "C" fn foo() -> ! { loop {} } diff --git a/cortex-m-rt/tests/compile-fail/entry-bad-signature-4.rs b/cortex-m-rt/tests/compile-fail/entry-bad-signature-4.rs deleted file mode 100644 index 2077f41..0000000 --- a/cortex-m-rt/tests/compile-fail/entry-bad-signature-4.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![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 `fn() -> !` -extern "C" fn foo() -> ! { - loop {} -} 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 index 1f4b707..966493e 100644 --- a/cortex-m-rt/tests/compile-fail/exception-bad-signature-1.rs +++ b/cortex-m-rt/tests/compile-fail/exception-bad-signature-1.rs @@ -12,5 +12,5 @@ fn foo() -> ! { } #[exception] //~ ERROR custom attribute panicked -//~^ HELP `#[exception]` functions other than `DefaultHandler` and `HardFault` must have signature `fn()` +//~^ 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 deleted file mode 100644 index 8b1011d..0000000 --- a/cortex-m-rt/tests/compile-fail/exception-bad-signature-2.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![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 `fn()` -unsafe fn SysTick() {} diff --git a/cortex-m-rt/tests/compile-fail/exception-bad-signature-3.rs b/cortex-m-rt/tests/compile-fail/exception-bad-signature-3.rs deleted file mode 100644 index 546ef90..0000000 --- a/cortex-m-rt/tests/compile-fail/exception-bad-signature-3.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![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 `fn()` -fn SysTick() -> ! { - loop {} -} 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 index 7c61240..83fda5f 100644 --- 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 @@ -12,7 +12,7 @@ fn foo() -> ! { } #[exception] //~ ERROR custom attribute panicked -//~^ HELP `HardFault` exception must have signature `fn(&ExceptionFrame) -> !` +//~^ 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-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-2.rs deleted file mode 100644 index add6943..0000000 --- a/cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-2.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![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 `fn(&ExceptionFrame) -> !` -unsafe fn HardFault(_ef: &ExceptionFrame) -> ! { - loop {} -} -- cgit v1.2.3 From 31713aba3f4a4aacd55d12b8a3435334ae61986a Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 6 Sep 2018 01:35:12 +0200 Subject: make the reachability requirement more **prominent** since we can't statically check that it holds --- cortex-m-rt/macros/src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 2619a44..bd34b04 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -15,8 +15,9 @@ use proc_macro::TokenStream; /// Attribute to declare 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. +/// **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 @@ -136,8 +137,9 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { /// Attribute to declare an exception handler /// -/// **NOTE** This macro must be invoked from an accessible module, ideally from the root of the -/// crate. +/// **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 /// @@ -429,6 +431,10 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { /// 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 -- cgit v1.2.3 From 5fd25ff084daf914207193d15a7ade53cff7411e Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 6 Sep 2018 16:09:01 +0200 Subject: v0.6.0 this also adds compile-fail soundness tests and patches a soundness issue in `#[entry]` --- cortex-m-rt/CHANGELOG.md | 9 +++++++ cortex-m-rt/Cargo.toml | 2 +- cortex-m-rt/macros/src/lib.rs | 6 ++--- cortex-m-rt/tests/compile-fail/entry-soundness.rs | 26 +++++++++++++++++++ .../tests/compile-fail/exception-soundness.rs | 29 ++++++++++++++++++++++ 5 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 cortex-m-rt/tests/compile-fail/entry-soundness.rs create mode 100644 cortex-m-rt/tests/compile-fail/exception-soundness.rs (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/CHANGELOG.md b/cortex-m-rt/CHANGELOG.md index 7027d5a..dce4dfc 100644 --- a/cortex-m-rt/CHANGELOG.md +++ b/cortex-m-rt/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.6.0] - 2018-09-06 + +### Changed + +- [breaking-change] the `entry!`, `pre_init!` and `exception!` macros have been + replaced with attributes: `#[entry]`, `#[pre_init]` and `#[exception]`, + respectively. This also changes the toolchain requirement to 1.30-beta or + newer. + ## [v0.5.3] - 2018-08-27 ### Changed diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml index 9cf2c14..dd55bb5 100644 --- a/cortex-m-rt/Cargo.toml +++ b/cortex-m-rt/Cargo.toml @@ -8,7 +8,7 @@ keywords = ["arm", "cortex-m", "runtime", "startup"] license = "MIT OR Apache-2.0" name = "cortex-m-rt" repository = "https://github.com/japaric/cortex-m-rt" -version = "0.5.3" +version = "0.6.0" [dependencies] r0 = "0.2.1" diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index bd34b04..3435756 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -99,7 +99,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { // XXX should we blacklist other attributes? let attrs = f.attrs; - let ident = f.ident; + let hash = random_ident(); let (statics, stmts) = extract_static_muts(f.block.stmts); let vars = statics @@ -123,11 +123,9 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { }).collect::>(); quote!( - // TODO(forbid) see tests/compile-fail/entry-hidden.rs - // #[forbid(dead_code)] #[export_name = "main"] #(#attrs)* - pub fn #ident() -> ! { + pub fn #hash() -> ! { #(#vars)* #(#stmts)* diff --git a/cortex-m-rt/tests/compile-fail/entry-soundness.rs b/cortex-m-rt/tests/compile-fail/entry-soundness.rs new file mode 100644 index 0000000..5e40a8f --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/entry-soundness.rs @@ -0,0 +1,26 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_semihosting; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + static mut COUNT: u64 = 0; + + loop { + if *COUNT % 2 == 0 { + *COUNT += 1; + } else { + *COUNT *= 2; + } + } +} + +#[exception] +fn SysTick() { + // If this was allowed it would lead to a data race as `SysTick` can preempt `foo` + foo(); //~ ERROR cannot find function `foo` in this scope +} diff --git a/cortex-m-rt/tests/compile-fail/exception-soundness.rs b/cortex-m-rt/tests/compile-fail/exception-soundness.rs new file mode 100644 index 0000000..07d73fa --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-soundness.rs @@ -0,0 +1,29 @@ +#![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() { + static mut COUNT: u64 = 0; + + if *COUNT % 2 == 0 { + *COUNT += 1; + } else { + *COUNT *= 2; + } +} + +#[exception] +fn SVCall() { + // If this was allowed it would lead to a data race as `SVCall` could preempt `SysTick` + SysTick(); //~ ERROR cannot find function `SysTick` in this scope +} -- cgit v1.2.3 From ca46815047dd5f23f9a46f6cda7cf6e1e638815e Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Fri, 7 Sep 2018 00:58:29 +0200 Subject: Use SmallRng instead of thread_rng which is not available on no_std The RNG is seeded from the current time and an counter increasing with each call to yield different random values on each invocation. Signed-off-by: Daniel Egger --- cortex-m-rt/macros/src/lib.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 3435756..579e566 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -4,13 +4,19 @@ extern crate proc_macro; extern crate rand; #[macro_use] extern crate quote; +extern crate core; extern crate proc_macro2; extern crate syn; use proc_macro2::Span; use rand::Rng; +use rand::SeedableRng; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::time::{SystemTime, UNIX_EPOCH}; use syn::{FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, Visibility}; +static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); + use proc_macro::TokenStream; /// Attribute to declare the entry point of the program @@ -492,7 +498,23 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { // Creates a random identifier fn random_ident() -> Ident { - let mut rng = rand::thread_rng(); + let secs = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + + let count: u64 = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u64; + let mut seed: [u8; 16] = [0; 16]; + + for (i, v) in seed.iter_mut().take(8).enumerate() { + *v = ((secs >> (i * 8)) & 0xFF) as u8 + } + + for (i, v) in seed.iter_mut().skip(8).enumerate() { + *v = ((count >> (i * 8)) & 0xFF) as u8 + } + + let mut rng = rand::rngs::SmallRng::from_seed(seed); Ident::new( &(0..16) .map(|i| { -- cgit v1.2.3 From 3498c7433c020b02dc30a5e532607d1ad850d112 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 17 Sep 2018 18:36:18 +0200 Subject: make all exception handlers extern "C" They'll be called by the hardware and must comply to the stable C ABI. Using the Rust ABI in this case was an oversight. --- cortex-m-rt/macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 579e566..4c65916 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -418,7 +418,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[export_name = #ident_s] #(#attrs)* - pub fn #hash() { + pub extern "C" fn #hash() { extern crate cortex_m_rt; // check that this exception actually exists -- cgit v1.2.3 From 8385882be93fd4ba4e20ea0a812766cdc1a74c33 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 18 Sep 2018 00:26:28 +0200 Subject: respect declared unsafety the `#[entry]` and `#[exception]` attributes ignored the declared unsafety and always expanded to a safe function. This caused the following valid code to error at compile time: ``` rust #[entry] unsafe fn main() -> ! { foo(); //~^ ERROR call to unsafe function is unsafe and requires unsafe function or block loop {} } unsafe fn foo() {} ``` --- cortex-m-rt/examples/unsafety.rs | 36 ++++++++++++++++++++++++++++++++++++ cortex-m-rt/macros/src/lib.rs | 10 ++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 cortex-m-rt/examples/unsafety.rs (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/examples/unsafety.rs b/cortex-m-rt/examples/unsafety.rs new file mode 100644 index 0000000..a9f0234 --- /dev/null +++ b/cortex-m-rt/examples/unsafety.rs @@ -0,0 +1,36 @@ +//! Checks that the declared unsafety is respected by the attributes + +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_semihosting; + +use cortex_m_rt::{entry, exception, ExceptionFrame}; + +#[entry] +unsafe fn main() -> ! { + foo(); + + loop {} +} + +#[exception] +unsafe fn DefaultHandler(_irqn: i16) { + foo(); +} + +#[exception] +unsafe fn HardFault(_ef: &ExceptionFrame) -> ! { + foo(); + + loop {} +} + +#[exception] +unsafe fn SysTick() { + foo(); +} + +unsafe fn foo() {} diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 4c65916..804dd64 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -105,6 +105,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { // XXX should we blacklist other attributes? let attrs = f.attrs; + let unsafety = f.unsafety; let hash = random_ident(); let (statics, stmts) = extract_static_muts(f.block.stmts); @@ -131,7 +132,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[export_name = "main"] #(#attrs)* - pub fn #hash() -> ! { + pub #unsafety fn #hash() -> ! { #(#vars)* #(#stmts)* @@ -282,6 +283,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let attrs = f.attrs; let block = f.block; let stmts = block.stmts; + let unsafety = f.unsafety; let hash = random_ident(); match exn { @@ -313,7 +315,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[export_name = #ident_s] #(#attrs)* - pub extern "C" fn #hash() { + pub #unsafety extern "C" fn #hash() { extern crate core; const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; @@ -362,7 +364,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[export_name = "UserHardFault"] #(#attrs)* - pub extern "C" fn #hash(#arg) -> ! { + pub #unsafety extern "C" fn #hash(#arg) -> ! { extern crate cortex_m_rt; // further type check of the input argument @@ -418,7 +420,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[export_name = #ident_s] #(#attrs)* - pub extern "C" fn #hash() { + pub #unsafety extern "C" fn #hash() { extern crate cortex_m_rt; // check that this exception actually exists -- cgit v1.2.3 From fb7368e658ed175a35cdf4a33a02b356aa139523 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 17 Sep 2018 20:12:25 +0200 Subject: implement `#[interrupt]` --- cortex-m-rt/Cargo.toml | 1 + cortex-m-rt/ci/script.sh | 2 +- cortex-m-rt/macros/src/lib.rs | 147 ++++++++++++++++++++- cortex-m-rt/src/lib.rs | 4 +- cortex-m-rt/tests/compile-fail/interrupt-args.rs | 20 +++ .../compile-fail/interrupt-bad-signature-1.rs | 20 +++ .../compile-fail/interrupt-bad-signature-2.rs | 22 +++ .../tests/compile-fail/interrupt-invalid.rs | 21 +++ .../tests/compile-fail/interrupt-not-reexported.rs | 15 +++ .../tests/compile-fail/interrupt-soundness.rs | 33 +++++ cortex-m-rt/tests/compile-fail/interrupt-twice.rs | 31 +++++ 11 files changed, 310 insertions(+), 6 deletions(-) create mode 100644 cortex-m-rt/tests/compile-fail/interrupt-args.rs create mode 100644 cortex-m-rt/tests/compile-fail/interrupt-bad-signature-1.rs create mode 100644 cortex-m-rt/tests/compile-fail/interrupt-bad-signature-2.rs create mode 100644 cortex-m-rt/tests/compile-fail/interrupt-invalid.rs create mode 100644 cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs create mode 100644 cortex-m-rt/tests/compile-fail/interrupt-soundness.rs create mode 100644 cortex-m-rt/tests/compile-fail/interrupt-twice.rs (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml index 41a958d..0123750 100644 --- a/cortex-m-rt/Cargo.toml +++ b/cortex-m-rt/Cargo.toml @@ -18,6 +18,7 @@ cortex-m-rt-macros = { path = "macros", version = "0.1.1" } cortex-m = "0.5.4" panic-abort = "0.3.0" panic-semihosting = "0.4.0" +panic-halt = "0.2.0" [dev-dependencies.rand] default-features = false diff --git a/cortex-m-rt/ci/script.sh b/cortex-m-rt/ci/script.sh index 9933372..3cffcad 100644 --- a/cortex-m-rt/ci/script.sh +++ b/cortex-m-rt/ci/script.sh @@ -8,7 +8,7 @@ main() { if [ $TARGET = x86_64-unknown-linux-gnu ]; then ( cd macros && cargo check && cargo test ) - cargo test --test compiletest + cargo test --features device --test compiletest fi local examples=( diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 804dd64..711cf0b 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -411,10 +411,10 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let expr = var.expr; quote!( - static mut #ident_: #ty = #expr; - #[allow(non_snake_case)] - let #ident: &mut #ty = unsafe { &mut #ident_ }; - ) + static mut #ident_: #ty = #expr; + #[allow(non_snake_case)] + let #ident: &mut #ty = unsafe { &mut #ident_ }; + ) }).collect::>(); quote!( @@ -435,6 +435,145 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { } } +/// Attribute to declare an interrupt (AKA device-specific 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. +/// +/// **NOTE**: This attribute is exposed by `cortex-m-rt` only when the `device` feature is enabled. +/// However, that export is not meant to be used directly -- using it will result in a compilation +/// error. You should instead use the device crate (usually generated using `svd2rust`) re-export of +/// that attribute. You need to use the re-export to have the compiler check that the interrupt +/// exists on the target device. +/// +/// # Syntax +/// +/// ``` ignore +/// extern crate device; +/// +/// // the attribute comes from the device crate not from cortex-m-rt +/// use device::interrupt; +/// +/// #[interrupt] +/// fn USART1() { +/// // .. +/// } +/// ``` +/// +/// where the name of the function must be one of the device interrupts. +/// +/// # Usage +/// +/// `#[interrupt] fn Name(..` overrides the default handler for the interrupt with the given `Name`. +/// These handlers must have signature `[unsafe] fn() [-> !]`. It's possible to add state to these +/// handlers 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. +/// +/// If the interrupt handler has not been overridden it will be dispatched by the default exception +/// handler (`DefaultHandler`). +/// +/// # Properties +/// +/// Interrupts handlers can only be called by the hardware. Other parts of the program can't refer +/// to the interrupt handlers, much less invoke them as if they were functions. +/// +/// `static mut` variables declared within an interrupt 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 +/// +/// - Using state within an interrupt handler +/// +/// ``` ignore +/// extern crate device; +/// +/// use device::interrupt; +/// +/// #[interrupt] +/// fn TIM2() { +/// static mut COUNT: i32 = 0; +/// +/// // `COUNT` is safe to access and has type `&mut i32` +/// *COUNT += 1; +/// +/// println!("{}", COUNT); +/// } +/// ``` +#[proc_macro_attribute] +pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { + let f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); + + assert!( + args.to_string() == "", + "`interrupt` attribute must have no arguments" + ); + + let ident = f.ident; + let ident_s = ident.to_string(); + + // XXX should we blacklist other attributes? + let attrs = f.attrs; + let block = f.block; + let stmts = block.stmts; + + 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, + }, + }, + "`#[interrupt]` functions 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::>(); + + let hash = random_ident(); + quote!( + #[export_name = #ident_s] + #(#attrs)* + pub extern "C" fn #hash() { + interrupt::#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 diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index f3e58d0..7488cc9 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -394,6 +394,8 @@ extern crate r0; use core::fmt; use core::sync::atomic::{self, Ordering}; +#[cfg(feature = "device")] +pub use macros::interrupt; pub use macros::{entry, exception, pre_init}; #[export_name = "error: cortex-m-rt appears more than once in the dependency graph"] @@ -674,7 +676,7 @@ pub static __EXCEPTIONS: [Vector; 14] = [ // If we are not targeting a specific device we bind all the potential device specific interrupts // to the default handler -#[cfg(all(not(feature = "device"), not(armv6m)))] +#[cfg(all(any(not(feature = "device"), test), not(armv6m)))] #[doc(hidden)] #[link_section = ".vector_table.interrupts"] #[no_mangle] diff --git a/cortex-m-rt/tests/compile-fail/interrupt-args.rs b/cortex-m-rt/tests/compile-fail/interrupt-args.rs new file mode 100644 index 0000000..36af388 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-args.rs @@ -0,0 +1,20 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +enum interrupt { + USART1, +} + +#[interrupt(true)] //~ ERROR custom attribute panicked +//~^ HELP `interrupt` attribute must have no arguments +fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-1.rs b/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-1.rs new file mode 100644 index 0000000..0222413 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-1.rs @@ -0,0 +1,20 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +enum interrupt { + USART1, +} + +#[interrupt] //~ ERROR custom attribute panicked +//~^ HELP `#[interrupt]` functions must have signature `[unsafe] fn() [-> !]` +fn USART1(undef: i32) {} diff --git a/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-2.rs b/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-2.rs new file mode 100644 index 0000000..0c9000b --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-bad-signature-2.rs @@ -0,0 +1,22 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +enum interrupt { + USART1, +} + +#[interrupt] //~ ERROR custom attribute panicked +//~^ HELP `#[interrupt]` functions must have signature `[unsafe] fn() [-> !]` +fn USART1() -> i32 { + 0 +} diff --git a/cortex-m-rt/tests/compile-fail/interrupt-invalid.rs b/cortex-m-rt/tests/compile-fail/interrupt-invalid.rs new file mode 100644 index 0000000..4e79e7d --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-invalid.rs @@ -0,0 +1,21 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +enum interrupt { + USART1, +} + +// NOTE this looks a bit better when using a device crate: +// "no variant named `foo` found for type `stm32f30x::Interrupt` in the current scope" +#[interrupt] //~ ERROR no variant named `foo` found for type `interrupt` in the current scope +fn foo() {} diff --git a/cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs b/cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs new file mode 100644 index 0000000..9530cf4 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs @@ -0,0 +1,15 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[interrupt] //~ ERROR failed to resolve. Use of undeclared type or module `interrupt` +fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/interrupt-soundness.rs b/cortex-m-rt/tests/compile-fail/interrupt-soundness.rs new file mode 100644 index 0000000..b473a94 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-soundness.rs @@ -0,0 +1,33 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +enum interrupt { + USART1, + USART2, +} + +#[interrupt] +fn USART1() { + static mut COUNT: u64 = 0; + + if *COUNT % 2 == 0 { + *COUNT += 1; + } else { + *COUNT *= 2; + } +} + +#[interrupt] +fn USART2() { + USART1(); //~ ERROR cannot find function `USART1` in this scope +} diff --git a/cortex-m-rt/tests/compile-fail/interrupt-twice.rs b/cortex-m-rt/tests/compile-fail/interrupt-twice.rs new file mode 100644 index 0000000..b59aea9 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/interrupt-twice.rs @@ -0,0 +1,31 @@ +#![allow(non_camel_case_types)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, interrupt}; + +#[entry] +fn foo() -> ! { + loop {} +} + +enum interrupt { + USART1, +} + +#[interrupt] +fn USART1() {} + +pub mod reachable { + use cortex_m_rt::interrupt; + + enum interrupt { + USART1, + } + + #[interrupt] //~ ERROR symbol `USART1` is already defined + fn USART1() {} +} -- cgit v1.2.3 From 879ae3c6296d19888bc00a18e50668f981deb7bb Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 18 Sep 2018 00:30:03 +0200 Subject: respect declared unsafety this is a backport of PR #123 --- cortex-m-rt/macros/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 711cf0b..8721010 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -518,6 +518,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { let attrs = f.attrs; let block = f.block; let stmts = block.stmts; + let unsafety = f.unsafety; assert!( f.constness.is_none() @@ -564,7 +565,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[export_name = #ident_s] #(#attrs)* - pub extern "C" fn #hash() { + pub #unsafety extern "C" fn #hash() { interrupt::#ident; #(#vars)* -- cgit v1.2.3 From 7f7138a62f9efdf9057cc4ec01733e448ecce55b Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 19 Sep 2018 23:41:59 +0200 Subject: bump the syn dependency and switch to the recommended way to parse tokens: `parse_macro_input!`. This improves (?) error messages when the user applies one of our attributes to an item that's not a function. Consider ``` rust #[entry] static MAIN: () = (); ``` The error message changed from: ``` error: custom attribute panicked --> src/main.rs:10:1 | 10 | #[entry] | ^^^^^^^^ | = help: message: `#[entry]` must be applied to a function: ParseError(Some("failed to parse fn item: failed to parse")) ``` to: ``` error: expected `fn` --> src/main.rs:11:1 | 11 | static MAIN: () = (); | ^^^^^^ error: aborting due to previous error ``` --- cortex-m-rt/macros/Cargo.toml | 6 +++--- cortex-m-rt/macros/src/lib.rs | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/Cargo.toml b/cortex-m-rt/macros/Cargo.toml index 8d7e517..eded8bf 100644 --- a/cortex-m-rt/macros/Cargo.toml +++ b/cortex-m-rt/macros/Cargo.toml @@ -13,12 +13,12 @@ version = "0.1.1" proc-macro = true [dependencies] -quote = "0.6.6" -proc-macro2 = "0.4.15" +quote = "0.6.8" +proc-macro2 = "0.4.19" [dependencies.syn] features = ["extra-traits", "full"] -version = "0.14.8" +version = "0.15.4" [dependencies.rand] version = "0.5.5" diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 804dd64..f9ae17e 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -6,6 +6,7 @@ extern crate rand; extern crate quote; extern crate core; extern crate proc_macro2; +#[macro_use] extern crate syn; use proc_macro2::Span; @@ -77,7 +78,7 @@ use proc_macro::TokenStream; /// ``` #[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"); + let f = parse_macro_input!(input as ItemFn); // check the function signature assert!( @@ -253,7 +254,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { /// ``` #[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"); + let f = parse_macro_input!(input as ItemFn); assert!( args.to_string() == "", @@ -459,7 +460,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { /// ``` #[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"); + let f = parse_macro_input!(input as ItemFn); // check the function signature assert!( -- cgit v1.2.3 From 58cdad2fba9763ac590bb0460ecd7193014a1e12 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 26 Oct 2018 20:39:39 +0200 Subject: entry/exception/interrupt: reduce namespace pollution when using static mut this changes code generation of code like this: ``` rust #[entry] fn main() -> ! { static mut FOO: u32 = 0; // .. } ``` from this: ``` rust fn main() -> ! { static mut FOO_: u32 = 0; let FOO: &'static mut u32 = unsafe { &mut FOO_ }; // .. } ``` to this: ``` rust fn main() -> ! { let FOO: &'static mut u32 = unsafe { static mut FOO: u32 = 0; &mut FOO; }; // .. } ``` this completely hides the `static mut` variable. Before it was possible to (unsafely) access it via `${ident}_` (e.g. `FOO_`). --- cortex-m-rt/macros/src/lib.rs | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 56ec88c..560a05f 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -114,19 +114,16 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { .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_ }; + let #ident: &'static mut #ty = unsafe { + static mut #ident: #ty = #expr; + + &mut #ident + }; ) }).collect::>(); @@ -402,19 +399,16 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .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_ }; + let #ident: &mut #ty = unsafe { + static mut #ident: #ty = #expr; + + &mut #ident + }; ) }).collect::>(); @@ -546,19 +540,16 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { .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_ }; + let #ident: &mut #ty = unsafe { + static mut #ident: #ty = #expr; + + &mut #ident + }; ) }).collect::>(); -- cgit v1.2.3 From 01802586446225d43790e160768dd6bfb07590a4 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 26 Oct 2018 20:45:59 +0200 Subject: entry/exception/interrupt: forward `static mut` attributes this is required to implement safe interfaces to things like writable Flash (e.g. EEPROM emulation) which require the `#[link_section]` attribute. --- cortex-m-rt/macros/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 560a05f..dd89f06 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -113,6 +113,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let vars = statics .into_iter() .map(|var| { + let attrs = var.attrs; let ident = var.ident; let ty = var.ty; let expr = var.expr; @@ -120,6 +121,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[allow(non_snake_case)] let #ident: &'static mut #ty = unsafe { + #(#attrs)* static mut #ident: #ty = #expr; &mut #ident @@ -398,6 +400,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let vars = statics .into_iter() .map(|var| { + let attrs = var.attrs; let ident = var.ident; let ty = var.ty; let expr = var.expr; @@ -405,6 +408,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[allow(non_snake_case)] let #ident: &mut #ty = unsafe { + #(#attrs)* static mut #ident: #ty = #expr; &mut #ident @@ -539,6 +543,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { let vars = statics .into_iter() .map(|var| { + let attrs = var.attrs; let ident = var.ident; let ty = var.ty; let expr = var.expr; @@ -546,6 +551,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[allow(non_snake_case)] let #ident: &mut #ty = unsafe { + #(#attrs)* static mut #ident: #ty = #expr; &mut #ident -- cgit v1.2.3 From 0f7ee4c83de809b46e2c0c78619ec1bad429aa0e Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 26 Oct 2018 20:59:47 +0200 Subject: entry/exception/interrupt: reachability restriction is 1.30-only Thanks to rust-lang/rust#54451, which will be available in 1.31, these attributes will work regardless of the visibility and reachability of the items. --- cortex-m-rt/macros/src/lib.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 56ec88c..8f78551 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -22,9 +22,10 @@ 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. +/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you +/// are using Rust 1.30 the attribute 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. This reachability restriction doesn't apply to Rust 1.31 and newer releases. /// /// 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 @@ -143,9 +144,10 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { /// 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. +/// **IMPORTANT**: If you are using Rust 1.30 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. This reachability restriction doesn't apply to Rust 1.31 +/// and newer releases. /// /// # Syntax /// @@ -438,9 +440,10 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { /// Attribute to declare an interrupt (AKA device-specific 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. +/// **IMPORTANT**: If you are using Rust 1.30 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. This reachability restriction doesn't apply to Rust 1.31 +/// and newer releases. /// /// **NOTE**: This attribute is exposed by `cortex-m-rt` only when the `device` feature is enabled. /// However, that export is not meant to be used directly -- using it will result in a compilation @@ -578,9 +581,11 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { /// 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. +/// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you +/// are using Rust 1.30 the attribute 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. This reachability restriction doesn't apply to Rust 1.31 and newer +/// releases. /// /// The function must have the signature of `unsafe fn()`. /// -- cgit v1.2.3 From adf49361cd19291005bbc9645e41ed334abb2e66 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 26 Oct 2018 21:22:43 +0200 Subject: attributes: turn panics into compile errors Sample error messages: ``` error: `#[entry]` function must have signature `[unsafe] fn() -> !` --> examples/error.rs:15:1 | 15 | fn main() { | ^^ error: aborting due to previous error ``` ``` error: This attribute accepts no arguments --> examples/error.rs:14:1 | 14 | #[entry(hello)] | ^^^^^^^^^^^^^^^ error: aborting due to previous error ``` ``` error: This is not a valid exception name --> examples/error.rs:20:4 | 20 | fn foo() {} | ^^^ error: aborting due to previous error ``` --- cortex-m-rt/macros/src/lib.rs | 343 +++++++++++++++++++++++++----------------- 1 file changed, 201 insertions(+), 142 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index dd89f06..54bbdfa 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -14,7 +14,10 @@ use rand::Rng; use rand::SeedableRng; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; -use syn::{FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, Visibility}; +use syn::{ + parse, spanned::Spanned, FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, + Visibility, +}; static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); @@ -81,28 +84,35 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); // 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, - }, + let valid_signature = 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() -> !`" - ); + }; + + if !valid_signature { + return parse::Error::new( + f.span(), + "`#[entry]` function must have signature `[unsafe] fn() -> !`", + ) + .to_compile_error() + .into(); + } - assert!( - args.to_string() == "", - "`entry` attribute must have no arguments" - ); + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } // XXX should we blacklist other attributes? let attrs = f.attrs; @@ -127,7 +137,8 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { &mut #ident }; ) - }).collect::>(); + }) + .collect::>(); quote!( #[export_name = "main"] @@ -137,7 +148,8 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { #(#stmts)* } - ).into() + ) + .into() } /// Attribute to declare an exception handler @@ -255,11 +267,13 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); - assert!( - args.to_string() == "", - "`exception` attribute must have no arguments" - ); + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } + let fspan = f.span(); let ident = f.ident; enum Exception { @@ -276,7 +290,11 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { // 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), + _ => { + return parse::Error::new(ident.span(), "This is not a valid exception name") + .to_compile_error() + .into(); + } }; // XXX should we blacklist other attributes? @@ -288,24 +306,30 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { 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, - }, + let valid_signature = 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) [-> !]`" - ); + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`DefaultHandler` must have signature `[unsafe] fn(i16) [-> !]`", + ) + .to_compile_error() + .into(); + } let arg = match f.decl.inputs[0] { FnArg::Captured(ref arg) => arg, @@ -324,35 +348,40 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { #(#stmts)* } - ).into() + ) + .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, - }, + let valid_signature = 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, - } - && 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) -> !`" - ); + _ => 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, + }, + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`HardFault` handler must have signature `[unsafe] fn(&ExceptionFrame) -> !`", + ) + .to_compile_error() + .into(); + } let arg = match f.decl.inputs[0] { FnArg::Captured(ref arg) => arg, @@ -372,28 +401,35 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { #(#stmts)* } - ).into() + ) + .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, - }, + let valid_signature = 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() [-> !]`" - ); + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`#[exception]` handlers other than `DefaultHandler` and `HardFault` must have \ + signature `[unsafe] fn() [-> !]`", + ) + .to_compile_error() + .into(); + } let (statics, stmts) = extract_static_muts(stmts); @@ -414,7 +450,8 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { &mut #ident }; ) - }).collect::>(); + }) + .collect::>(); quote!( #[export_name = #ident_s] @@ -429,7 +466,8 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { #(#stmts)* } - ).into() + ) + .into() } } } @@ -505,11 +543,13 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { let f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); - assert!( - args.to_string() == "", - "`interrupt` attribute must have no arguments" - ); + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } + let fspan = f.span(); let ident = f.ident; let ident_s = ident.to_string(); @@ -519,24 +559,30 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { let stmts = block.stmts; let unsafety = f.unsafety; - 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, - }, + let valid_signature = 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, }, - "`#[interrupt]` functions must have signature `[unsafe] fn() [-> !]`" - ); + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", + ) + .to_compile_error() + .into(); + } let (statics, stmts) = extract_static_muts(stmts); @@ -557,7 +603,8 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { &mut #ident }; ) - }).collect::>(); + }) + .collect::>(); let hash = random_ident(); quote!( @@ -570,7 +617,8 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { #(#stmts)* } - ).into() + ) + .into() } /// Attribute to mark which function will be called at the beginning of the reset handler. @@ -600,29 +648,36 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); // 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, - }, + let valid_signature = 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()`" - ); + }; + + if !valid_signature { + return parse::Error::new( + f.span(), + "`#[pre_init]` function must have signature `unsafe fn()`", + ) + .to_compile_error() + .into(); + } - assert!( - args.to_string() == "", - "`pre_init` attribute must have no arguments" - ); + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } // XXX should we blacklist other attributes? let attrs = f.attrs; @@ -633,7 +688,8 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { #[export_name = "__pre_init"] #(#attrs)* pub unsafe fn #ident() #block - ).into() + ) + .into() } // Creates a random identifier @@ -663,7 +719,8 @@ fn random_ident() -> Ident { } else { ('0' as u8 + rng.gen::() % 10) as char } - }).collect::(), + }) + .collect::(), Span::call_site(), ) } @@ -676,11 +733,13 @@ fn extract_static_muts(stmts: Vec) -> (Vec, 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))); - }, + 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; -- cgit v1.2.3 From 98eb7ea7816e17b38f03c39173804358adb15a7c Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 26 Oct 2018 23:04:03 +0200 Subject: `b UserHardFault` this commit replaces the `bl UserHardFault` instruction in HardFault with `b UserHardFault`. This lets us drop the prologue (`push {r0,lr}`) while preserving the ability to unwind the stack using GDB. To prevent linker errors about relocations when UserHardFault and HardFault end up far away from each other and the target is ARMv6-M (where the `b` instruction only supports offsets of +/- 2KB) we use two *input* sections: .HardFault and .UserHardFault. HardFault is placed in the .HardFault section and UserHardFault is placed in the .UserHardFault section. The .HardFault input section is placed in the output .text section after all the input .text sections, and the .UserHardFault input section is placed after the .HardFault section. This causes the two symbols to always appear next to each other in the output binary, furthermore UserHardFault *always* appears after HardFault so the branch offset of `b UserHardFault` is always a few bytes. It should be noted that neither .HardFault or .UserHardFault will appear in the output of the `size -A` command as they are input sections that get merged into the output .text section. IOW, the sizes of HardFault and UserHardFault will continue to be reported under the .text section. --- cortex-m-rt/asm.s | 9 ++++++--- cortex-m-rt/bin/thumbv6m-none-eabi.a | Bin 920 -> 882 bytes cortex-m-rt/bin/thumbv7em-none-eabi.a | Bin 920 -> 882 bytes cortex-m-rt/bin/thumbv7em-none-eabihf.a | Bin 920 -> 882 bytes cortex-m-rt/bin/thumbv7m-none-eabi.a | Bin 920 -> 882 bytes cortex-m-rt/link.x.in | 2 ++ cortex-m-rt/macros/src/lib.rs | 1 + cortex-m-rt/src/lib.rs | 1 + 8 files changed, 10 insertions(+), 3 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/asm.s b/cortex-m-rt/asm.s index a5748a5..4636bc3 100644 --- a/cortex-m-rt/asm.s +++ b/cortex-m-rt/asm.s @@ -1,7 +1,10 @@ - .section .text.HardFault + # LLD requires that the section flags are explicitly set here + .section .HardFault, "ax" .global HardFault + # .type and .thumb_func are both required; otherwise its Thumb bit does not + # get set and an invalid vector table is generated + .type HardFault,%function .thumb_func HardFault: - push {r0, lr} mrs r0, MSP - bl UserHardFault + b UserHardFault diff --git a/cortex-m-rt/bin/thumbv6m-none-eabi.a b/cortex-m-rt/bin/thumbv6m-none-eabi.a index 4a2f178..9857abc 100644 Binary files a/cortex-m-rt/bin/thumbv6m-none-eabi.a and b/cortex-m-rt/bin/thumbv6m-none-eabi.a differ diff --git a/cortex-m-rt/bin/thumbv7em-none-eabi.a b/cortex-m-rt/bin/thumbv7em-none-eabi.a index e5ffd6f..3f8b8fe 100644 Binary files a/cortex-m-rt/bin/thumbv7em-none-eabi.a and b/cortex-m-rt/bin/thumbv7em-none-eabi.a differ diff --git a/cortex-m-rt/bin/thumbv7em-none-eabihf.a b/cortex-m-rt/bin/thumbv7em-none-eabihf.a index e5ffd6f..3f8b8fe 100644 Binary files a/cortex-m-rt/bin/thumbv7em-none-eabihf.a and b/cortex-m-rt/bin/thumbv7em-none-eabihf.a differ diff --git a/cortex-m-rt/bin/thumbv7m-none-eabi.a b/cortex-m-rt/bin/thumbv7m-none-eabi.a index d3cc07e..43c843e 100644 Binary files a/cortex-m-rt/bin/thumbv7m-none-eabi.a and b/cortex-m-rt/bin/thumbv7m-none-eabi.a differ diff --git a/cortex-m-rt/link.x.in b/cortex-m-rt/link.x.in index fde4e70..e6e0d3b 100644 --- a/cortex-m-rt/link.x.in +++ b/cortex-m-rt/link.x.in @@ -86,6 +86,8 @@ SECTIONS .text _stext : { *(.text .text.*); + *(.HardFault); + *(.UserHardFault); } > FLASH /* ### .rodata */ diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 0d29f55..9ce80ad 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -365,6 +365,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[export_name = "UserHardFault"] + #[link_section = ".UserHardFault"] #(#attrs)* pub #unsafety extern "C" fn #hash(#arg) -> ! { extern crate cortex_m_rt; diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index 7488cc9..5fb5057 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -532,6 +532,7 @@ pub unsafe extern "C" fn Reset() -> ! { #[allow(unused_variables)] #[doc(hidden)] +#[link_section = ".UserHardFault"] #[no_mangle] pub unsafe extern "C" fn UserHardFault_(ef: &ExceptionFrame) -> ! { loop { -- cgit v1.2.3 From b6facc16d893c2e886401cb0743bb29700388293 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 4 Nov 2018 01:08:52 +0100 Subject: reject duplicate `static mut` variables after #140 landed the entry, exception and interrupt attributes started accepting code like this: ``` rust #[entry] fn main() -> ! { static mut FOO: u32 = 0; static mut FOO: i32 = 0; } ``` because that code expands into: ``` rust fn main() -> ! { let FOO: &'static mut u32 = unsafe { static mut FOO: u32 = 0; &mut FOO }; // shadows previous variable let FOO: &'static mut u32 = unsafe { static mut FOO: i32 = 0; &mut FOO }; } ``` this commit adds a check that rejects `static mut`s with duplicated names to these three attributes. --- cortex-m-rt/macros/src/lib.rs | 29 ++++++++++++++++---- cortex-m-rt/tests/compile-fail/duplicate-static.rs | 31 ++++++++++++++++++++++ 2 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 cortex-m-rt/tests/compile-fail/duplicate-static.rs (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 0032837..dfa936b 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -12,6 +12,7 @@ extern crate syn; use proc_macro2::Span; use rand::Rng; use rand::SeedableRng; +use std::collections::HashSet; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; use syn::{ @@ -119,7 +120,10 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let attrs = f.attrs; let unsafety = f.unsafety; let hash = random_ident(); - let (statics, stmts) = extract_static_muts(f.block.stmts); + let (statics, stmts) = match extract_static_muts(f.block.stmts) { + Err(e) => return e.to_compile_error().into(), + Ok(x) => x, + }; let vars = statics .into_iter() @@ -434,7 +438,10 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } - let (statics, stmts) = extract_static_muts(stmts); + let (statics, stmts) = match extract_static_muts(stmts) { + Err(e) => return e.to_compile_error().into(), + Ok(x) => x, + }; let vars = statics .into_iter() @@ -588,7 +595,10 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } - let (statics, stmts) = extract_static_muts(stmts); + let (statics, stmts) = match extract_static_muts(stmts) { + Err(e) => return e.to_compile_error().into(), + Ok(x) => x, + }; let vars = statics .into_iter() @@ -732,15 +742,24 @@ fn random_ident() -> Ident { } /// Extracts `static mut` vars from the beginning of the given statements -fn extract_static_muts(stmts: Vec) -> (Vec, Vec) { +fn extract_static_muts(stmts: Vec) -> Result<(Vec, Vec), parse::Error> { let mut istmts = stmts.into_iter(); + let mut seen = HashSet::new(); 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() { + if seen.contains(&var.ident) { + return Err(parse::Error::new( + var.ident.span(), + format!("the name `{}` is defined multiple times", var.ident), + )); + } + + seen.insert(var.ident.clone()); statics.push(var); } else { stmts.push(Stmt::Item(Item::Static(var))); @@ -755,5 +774,5 @@ fn extract_static_muts(stmts: Vec) -> (Vec, Vec) { stmts.extend(istmts); - (statics, stmts) + Ok((statics, stmts)) } diff --git a/cortex-m-rt/tests/compile-fail/duplicate-static.rs b/cortex-m-rt/tests/compile-fail/duplicate-static.rs new file mode 100644 index 0000000..fccb65f --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/duplicate-static.rs @@ -0,0 +1,31 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +enum interrupt { + UART0, +} + +#[entry] +fn foo() -> ! { + static mut X: u32 = 0; + static mut X: i32 = 0; //~ ERROR the name `X` is defined multiple times + + loop {} +} + +#[exception] +fn SVCall() { + static mut X: u32 = 0; + static mut X: i32 = 0; //~ ERROR the name `X` is defined multiple times +} + +#[interrupt] +fn UART0() { + static mut X: u32 = 0; + static mut X: i32 = 0; //~ ERROR the name `X` is defined multiple times +} -- cgit v1.2.3 From e883c5e962dd7fb420554787010df7f1a10c9926 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 27 Oct 2018 14:19:28 +0200 Subject: rename UserHardFault to HardFault so it matches the exception name (`#[exception] fn HardFault(..`) --- cortex-m-rt/asm.s | 10 +++++----- cortex-m-rt/bin/thumbv6m-none-eabi.a | Bin 882 -> 920 bytes cortex-m-rt/bin/thumbv7em-none-eabi.a | Bin 882 -> 920 bytes cortex-m-rt/bin/thumbv7em-none-eabihf.a | Bin 882 -> 920 bytes cortex-m-rt/bin/thumbv7m-none-eabi.a | Bin 882 -> 920 bytes cortex-m-rt/link.x.in | 8 ++++---- cortex-m-rt/macros/src/lib.rs | 4 ++-- cortex-m-rt/src/lib.rs | 18 +++++++++--------- 8 files changed, 20 insertions(+), 20 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/asm.s b/cortex-m-rt/asm.s index 4636bc3..1f0e74d 100644 --- a/cortex-m-rt/asm.s +++ b/cortex-m-rt/asm.s @@ -1,10 +1,10 @@ # LLD requires that the section flags are explicitly set here - .section .HardFault, "ax" - .global HardFault + .section .HardFaultTrampoline, "ax" + .global HardFaultTrampoline # .type and .thumb_func are both required; otherwise its Thumb bit does not # get set and an invalid vector table is generated - .type HardFault,%function + .type HardFaultTrampoline,%function .thumb_func -HardFault: +HardFaultTrampoline: mrs r0, MSP - b UserHardFault + b HardFault diff --git a/cortex-m-rt/bin/thumbv6m-none-eabi.a b/cortex-m-rt/bin/thumbv6m-none-eabi.a index 9857abc..5938e99 100644 Binary files a/cortex-m-rt/bin/thumbv6m-none-eabi.a and b/cortex-m-rt/bin/thumbv6m-none-eabi.a differ diff --git a/cortex-m-rt/bin/thumbv7em-none-eabi.a b/cortex-m-rt/bin/thumbv7em-none-eabi.a index 3f8b8fe..fc426f9 100644 Binary files a/cortex-m-rt/bin/thumbv7em-none-eabi.a and b/cortex-m-rt/bin/thumbv7em-none-eabi.a differ diff --git a/cortex-m-rt/bin/thumbv7em-none-eabihf.a b/cortex-m-rt/bin/thumbv7em-none-eabihf.a index 3f8b8fe..fc426f9 100644 Binary files a/cortex-m-rt/bin/thumbv7em-none-eabihf.a and b/cortex-m-rt/bin/thumbv7em-none-eabihf.a differ diff --git a/cortex-m-rt/bin/thumbv7m-none-eabi.a b/cortex-m-rt/bin/thumbv7m-none-eabi.a index 43c843e..3359875 100644 Binary files a/cortex-m-rt/bin/thumbv7m-none-eabi.a and b/cortex-m-rt/bin/thumbv7m-none-eabi.a differ diff --git a/cortex-m-rt/link.x.in b/cortex-m-rt/link.x.in index e6e0d3b..dff43e0 100644 --- a/cortex-m-rt/link.x.in +++ b/cortex-m-rt/link.x.in @@ -35,7 +35,7 @@ EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */ EXTERN(DefaultHandler); PROVIDE(NonMaskableInt = DefaultHandler); -EXTERN(HardFault); +EXTERN(HardFaultTrampoline); PROVIDE(MemoryManagement = DefaultHandler); PROVIDE(BusFault = DefaultHandler); PROVIDE(UsageFault = DefaultHandler); @@ -46,7 +46,7 @@ PROVIDE(PendSV = DefaultHandler); PROVIDE(SysTick = DefaultHandler); PROVIDE(DefaultHandler = DefaultHandler_); -PROVIDE(UserHardFault = UserHardFault_); +PROVIDE(HardFault = HardFault_); /* # Interrupt vectors */ EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */ @@ -86,8 +86,8 @@ SECTIONS .text _stext : { *(.text .text.*); - *(.HardFault); - *(.UserHardFault); + *(.HardFaultTrampoline); + *(.HardFault.*); } > FLASH /* ### .rodata */ diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index dfa936b..1013f5e 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -397,8 +397,8 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let pat = &arg.pat; quote!( - #[export_name = "UserHardFault"] - #[link_section = ".UserHardFault"] + #[export_name = "HardFault"] + #[link_section = ".HardFault.user"] #(#attrs)* pub #unsafety extern "C" fn #hash(#arg) -> ! { extern crate cortex_m_rt; diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index f418e7f..9f2a20a 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -203,12 +203,12 @@ //! - `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 named `UserHardFault`. The trampoline is required to -//! set up the pointer to the stacked exception frame. +//! - `HardFaultTrampoline`. This is the real hard fault handler. This function is simply a +//! trampoline that jumps into the user defined hard fault handler named `HardFault`. The +//! trampoline is required to set up the pointer to the stacked exception frame. //! -//! - `UserHardFault`. This is the user defined hard fault handler. If not overridden using -//! `#[exception] fn HardFault(..` this will be an infinite loop. +//! - `HardFault`. This is the user defined hard fault handler. If not overridden using +//! `#[exception] fn HardFault(..` it will default to 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 @@ -534,9 +534,9 @@ pub unsafe extern "C" fn Reset() -> ! { #[allow(unused_variables)] #[doc(hidden)] -#[link_section = ".UserHardFault"] +#[link_section = ".HardFault.default"] #[no_mangle] -pub unsafe extern "C" fn UserHardFault_(ef: &ExceptionFrame) -> ! { +pub unsafe extern "C" fn HardFault_(ef: &ExceptionFrame) -> ! { loop { // add some side effect to prevent this from turning into a UDF instruction // see rust-lang/rust#28728 for details @@ -590,7 +590,7 @@ pub enum Exception { extern "C" { fn NonMaskableInt(); - fn HardFault(); + fn HardFaultTrampoline(); #[cfg(not(armv6m))] fn MemoryManagement(); @@ -629,7 +629,7 @@ pub static __EXCEPTIONS: [Vector; 14] = [ handler: NonMaskableInt, }, // Exception 3: Hard Fault Interrupt. - Vector { handler: HardFault }, + Vector { handler: HardFaultTrampoline }, // Exception 4: Memory Management Interrupt [not on Cortex-M0 variants]. #[cfg(not(armv6m))] Vector { -- cgit v1.2.3 From c69ff66f4c1d353be5d683ca8c131a04e997e4ed Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 15 Dec 2018 15:37:20 +0100 Subject: static mut transform: forward `#[cfg]` --- cortex-m-rt/examples/cfg-static.rs | 25 +++++++++++++++++++++++ cortex-m-rt/macros/src/lib.rs | 41 ++++++++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 cortex-m-rt/examples/cfg-static.rs (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/examples/cfg-static.rs b/cortex-m-rt/examples/cfg-static.rs new file mode 100644 index 0000000..2ffee13 --- /dev/null +++ b/cortex-m-rt/examples/cfg-static.rs @@ -0,0 +1,25 @@ +//! using `#[cfg]` on `static` shouldn't cause compile errors + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate cortex_m_rt as rt; +extern crate panic_halt; + +use rt::{entry, exception}; + +#[entry] +fn main() -> ! { + #[cfg(never)] + static mut COUNT: u32 = 0; + + loop {} +} + +#[exception] +fn SysTick() { + #[cfg(never)] + static mut FOO: u32 = 0; +} diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 1013f5e..7528586 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -16,8 +16,8 @@ use std::collections::HashSet; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; use syn::{ - parse, spanned::Spanned, FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, - Visibility, + parse, spanned::Spanned, AttrStyle, Attribute, FnArg, Ident, Item, ItemFn, ItemStatic, + PathArguments, ReturnType, Stmt, Type, Visibility, }; static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); @@ -128,15 +128,17 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let vars = statics .into_iter() .map(|var| { - let attrs = var.attrs; + let (ref cfgs, ref attrs) = extract_cfgs(var.attrs); let ident = var.ident; let ty = var.ty; let expr = var.expr; quote!( #[allow(non_snake_case)] + #(#cfgs)* let #ident: &'static mut #ty = unsafe { #(#attrs)* + #(#cfgs)* static mut #ident: #ty = #expr; &mut #ident @@ -405,7 +407,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { // further type check of the input argument let #pat: &cortex_m_rt::ExceptionFrame = #pat; - #(#stmts)* } ) @@ -446,15 +447,17 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let vars = statics .into_iter() .map(|var| { - let attrs = var.attrs; + let (ref cfgs, ref attrs) = extract_cfgs(var.attrs); let ident = var.ident; let ty = var.ty; let expr = var.expr; quote!( #[allow(non_snake_case)] + #(#cfgs)* let #ident: &mut #ty = unsafe { #(#attrs)* + #(#cfgs)* static mut #ident: #ty = #expr; &mut #ident @@ -603,15 +606,17 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { let vars = statics .into_iter() .map(|var| { - let attrs = var.attrs; + let (ref cfgs, ref attrs) = extract_cfgs(var.attrs); let ident = var.ident; let ty = var.ty; let expr = var.expr; quote!( #[allow(non_snake_case)] + #(#cfgs)* let #ident: &mut #ty = unsafe { #(#attrs)* + #(#cfgs)* static mut #ident: #ty = #expr; &mut #ident @@ -776,3 +781,27 @@ fn extract_static_muts(stmts: Vec) -> Result<(Vec, Vec), Ok((statics, stmts)) } + +fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { + let mut cfgs = vec![]; + let mut not_cfgs = vec![]; + + for attr in attrs { + if eq(&attr, "cfg") { + cfgs.push(attr); + } else { + not_cfgs.push(attr); + } + } + + (cfgs, not_cfgs) +} + +/// Returns `true` if `attr.path` matches `name` +fn eq(attr: &Attribute, name: &str) -> bool { + attr.style == AttrStyle::Outer && attr.path.segments.len() == 1 && { + let pair = attr.path.segments.first().unwrap(); + let segment = pair.value(); + segment.arguments == PathArguments::None && segment.ident.to_string() == name + } +} -- cgit v1.2.3 From 37909ec6f9547beabf046765a00ea6c637cdbd8d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 26 Aug 2019 10:50:06 -0700 Subject: Update to Syn 1.0 --- cortex-m-rt/macros/Cargo.toml | 6 +-- cortex-m-rt/macros/src/lib.rs | 120 ++++++++++++++++++++---------------------- 2 files changed, 61 insertions(+), 65 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/Cargo.toml b/cortex-m-rt/macros/Cargo.toml index ba6db64..0875b18 100644 --- a/cortex-m-rt/macros/Cargo.toml +++ b/cortex-m-rt/macros/Cargo.toml @@ -13,12 +13,12 @@ version = "0.1.5" proc-macro = true [dependencies] -quote = "0.6.8" -proc-macro2 = "0.4.20" +quote = "1.0" +proc-macro2 = "1.0" [dependencies.syn] features = ["extra-traits", "full"] -version = "0.15.13" +version = "1.0" [dependencies.rand] version = "0.5.5" diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 7528586..8f437ff 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -17,7 +17,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; use syn::{ parse, spanned::Spanned, AttrStyle, Attribute, FnArg, Ident, Item, ItemFn, ItemStatic, - PathArguments, ReturnType, Stmt, Type, Visibility, + ReturnType, Stmt, Type, Visibility, }; static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); @@ -86,14 +86,14 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); // check the function signature - let valid_signature = f.constness.is_none() + let valid_signature = f.sig.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 { + && f.sig.abi.is_none() + && f.sig.inputs.is_empty() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { ReturnType::Default => false, ReturnType::Type(_, ref ty) => match **ty { Type::Never(_) => true, @@ -118,7 +118,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { // XXX should we blacklist other attributes? let attrs = f.attrs; - let unsafety = f.unsafety; + let unsafety = f.sig.unsafety; let hash = random_ident(); let (statics, stmts) = match extract_static_muts(f.block.stmts) { Err(e) => return e.to_compile_error().into(), @@ -282,7 +282,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { } let fspan = f.span(); - let ident = f.ident; + let ident = f.sig.ident; enum Exception { DefaultHandler, @@ -309,19 +309,19 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let attrs = f.attrs; let block = f.block; let stmts = block.stmts; - let unsafety = f.unsafety; + let unsafety = f.sig.unsafety; let hash = random_ident(); match exn { Exception::DefaultHandler => { - let valid_signature = f.constness.is_none() + let valid_signature = f.sig.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 { + && f.sig.abi.is_none() + && f.sig.inputs.len() == 1 + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { ReturnType::Default => true, ReturnType::Type(_, ref ty) => match **ty { Type::Tuple(ref tuple) => tuple.elems.is_empty(), @@ -339,8 +339,8 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } - let arg = match f.decl.inputs[0] { - FnArg::Captured(ref arg) => arg, + let arg = match f.sig.inputs[0] { + FnArg::Typed(ref arg) => arg, _ => unreachable!(), }; @@ -360,21 +360,21 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .into() } Exception::HardFault => { - let valid_signature = f.constness.is_none() + let valid_signature = f.sig.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(), + && f.sig.abi.is_none() + && f.sig.inputs.len() == 1 + && match &f.sig.inputs[0] { + FnArg::Typed(arg) => match arg.ty.as_ref() { + Type::Reference(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 { + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { ReturnType::Default => false, ReturnType::Type(_, ref ty) => match **ty { Type::Never(_) => true, @@ -391,8 +391,8 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } - let arg = match f.decl.inputs[0] { - FnArg::Captured(ref arg) => arg, + let arg = match f.sig.inputs[0] { + FnArg::Typed(ref arg) => arg, _ => unreachable!(), }; @@ -413,14 +413,14 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .into() } Exception::Other => { - let valid_signature = f.constness.is_none() + let valid_signature = f.sig.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 { + && f.sig.abi.is_none() + && f.sig.inputs.is_empty() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { ReturnType::Default => true, ReturnType::Type(_, ref ty) => match **ty { Type::Tuple(ref tuple) => tuple.elems.is_empty(), @@ -564,23 +564,23 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { } let fspan = f.span(); - let ident = f.ident; + let ident = f.sig.ident; let ident_s = ident.to_string(); // XXX should we blacklist other attributes? let attrs = f.attrs; let block = f.block; let stmts = block.stmts; - let unsafety = f.unsafety; + let unsafety = f.sig.unsafety; - let valid_signature = f.constness.is_none() + let valid_signature = f.sig.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 { + && f.sig.abi.is_none() + && f.sig.inputs.is_empty() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { ReturnType::Default => true, ReturnType::Type(_, ref ty) => match **ty { Type::Tuple(ref tuple) => tuple.elems.is_empty(), @@ -669,15 +669,15 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); // check the function signature - let valid_signature = f.constness.is_none() + let valid_signature = f.sig.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 { + && f.sig.unsafety.is_some() + && f.sig.abi.is_none() + && f.sig.inputs.is_empty() + && f.sig.generics.params.is_empty() + && f.sig.generics.where_clause.is_none() + && f.sig.variadic.is_none() + && match f.sig.output { ReturnType::Default => true, ReturnType::Type(_, ref ty) => match **ty { Type::Tuple(ref tuple) => tuple.elems.is_empty(), @@ -702,7 +702,7 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { // XXX should we blacklist other attributes? let attrs = f.attrs; - let ident = f.ident; + let ident = f.sig.ident; let block = f.block; quote!( @@ -799,9 +799,5 @@ fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { /// Returns `true` if `attr.path` matches `name` fn eq(attr: &Attribute, name: &str) -> bool { - attr.style == AttrStyle::Outer && attr.path.segments.len() == 1 && { - let pair = attr.path.segments.first().unwrap(); - let segment = pair.value(); - segment.arguments == PathArguments::None && segment.ident.to_string() == name - } + attr.style == AttrStyle::Outer && attr.path.is_ident(name) } -- cgit v1.2.3 From 67b955701ed8b7a41fe2502ff655fcc8a1025bf2 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Fri, 1 Nov 2019 18:04:44 +0100 Subject: Edition-2018ify crate Signed-off-by: Daniel Egger --- cortex-m-rt/macros/Cargo.toml | 1 + cortex-m-rt/macros/src/lib.rs | 22 +++++++--------------- 2 files changed, 8 insertions(+), 15 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/Cargo.toml b/cortex-m-rt/macros/Cargo.toml index c53ec89..4d8d4d8 100644 --- a/cortex-m-rt/macros/Cargo.toml +++ b/cortex-m-rt/macros/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rt-macros" repository = "https://github.com/japaric/cortex-m-rt" version = "0.1.6" +edition = "2018" [lib] proc-macro = true diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 8f437ff..bf930d0 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -1,29 +1,21 @@ #![deny(warnings)] extern crate proc_macro; -extern crate rand; -#[macro_use] -extern crate quote; -extern crate core; -extern crate proc_macro2; -#[macro_use] -extern crate syn; +use proc_macro::TokenStream; use proc_macro2::Span; -use rand::Rng; -use rand::SeedableRng; +use rand::{Rng, SeedableRng}; use std::collections::HashSet; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; use syn::{ - parse, spanned::Spanned, AttrStyle, Attribute, FnArg, Ident, Item, ItemFn, ItemStatic, - ReturnType, Stmt, Type, Visibility, + parse, parse_macro_input, spanned::Spanned, AttrStyle, Attribute, FnArg, Ident, Item, ItemFn, + ItemStatic, ReturnType, Stmt, Type, Visibility, }; +use quote::quote; static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); -use proc_macro::TokenStream; - /// Attribute to declare the entry point of the program /// /// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you @@ -736,9 +728,9 @@ fn random_ident() -> Ident { &(0..16) .map(|i| { if i == 0 || rng.gen() { - ('a' as u8 + rng.gen::() % 25) as char + (b'a' + rng.gen::() % 25) as char } else { - ('0' as u8 + rng.gen::() % 10) as char + (b'0' + rng.gen::() % 10) as char } }) .collect::(), -- cgit v1.2.3 From d5fce0daaef5c02ab18cd084611cf6f82351f79d Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 21 Nov 2019 22:05:29 +0100 Subject: Generate trampolines instead of randomizing idents --- cortex-m-rt/macros/src/lib.rs | 285 ++++++++++++++++++++---------------------- 1 file changed, 138 insertions(+), 147 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index bf930d0..052b075 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -4,17 +4,13 @@ extern crate proc_macro; use proc_macro::TokenStream; use proc_macro2::Span; -use rand::{Rng, SeedableRng}; +use quote::quote; use std::collections::HashSet; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::iter; use syn::{ parse, parse_macro_input, spanned::Spanned, AttrStyle, Attribute, FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, Visibility, }; -use quote::quote; - -static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); /// Attribute to declare the entry point of the program /// @@ -75,7 +71,7 @@ static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); /// ``` #[proc_macro_attribute] pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { - let f = parse_macro_input!(input as ItemFn); + let mut f = parse_macro_input!(input as ItemFn); // check the function signature let valid_signature = f.sig.constness.is_none() @@ -109,44 +105,51 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { } // XXX should we blacklist other attributes? - let attrs = f.attrs; - let unsafety = f.sig.unsafety; - let hash = random_ident(); let (statics, stmts) = match extract_static_muts(f.block.stmts) { Err(e) => return e.to_compile_error().into(), Ok(x) => x, }; - let vars = statics - .into_iter() - .map(|var| { - let (ref cfgs, ref attrs) = extract_cfgs(var.attrs); - let ident = var.ident; - let ty = var.ty; - let expr = var.expr; - - quote!( - #[allow(non_snake_case)] + f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site()); + f.sig.inputs.extend(statics.iter().map(|statik| { + let ident = &statik.ident; + let ty = &statik.ty; + let attrs = &statik.attrs; + syn::parse::(quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into()) + .unwrap() + })); + f.block.stmts = stmts; + + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); + let ident = &f.sig.ident; + + let resource_args = statics + .iter() + .map(|statik| { + let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone()); + let ident = &statik.ident; + let ty = &statik.ty; + let expr = &statik.expr; + quote! { #(#cfgs)* - let #ident: &'static mut #ty = unsafe { + { #(#attrs)* - #(#cfgs)* static mut #ident: #ty = #expr; - &mut #ident - }; - ) + } + } }) .collect::>(); quote!( #[export_name = "main"] - #(#attrs)* - pub #unsafety fn #hash() -> ! { - #(#vars)* - - #(#stmts)* + pub unsafe extern "C" fn #tramp_ident() { + #ident( + #(#resource_args),* + ) } + + #f ) .into() } @@ -265,7 +268,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { - let f = parse_macro_input!(input as ItemFn); + let mut f = parse_macro_input!(input as ItemFn); if !args.is_empty() { return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") @@ -274,7 +277,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { } let fspan = f.span(); - let ident = f.sig.ident; + let ident = f.sig.ident.clone(); enum Exception { DefaultHandler, @@ -298,12 +301,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { }; // XXX should we blacklist other attributes? - let attrs = f.attrs; - let block = f.block; - let stmts = block.stmts; - let unsafety = f.sig.unsafety; - let hash = random_ident(); match exn { Exception::DefaultHandler => { let valid_signature = f.sig.constness.is_none() @@ -331,23 +329,23 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } - let arg = match f.sig.inputs[0] { - FnArg::Typed(ref arg) => arg, - _ => unreachable!(), - }; + f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site()); + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); + let ident = &f.sig.ident; quote!( #[export_name = #ident_s] - #(#attrs)* - pub #unsafety extern "C" fn #hash() { + pub unsafe extern "C" fn #tramp_ident() { 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 }; + let irqn = unsafe { core::ptr::read(SCB_ICSR) as u8 as i16 - 16 }; - #(#stmts)* + #ident(irqn) } + + #f ) .into() } @@ -383,24 +381,18 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } - let arg = match f.sig.inputs[0] { - FnArg::Typed(ref arg) => arg, - _ => unreachable!(), - }; - - let pat = &arg.pat; + f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site()); + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); + let ident = &f.sig.ident; quote!( #[export_name = "HardFault"] #[link_section = ".HardFault.user"] - #(#attrs)* - pub #unsafety 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)* + pub unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) { + #ident(frame) } + + #f ) .into() } @@ -431,46 +423,63 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } - let (statics, stmts) = match extract_static_muts(stmts) { + let (statics, stmts) = match extract_static_muts(f.block.stmts) { Err(e) => return e.to_compile_error().into(), Ok(x) => x, }; - let vars = statics - .into_iter() - .map(|var| { - let (ref cfgs, ref attrs) = extract_cfgs(var.attrs); - let ident = var.ident; - let ty = var.ty; - let expr = var.expr; + f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site()); + f.sig.inputs.extend(statics.iter().map(|statik| { + let ident = &statik.ident; + let ty = &statik.ty; + let attrs = &statik.attrs; + syn::parse::( + quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into(), + ) + .unwrap() + })); + f.block.stmts = iter::once( + syn::parse2(quote! {{ + extern crate cortex_m_rt; - quote!( - #[allow(non_snake_case)] + // check that this exception actually exists + cortex_m_rt::Exception::#ident; + }}) + .unwrap(), + ) + .chain(stmts) + .collect(); + + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); + let ident = &f.sig.ident; + + let resource_args = statics + .iter() + .map(|statik| { + let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone()); + let ident = &statik.ident; + let ty = &statik.ty; + let expr = &statik.expr; + quote! { #(#cfgs)* - let #ident: &mut #ty = unsafe { + { #(#attrs)* - #(#cfgs)* static mut #ident: #ty = #expr; - &mut #ident - }; - ) + } + } }) .collect::>(); quote!( #[export_name = #ident_s] - #(#attrs)* - pub #unsafety extern "C" fn #hash() { - extern crate cortex_m_rt; - - // check that this exception actually exists - cortex_m_rt::Exception::#ident; - - #(#vars)* - - #(#stmts)* + pub unsafe extern "C" fn #tramp_ident() { + #ident( + #(#resource_args),* + ) } + + #f ) .into() } @@ -547,7 +556,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { - let f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); + let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); if !args.is_empty() { return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") @@ -556,14 +565,10 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { } let fspan = f.span(); - let ident = f.sig.ident; + let ident = f.sig.ident.clone(); let ident_s = ident.to_string(); // XXX should we blacklist other attributes? - let attrs = f.attrs; - let block = f.block; - let stmts = block.stmts; - let unsafety = f.sig.unsafety; let valid_signature = f.sig.constness.is_none() && f.vis == Visibility::Inherited @@ -590,44 +595,61 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } - let (statics, stmts) = match extract_static_muts(stmts) { + let (statics, stmts) = match extract_static_muts(f.block.stmts.iter().cloned()) { Err(e) => return e.to_compile_error().into(), Ok(x) => x, }; - let vars = statics - .into_iter() - .map(|var| { - let (ref cfgs, ref attrs) = extract_cfgs(var.attrs); - let ident = var.ident; - let ty = var.ty; - let expr = var.expr; - - quote!( - #[allow(non_snake_case)] + f.sig.ident = Ident::new(&format!("__cortex_m_rt_{}", f.sig.ident), Span::call_site()); + f.sig.inputs.extend(statics.iter().map(|statik| { + let ident = &statik.ident; + let ty = &statik.ty; + let attrs = &statik.attrs; + syn::parse::(quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into()) + .unwrap() + })); + f.block.stmts = iter::once( + syn::parse2(quote! {{ + extern crate cortex_m_rt; + + // Check that this interrupt actually exists + interrupt::#ident; + }}) + .unwrap(), + ) + .chain(stmts) + .collect(); + + let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); + let ident = &f.sig.ident; + + let resource_args = statics + .iter() + .map(|statik| { + let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone()); + let ident = &statik.ident; + let ty = &statik.ty; + let expr = &statik.expr; + quote! { #(#cfgs)* - let #ident: &mut #ty = unsafe { + { #(#attrs)* - #(#cfgs)* static mut #ident: #ty = #expr; - &mut #ident - }; - ) + } + } }) .collect::>(); - let hash = random_ident(); quote!( #[export_name = #ident_s] - #(#attrs)* - pub #unsafety extern "C" fn #hash() { - interrupt::#ident; - - #(#vars)* - - #(#stmts)* + pub unsafe extern "C" fn #tramp_ident() { + #ident( + #(#resource_args),* + ) } + + #f ) .into() } @@ -705,41 +727,10 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { .into() } -// Creates a random identifier -fn random_ident() -> Ident { - let secs = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - - let count: u64 = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u64; - let mut seed: [u8; 16] = [0; 16]; - - for (i, v) in seed.iter_mut().take(8).enumerate() { - *v = ((secs >> (i * 8)) & 0xFF) as u8 - } - - for (i, v) in seed.iter_mut().skip(8).enumerate() { - *v = ((count >> (i * 8)) & 0xFF) as u8 - } - - let mut rng = rand::rngs::SmallRng::from_seed(seed); - Ident::new( - &(0..16) - .map(|i| { - if i == 0 || rng.gen() { - (b'a' + rng.gen::() % 25) as char - } else { - (b'0' + rng.gen::() % 10) as char - } - }) - .collect::(), - Span::call_site(), - ) -} - /// Extracts `static mut` vars from the beginning of the given statements -fn extract_static_muts(stmts: Vec) -> Result<(Vec, Vec), parse::Error> { +fn extract_static_muts( + stmts: impl IntoIterator, +) -> Result<(Vec, Vec), parse::Error> { let mut istmts = stmts.into_iter(); let mut seen = HashSet::new(); -- cgit v1.2.3 From f6729db2e7004954606a020d8fb67a7589e679e5 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 21 Nov 2019 22:25:28 +0100 Subject: Re-allow `&'static mut` resources for `#[entry]` --- cortex-m-rt/macros/src/lib.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 052b075..b6b0fe4 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -115,8 +115,13 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let ident = &statik.ident; let ty = &statik.ty; let attrs = &statik.attrs; - syn::parse::(quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into()) - .unwrap() + + // Note that we use an explicit `'static` lifetime for the entry point arguments. This makes + // it more flexible, and is sound here, since the entry will not be called again, ever. + syn::parse::( + quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &'static mut #ty).into(), + ) + .unwrap() })); f.block.stmts = stmts; -- cgit v1.2.3 From c284d555b5ac3f6807be633e218302ba8cc3bbcf Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 26 Nov 2019 22:49:27 +0100 Subject: Inline attr. macro docs and fix links --- cortex-m-rt/macros/src/lib.rs | 4 ++++ cortex-m-rt/src/lib.rs | 39 +++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 16 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index b6b0fe4..543130c 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -1,3 +1,7 @@ +//! Internal implementation details of `cortex-m-rt`. +//! +//! Do not use this crate directly. + #![deny(warnings)] extern crate proc_macro; diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index d89df2a..85d9496 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -16,16 +16,17 @@ //! //! This crate also provides the following attributes: //! -//! - `#[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]`][attr-entry] to declare the entry point of the program +//! - [`#[exception]`][attr-exception] to override an exception handler. If not overridden all +//! exception handlers default to an infinite loop. +//! - [`#[pre_init]`][attr-pre_init] to run code *before* `static` variables are initialized //! //! This crate also implements a related attribute called `#[interrupt]`, which allows you //! to define interrupt handlers. However, since which interrupts are available depends on the //! microcontroller in use, this attribute should be re-exported and used from a device crate. //! -//! The documentation for these attributes can be found in the [Reexports](#reexports) section. +//! The documentation for these attributes can be found in the [Attribute Macros](#attributes) +//! section. //! //! # Requirements //! @@ -194,11 +195,10 @@ //! One will always find the following (unmangled) symbols in `cortex-m-rt` applications: //! //! - `Reset`. This is the reset handler. The microcontroller will executed this function upon -//! booting. This function will call the user program entry point (cf. [`#[entry]`]) using the `main` -//! symbol so you may also find that symbol in your program; if you do, `main` will contain your -//! application code. Some other times `main` gets inlined into `Reset` so you won't find it. -//! -//! [`#[entry]`]: https://docs.rs/cortex-m-rt-macros/0.1.5/cortex_m_rt_macros/attr.entry.html +//! booting. This function will call the user program entry point (cf. [`#[entry]`][attr-entry]) +//! using the `main` symbol so you may also find that symbol in your program; if you do, `main` +//! will contain your application code. Some other times `main` gets inlined into `Reset` so you +//! won't find it. //! //! - `DefaultHandler`. This is the default handler. If not overridden using `#[exception] fn //! DefaultHandler(..` this will be an infinite loop. @@ -227,9 +227,9 @@ //! `__EXCEPTIONS` in the `.vector_table` section. //! //! - `__pre_init`. This is a function to be run before RAM is initialized. It defaults to an empty -//! function. The function called can be changed by calling the `pre_init!` macro. The empty -//! function is not optimized out by default, but if an empty function is passed to `pre_init!` the -//! function call will be optimized out. +//! function. The function called can be changed by applying the [`#[pre_init]`][attr-pre_init] +//! attribute to a function. The empty function is not optimized out by default, but if an empty +//! function is passed to [`#[pre_init]`][attr-pre_init] the function call will be optimized out. //! //! If you override any exception handler you'll find it as an unmangled symbol, e.g. `SysTick` or //! `SVCall`, in the output of `objdump`, @@ -245,13 +245,14 @@ //! //! ## Setting the program entry point //! -//! This section describes how `#[entry]` is implemented. This information is useful to developers who -//! want to provide an alternative to `#[entry]` that provides extra guarantees. +//! This section describes how [`#[entry]`][attr-entry] is implemented. This information is useful +//! to developers who want to provide an alternative to [`#[entry]`][attr-entry] that provides extra +//! guarantees. //! //! The `Reset` handler will call a symbol named `main` (unmangled) *after* initializing `.bss` and //! `.data`, and enabling the FPU (if the target is `thumbv7em-none-eabihf`). A function with the //! `entry` attribute will be set to have the export name "`main`"; in addition, its mutable -//! statics are turned into safe mutable references (see [`#[entry]`] for details). +//! statics are turned into safe mutable references (see [`#[entry]`][attr-entry] for details). //! //! The unmangled `main` symbol must have signature `extern "C" fn() -> !` or its invocation from //! `Reset` will result in undefined behavior. @@ -385,6 +386,10 @@ //! Be very careful with the `link_section` attribute because it's easy to misuse in ways that cause //! undefined behavior. At some point in the future we may add an attribute to safely place static //! variables in this section. +//! +//! [attr-entry]: attr.entry.html +//! [attr-exception]: attr.exception.html +//! [attr-pre_init]: attr.pre_init.html // # Developer notes // @@ -402,7 +407,9 @@ use core::fmt; use core::sync::atomic::{self, Ordering}; #[cfg(feature = "device")] +#[doc(inline)] pub use macros::interrupt; +#[doc(inline)] pub use macros::{entry, exception, pre_init}; #[export_name = "error: cortex-m-rt appears more than once in the dependency graph"] -- cgit v1.2.3 From 43d43bd5c84631fc183f42bc9651e0a076a06f65 Mon Sep 17 00:00:00 2001 From: Ian McIntyre Date: Mon, 9 Dec 2019 20:24:44 -0500 Subject: Remove 'extern crate cortex_m_rt' from macros See justification on 519d46a. Using 'extern crate cortex_m_rt' inside of the macros limits our ability to use the macros crate in other contexts. As long as another crate publicly exports an enumeration of `interrupt` and an enumeration of `exception`, the macros crate may be used in other cortex-m-rt-like systems. --- cortex-m-rt/macros/src/lib.rs | 8 ++------ cortex-m-rt/src/lib.rs | 2 ++ 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 543130c..a844305 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -173,7 +173,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { /// # Syntax /// /// ``` -/// # use cortex_m_rt_macros::exception; +/// # use cortex_m_rt::exception; /// #[exception] /// fn SysTick() { /// // .. @@ -449,10 +449,8 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { })); f.block.stmts = iter::once( syn::parse2(quote! {{ - extern crate cortex_m_rt; - // check that this exception actually exists - cortex_m_rt::Exception::#ident; + exception::#ident; }}) .unwrap(), ) @@ -619,8 +617,6 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { })); f.block.stmts = iter::once( syn::parse2(quote! {{ - extern crate cortex_m_rt; - // Check that this interrupt actually exists interrupt::#ident; }}) diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index 85d9496..8bfdd69 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -599,6 +599,8 @@ pub enum Exception { SysTick, } +pub use self::Exception as exception; + extern "C" { fn NonMaskableInt(); -- cgit v1.2.3 From 59ec527ad111baf26bb301193fcaf83eca9c3ee1 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 28 Dec 2019 14:52:17 +0100 Subject: Hide trampolines from docs --- cortex-m-rt/macros/src/lib.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index a844305..ce116a1 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -151,6 +151,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { .collect::>(); quote!( + #[doc(hidden)] #[export_name = "main"] pub unsafe extern "C" fn #tramp_ident() { #ident( -- cgit v1.2.3 From eb2b5a4786e93fd355f5bfc2fbc8081eff45e719 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 28 Dec 2019 15:19:21 +0100 Subject: Added missing hidden doc --- cortex-m-rt/macros/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index ce116a1..763b721 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -344,6 +344,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let ident = &f.sig.ident; quote!( + #[doc(hidden)] #[export_name = #ident_s] pub unsafe extern "C" fn #tramp_ident() { extern crate core; @@ -396,6 +397,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let ident = &f.sig.ident; quote!( + #[doc(hidden)] #[export_name = "HardFault"] #[link_section = ".HardFault.user"] pub unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) { @@ -480,6 +482,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .collect::>(); quote!( + #[doc(hidden)] #[export_name = #ident_s] pub unsafe extern "C" fn #tramp_ident() { #ident( @@ -648,6 +651,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { .collect::>(); quote!( + #[doc(hidden)] #[export_name = #ident_s] pub unsafe extern "C" fn #tramp_ident() { #ident( -- cgit v1.2.3 From 1b5da39d9d1c3bc6558585a6ecf8ce2448ff82fd Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 29 Dec 2019 13:53:28 +0100 Subject: Added blacklisted attributes --- cortex-m-rt/macros/src/lib.rs | 64 +++++++++++++++++++++++++++ cortex-m-rt/tests/compile-fail/blacklist-1.rs | 26 +++++++++++ cortex-m-rt/tests/compile-fail/blacklist-2.rs | 26 +++++++++++ cortex-m-rt/tests/compile-fail/blacklist-3.rs | 26 +++++++++++ cortex-m-rt/tests/compile-fail/blacklist-4.rs | 26 +++++++++++ 5 files changed, 168 insertions(+) create mode 100644 cortex-m-rt/tests/compile-fail/blacklist-1.rs create mode 100644 cortex-m-rt/tests/compile-fail/blacklist-2.rs create mode 100644 cortex-m-rt/tests/compile-fail/blacklist-3.rs create mode 100644 cortex-m-rt/tests/compile-fail/blacklist-4.rs (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 763b721..9755153 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -150,7 +150,15 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { }) .collect::>(); + if let Some(error) = check_for_blacklisted_attrs(&f.attrs) { + return error; + } + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + quote!( + #(#cfgs)* + #(#attrs)* #[doc(hidden)] #[export_name = "main"] pub unsafe extern "C" fn #tramp_ident() { @@ -343,7 +351,15 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); let ident = &f.sig.ident; + if let Some(error) = check_for_blacklisted_attrs(&f.attrs) { + return error; + } + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + quote!( + #(#cfgs)* + #(#attrs)* #[doc(hidden)] #[export_name = #ident_s] pub unsafe extern "C" fn #tramp_ident() { @@ -396,7 +412,15 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); let ident = &f.sig.ident; + if let Some(error) = check_for_blacklisted_attrs(&f.attrs) { + return error; + } + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + quote!( + #(#cfgs)* + #(#attrs)* #[doc(hidden)] #[export_name = "HardFault"] #[link_section = ".HardFault.user"] @@ -481,7 +505,15 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { }) .collect::>(); + if let Some(error) = check_for_blacklisted_attrs(&f.attrs) { + return error; + } + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + quote!( + #(#cfgs)* + #(#attrs)* #[doc(hidden)] #[export_name = #ident_s] pub unsafe extern "C" fn #tramp_ident() { @@ -650,7 +682,15 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { }) .collect::>(); + if let Some(error) = check_for_blacklisted_attrs(&f.attrs) { + return error; + } + + let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); + quote!( + #(#cfgs)* + #(#attrs)* #[doc(hidden)] #[export_name = #ident_s] pub unsafe extern "C" fn #tramp_ident() { @@ -790,6 +830,30 @@ fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { (cfgs, not_cfgs) } +fn check_for_blacklisted_attrs(attrs: &[Attribute]) -> Option { + if let Some(val) = containts_blacklist_attrs(attrs) { + return Some(parse::Error::new(val.span(), "This attribute is not allowed [blacklisted]") + .to_compile_error() + .into()); + } + + None +} + +fn containts_blacklist_attrs(attrs: &[Attribute]) -> Option { + let blacklist = &["inline", "export_name", "no_mangle", "must_use"]; + + for attr in attrs { + for val in blacklist { + if eq(&attr, &val) { + return Some(attr.clone()); + } + } + } + + None +} + /// Returns `true` if `attr.path` matches `name` fn eq(attr: &Attribute, name: &str) -> bool { attr.style == AttrStyle::Outer && attr.path.is_ident(name) diff --git a/cortex-m-rt/tests/compile-fail/blacklist-1.rs b/cortex-m-rt/tests/compile-fail/blacklist-1.rs new file mode 100644 index 0000000..57d90e3 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/blacklist-1.rs @@ -0,0 +1,26 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[inline] //~ ERROR This attribute is not allowed [blacklisted] +#[entry] +fn foo() -> ! { + loop {} +} + +#[inline] //~ ERROR This attribute is not allowed [blacklisted] +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, +} + +#[inline] //~ ERROR This attribute is not allowed [blacklisted] +#[interrupt] +fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/blacklist-2.rs b/cortex-m-rt/tests/compile-fail/blacklist-2.rs new file mode 100644 index 0000000..ab30235 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/blacklist-2.rs @@ -0,0 +1,26 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[export_name = "not_allowed"] //~ ERROR This attribute is not allowed [blacklisted] +#[entry] +fn foo() -> ! { + loop {} +} + +#[export_name = "not_allowed"] //~ ERROR This attribute is not allowed [blacklisted] +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, +} + +#[export_name = "not_allowed"] //~ ERROR This attribute is not allowed [blacklisted] +#[interrupt] +fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/blacklist-3.rs b/cortex-m-rt/tests/compile-fail/blacklist-3.rs new file mode 100644 index 0000000..c0990bb --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/blacklist-3.rs @@ -0,0 +1,26 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[no_mangle] //~ ERROR This attribute is not allowed [blacklisted] +#[entry] +fn foo() -> ! { + loop {} +} + +#[no_mangle] //~ ERROR This attribute is not allowed [blacklisted] +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, +} + +#[no_mangle] //~ ERROR This attribute is not allowed [blacklisted] +#[interrupt] +fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/blacklist-4.rs b/cortex-m-rt/tests/compile-fail/blacklist-4.rs new file mode 100644 index 0000000..d0753e0 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/blacklist-4.rs @@ -0,0 +1,26 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[must_use] //~ ERROR This attribute is not allowed [blacklisted] +#[entry] +fn foo() -> ! { + loop {} +} + +#[must_use] //~ ERROR This attribute is not allowed [blacklisted] +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, +} + +#[must_use] //~ ERROR This attribute is not allowed [blacklisted] +#[interrupt] +fn USART1() {} -- cgit v1.2.3 From 6cfbff156b9475da4a2fd3bdaed12244a79085bd Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 29 Dec 2019 20:05:09 +0100 Subject: Changed to whitelist --- cortex-m-rt/macros/src/lib.rs | 8 ++++---- cortex-m-rt/tests/compile-fail/blacklist-1.rs | 6 +++--- cortex-m-rt/tests/compile-fail/blacklist-2.rs | 6 +++--- cortex-m-rt/tests/compile-fail/blacklist-3.rs | 6 +++--- cortex-m-rt/tests/compile-fail/blacklist-4.rs | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 9755153..803915c 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -832,7 +832,7 @@ fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { fn check_for_blacklisted_attrs(attrs: &[Attribute]) -> Option { if let Some(val) = containts_blacklist_attrs(attrs) { - return Some(parse::Error::new(val.span(), "This attribute is not allowed [blacklisted]") + return Some(parse::Error::new(val.span(), "this attribute is not allowed on a function controlled by cortex-m-rt") .to_compile_error() .into()); } @@ -841,11 +841,11 @@ fn check_for_blacklisted_attrs(attrs: &[Attribute]) -> Option { } fn containts_blacklist_attrs(attrs: &[Attribute]) -> Option { - let blacklist = &["inline", "export_name", "no_mangle", "must_use"]; + let whitelist = &["doc", "link_section"]; for attr in attrs { - for val in blacklist { - if eq(&attr, &val) { + for val in whitelist { + if !eq(&attr, &val) { return Some(attr.clone()); } } diff --git a/cortex-m-rt/tests/compile-fail/blacklist-1.rs b/cortex-m-rt/tests/compile-fail/blacklist-1.rs index 57d90e3..e76e221 100644 --- a/cortex-m-rt/tests/compile-fail/blacklist-1.rs +++ b/cortex-m-rt/tests/compile-fail/blacklist-1.rs @@ -6,13 +6,13 @@ extern crate panic_halt; use cortex_m_rt::{entry, exception, interrupt}; -#[inline] //~ ERROR This attribute is not allowed [blacklisted] +#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[entry] fn foo() -> ! { loop {} } -#[inline] //~ ERROR This attribute is not allowed [blacklisted] +#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[exception] fn SysTick() {} @@ -21,6 +21,6 @@ enum interrupt { USART1, } -#[inline] //~ ERROR This attribute is not allowed [blacklisted] +#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[interrupt] fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/blacklist-2.rs b/cortex-m-rt/tests/compile-fail/blacklist-2.rs index ab30235..34ec760 100644 --- a/cortex-m-rt/tests/compile-fail/blacklist-2.rs +++ b/cortex-m-rt/tests/compile-fail/blacklist-2.rs @@ -6,13 +6,13 @@ extern crate panic_halt; use cortex_m_rt::{entry, exception, interrupt}; -#[export_name = "not_allowed"] //~ ERROR This attribute is not allowed [blacklisted] +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[entry] fn foo() -> ! { loop {} } -#[export_name = "not_allowed"] //~ ERROR This attribute is not allowed [blacklisted] +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[exception] fn SysTick() {} @@ -21,6 +21,6 @@ enum interrupt { USART1, } -#[export_name = "not_allowed"] //~ ERROR This attribute is not allowed [blacklisted] +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[interrupt] fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/blacklist-3.rs b/cortex-m-rt/tests/compile-fail/blacklist-3.rs index c0990bb..8cc3dda 100644 --- a/cortex-m-rt/tests/compile-fail/blacklist-3.rs +++ b/cortex-m-rt/tests/compile-fail/blacklist-3.rs @@ -6,13 +6,13 @@ extern crate panic_halt; use cortex_m_rt::{entry, exception, interrupt}; -#[no_mangle] //~ ERROR This attribute is not allowed [blacklisted] +#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[entry] fn foo() -> ! { loop {} } -#[no_mangle] //~ ERROR This attribute is not allowed [blacklisted] +#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[exception] fn SysTick() {} @@ -21,6 +21,6 @@ enum interrupt { USART1, } -#[no_mangle] //~ ERROR This attribute is not allowed [blacklisted] +#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[interrupt] fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/blacklist-4.rs b/cortex-m-rt/tests/compile-fail/blacklist-4.rs index d0753e0..150e7f9 100644 --- a/cortex-m-rt/tests/compile-fail/blacklist-4.rs +++ b/cortex-m-rt/tests/compile-fail/blacklist-4.rs @@ -6,13 +6,13 @@ extern crate panic_halt; use cortex_m_rt::{entry, exception, interrupt}; -#[must_use] //~ ERROR This attribute is not allowed [blacklisted] +#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[entry] fn foo() -> ! { loop {} } -#[must_use] //~ ERROR This attribute is not allowed [blacklisted] +#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[exception] fn SysTick() {} @@ -21,6 +21,6 @@ enum interrupt { USART1, } -#[must_use] //~ ERROR This attribute is not allowed [blacklisted] +#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt #[interrupt] fn USART1() {} -- cgit v1.2.3 From 2f5a6487cfd94d6ccde3128c82fa43bddd2d228f Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 29 Dec 2019 20:16:18 +0100 Subject: Fixed logic error --- cortex-m-rt/macros/src/lib.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 803915c..be27165 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -832,9 +832,14 @@ fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { fn check_for_blacklisted_attrs(attrs: &[Attribute]) -> Option { if let Some(val) = containts_blacklist_attrs(attrs) { - return Some(parse::Error::new(val.span(), "this attribute is not allowed on a function controlled by cortex-m-rt") + return Some( + parse::Error::new( + val.span(), + "this attribute is not allowed on a function controlled by cortex-m-rt", + ) .to_compile_error() - .into()); + .into(), + ); } None @@ -843,12 +848,14 @@ fn check_for_blacklisted_attrs(attrs: &[Attribute]) -> Option { fn containts_blacklist_attrs(attrs: &[Attribute]) -> Option { let whitelist = &["doc", "link_section"]; - for attr in attrs { + 'o: for attr in attrs { for val in whitelist { - if !eq(&attr, &val) { - return Some(attr.clone()); + if eq(&attr, &val) { + continue 'o; } } + + return Some(attr.clone()); } None -- cgit v1.2.3 From 5fdbab999bc849fd41e376f5f97f03fdf85c7442 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 7 Jan 2020 21:19:09 +0100 Subject: Updated per comments --- cortex-m-rt/macros/src/lib.rs | 43 +++++++++++---------------- cortex-m-rt/tests/compile-fail/blacklist-1.rs | 26 ---------------- cortex-m-rt/tests/compile-fail/blacklist-2.rs | 26 ---------------- cortex-m-rt/tests/compile-fail/blacklist-3.rs | 26 ---------------- cortex-m-rt/tests/compile-fail/blacklist-4.rs | 26 ---------------- cortex-m-rt/tests/compile-fail/whitelist-1.rs | 32 ++++++++++++++++++++ cortex-m-rt/tests/compile-fail/whitelist-2.rs | 32 ++++++++++++++++++++ cortex-m-rt/tests/compile-fail/whitelist-3.rs | 32 ++++++++++++++++++++ cortex-m-rt/tests/compile-fail/whitelist-4.rs | 32 ++++++++++++++++++++ cortex-m-rt/tests/compiletest.rs | 2 +- 10 files changed, 147 insertions(+), 130 deletions(-) delete mode 100644 cortex-m-rt/tests/compile-fail/blacklist-1.rs delete mode 100644 cortex-m-rt/tests/compile-fail/blacklist-2.rs delete mode 100644 cortex-m-rt/tests/compile-fail/blacklist-3.rs delete mode 100644 cortex-m-rt/tests/compile-fail/blacklist-4.rs create mode 100644 cortex-m-rt/tests/compile-fail/whitelist-1.rs create mode 100644 cortex-m-rt/tests/compile-fail/whitelist-2.rs create mode 100644 cortex-m-rt/tests/compile-fail/whitelist-3.rs create mode 100644 cortex-m-rt/tests/compile-fail/whitelist-4.rs (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index be27165..d18cc58 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -150,7 +150,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { }) .collect::>(); - if let Some(error) = check_for_blacklisted_attrs(&f.attrs) { + if let Err(error) = check_attr_whitelist(&f.attrs) { return error; } @@ -351,7 +351,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); let ident = &f.sig.ident; - if let Some(error) = check_for_blacklisted_attrs(&f.attrs) { + if let Err(error) = check_attr_whitelist(&f.attrs) { return error; } @@ -412,7 +412,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); let ident = &f.sig.ident; - if let Some(error) = check_for_blacklisted_attrs(&f.attrs) { + if let Err(error) = check_attr_whitelist(&f.attrs) { return error; } @@ -505,7 +505,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { }) .collect::>(); - if let Some(error) = check_for_blacklisted_attrs(&f.attrs) { + if let Err(error) = check_attr_whitelist(&f.attrs) { return error; } @@ -682,7 +682,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { }) .collect::>(); - if let Some(error) = check_for_blacklisted_attrs(&f.attrs) { + if let Err(error) = check_attr_whitelist(&f.attrs) { return error; } @@ -830,23 +830,8 @@ fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { (cfgs, not_cfgs) } -fn check_for_blacklisted_attrs(attrs: &[Attribute]) -> Option { - if let Some(val) = containts_blacklist_attrs(attrs) { - return Some( - parse::Error::new( - val.span(), - "this attribute is not allowed on a function controlled by cortex-m-rt", - ) - .to_compile_error() - .into(), - ); - } - - None -} - -fn containts_blacklist_attrs(attrs: &[Attribute]) -> Option { - let whitelist = &["doc", "link_section"]; +fn check_attr_whitelist(attrs: &[Attribute]) -> Result<(), TokenStream> { + let whitelist = &["doc", "link_section", "cfg", "allow", "warn", "deny", "forbid", "cold"]; 'o: for attr in attrs { for val in whitelist { @@ -855,10 +840,18 @@ fn containts_blacklist_attrs(attrs: &[Attribute]) -> Option { } } - return Some(attr.clone()); - } + return Err( + parse::Error::new( + attr.span(), + "this attribute is not allowed on a function controlled by cortex-m-rt", + ) + .to_compile_error() + .into(), + ); + + }; - None + Ok(()) } /// Returns `true` if `attr.path` matches `name` diff --git a/cortex-m-rt/tests/compile-fail/blacklist-1.rs b/cortex-m-rt/tests/compile-fail/blacklist-1.rs deleted file mode 100644 index e76e221..0000000 --- a/cortex-m-rt/tests/compile-fail/blacklist-1.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_main] -#![no_std] - -extern crate cortex_m_rt; -extern crate panic_halt; - -use cortex_m_rt::{entry, exception, interrupt}; - -#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[entry] -fn foo() -> ! { - loop {} -} - -#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[exception] -fn SysTick() {} - -#[allow(non_camel_case_types)] -enum interrupt { - USART1, -} - -#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[interrupt] -fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/blacklist-2.rs b/cortex-m-rt/tests/compile-fail/blacklist-2.rs deleted file mode 100644 index 34ec760..0000000 --- a/cortex-m-rt/tests/compile-fail/blacklist-2.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_main] -#![no_std] - -extern crate cortex_m_rt; -extern crate panic_halt; - -use cortex_m_rt::{entry, exception, interrupt}; - -#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[entry] -fn foo() -> ! { - loop {} -} - -#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[exception] -fn SysTick() {} - -#[allow(non_camel_case_types)] -enum interrupt { - USART1, -} - -#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[interrupt] -fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/blacklist-3.rs b/cortex-m-rt/tests/compile-fail/blacklist-3.rs deleted file mode 100644 index 8cc3dda..0000000 --- a/cortex-m-rt/tests/compile-fail/blacklist-3.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_main] -#![no_std] - -extern crate cortex_m_rt; -extern crate panic_halt; - -use cortex_m_rt::{entry, exception, interrupt}; - -#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[entry] -fn foo() -> ! { - loop {} -} - -#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[exception] -fn SysTick() {} - -#[allow(non_camel_case_types)] -enum interrupt { - USART1, -} - -#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[interrupt] -fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/blacklist-4.rs b/cortex-m-rt/tests/compile-fail/blacklist-4.rs deleted file mode 100644 index 150e7f9..0000000 --- a/cortex-m-rt/tests/compile-fail/blacklist-4.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_main] -#![no_std] - -extern crate cortex_m_rt; -extern crate panic_halt; - -use cortex_m_rt::{entry, exception, interrupt}; - -#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[entry] -fn foo() -> ! { - loop {} -} - -#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[exception] -fn SysTick() {} - -#[allow(non_camel_case_types)] -enum interrupt { - USART1, -} - -#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt -#[interrupt] -fn USART1() {} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-1.rs b/cortex-m-rt/tests/compile-fail/whitelist-1.rs new file mode 100644 index 0000000..d8a334c --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/whitelist-1.rs @@ -0,0 +1,32 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[entry] +fn foo() -> ! { + loop {} +} + +#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, + USART2, +} + +#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[interrupt] +fn USART1() {} + +#[cfg(feature = "device")] +#[cfg_attr(feature = "device", inline)] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[interrupt] +fn USART2() {} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-2.rs b/cortex-m-rt/tests/compile-fail/whitelist-2.rs new file mode 100644 index 0000000..1550ca0 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/whitelist-2.rs @@ -0,0 +1,32 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[entry] +fn foo() -> ! { + loop {} +} + +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, + USART2, +} + +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[interrupt] +fn USART1() {} + +#[cfg(feature = "device")] +#[cfg_attr(feature = "device", export_name = "not_allowed")] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[interrupt] +fn USART2() {} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-3.rs b/cortex-m-rt/tests/compile-fail/whitelist-3.rs new file mode 100644 index 0000000..c8e7bb2 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/whitelist-3.rs @@ -0,0 +1,32 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[entry] +fn foo() -> ! { + loop {} +} + +#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, + USART2, +} + +#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[interrupt] +fn USART1() {} + +#[cfg(feature = "device")] +#[cfg_attr(feature = "device", no_mangle)] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[interrupt] +fn USART2() {} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-4.rs b/cortex-m-rt/tests/compile-fail/whitelist-4.rs new file mode 100644 index 0000000..f0c5ca2 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/whitelist-4.rs @@ -0,0 +1,32 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception, interrupt}; + +#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[entry] +fn foo() -> ! { + loop {} +} + +#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[exception] +fn SysTick() {} + +#[allow(non_camel_case_types)] +enum interrupt { + USART1, + USART2, +} + +#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[interrupt] +fn USART1() {} + +#[cfg(feature = "device")] +#[cfg_attr(feature = "device", must_use)] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[interrupt] +fn USART2() {} diff --git a/cortex-m-rt/tests/compiletest.rs b/cortex-m-rt/tests/compiletest.rs index 6cea3ac..82dda07 100644 --- a/cortex-m-rt/tests/compiletest.rs +++ b/cortex-m-rt/tests/compiletest.rs @@ -9,7 +9,7 @@ fn run_mode(mode: &'static str) { 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()); + Some("-L target/debug -L target/debug/deps -C panic=abort --cfg feature=\"device\"".to_owned()); // config.clean_rmeta(); // If your tests import the parent crate, this helps with E0464 compiletest::run_tests(&config); -- cgit v1.2.3 From fda8b742a1a6e72740e3a9512378efe5a82b5487 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 7 Jan 2020 22:01:15 +0100 Subject: Reduced number of whitelist checks --- cortex-m-rt/macros/src/lib.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index d18cc58..eda77f2 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -294,6 +294,11 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } + if let Err(error) = check_attr_whitelist(&f.attrs) { + return error; + } + + let fspan = f.span(); let ident = f.sig.ident.clone(); @@ -351,10 +356,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); let ident = &f.sig.ident; - if let Err(error) = check_attr_whitelist(&f.attrs) { - return error; - } - let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); quote!( @@ -412,10 +413,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), Span::call_site()); let ident = &f.sig.ident; - if let Err(error) = check_attr_whitelist(&f.attrs) { - return error; - } - let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); quote!( @@ -505,10 +502,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { }) .collect::>(); - if let Err(error) = check_attr_whitelist(&f.attrs) { - return error; - } - let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); quote!( -- cgit v1.2.3 From 58949206e3a752dbfd47521d2b6bfdafa9010c90 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 16 Jan 2020 12:58:18 +0100 Subject: Better error message on multiple attributes --- cortex-m-rt/macros/src/lib.rs | 14 ++++++++++++++ cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index eda77f2..177ed4e 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -825,6 +825,7 @@ fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { fn check_attr_whitelist(attrs: &[Attribute]) -> Result<(), TokenStream> { let whitelist = &["doc", "link_section", "cfg", "allow", "warn", "deny", "forbid", "cold"]; + let cortex_m_rt_blacklist = &["entry", "exception", "interrupt", "pre_init"]; 'o: for attr in attrs { for val in whitelist { @@ -833,6 +834,19 @@ fn check_attr_whitelist(attrs: &[Attribute]) -> Result<(), TokenStream> { } } + for val in cortex_m_rt_blacklist { + if eq(&attr, &val) { + return Err( + parse::Error::new( + attr.span(), + "cortex-m-rt does not support multiple attributes on the same function", + ) + .to_compile_error() + .into(), + ); + } + } + return Err( parse::Error::new( attr.span(), diff --git a/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs b/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs index 9005209..db17fb5 100644 --- a/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs +++ b/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs @@ -7,7 +7,7 @@ extern crate panic_halt; use cortex_m_rt::{entry, exception}; #[exception] -#[entry] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[entry] //~ ERROR cortex-m-rt does not support multiple attributes on the same function fn SVCall() -> ! { loop {} } -- cgit v1.2.3 From 447798a4f9ce76d9c8d51bc1c53d6ce51fa9d7e6 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 16 Jan 2020 13:02:47 +0100 Subject: Better error message Co-Authored-By: Jonas Schievink --- cortex-m-rt/macros/src/lib.rs | 2 +- cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 177ed4e..57cddd1 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -839,7 +839,7 @@ fn check_attr_whitelist(attrs: &[Attribute]) -> Result<(), TokenStream> { return Err( parse::Error::new( attr.span(), - "cortex-m-rt does not support multiple attributes on the same function", + "multiple cortex-m-rt attributes are not supported on the same function", ) .to_compile_error() .into(), diff --git a/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs b/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs index db17fb5..2900eb7 100644 --- a/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs +++ b/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs @@ -7,7 +7,7 @@ extern crate panic_halt; use cortex_m_rt::{entry, exception}; #[exception] -#[entry] //~ ERROR cortex-m-rt does not support multiple attributes on the same function +#[entry] //~ ERROR multiple cortex-m-rt attributes are not supported on the same function fn SVCall() -> ! { loop {} } -- cgit v1.2.3 From 6c0464c63b08dcd9cb61b07c67c86d884a34b11d Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 16 Jan 2020 14:23:04 +0100 Subject: Even better error messages # Please enter the commit message for your changes. Lines starting Formating fix --- cortex-m-rt/macros/src/lib.rs | 72 +++++++++++++++------- .../tests/compile-fail/whitelist-double-attr.rs | 2 +- 2 files changed, 50 insertions(+), 24 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 57cddd1..5d0ab55 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -150,7 +150,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { }) .collect::>(); - if let Err(error) = check_attr_whitelist(&f.attrs) { + if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Entry) { return error; } @@ -294,11 +294,10 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } - if let Err(error) = check_attr_whitelist(&f.attrs) { + if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Exception) { return error; } - let fspan = f.span(); let ident = f.sig.ident.clone(); @@ -675,7 +674,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { }) .collect::>(); - if let Err(error) = check_attr_whitelist(&f.attrs) { + if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) { return error; } @@ -757,6 +756,10 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } + if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::PreInit) { + return error; + } + // XXX should we blacklist other attributes? let attrs = f.attrs; let ident = f.sig.ident; @@ -823,8 +826,24 @@ fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { (cfgs, not_cfgs) } -fn check_attr_whitelist(attrs: &[Attribute]) -> Result<(), TokenStream> { - let whitelist = &["doc", "link_section", "cfg", "allow", "warn", "deny", "forbid", "cold"]; +enum WhiteListCaller { + Entry, + Exception, + Interrupt, + PreInit, +} + +fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<(), TokenStream> { + let whitelist = &[ + "doc", + "link_section", + "cfg", + "allow", + "warn", + "deny", + "forbid", + "cold", + ]; let cortex_m_rt_blacklist = &["entry", "exception", "interrupt", "pre_init"]; 'o: for attr in attrs { @@ -836,27 +855,34 @@ fn check_attr_whitelist(attrs: &[Attribute]) -> Result<(), TokenStream> { for val in cortex_m_rt_blacklist { if eq(&attr, &val) { - return Err( - parse::Error::new( - attr.span(), - "multiple cortex-m-rt attributes are not supported on the same function", - ) + let err_str = match caller { + WhiteListCaller::Entry => { + &"this attribute is not allowed on a cortex-m-rt entry point" + } + WhiteListCaller::Exception => { + &"this attribute is not allowed on an exception handler" + } + WhiteListCaller::Interrupt => { + &"this attribute is not allowed on an interrupt handler" + } + WhiteListCaller::PreInit => { + &"this attribute is not allowed on an interrupt handler" + } + }; + + return Err(parse::Error::new(attr.span(), err_str) .to_compile_error() - .into(), - ); + .into()); } } - return Err( - parse::Error::new( - attr.span(), - "this attribute is not allowed on a function controlled by cortex-m-rt", - ) - .to_compile_error() - .into(), - ); - - }; + return Err(parse::Error::new( + attr.span(), + "this attribute is not allowed on a function controlled by cortex-m-rt", + ) + .to_compile_error() + .into()); + } Ok(()) } diff --git a/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs b/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs index 2900eb7..31da76a 100644 --- a/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs +++ b/cortex-m-rt/tests/compile-fail/whitelist-double-attr.rs @@ -7,7 +7,7 @@ extern crate panic_halt; use cortex_m_rt::{entry, exception}; #[exception] -#[entry] //~ ERROR multiple cortex-m-rt attributes are not supported on the same function +#[entry] //~ ERROR this attribute is not allowed on an exception handler fn SVCall() -> ! { loop {} } -- cgit v1.2.3 From 3dd900d8ad7c66fc2044e12e3d55274a5afb43a1 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 16 Jan 2020 17:59:54 +0100 Subject: Updated to not need blacklist --- cortex-m-rt/macros/src/lib.rs | 42 +++++++++------------------ cortex-m-rt/tests/compile-fail/whitelist-1.rs | 8 ++--- cortex-m-rt/tests/compile-fail/whitelist-2.rs | 8 ++--- cortex-m-rt/tests/compile-fail/whitelist-3.rs | 8 ++--- cortex-m-rt/tests/compile-fail/whitelist-4.rs | 8 ++--- 5 files changed, 30 insertions(+), 44 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 5d0ab55..d919faa 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -844,7 +844,6 @@ fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result< "forbid", "cold", ]; - let cortex_m_rt_blacklist = &["entry", "exception", "interrupt", "pre_init"]; 'o: for attr in attrs { for val in whitelist { @@ -853,35 +852,22 @@ fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result< } } - for val in cortex_m_rt_blacklist { - if eq(&attr, &val) { - let err_str = match caller { - WhiteListCaller::Entry => { - &"this attribute is not allowed on a cortex-m-rt entry point" - } - WhiteListCaller::Exception => { - &"this attribute is not allowed on an exception handler" - } - WhiteListCaller::Interrupt => { - &"this attribute is not allowed on an interrupt handler" - } - WhiteListCaller::PreInit => { - &"this attribute is not allowed on an interrupt handler" - } - }; - - return Err(parse::Error::new(attr.span(), err_str) - .to_compile_error() - .into()); + let err_str = match caller { + WhiteListCaller::Entry => "this attribute is not allowed on a cortex-m-rt entry point", + WhiteListCaller::Exception => { + "this attribute is not allowed on an exception handler controlled by cortex-m-rt" } - } + WhiteListCaller::Interrupt => { + "this attribute is not allowed on an interrupt handler controlled by cortex-m-rt" + } + WhiteListCaller::PreInit => { + "this attribute is not allowed on a pre-init controlled by cortex-m-rt" + } + }; - return Err(parse::Error::new( - attr.span(), - "this attribute is not allowed on a function controlled by cortex-m-rt", - ) - .to_compile_error() - .into()); + return Err(parse::Error::new(attr.span(), &err_str) + .to_compile_error() + .into()); } Ok(()) diff --git a/cortex-m-rt/tests/compile-fail/whitelist-1.rs b/cortex-m-rt/tests/compile-fail/whitelist-1.rs index d8a334c..9c5133b 100644 --- a/cortex-m-rt/tests/compile-fail/whitelist-1.rs +++ b/cortex-m-rt/tests/compile-fail/whitelist-1.rs @@ -6,13 +6,13 @@ extern crate panic_halt; use cortex_m_rt::{entry, exception, interrupt}; -#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[inline] //~ ERROR this attribute is not allowed on a cortex-m-rt entry point #[entry] fn foo() -> ! { loop {} } -#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[inline] //~ ERROR this attribute is not allowed on an exception handler controlled by cortex-m-rt #[exception] fn SysTick() {} @@ -22,11 +22,11 @@ enum interrupt { USART2, } -#[inline] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[inline] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt #[interrupt] fn USART1() {} #[cfg(feature = "device")] -#[cfg_attr(feature = "device", inline)] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[cfg_attr(feature = "device", inline)] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt #[interrupt] fn USART2() {} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-2.rs b/cortex-m-rt/tests/compile-fail/whitelist-2.rs index 1550ca0..086f909 100644 --- a/cortex-m-rt/tests/compile-fail/whitelist-2.rs +++ b/cortex-m-rt/tests/compile-fail/whitelist-2.rs @@ -6,13 +6,13 @@ extern crate panic_halt; use cortex_m_rt::{entry, exception, interrupt}; -#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a cortex-m-rt entry point #[entry] fn foo() -> ! { loop {} } -#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on an exception handler controlled by cortex-m-rt #[exception] fn SysTick() {} @@ -22,11 +22,11 @@ enum interrupt { USART2, } -#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[export_name = "not_allowed"] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt #[interrupt] fn USART1() {} #[cfg(feature = "device")] -#[cfg_attr(feature = "device", export_name = "not_allowed")] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[cfg_attr(feature = "device", export_name = "not_allowed")] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt #[interrupt] fn USART2() {} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-3.rs b/cortex-m-rt/tests/compile-fail/whitelist-3.rs index c8e7bb2..9e5fb33 100644 --- a/cortex-m-rt/tests/compile-fail/whitelist-3.rs +++ b/cortex-m-rt/tests/compile-fail/whitelist-3.rs @@ -6,13 +6,13 @@ extern crate panic_halt; use cortex_m_rt::{entry, exception, interrupt}; -#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[no_mangle] //~ ERROR this attribute is not allowed on a cortex-m-rt entry point #[entry] fn foo() -> ! { loop {} } -#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[no_mangle] //~ ERROR this attribute is not allowed on an exception handler controlled by cortex-m-rt #[exception] fn SysTick() {} @@ -22,11 +22,11 @@ enum interrupt { USART2, } -#[no_mangle] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[no_mangle] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt #[interrupt] fn USART1() {} #[cfg(feature = "device")] -#[cfg_attr(feature = "device", no_mangle)] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[cfg_attr(feature = "device", no_mangle)] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt #[interrupt] fn USART2() {} diff --git a/cortex-m-rt/tests/compile-fail/whitelist-4.rs b/cortex-m-rt/tests/compile-fail/whitelist-4.rs index f0c5ca2..95dad29 100644 --- a/cortex-m-rt/tests/compile-fail/whitelist-4.rs +++ b/cortex-m-rt/tests/compile-fail/whitelist-4.rs @@ -6,13 +6,13 @@ extern crate panic_halt; use cortex_m_rt::{entry, exception, interrupt}; -#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[must_use] //~ ERROR this attribute is not allowed on a cortex-m-rt entry point #[entry] fn foo() -> ! { loop {} } -#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[must_use] //~ ERROR this attribute is not allowed on an exception handler controlled by cortex-m-rt #[exception] fn SysTick() {} @@ -22,11 +22,11 @@ enum interrupt { USART2, } -#[must_use] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[must_use] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt #[interrupt] fn USART1() {} #[cfg(feature = "device")] -#[cfg_attr(feature = "device", must_use)] //~ ERROR this attribute is not allowed on a function controlled by cortex-m-rt +#[cfg_attr(feature = "device", must_use)] //~ ERROR this attribute is not allowed on an interrupt handler controlled by cortex-m-rt #[interrupt] fn USART2() {} -- cgit v1.2.3 From 36b33b21af1bed29a9e37e07d96ab779f3c2314e Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sun, 26 Jan 2020 18:26:13 +0100 Subject: `#[allow(missing_docs)]` on `#[pre_init]` hooks --- cortex-m-rt/macros/src/lib.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index d919faa..8c1d182 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -767,6 +767,7 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { quote!( #[export_name = "__pre_init"] + #[allow(missing_docs)] // we make a private fn public, which can trigger this lint #(#attrs)* pub unsafe fn #ident() #block ) -- cgit v1.2.3 From d4a5321c914c49a82edcaac803b1ffa2210bf022 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 6 Feb 2020 00:54:41 +0100 Subject: Fix doctests and run them in CI --- cortex-m-rt/ci/script.sh | 2 + cortex-m-rt/macros/Cargo.toml | 3 - cortex-m-rt/macros/src/lib.rs | 261 ----------------------------------- cortex-m-rt/src/lib.rs | 307 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 288 insertions(+), 285 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/ci/script.sh b/cortex-m-rt/ci/script.sh index 40ff22a..69262f6 100755 --- a/cortex-m-rt/ci/script.sh +++ b/cortex-m-rt/ci/script.sh @@ -33,6 +33,8 @@ main() { data_overflow ) if [ "$TARGET" != x86_64-unknown-linux-gnu ]; then + RUSTDOCFLAGS="-Cpanic=abort" cargo test --doc + # linking with GNU LD for ex in "${examples[@]}"; do cargo rustc --target "$TARGET" --example "$ex" -- \ diff --git a/cortex-m-rt/macros/Cargo.toml b/cortex-m-rt/macros/Cargo.toml index 96dc22a..8c48e0d 100644 --- a/cortex-m-rt/macros/Cargo.toml +++ b/cortex-m-rt/macros/Cargo.toml @@ -20,6 +20,3 @@ proc-macro2 = "1.0" [dependencies.syn] features = ["extra-traits", "full"] version = "1.0" - -[dev-dependencies] -cortex-m-rt = { path = "..", version = "0.6" } diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 8c1d182..09881b8 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -16,63 +16,6 @@ use syn::{ ItemStatic, ReturnType, Stmt, Type, Visibility, }; -/// Attribute to declare the entry point of the program -/// -/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you -/// are using Rust 1.30 the attribute 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. This reachability restriction doesn't apply to Rust 1.31 and newer releases. -/// -/// 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 mut f = parse_macro_input!(input as ItemFn); @@ -172,118 +115,6 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { .into() } -/// Attribute to declare an exception handler -/// -/// **IMPORTANT**: If you are using Rust 1.30 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. This reachability restriction doesn't apply to Rust 1.31 -/// and newer releases. -/// -/// # Syntax -/// -/// ``` -/// # use cortex_m_rt::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 mut f = parse_macro_input!(input as ItemFn); @@ -521,74 +352,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { } } -/// Attribute to declare an interrupt (AKA device-specific exception) handler -/// -/// **IMPORTANT**: If you are using Rust 1.30 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. This reachability restriction doesn't apply to Rust 1.31 -/// and newer releases. -/// -/// **NOTE**: This attribute is exposed by `cortex-m-rt` only when the `device` feature is enabled. -/// However, that export is not meant to be used directly -- using it will result in a compilation -/// error. You should instead use the device crate (usually generated using `svd2rust`) re-export of -/// that attribute. You need to use the re-export to have the compiler check that the interrupt -/// exists on the target device. -/// -/// # Syntax -/// -/// ``` ignore -/// extern crate device; -/// -/// // the attribute comes from the device crate not from cortex-m-rt -/// use device::interrupt; -/// -/// #[interrupt] -/// fn USART1() { -/// // .. -/// } -/// ``` -/// -/// where the name of the function must be one of the device interrupts. -/// -/// # Usage -/// -/// `#[interrupt] fn Name(..` overrides the default handler for the interrupt with the given `Name`. -/// These handlers must have signature `[unsafe] fn() [-> !]`. It's possible to add state to these -/// handlers 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. -/// -/// If the interrupt handler has not been overridden it will be dispatched by the default exception -/// handler (`DefaultHandler`). -/// -/// # Properties -/// -/// Interrupts handlers can only be called by the hardware. Other parts of the program can't refer -/// to the interrupt handlers, much less invoke them as if they were functions. -/// -/// `static mut` variables declared within an interrupt 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 -/// -/// - Using state within an interrupt handler -/// -/// ``` ignore -/// extern crate device; -/// -/// use device::interrupt; -/// -/// #[interrupt] -/// fn TIM2() { -/// static mut COUNT: i32 = 0; -/// -/// // `COUNT` is safe to access and has type `&mut i32` -/// *COUNT += 1; -/// -/// println!("{}", COUNT); -/// } -/// ``` #[proc_macro_attribute] pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); @@ -696,30 +459,6 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { .into() } -/// Attribute to mark which function will be called at the beginning of the reset handler. -/// -/// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you -/// are using Rust 1.30 the attribute 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. This reachability restriction doesn't apply to Rust 1.31 and newer -/// releases. -/// -/// 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 = parse_macro_input!(input as ItemFn); diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index 150ca8e..40b10dc 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -42,7 +42,7 @@ //! program will be placed in the `FLASH` region, whereas the `.bss` and `.data` sections, as well //! as the heap,will be placed in the `RAM` region. //! -//! ``` text +//! ```text //! /* Linker script for the STM32F103C8T6 */ //! MEMORY //! { @@ -58,7 +58,7 @@ //! region -- the stack grows downwards towards smaller address. This symbol can be used to place //! the stack in a different memory region, for example: //! -//! ``` text +//! ```text //! /* Linker script for the STM32F303VCT6 */ //! MEMORY //! { @@ -82,7 +82,7 @@ //! for these devices one must place the `.text` section after this configuration section -- //! `_stext` can be used for this purpose. //! -//! ``` text +//! ```text //! MEMORY //! { //! /* .. */ @@ -99,17 +99,15 @@ //! handler and the default exception handler must also be defined somewhere in the dependency //! graph (see [`#[exception]`]). In this example we define them in the binary crate: //! -//! ``` ignore +//! ```no_run //! // IMPORTANT the standard `main` interface is not used because it requires nightly //! #![no_main] //! #![no_std] //! -//! extern crate cortex_m_rt as rt; +//! // Some panic handler needs to be included. This one halts the processor on panic. +//! extern crate panic_halt; //! -//! // makes `panic!` print messages to the host stderr using semihosting -//! extern crate panic_semihosting; -//! -//! use rt::entry; +//! use cortex_m_rt::entry; //! //! // use `main` as the entry point of this application //! // `main` is not allowed to return @@ -127,7 +125,7 @@ //! can find it, e.g. in the current directory; and then link the program using `cortex-m-rt`'s //! linker script: `link.x`. The required steps are shown below: //! -//! ``` text +//! ```text //! $ cat > memory.x < !]`. It's possible to add state to these +/// handlers 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. +/// +/// If the interrupt handler has not been overridden it will be dispatched by the default exception +/// handler (`DefaultHandler`). +/// +/// # Properties +/// +/// Interrupts handlers can only be called by the hardware. Other parts of the program can't refer +/// to the interrupt handlers, much less invoke them as if they were functions. +/// +/// `static mut` variables declared within an interrupt 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 +/// +/// - Using state within an interrupt handler +/// +/// ``` ignore +/// extern crate device; +/// +/// use device::interrupt; +/// +/// #[interrupt] +/// fn TIM2() { +/// static mut COUNT: i32 = 0; +/// +/// // `COUNT` is safe to access and has type `&mut i32` +/// *COUNT += 1; +/// +/// println!("{}", COUNT); +/// } +/// ``` #[cfg(feature = "device")] -#[doc(inline)] pub use macros::interrupt; -#[doc(inline)] -pub use macros::{entry, exception, pre_init}; + +/// Attribute to declare the entry point of the program +/// +/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you +/// are using Rust 1.30 the attribute 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. This reachability restriction doesn't apply to Rust 1.31 and newer releases. +/// +/// 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::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::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 { +/// /* .. */ +/// } +/// } +/// ``` +pub use macros::entry; + +/// Attribute to declare an exception handler +/// +/// **IMPORTANT**: If you are using Rust 1.30 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. This reachability restriction doesn't apply to Rust 1.31 +/// and newer releases. +/// +/// # Syntax +/// +/// ``` +/// # use cortex_m_rt::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::{ExceptionFrame, exception}; +/// +/// #[exception] +/// fn HardFault(ef: &ExceptionFrame) -> ! { +/// // prints the exception frame as a panic message +/// panic!("{:#?}", ef); +/// } +/// +/// # fn main() {} +/// ``` +/// +/// - Setting the default handler +/// +/// ``` +/// use cortex_m_rt::exception; +/// +/// #[exception] +/// fn DefaultHandler(irqn: i16) { +/// println!("IRQn = {}", irqn); +/// } +/// +/// # fn main() {} +/// ``` +/// +/// - Overriding the `SysTick` handler +/// +/// ``` +/// use cortex_m_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() {} +/// ``` +pub use macros::exception; + +/// Attribute to mark which function will be called at the beginning of the reset handler. +/// +/// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you +/// are using Rust 1.30 the attribute 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. This reachability restriction doesn't apply to Rust 1.31 and newer +/// releases. +/// +/// 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::pre_init; +/// #[pre_init] +/// unsafe fn before_main() { +/// // do something here +/// } +/// +/// # fn main() {} +/// ``` +pub use macros::pre_init; #[export_name = "error: cortex-m-rt appears more than once in the dependency graph"] #[doc(hidden)] @@ -722,6 +984,7 @@ pub enum Exception { SysTick, } +#[doc(hidden)] pub use self::Exception as exception; extern "C" { @@ -766,7 +1029,9 @@ pub static __EXCEPTIONS: [Vector; 14] = [ handler: NonMaskableInt, }, // Exception 3: Hard Fault Interrupt. - Vector { handler: HardFaultTrampoline }, + Vector { + handler: HardFaultTrampoline, + }, // Exception 4: Memory Management Interrupt [not on Cortex-M0 variants]. #[cfg(not(armv6m))] Vector { -- cgit v1.2.3 From 169328090dbbfd6e792f994d337958ee4e53e0a6 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 19 May 2020 19:56:58 +0200 Subject: Get rid of #[deny(warnings)] This is considered harmful as the warnings may change with any compiler release. Signed-off-by: Daniel Egger --- cortex-m-rt/macros/src/lib.rs | 2 -- cortex-m-rt/src/lib.rs | 1 - 2 files changed, 3 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 09881b8..3c532c0 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -2,8 +2,6 @@ //! //! Do not use this crate directly. -#![deny(warnings)] - extern crate proc_macro; use proc_macro::TokenStream; diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index d680b6d..1988df6 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -401,7 +401,6 @@ // here will appear in the linker script (`link.x`) in conjunction with the `KEEP` command. #![deny(missing_docs)] -#![deny(warnings)] #![no_std] extern crate cortex_m_rt_macros as macros; -- cgit v1.2.3 From 679d42223fe79759bc634675694c46ae8ba0ce62 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sun, 23 Aug 2020 21:57:08 +0200 Subject: Make it unsafe to define NMI handlers --- cortex-m-rt/macros/src/lib.rs | 42 +++++++++++++++------- .../default-handler-bad-signature-1.rs | 4 +-- .../default-handler-bad-signature-2.rs | 4 +-- .../tests/compile-fail/default-handler-hidden.rs | 2 +- .../tests/compile-fail/default-handler-twice.rs | 4 +-- .../tests/compile-fail/exception-nmi-unsafe.rs | 24 +++++++++++++ .../compile-fail/hard-fault-bad-signature-1.rs | 4 +-- cortex-m-rt/tests/compile-fail/hard-fault-twice.rs | 4 +-- .../tests/compile-fail/unsafe-init-static.rs | 4 +-- 9 files changed, 67 insertions(+), 25 deletions(-) create mode 100644 cortex-m-rt/tests/compile-fail/exception-nmi-unsafe.rs (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 3c532c0..7e54a5c 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -113,6 +113,14 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { .into() } +#[derive(Debug, PartialEq)] +enum Exception { + DefaultHandler, + HardFault, + NonMaskableInt, + Other, +} + #[proc_macro_attribute] pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let mut f = parse_macro_input!(input as ItemFn); @@ -130,20 +138,15 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let fspan = f.span(); let ident = f.sig.ident.clone(); - enum Exception { - DefaultHandler, - HardFault, - Other, - } - let ident_s = ident.to_string(); let exn = match &*ident_s { "DefaultHandler" => Exception::DefaultHandler, "HardFault" => Exception::HardFault, + "NonMaskableInt" => Exception::NonMaskableInt, // 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, + "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall" + | "DebugMonitor" | "PendSV" | "SysTick" => Exception::Other, _ => { return parse::Error::new(ident.span(), "This is not a valid exception name") .to_compile_error() @@ -151,7 +154,22 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { } }; - // XXX should we blacklist other attributes? + if f.sig.unsafety.is_none() { + match exn { + Exception::DefaultHandler | Exception::HardFault | Exception::NonMaskableInt => { + // These are unsafe to define. + let name = if exn == Exception::DefaultHandler { + format!("`DefaultHandler`") + } else { + format!("`{:?}` handler", exn) + }; + return parse::Error::new(ident.span(), format_args!("defining a {} is unsafe and requires an `unsafe fn` (see the cortex-m-rt docs)", name)) + .to_compile_error() + .into(); + } + Exception::Other => {} + } + } match exn { Exception::DefaultHandler => { @@ -174,7 +192,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { if !valid_signature { return parse::Error::new( fspan, - "`DefaultHandler` must have signature `[unsafe] fn(i16) [-> !]`", + "`DefaultHandler` must have signature `unsafe fn(i16) [-> !]`", ) .to_compile_error() .into(); @@ -231,7 +249,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { if !valid_signature { return parse::Error::new( fspan, - "`HardFault` handler must have signature `[unsafe] fn(&ExceptionFrame) -> !`", + "`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`", ) .to_compile_error() .into(); @@ -257,7 +275,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { ) .into() } - Exception::Other => { + Exception::NonMaskableInt | Exception::Other => { let valid_signature = f.sig.constness.is_none() && f.vis == Visibility::Inherited && f.sig.abi.is_none() 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 index 5436115..b590883 100644 --- 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 @@ -12,5 +12,5 @@ fn foo() -> ! { } #[exception] -fn DefaultHandler(_irqn: i16, undef: u32) {} -//~^ ERROR `DefaultHandler` must have signature `[unsafe] fn(i16) [-> !]` +unsafe fn DefaultHandler(_irqn: i16, undef: u32) {} +//~^ ERROR `DefaultHandler` must have signature `unsafe fn(i16) [-> !]` 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 index 1cca10c..0dadd6a 100644 --- 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 @@ -12,7 +12,7 @@ fn foo() -> ! { } #[exception] -fn DefaultHandler(_irqn: i16) -> u32 { - //~^ ERROR `DefaultHandler` must have signature `[unsafe] fn(i16) [-> !]` +unsafe fn DefaultHandler(_irqn: i16) -> u32 { + //~^ ERROR `DefaultHandler` must have signature `unsafe fn(i16) [-> !]` 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 index e57cb31..c658e2b 100644 --- a/cortex-m-rt/tests/compile-fail/default-handler-hidden.rs +++ b/cortex-m-rt/tests/compile-fail/default-handler-hidden.rs @@ -18,5 +18,5 @@ mod hidden { use cortex_m_rt::exception; #[exception] - fn DefaultHandler(_irqn: i16) {} + unsafe 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 index ad1c3f9..bbf2edd 100644 --- a/cortex-m-rt/tests/compile-fail/default-handler-twice.rs +++ b/cortex-m-rt/tests/compile-fail/default-handler-twice.rs @@ -12,11 +12,11 @@ fn foo() -> ! { } #[exception] -fn DefaultHandler(_irqn: i16) {} +unsafe fn DefaultHandler(_irqn: i16) {} pub mod reachable { use cortex_m_rt::exception; #[exception] //~ ERROR symbol `DefaultHandler` is already defined - fn DefaultHandler(_irqn: i16) {} + unsafe fn DefaultHandler(_irqn: i16) {} } diff --git a/cortex-m-rt/tests/compile-fail/exception-nmi-unsafe.rs b/cortex-m-rt/tests/compile-fail/exception-nmi-unsafe.rs new file mode 100644 index 0000000..f5de2f8 --- /dev/null +++ b/cortex-m-rt/tests/compile-fail/exception-nmi-unsafe.rs @@ -0,0 +1,24 @@ +#![no_main] +#![no_std] + +extern crate cortex_m_rt; +extern crate panic_halt; + +use cortex_m_rt::{entry, exception}; + +#[entry] +fn foo() -> ! { + loop {} +} + +#[exception] +fn DefaultHandler(_irq: i16) {} +//~^ ERROR defining a `DefaultHandler` is unsafe and requires an `unsafe fn` + +#[exception] +fn HardFault() {} +//~^ ERROR defining a `HardFault` handler is unsafe and requires an `unsafe fn` + +#[exception] +fn NonMaskableInt() {} +//~^ ERROR defining a `NonMaskableInt` handler is unsafe and requires an `unsafe fn` 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 index d3b4392..11b53dc 100644 --- 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 @@ -12,7 +12,7 @@ fn foo() -> ! { } #[exception] -fn HardFault(_ef: &ExceptionFrame, undef: u32) -> ! { - //~^ ERROR `HardFault` handler must have signature `[unsafe] fn(&ExceptionFrame) -> !` +unsafe fn HardFault(_ef: &ExceptionFrame, undef: u32) -> ! { + //~^ ERROR `HardFault` handler must have signature `unsafe fn(&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 index 030b54c..03b79a5 100644 --- a/cortex-m-rt/tests/compile-fail/hard-fault-twice.rs +++ b/cortex-m-rt/tests/compile-fail/hard-fault-twice.rs @@ -12,7 +12,7 @@ fn foo() -> ! { } #[exception] -fn HardFault(_ef: &ExceptionFrame) -> ! { +unsafe fn HardFault(_ef: &ExceptionFrame) -> ! { loop {} } @@ -20,7 +20,7 @@ pub mod reachable { use cortex_m_rt::{exception, ExceptionFrame}; #[exception] //~ ERROR symbol `HardFault` is already defined - fn HardFault(_ef: &ExceptionFrame) -> ! { + unsafe fn HardFault(_ef: &ExceptionFrame) -> ! { loop {} } } diff --git a/cortex-m-rt/tests/compile-fail/unsafe-init-static.rs b/cortex-m-rt/tests/compile-fail/unsafe-init-static.rs index c040173..23105f1 100644 --- a/cortex-m-rt/tests/compile-fail/unsafe-init-static.rs +++ b/cortex-m-rt/tests/compile-fail/unsafe-init-static.rs @@ -29,12 +29,12 @@ fn SVCall() { } #[exception] -fn DefaultHandler(_irq: i16) { +unsafe fn DefaultHandler(_irq: i16) { static mut X: u32 = init(); //~ ERROR requires unsafe } #[exception] -fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! { +unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! { static mut X: u32 = init(); //~ ERROR requires unsafe loop {} } -- cgit v1.2.3 From dbd0f6cb42000ebe019f264bf14a8899d585c25b Mon Sep 17 00:00:00 2001 From: Anatol Ulrich Date: Tue, 26 Jan 2021 16:10:25 +0100 Subject: fix .HardFault.user section name for macos --- cortex-m-rt/macros/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 7e54a5c..a486558 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -266,7 +266,8 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { #(#attrs)* #[doc(hidden)] #[export_name = "HardFault"] - #[link_section = ".HardFault.user"] + #[cfg_attr(target_os = "macos", link_section = ".HardFault,user")] + #[cfg_attr(not(target_os = "macos"), link_section = ".HardFault.user")] pub unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) { #ident(frame) } -- cgit v1.2.3 From 774ad318fd281dc25eb2863fd521bb7480932a35 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 26 Jan 2021 21:43:33 +0100 Subject: Check presence of exceptions --- cortex-m-rt/macros/src/lib.rs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index a486558..74041ef 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -171,7 +171,20 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { } } - match exn { + // Emit a reference to the `Exception` variant corresponding to our exception. + // This will fail compilation when the target doesn't have that exception. + let assertion = match exn { + Exception::Other => { + quote! { + const _: () = { + let _ = cortex_m_rt::Exception::#ident; + }; + } + } + _ => quote!(), + }; + + let handler = match exn { Exception::DefaultHandler => { let valid_signature = f.sig.constness.is_none() && f.vis == Visibility::Inherited @@ -221,7 +234,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { #f ) - .into() } Exception::HardFault => { let valid_signature = f.sig.constness.is_none() @@ -274,7 +286,6 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { #f ) - .into() } Exception::NonMaskableInt | Exception::Other => { let valid_signature = f.sig.constness.is_none() @@ -364,9 +375,14 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { #f ) - .into() } - } + }; + + quote!( + #assertion + #handler + ) + .into() } #[proc_macro_attribute] -- cgit v1.2.3 From f0e2218330adcffe7dc8a05869dac0e5399058c3 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Wed, 27 Jan 2021 11:14:20 +0000 Subject: Only emit link_section for cortex-m. Previously we always emitted link_section, even though it only had an effect when our linker script was being used (and only made sense on cortex-m targets). This breaks building the code for a MacOS target, which is occasionally useful for running `cargo check` etc. In the macros crate we don't have the target information available, so instead we continue to emit link_section except specifically on MacOS. --- cortex-m-rt/macros/src/lib.rs | 6 ++++-- cortex-m-rt/src/lib.rs | 12 ++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 74041ef..fe0264f 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -278,8 +278,10 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { #(#attrs)* #[doc(hidden)] #[export_name = "HardFault"] - #[cfg_attr(target_os = "macos", link_section = ".HardFault,user")] - #[cfg_attr(not(target_os = "macos"), link_section = ".HardFault.user")] + // Only emit link_section when building for embedded targets, + // because some hosted platforms (used to check the build) + // cannot handle the long link section names. + #[cfg_attr(target_os = "none", link_section = ".HardFault.user")] pub unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) { #ident(frame) } diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index 93125b7..cb1b5f7 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -910,15 +910,13 @@ pub fn heap_start() -> *mut u32 { // Entry point is Reset. #[doc(hidden)] -#[cfg_attr(target_os = "macos", link_section = ".vector_table,reset_vector")] -#[cfg_attr(not(target_os = "macos"), link_section = ".vector_table.reset_vector")] +#[cfg_attr(cortex_m, link_section = ".vector_table.reset_vector")] #[no_mangle] pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset; #[allow(unused_variables)] #[doc(hidden)] -#[cfg_attr(target_os = "macos", link_section = ".HardFault,default")] -#[cfg_attr(not(target_os = "macos"), link_section = ".HardFault.default")] +#[cfg_attr(cortex_m, link_section = ".HardFault.default")] #[no_mangle] pub unsafe extern "C" fn HardFault_(ef: &ExceptionFrame) -> ! { loop { @@ -1010,8 +1008,7 @@ pub union Vector { } #[doc(hidden)] -#[cfg_attr(target_os = "macos", link_section = ".vector_table,exceptions")] -#[cfg_attr(not(target_os = "macos"), link_section = ".vector_table.exceptions")] +#[cfg_attr(cortex_m, link_section = ".vector_table.exceptions")] #[no_mangle] pub static __EXCEPTIONS: [Vector; 14] = [ // Exception 2: Non Maskable Interrupt. @@ -1073,8 +1070,7 @@ pub static __EXCEPTIONS: [Vector; 14] = [ // to the default handler #[cfg(all(any(not(feature = "device"), test), not(armv6m)))] #[doc(hidden)] -#[cfg_attr(target_os = "macos", link_section = ".vector_table,interrupts")] -#[cfg_attr(not(target_os = "macos"), link_section = ".vector_table.interrupts")] +#[cfg_attr(cortex_m, link_section = ".vector_table.interrupts")] #[no_mangle] pub static __INTERRUPTS: [unsafe extern "C" fn(); 240] = [{ extern "C" { -- cgit v1.2.3 From d802c58d0fafb45538864c8798fcf71e04de0ad3 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 25 Feb 2021 15:04:42 +0100 Subject: Use volatile read for ICSR register This prevents the compiler from optimizing the read --- cortex-m-rt/macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index fe0264f..7877348 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -227,7 +227,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; - let irqn = unsafe { core::ptr::read(SCB_ICSR) as u8 as i16 - 16 }; + let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16 }; #ident(irqn) } -- cgit v1.2.3 From 59d30a44e1584b547f2b13e69a916ceb05a6564e Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 25 Feb 2021 15:25:57 +0100 Subject: Read all bits from ICSR register The 9th bit is needed to all 480 interrupts on Cortex-M33 --- cortex-m-rt/macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cortex-m-rt/macros/src') diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index 7877348..817e9a1 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -227,7 +227,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; - let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16 }; + let irqn = unsafe { (core::ptr::read_volatile(SCB_ICSR) & 0x1FF) as i16 - 16 }; #ident(irqn) } -- cgit v1.2.3