Skip to main content

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}