#[ctor]
Expand description
Marks a function or static variable as a library/executable constructor. This uses OS-specific linker sections to call a specific function at load time.
§Important notes
Rust does not make any guarantees about stdlib support for life-before or
life-after main. This means that the ctor
crate may not work as expected
in some cases, such as when used in an async
runtime or making use of
stdlib services.
Multiple startup functions/statics are supported, but the invocation order is not guaranteed.
The ctor
crate assumes it is available as a direct dependency, with
extern crate ctor
. If you re-export ctor
items as part of your crate,
you can use the crate_path
parameter to redirect the macro’s output to the
correct crate.
§Attribute parameters
crate_path = ::path::to::ctor::crate
: The path to thector
crate containing the support macros. If you re-exportctor
items as part of your crate, you can use this to redirect the macro’s output to the correct crate.used(linker)
: (Advanced) Mark the function as being used in the link phase.link_section = "section"
: The section to place the constructor in.anonymous
: Do not give the constructor a name in the generated code (allows for multiple constructors with the same name).
§Examples
Print a startup message (using libc_print
for safety):
use libc_print::std_name::println;
#[ctor]
unsafe fn foo() {
// Using libc_print which is safe in `#[ctor]`
println!("Hello, world!");
}
println!("main()");
Make changes to static
variables:
static INITED: AtomicBool = AtomicBool::new(false);
#[ctor]
unsafe fn set_inited() {
INITED.store(true, Ordering::SeqCst);
}
Initialize a HashMap
at startup time:
#[ctor]
pub static STATIC_CTOR: HashMap<u32, String> = unsafe {
let mut m = HashMap::new();
for i in 0..100 {
m.insert(i, format!("x*100={}", i*100));
}
m
};
§Details
The #[ctor]
macro makes use of linker sections to ensure that a function
is run at startup time.
#[ctor]
unsafe fn my_init_fn() {
/* ... */
}
The above example translates into the following Rust code (approximately):
#[used]
#[cfg_attr(target_os = "linux", link_section = ".init_array")]
#[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")]
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
/* ... other platforms elided ... */
static INIT_FN: extern fn() = {
extern fn init_fn() { my_init_fn(); };
init_fn
};
For static
items, the macro generates a std::sync::OnceLock
that is
initialized at startup time.
#[ctor]
static FOO: HashMap<u32, String> = unsafe {
let mut m = HashMap::new();
for i in 0..100 {
m.insert(i, format!("x*100={}", i*100));
}
m
};
The above example translates into the following Rust code (approximately),
which eagerly initializes the HashMap
inside a OnceLock
at startup time:
static FOO: FooStatic = FooStatic { value: ::std::sync::OnceLock::new() };
struct FooStatic {
value: ::std::sync::OnceLock<HashMap<u32, String>>,
}
impl ::std::ops::Deref for FooStatic {
type Target = HashMap<u32, String>;
fn deref(&self) -> &Self::Target {
self.value.get_or_init(|| unsafe {
let mut m = HashMap::new();
for i in 0..100 {
m.insert(i, format!("x*100={}", i*100));
}
m
})
}
}
#[ctor]
unsafe fn init_foo_ctor() {
_ = &*FOO;
}