]>
git.proxmox.com Git - rustc.git/blob - vendor/ctor/src/lib.rs
1 #![recursion_limit = "256"]
3 //! Procedural macro for defining global constructor/destructor functions.
5 //! This provides module initialization/teardown functions for Rust (like
6 //! `__attribute__((constructor))` in C/C++) for Linux, OSX, and Windows via
7 //! the `#[ctor]` and `#[dtor]` macros.
9 //! This library works and is regularly tested on Linux, OSX and Windows, with both `+crt-static` and `-crt-static`.
10 //! Other platforms are supported but not tested as part of the automatic builds. This library will also work as expected in both
11 //! `bin` and `cdylib` outputs, ie: the `ctor` and `dtor` will run at executable or library
12 //! startup/shutdown respectively.
14 //! This library currently requires Rust > `1.31.0` at a minimum for the
15 //! procedural macro support.
19 // You might wonder why we don't use `__attribute__((destructor))`/etc for
20 // dtor. Unfortunately mingw doesn't appear to properly support section-based
21 // hooks for shutdown, ie:
23 // https://github.com/Alexpux/mingw-w64/blob/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-crt/crt/crtdll.c
25 // In addition, OSX has removed support for section-based shutdown hooks after
26 // warning about it for a number of years:
28 // https://reviews.llvm.org/D45578
30 extern crate proc_macro
;
35 use proc_macro
::{TokenStream}
;
37 /// Attributes required to mark a function as a constructor. This may be exposed in the future if we determine
40 macro_rules
! ctor_attributes
{
43 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".init_array")]
44 #[cfg_attr(target_os = "freebsd", link_section = ".init_array")]
45 #[cfg_attr(target_os = "netbsd", link_section = ".init_array")]
46 #[cfg_attr(target_os = "openbsd", link_section = ".init_array")]
47 #[cfg_attr(target_os = "dragonfly", link_section = ".init_array")]
48 #[cfg_attr(target_os = "illumos", link_section = ".init_array")]
49 #[cfg_attr(target_os = "haiku", link_section = ".init_array")]
50 #[cfg_attr(any(target_os = "macos", target_os = "ios"), link_section = "__DATA,__mod_init_func")]
51 #[cfg_attr(windows, link_section = ".CRT$XCU")]
56 /// Marks a function or static variable as a library/executable constructor.
57 /// This uses OS-specific linker sections to call a specific function at
60 /// Multiple startup functions/statics are supported, but the invocation order is not
65 /// Print a startup message (using `libc_print` for safety):
68 /// # extern crate ctor;
70 /// use libc_print::std_name::println;
74 /// println!("Hello, world!");
78 /// println!("main()");
82 /// Make changes to `static` variables:
85 /// # extern crate ctor;
87 /// # use std::sync::atomic::{AtomicBool, Ordering};
88 /// static INITED: AtomicBool = AtomicBool::new(false);
92 /// INITED.store(true, Ordering::SeqCst);
96 /// Initialize a `HashMap` at startup time:
99 /// # extern crate ctor;
100 /// # use std::collections::HashMap;
103 /// static STATIC_CTOR: HashMap<u32, String> = {
104 /// let mut m = HashMap::new();
105 /// for i in 0..100 {
106 /// m.insert(i, format!("x*100={}", i*100));
111 /// # pub fn main() {
112 /// # assert_eq!(STATIC_CTOR.len(), 100);
113 /// # assert_eq!(STATIC_CTOR[&20], "x*100=2000");
119 /// The `#[ctor]` macro makes use of linker sections to ensure that a
120 /// function is run at startup time.
122 /// The above example translates into the following Rust code (approximately):
126 /// #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".init_array")]
127 /// #[cfg_attr(target_os = "freebsd", link_section = ".init_array")]
128 /// #[cfg_attr(target_os = "netbsd", link_section = ".init_array")]
129 /// #[cfg_attr(target_os = "openbsd", link_section = ".init_array")]
130 /// #[cfg_attr(target_os = "illumos", link_section = ".init_array")]
131 /// #[cfg_attr(any(target_os = "macos", target_os = "ios"), link_section = "__DATA,__mod_init_func")]
132 /// #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
133 /// static FOO: extern fn() = {
134 /// #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
135 /// extern fn foo() { /* ... */ };
139 #[proc_macro_attribute]
140 pub fn ctor(_attribute
: TokenStream
, function
: TokenStream
) -> TokenStream
{
141 let item
: syn
::Item
= syn
::parse_macro_input
!(function
);
142 if let syn
::Item
::Fn(function
) = item
{
143 validate_item("ctor", &function
);
160 // Linux/ELF: https://www.exploit-db.com/papers/13234
162 // Mac details: https://blog.timac.org/2016/0716-constructor-and-destructor-attributes/
164 // Why .CRT$XCU on Windows? https://www.cnblogs.com/sunkang/archive/2011/05/24/2055635.html
165 // 'I'=C init, 'C'=C++ init, 'P'=Pre-terminators and 'T'=Terminators
168 syn
::parse_str
::<syn
::Ident
>(format
!("{}___rust_ctor___ctor", ident
).as_ref())
169 .expect("Unable to create identifier");
171 let tokens
= ctor_attributes
!();
173 #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", target_os = "illumos", target_os = "haiku", target_os = "macos", target_os = "ios", windows)))]
174 compile_error
!("#[ctor] is not supported on the current target");
177 #vis #unsafety extern #abi #constness fn #ident() #block
180 #[allow(non_upper_case_globals)]
185 unsafe extern "C" fn() =
187 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
188 unsafe extern "C" fn #ctor_ident() { #ident() };
194 // eprintln!("{}", output);
197 } else if let syn
::Item
::Static(var
) = item
{
198 let syn
::ItemStatic
{
208 if mutability
.is_some() {
209 panic
!("#[ctor]-annotated static objects must not be mutable");
212 if attrs
.iter().any(|attr
| {
216 .any(|segment
| segment
.ident
== "no_mangle")
218 panic
!("#[ctor]-annotated static objects do not support #[no_mangle]");
222 syn
::parse_str
::<syn
::Ident
>(format
!("{}___rust_ctor___ctor", ident
).as_ref())
223 .expect("Unable to create identifier");
225 syn
::parse_str
::<syn
::Ident
>(format
!("{}___rust_ctor___storage", ident
).as_ref())
226 .expect("Unable to create identifier");
228 let tokens
= ctor_attributes
!();
230 #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", target_os = "illumos", target_os = "haiku", target_os = "macos", target_os = "ios", windows)))]
231 compile_error
!("#[ctor] is not supported on the current target");
233 // This is mutable, but only by this macro code!
234 static mut #storage_ident: Option<#ty> = None;
237 #[allow(non_camel_case_types)]
238 #vis struct #ident<T> {
239 _data
: core
::marker
::PhantomData
<T
>
243 #vis static #ident: #ident<#ty> = #ident {
244 _data
: core
::marker
::PhantomData
::<#ty>
247 impl core
::ops
::Deref
for #ident<#ty> {
249 fn deref(&self) -> &'
static #ty {
251 #storage_ident.as_ref().unwrap()
257 #[allow(non_upper_case_globals)]
261 unsafe extern "C" fn() = {
262 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
263 unsafe extern "C" fn initer() {
264 #storage_ident = Some(#expr);
269 // eprintln!("{}", output);
273 panic
!("#[ctor] items must be functions or static globals");
277 /// Marks a function as a library/executable destructor. This uses OS-specific
278 /// linker sections to call a specific function at termination time.
280 /// Multiple shutdown functions are supported, but the invocation order is not
283 /// `sys_common::at_exit` is usually a better solution for shutdown handling, as
284 /// it allows you to use `stdout` in your handlers.
287 /// # extern crate ctor;
296 #[proc_macro_attribute]
297 pub fn dtor(_attribute
: TokenStream
, function
: TokenStream
) -> TokenStream
{
298 let function
: syn
::ItemFn
= syn
::parse_macro_input
!(function
);
299 validate_item("dtor", &function
);
316 let mod_ident
= syn
::parse_str
::<syn
::Ident
>(format
!("{}___rust_dtor___mod", ident
).as_ref())
317 .expect("Unable to create identifier");
319 let dtor_ident
= syn
::parse_str
::<syn
::Ident
>(format
!("{}___rust_dtor___dtor", ident
).as_ref())
320 .expect("Unable to create identifier");
322 let tokens
= ctor_attributes
!();
324 #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", target_os = "illumos", target_os = "haiku", target_os = "macos", target_os = "ios", windows)))]
325 compile_error
!("#[dtor] is not supported on the current target");
328 #vis #unsafety extern #abi #constness fn #ident() #block
333 // Note that we avoid a dep on the libc crate by linking directly to atexit functions
335 #[cfg(not(any(target_os = "macos", target_os = "ios")))]
337 unsafe fn do_atexit(cb
: unsafe extern fn()) {
339 fn atexit(cb
: unsafe extern fn());
344 // For platforms that have __cxa_atexit, we register the dtor as scoped to dso_handle
345 #[cfg(any(target_os = "macos", target_os = "ios"))]
347 unsafe fn do_atexit(cb
: unsafe extern fn()) {
349 static __dso_handle
: *const u8;
350 fn __cxa_atexit(cb
: unsafe extern fn(), arg
: *const u8, dso_handle
: *const u8);
352 __cxa_atexit(cb
, std
::ptr
::null(), __dso_handle
);
356 #[allow(non_upper_case_globals)]
360 unsafe extern "C" fn() =
362 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.exit")]
363 unsafe extern "C" fn #dtor_ident() { #ident() };
364 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
365 unsafe extern fn __dtor_atexit() {
366 do_atexit(#dtor_ident);
373 // eprintln!("{}", output);
378 fn validate_item(typ
: &str, item
: &syn
::ItemFn
) {
379 let syn
::ItemFn { vis, sig, .. }
= item
;
381 // Ensure that visibility modifier is not present
383 syn
::Visibility
::Inherited
=> {}
384 _
=> panic
!("#[{}] methods must not have visibility modifiers", typ
),
387 // No parameters allowed
388 if !sig
.inputs
.is_empty() {
389 panic
!("#[{}] methods may not have parameters", typ
);
392 // No return type allowed
394 syn
::ReturnType
::Default
=> {}
395 _
=> panic
!("#[{}] methods must not have return types", typ
),