pie_boot_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Ident, ItemFn, parse::Parse, parse_macro_input};
4
5mod entry;
6
7#[proc_macro_attribute]
8pub fn start_code(args: TokenStream, input: TokenStream) -> TokenStream {
9    // 解析参数中的选项,例如 naked
10    let parsed_args = parse_macro_input!(args as StartCodeArgs);
11
12    let f = parse_macro_input!(input as ItemFn);
13    let attrs = f.attrs;
14    let vis = f.vis;
15    let name = f.sig.ident;
16    let args = f.sig.inputs;
17    let stmts = f.block.stmts;
18
19    let naked_prefix;
20    let naked_attr;
21    if parsed_args.naked {
22        naked_attr = quote! {
23            #[unsafe(naked)]
24        };
25        naked_prefix = quote! {
26            unsafe extern "C"
27        };
28    } else {
29        naked_attr = quote! {};
30        naked_prefix = quote! {};
31    };
32
33    quote!(
34        #naked_attr
35        #[unsafe(link_section = ".idmap.text")]
36        #(#attrs)*
37        #vis #naked_prefix fn #name(#args) {
38            #(#stmts)*
39        }
40    )
41    .into()
42}
43
44struct StartCodeArgs {
45    naked: bool,
46}
47
48impl Parse for StartCodeArgs {
49    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
50        let mut naked = false;
51
52        if !input.is_empty() {
53            let ident: Ident = input.parse()?;
54            if ident == "naked" {
55                naked = true;
56            } else {
57                return Err(input.error("unexpected argument, expected `naked`"));
58            }
59        }
60
61        Ok(StartCodeArgs { naked })
62    }
63}
64
65/// Attribute to declare the entry point of the program
66///
67/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you
68/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
69/// private modules between the item and the root of the crate); if the item is in the root of the
70/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases.
71///
72/// The specified function will be called by the reset handler *after* RAM has been initialized.
73/// If present, the FPU will also be enabled before the function is called.
74///
75/// The type of the specified function must be `[unsafe] fn() -> !` (never ending function)
76///
77/// # Properties
78///
79/// The entry point will be called by the reset handler. The program can't reference to the entry
80/// point, much less invoke it.
81///
82/// # Examples
83///
84/// - Simple entry point
85///
86/// ``` no_run
87/// # #![no_main]
88/// # use pie_boot::entry;
89/// #[entry]
90/// fn main(args: &pie_boot::BootArgs) -> ! {
91///     loop {
92///         /* .. */
93///     }
94/// }
95/// ```
96#[proc_macro_attribute]
97pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
98    entry::entry(args, input, "__pie_boot_main")
99}