riscv_minimal_rt_macros/
lib.rs

1#![deny(warnings)]
2
3extern crate proc_macro;
4extern crate rand;
5#[macro_use]
6extern crate quote;
7extern crate core;
8extern crate proc_macro2;
9#[macro_use]
10extern crate syn;
11
12use proc_macro2::Span;
13use rand::Rng;
14use rand::SeedableRng;
15use std::sync::atomic::{AtomicUsize, Ordering};
16use std::time::{SystemTime, UNIX_EPOCH};
17use syn::{
18    parse, spanned::Spanned, Ident, ItemFn, ReturnType, Type, Visibility,
19};
20
21static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
22
23use proc_macro::TokenStream;
24
25/// Attribute to declare the entry point of the program
26///
27/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you
28/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
29/// private modules between the item and the root of the crate); if the item is in the root of the
30/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases.
31///
32/// The specified function will be called by the reset handler *after* RAM has been initialized.
33/// If present, the FPU will also be enabled before the function is called.
34///
35/// The type of the specified function must be `[unsafe] fn() -> !` (never ending function)
36///
37/// # Properties
38///
39/// The entry point will be called by the reset handler. The program can't reference to the entry
40/// point, much less invoke it.
41///
42/// # Examples
43///
44/// - Simple entry point
45///
46/// ``` no_run
47/// # #![no_main]
48/// # use riscv_minimal_rt_macros::entry;
49/// #[entry]
50/// fn main() -> ! {
51///     loop {
52///         /* .. */
53///     }
54/// }
55/// ```
56#[proc_macro_attribute]
57pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
58    let f = parse_macro_input!(input as ItemFn);
59
60    // check the function signature
61    let valid_signature = f.constness.is_none()
62        && f.vis == Visibility::Inherited
63        && f.abi.is_none()
64        && f.decl.inputs.is_empty()
65        && f.decl.generics.params.is_empty()
66        && f.decl.generics.where_clause.is_none()
67        && f.decl.variadic.is_none()
68        && match f.decl.output {
69            ReturnType::Default => false,
70            ReturnType::Type(_, ref ty) => match **ty {
71                Type::Never(_) => true,
72                _ => false,
73            },
74        };
75
76    if !valid_signature {
77        return parse::Error::new(
78            f.span(),
79            "`#[entry]` function must have signature `[unsafe] fn() -> !`",
80        )
81        .to_compile_error()
82        .into();
83    }
84
85    if !args.is_empty() {
86        return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
87            .to_compile_error()
88            .into();
89    }
90
91    // XXX should we blacklist other attributes?
92    let attrs = f.attrs;
93    let unsafety = f.unsafety;
94    let hash = random_ident();
95    let stmts = f.block.stmts;
96
97    quote!(
98        #[export_name = "main"]
99        #(#attrs)*
100        pub #unsafety fn #hash() -> ! {
101            #(#stmts)*
102        }
103    )
104    .into()
105}
106
107/// Attribute to mark which function will be called at the beginning of the reset handler.
108///
109/// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you
110/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
111/// private modules between the item and the root of the crate); if the item is in the root of the
112/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer
113/// releases.
114///
115/// The function must have the signature of `unsafe fn()`.
116///
117/// The function passed will be called before static variables are initialized. Any access of static
118/// variables will result in undefined behavior.
119///
120/// # Examples
121///
122/// ```
123/// # use riscv_minimal_rt_macros::pre_init;
124/// #[pre_init]
125/// unsafe fn before_main() {
126///     // do something here
127/// }
128///
129/// # fn main() {}
130/// ```
131#[proc_macro_attribute]
132pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream {
133    let f = parse_macro_input!(input as ItemFn);
134
135    // check the function signature
136    let valid_signature = f.constness.is_none()
137        && f.vis == Visibility::Inherited
138        && f.unsafety.is_some()
139        && f.abi.is_none()
140        && f.decl.inputs.is_empty()
141        && f.decl.generics.params.is_empty()
142        && f.decl.generics.where_clause.is_none()
143        && f.decl.variadic.is_none()
144        && match f.decl.output {
145            ReturnType::Default => true,
146            ReturnType::Type(_, ref ty) => match **ty {
147                Type::Tuple(ref tuple) => tuple.elems.is_empty(),
148                _ => false,
149            },
150        };
151
152    if !valid_signature {
153        return parse::Error::new(
154            f.span(),
155            "`#[pre_init]` function must have signature `unsafe fn()`",
156        )
157        .to_compile_error()
158        .into();
159    }
160
161    if !args.is_empty() {
162        return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
163            .to_compile_error()
164            .into();
165    }
166
167    // XXX should we blacklist other attributes?
168    let attrs = f.attrs;
169    let ident = f.ident;
170    let block = f.block;
171
172    quote!(
173        #[export_name = "__pre_init"]
174        #(#attrs)*
175        pub unsafe fn #ident() #block
176    )
177    .into()
178}
179
180// Creates a random identifier
181fn random_ident() -> Ident {
182    let secs = SystemTime::now()
183        .duration_since(UNIX_EPOCH)
184        .unwrap()
185        .as_secs();
186
187    let count: u64 = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u64;
188    let mut seed: [u8; 16] = [0; 16];
189
190    for (i, v) in seed.iter_mut().take(8).enumerate() {
191        *v = ((secs >> (i * 8)) & 0xFF) as u8
192    }
193
194    for (i, v) in seed.iter_mut().skip(8).enumerate() {
195        *v = ((count >> (i * 8)) & 0xFF) as u8
196    }
197
198    let mut rng = rand::rngs::SmallRng::from_seed(seed);
199    Ident::new(
200        &(0..16)
201            .map(|i| {
202                if i == 0 || rng.gen() {
203                    ('a' as u8 + rng.gen::<u8>() % 25) as char
204                } else {
205                    ('0' as u8 + rng.gen::<u8>() % 10) as char
206                }
207            })
208            .collect::<String>(),
209        Span::call_site(),
210    )
211}