ax_ctor_bare_macros/lib.rs
1//!Macros for registering constructor functions for Rust under no_std, which is like __attribute__((constructor)) in C/C++.
2//! **DO NOT** use this crate directly. Use the [ax-ctor-bare](https://docs.rs/ax-ctor-bare) crate instead.
3//!
4//! After attching the `register_ctor` macro to the given function, a pointer pointing to it will be stored in the `.init_array` section.
5//! When the program is loaded, this section will be linked into the binary. The `call_ctors` function in the `ax-ctor-bare`
6//! crate will call all the constructor functions in the `.init_array` section.
7//!
8//! See the documentation of the [ax-ctor-bare](https://docs.rs/ax-ctor-bare) crate for more details.
9
10use proc_macro::TokenStream;
11use proc_macro2::Span;
12use quote::{format_ident, quote};
13use syn::{Error, Item, parse_macro_input};
14
15/// Register a constructor function to be called before `main`.
16///
17/// The function should have no input arguments and return nothing.
18///
19/// See the documentation of the [ax-ctor-bare](https://docs.rs/ax-ctor-bare) crate for more details.
20#[proc_macro_attribute]
21pub fn register_ctor(attr: TokenStream, function: TokenStream) -> TokenStream {
22 if !attr.is_empty() {
23 return Error::new(
24 Span::call_site(),
25 "expect an empty attribute: `#[register_ctor]`",
26 )
27 .to_compile_error()
28 .into();
29 }
30
31 let item: Item = parse_macro_input!(function as Item);
32 if let Item::Fn(func) = item {
33 let name = &func.sig.ident;
34 let name_str = name.to_string();
35 let name_ident = format_ident!("_INIT_{}", name_str);
36 let output = &func.sig.output;
37 // Constructor functions should not have any return value.
38 if let syn::ReturnType::Type(..) = output {
39 return Error::new(
40 Span::call_site(),
41 "expect no return value for the constructor function",
42 )
43 .to_compile_error()
44 .into();
45 }
46 let inputs = &func.sig.inputs;
47 // Constructor functions should not have any input arguments.
48 if !inputs.is_empty() {
49 return Error::new(
50 Span::call_site(),
51 "expect no input arguments for the constructor function",
52 )
53 .to_compile_error()
54 .into();
55 }
56 let block = &func.block;
57
58 quote! {
59 #[unsafe(link_section = ".init_array")]
60 #[used]
61 #[allow(non_upper_case_globals)]
62 static #name_ident: extern "C" fn() = #name;
63
64 #[unsafe(no_mangle)]
65 #[allow(non_upper_case_globals)]
66 pub extern "C" fn #name() {
67 #block
68 }
69 }
70 .into()
71 } else {
72 Error::new(Span::call_site(), "expect a function to be registered")
73 .to_compile_error()
74 .into()
75 }
76}