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