]> git.proxmox.com Git - rustc.git/blob - vendor/ctor/src/lib.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / vendor / ctor / src / lib.rs
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
30 extern crate proc_macro;
31 extern crate syn;
32 #[macro_use]
33 extern crate quote;
34
35 use 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)]
40 macro_rules! ctor_attributes {
41 () => {
42 quote!(
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")]
52 )
53 };
54 }
55
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
58 /// load time.
59 ///
60 /// Multiple startup functions/statics are supported, but the invocation order is not
61 /// guaranteed.
62 ///
63 /// # Examples
64 ///
65 /// Print a startup message (using `libc_print` for safety):
66 ///
67 /// ```rust
68 /// # extern crate ctor;
69 /// # use ctor::*;
70 /// use libc_print::std_name::println;
71 ///
72 /// #[ctor]
73 /// fn foo() {
74 /// println!("Hello, world!");
75 /// }
76 ///
77 /// # fn main() {
78 /// println!("main()");
79 /// # }
80 /// ```
81 ///
82 /// Make changes to `static` variables:
83 ///
84 /// ```rust
85 /// # extern crate ctor;
86 /// # use ctor::*;
87 /// # use std::sync::atomic::{AtomicBool, Ordering};
88 /// static INITED: AtomicBool = AtomicBool::new(false);
89 ///
90 /// #[ctor]
91 /// fn foo() {
92 /// INITED.store(true, Ordering::SeqCst);
93 /// }
94 /// ```
95 ///
96 /// Initialize a `HashMap` at startup time:
97 ///
98 /// ```rust
99 /// # extern crate ctor;
100 /// # use std::collections::HashMap;
101 /// # use ctor::*;
102 /// #[ctor]
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));
107 /// }
108 /// m
109 /// };
110 ///
111 /// # pub fn main() {
112 /// # assert_eq!(STATIC_CTOR.len(), 100);
113 /// # assert_eq!(STATIC_CTOR[&20], "x*100=2000");
114 /// # }
115 /// ```
116 ///
117 /// # Details
118 ///
119 /// The `#[ctor]` macro makes use of linker sections to ensure that a
120 /// function is run at startup time.
121 ///
122 /// The above example translates into the following Rust code (approximately):
123 ///
124 ///```rust
125 /// #[used]
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() { /* ... */ };
136 /// foo
137 /// };
138 /// ```
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);
144
145 let syn::ItemFn {
146 attrs,
147 block,
148 vis,
149 sig:
150 syn::Signature {
151 ident,
152 unsafety,
153 constness,
154 abi,
155 ..
156 },
157 ..
158 } = function;
159
160 // Linux/ELF: https://www.exploit-db.com/papers/13234
161
162 // Mac details: https://blog.timac.org/2016/0716-constructor-and-destructor-attributes/
163
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
166
167 let ctor_ident =
168 syn::parse_str::<syn::Ident>(format!("{}___rust_ctor___ctor", ident).as_ref())
169 .expect("Unable to create identifier");
170
171 let tokens = ctor_attributes!();
172 let output = quote!(
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");
175
176 #(#attrs)*
177 #vis #unsafety extern #abi #constness fn #ident() #block
178
179 #[used]
180 #[allow(non_upper_case_globals)]
181 #[doc(hidden)]
182 #tokens
183 static #ctor_ident
184 :
185 unsafe extern "C" fn() =
186 {
187 #[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
188 unsafe extern "C" fn #ctor_ident() { #ident() };
189 #ctor_ident
190 }
191 ;
192 );
193
194 // eprintln!("{}", output);
195
196 output.into()
197 } else if let syn::Item::Static(var) = item {
198 let syn::ItemStatic {
199 ident,
200 mutability,
201 expr,
202 attrs,
203 ty,
204 vis,
205 ..
206 } = var;
207
208 if mutability.is_some() {
209 panic!("#[ctor]-annotated static objects must not be mutable");
210 }
211
212 if attrs.iter().any(|attr| {
213 attr.path
214 .segments
215 .iter()
216 .any(|segment| segment.ident == "no_mangle")
217 }) {
218 panic!("#[ctor]-annotated static objects do not support #[no_mangle]");
219 }
220
221 let ctor_ident =
222 syn::parse_str::<syn::Ident>(format!("{}___rust_ctor___ctor", ident).as_ref())
223 .expect("Unable to create identifier");
224 let storage_ident =
225 syn::parse_str::<syn::Ident>(format!("{}___rust_ctor___storage", ident).as_ref())
226 .expect("Unable to create identifier");
227
228 let tokens = ctor_attributes!();
229 let output = quote!(
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");
232
233 // This is mutable, but only by this macro code!
234 static mut #storage_ident: Option<#ty> = None;
235
236 #[doc(hidden)]
237 #[allow(non_camel_case_types)]
238 #vis struct #ident<T> {
239 _data: core::marker::PhantomData<T>
240 }
241
242 #(#attrs)*
243 #vis static #ident: #ident<#ty> = #ident {
244 _data: core::marker::PhantomData::<#ty>
245 };
246
247 impl core::ops::Deref for #ident<#ty> {
248 type Target = #ty;
249 fn deref(&self) -> &'static #ty {
250 unsafe {
251 #storage_ident.as_ref().unwrap()
252 }
253 }
254 }
255
256 #[used]
257 #[allow(non_upper_case_globals)]
258 #tokens
259 static #ctor_ident
260 :
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);
265 }; initer }
266 ;
267 );
268
269 // eprintln!("{}", output);
270
271 output.into()
272 } else {
273 panic!("#[ctor] items must be functions or static globals");
274 }
275 }
276
277 /// Marks a function as a library/executable destructor. This uses OS-specific
278 /// linker sections to call a specific function at termination time.
279 ///
280 /// Multiple shutdown functions are supported, but the invocation order is not
281 /// guaranteed.
282 ///
283 /// `sys_common::at_exit` is usually a better solution for shutdown handling, as
284 /// it allows you to use `stdout` in your handlers.
285 ///
286 /// ```rust
287 /// # extern crate ctor;
288 /// # use ctor::*;
289 /// # fn main() {}
290 ///
291 /// #[dtor]
292 /// fn shutdown() {
293 /// /* ... */
294 /// }
295 /// ```
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);
300
301 let syn::ItemFn {
302 attrs,
303 block,
304 vis,
305 sig:
306 syn::Signature {
307 ident,
308 unsafety,
309 constness,
310 abi,
311 ..
312 },
313 ..
314 } = function;
315
316 let mod_ident = syn::parse_str::<syn::Ident>(format!("{}___rust_dtor___mod", ident).as_ref())
317 .expect("Unable to create identifier");
318
319 let dtor_ident = syn::parse_str::<syn::Ident>(format!("{}___rust_dtor___dtor", ident).as_ref())
320 .expect("Unable to create identifier");
321
322 let tokens = ctor_attributes!();
323 let output = quote!(
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");
326
327 #(#attrs)*
328 #vis #unsafety extern #abi #constness fn #ident() #block
329
330 mod #mod_ident {
331 use super::#ident;
332
333 // Note that we avoid a dep on the libc crate by linking directly to atexit functions
334
335 #[cfg(not(any(target_os = "macos", target_os = "ios")))]
336 #[inline(always)]
337 unsafe fn do_atexit(cb: unsafe extern fn()) {
338 extern "C" {
339 fn atexit(cb: unsafe extern fn());
340 }
341 atexit(cb);
342 }
343
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"))]
346 #[inline(always)]
347 unsafe fn do_atexit(cb: unsafe extern fn()) {
348 extern "C" {
349 static __dso_handle: *const u8;
350 fn __cxa_atexit(cb: unsafe extern fn(), arg: *const u8, dso_handle: *const u8);
351 }
352 __cxa_atexit(cb, std::ptr::null(), __dso_handle);
353 }
354
355 #[used]
356 #[allow(non_upper_case_globals)]
357 #tokens
358 static __dtor_export
359 :
360 unsafe extern "C" fn() =
361 {
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);
367 };
368 __dtor_atexit
369 };
370 }
371 );
372
373 // eprintln!("{}", output);
374
375 output.into()
376 }
377
378 fn validate_item(typ: &str, item: &syn::ItemFn) {
379 let syn::ItemFn { vis, sig, .. } = item;
380
381 // Ensure that visibility modifier is not present
382 match vis {
383 syn::Visibility::Inherited => {}
384 _ => panic!("#[{}] methods must not have visibility modifiers", typ),
385 }
386
387 // No parameters allowed
388 if !sig.inputs.is_empty() {
389 panic!("#[{}] methods may not have parameters", typ);
390 }
391
392 // No return type allowed
393 match sig.output {
394 syn::ReturnType::Default => {}
395 _ => panic!("#[{}] methods must not have return types", typ),
396 }
397 }