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}