1#![recursion_limit = "256"]
2
3//! Procedural macro for defining global constructor/destructor functions.
4//!
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.
8//!
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.
13//!
14//! This library currently requires Rust > `1.31.0` at a minimum for the
15//! procedural macro support.
16
17// Code note:
18
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:
22
23// https://github.com/Alexpux/mingw-w64/blob/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-crt/crt/crtdll.c
24
25// In addition, OSX has removed support for section-based shutdown hooks after
26// warning about it for a number of years:
27
28// https://reviews.llvm.org/D45578
29
30extern crate proc_macro;
31extern crate syn;
32#[macro_use]
33extern crate quote;
34
35use proc_macro::TokenStream;
36
37/// Attributes required to mark a function as a constructor. This may be exposed in the future if we determine
38/// it to be stable.
39#[doc(hidden)]
40macro_rules! ctor_attributes {
41 () => {
42 // Linux/ELF: https://www.exploit-db.com/papers/13234
43
44 // Mac details: https://blog.timac.org/2016/0716-constructor-and-destructor-attributes/
45
46 // Why .CRT$XCU on Windows? https://www.cnblogs.com/sunkang/archive/2011/05/24/2055635.html
47 // 'I'=C init, 'C'=C++ init, 'P'=Pre-terminators and 'T'=Terminators
48 quote!(
49 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".init_array")]
50 #[cfg_attr(target_os = "freebsd", link_section = ".init_array")]
51 #[cfg_attr(target_os = "netbsd", link_section = ".init_array")]
52 #[cfg_attr(target_os = "openbsd", link_section = ".init_array")]
53 #[cfg_attr(target_os = "dragonfly", link_section = ".init_array")]
54 #[cfg_attr(target_os = "illumos", link_section = ".init_array")]
55 #[cfg_attr(target_os = "haiku", link_section = ".init_array")]
56 #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")]
57 #[cfg_attr(windows, link_section = ".CRT$XCU")]
58 )
59 };
60}
61
62/// Marks a function or static variable as a library/executable constructor.
63/// This uses OS-specific linker sections to call a specific function at
64/// load time.
65///
66/// Multiple startup functions/statics are supported, but the invocation order is not
67/// guaranteed.
68///
69/// # Examples
70///
71/// Print a startup message (using `libc_print` for safety):
72///
73/// ```rust
74/// # #![cfg_attr(feature="used_linker", feature(used_with_arg))]
75/// # extern crate ctor;
76/// # use ctor::*;
77/// use libc_print::std_name::println;
78///
79/// #[ctor]
80/// fn foo() {
81/// println!("Hello, world!");
82/// }
83///
84/// # fn main() {
85/// println!("main()");
86/// # }
87/// ```
88///
89/// Make changes to `static` variables:
90///
91/// ```rust
92/// # #![cfg_attr(feature="used_linker", feature(used_with_arg))]
93/// # extern crate ctor;
94/// # use ctor::*;
95/// # use std::sync::atomic::{AtomicBool, Ordering};
96/// static INITED: AtomicBool = AtomicBool::new(false);
97///
98/// #[ctor]
99/// fn foo() {
100/// INITED.store(true, Ordering::SeqCst);
101/// }
102/// ```
103///
104/// Initialize a `HashMap` at startup time:
105///
106/// ```rust
107/// # extern crate ctor;
108/// # use std::collections::HashMap;
109/// # use ctor::*;
110/// #[ctor]
111/// static STATIC_CTOR: HashMap<u32, String> = {
112/// let mut m = HashMap::new();
113/// for i in 0..100 {
114/// m.insert(i, format!("x*100={}", i*100));
115/// }
116/// m
117/// };
118///
119/// # pub fn main() {
120/// # assert_eq!(STATIC_CTOR.len(), 100);
121/// # assert_eq!(STATIC_CTOR[&20], "x*100=2000");
122/// # }
123/// ```
124///
125/// # Details
126///
127/// The `#[ctor]` macro makes use of linker sections to ensure that a
128/// function is run at startup time.
129///
130/// The above example translates into the following Rust code (approximately):
131///
132///```rust
133/// #[used]
134/// #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".init_array")]
135/// #[cfg_attr(target_os = "freebsd", link_section = ".init_array")]
136/// #[cfg_attr(target_os = "netbsd", link_section = ".init_array")]
137/// #[cfg_attr(target_os = "openbsd", link_section = ".init_array")]
138/// #[cfg_attr(target_os = "illumos", link_section = ".init_array")]
139/// #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")]
140/// #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
141/// static FOO: extern fn() = {
142/// #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
143/// extern fn foo() { /* ... */ };
144/// foo
145/// };
146/// ```
147#[proc_macro_attribute]
148pub fn ctor(_attribute: TokenStream, function: TokenStream) -> TokenStream {
149 let item: syn::Item = syn::parse_macro_input!(function);
150 if let syn::Item::Fn(function) = item {
151 validate_item("ctor", &function);
152
153 let syn::ItemFn {
154 attrs,
155 block,
156 vis,
157 sig:
158 syn::Signature {
159 ident,
160 unsafety,
161 constness,
162 abi,
163 ..
164 },
165 ..
166 } = function;
167
168 let ctor_ident =
169 syn::parse_str::<syn::Ident>(format!("{}___rust_ctor___ctor", ident).as_ref())
170 .expect("Unable to create identifier");
171
172 let tokens = ctor_attributes!();
173
174 let used = if cfg!(feature = "used_linker") {
175 quote!(#[used(linker)])
176 } else {
177 quote!(#[used])
178 };
179
180 let output = quote!(
181 #[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_vendor = "apple", windows)))]
182 compile_error!("#[ctor] is not supported on the current target");
183
184 #(#attrs)*
185 #vis #unsafety extern #abi #constness fn #ident() #block
186
187 #used
188 #[allow(non_upper_case_globals, non_snake_case)]
189 #[doc(hidden)]
190 #tokens
191 static #ctor_ident
192 :
193 unsafe extern "C" fn() -> usize =
194 {
195 #[allow(non_snake_case)]
196 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
197 unsafe extern "C" fn #ctor_ident() -> usize { #ident(); 0 };
198 #ctor_ident
199 }
200 ;
201 );
202
203 // eprintln!("{}", output);
204
205 output.into()
206 } else if let syn::Item::Static(var) = item {
207 let syn::ItemStatic {
208 ident,
209 mutability,
210 expr,
211 attrs,
212 ty,
213 vis,
214 ..
215 } = var;
216
217 if matches!(mutability, syn::StaticMutability::Mut(_)) {
218 panic!("#[ctor]-annotated static objects must not be mutable");
219 }
220
221 if attrs.iter().any(|attr| {
222 attr.path()
223 .segments
224 .iter()
225 .any(|segment| segment.ident == "no_mangle")
226 }) {
227 panic!("#[ctor]-annotated static objects do not support #[no_mangle]");
228 }
229
230 let ctor_ident =
231 syn::parse_str::<syn::Ident>(format!("{}___rust_ctor___ctor", ident).as_ref())
232 .expect("Unable to create identifier");
233 let storage_ident =
234 syn::parse_str::<syn::Ident>(format!("{}___rust_ctor___storage", ident).as_ref())
235 .expect("Unable to create identifier");
236
237 let tokens = ctor_attributes!();
238 let output = quote!(
239 #[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_vendor = "apple", windows)))]
240 compile_error!("#[ctor] is not supported on the current target");
241
242 // This is mutable, but only by this macro code!
243 static mut #storage_ident: Option<#ty> = None;
244
245 #[doc(hidden)]
246 #[allow(non_camel_case_types)]
247 #vis struct #ident<T> {
248 _data: ::core::marker::PhantomData<T>
249 }
250
251 #(#attrs)*
252 #vis static #ident: #ident<#ty> = #ident {
253 _data: ::core::marker::PhantomData::<#ty>
254 };
255
256 impl ::core::ops::Deref for #ident<#ty> {
257 type Target = #ty;
258 fn deref(&self) -> &'static #ty {
259 unsafe {
260 #storage_ident.as_ref().unwrap()
261 }
262 }
263 }
264
265 #[used]
266 #[allow(non_upper_case_globals)]
267 #tokens
268 static #ctor_ident
269 :
270 unsafe extern "C" fn() = {
271 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
272 extern "C" fn initer() {
273 let val = Some(#expr);
274 // Only write the value to `storage_ident` on startup
275 unsafe {
276 #storage_ident = val;
277 }
278 }; initer }
279 ;
280 );
281
282 // eprintln!("{}", output);
283
284 output.into()
285 } else {
286 panic!("#[ctor] items must be functions or static globals");
287 }
288}
289
290/// Marks a function as a library/executable destructor. This uses OS-specific
291/// linker sections to call a specific function at termination time.
292///
293/// Multiple shutdown functions are supported, but the invocation order is not
294/// guaranteed.
295///
296/// `sys_common::at_exit` is usually a better solution for shutdown handling, as
297/// it allows you to use `stdout` in your handlers.
298///
299/// ```rust
300/// # extern crate ctor;
301/// # use ctor::*;
302/// # fn main() {}
303///
304/// #[dtor]
305/// fn shutdown() {
306/// /* ... */
307/// }
308/// ```
309#[proc_macro_attribute]
310pub fn dtor(_attribute: TokenStream, function: TokenStream) -> TokenStream {
311 let function: syn::ItemFn = syn::parse_macro_input!(function);
312 validate_item("dtor", &function);
313
314 let syn::ItemFn {
315 attrs,
316 block,
317 vis,
318 sig:
319 syn::Signature {
320 ident,
321 unsafety,
322 constness,
323 abi,
324 ..
325 },
326 ..
327 } = function;
328
329 let mod_ident = syn::parse_str::<syn::Ident>(format!("{}___rust_dtor___mod", ident).as_ref())
330 .expect("Unable to create identifier");
331
332 let dtor_ident = syn::parse_str::<syn::Ident>(format!("{}___rust_dtor___dtor", ident).as_ref())
333 .expect("Unable to create identifier");
334
335 let tokens = ctor_attributes!();
336 let output = quote!(
337 #[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_vendor = "apple", windows)))]
338 compile_error!("#[dtor] is not supported on the current target");
339
340 #(#attrs)*
341 #vis #unsafety extern #abi #constness fn #ident() #block
342
343 mod #mod_ident {
344 use super::#ident;
345
346 // Note that we avoid a dep on the libc crate by linking directly to atexit functions
347
348 #[cfg(not(target_vendor = "apple"))]
349 #[inline(always)]
350 unsafe fn do_atexit(cb: unsafe extern fn()) {
351 extern "C" {
352 fn atexit(cb: unsafe extern fn());
353 }
354 atexit(cb);
355 }
356
357 // For platforms that have __cxa_atexit, we register the dtor as scoped to dso_handle
358 #[cfg(target_vendor = "apple")]
359 #[inline(always)]
360 unsafe fn do_atexit(cb: unsafe extern fn(_: *const u8)) {
361 extern "C" {
362 static __dso_handle: *const u8;
363 fn __cxa_atexit(cb: unsafe extern fn(_: *const u8), arg: *const u8, dso_handle: *const u8);
364 }
365 __cxa_atexit(cb, core::ptr::null(), __dso_handle);
366 }
367
368 #[used]
369 #[allow(non_upper_case_globals)]
370 #tokens
371 static __dtor_export
372 :
373 unsafe extern "C" fn() =
374 {
375 #[cfg(not(target_vendor = "apple"))]
376 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.exit")]
377 unsafe extern "C" fn #dtor_ident() { #ident() };
378 #[cfg(target_vendor = "apple")]
379 unsafe extern "C" fn #dtor_ident(_: *const u8) { #ident() };
380 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
381 unsafe extern fn __dtor_atexit() {
382 do_atexit(#dtor_ident);
383 };
384 __dtor_atexit
385 };
386 }
387 );
388
389 // eprintln!("{}", output);
390
391 output.into()
392}
393
394fn validate_item(typ: &str, item: &syn::ItemFn) {
395 let syn::ItemFn { vis: &Visibility, sig: &Signature, .. } = item;
396
397 // Ensure that visibility modifier is not present
398 match vis {
399 syn::Visibility::Inherited => {}
400 _ => panic!("#[{}] methods must not have visibility modifiers", typ),
401 }
402
403 // No parameters allowed
404 if !sig.inputs.is_empty() {
405 panic!("#[{}] methods may not have parameters", typ);
406 }
407
408 // No return type allowed
409 match sig.output {
410 syn::ReturnType::Default => {}
411 _ => panic!("#[{}] methods must not have return types", typ),
412 }
413}
414