ctor/macros/
mod.rs

1#[doc(hidden)]
2#[allow(unused)]
3pub mod __support {
4    /// Return type for the constructor. Why is this needed?
5    ///
6    /// On Windows, `.CRT$XIA` … `.CRT$XIZ` constructors are required to return a `usize` value. We don't know
7    /// if the user is putting this function into a retval-requiring section or a non-retval section, so we
8    /// just return a `usize` value which is always valid and just ignored if not needed.
9    ///
10    /// Miri is pedantic about this, so we just return `()` if we're running under miri.
11    ///
12    /// See <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/initterm-initterm-e?view=msvc-170>
13    #[cfg(all(windows, not(miri)))]
14    pub type CtorRetType = usize;
15    #[cfg(any(not(windows), miri))]
16    pub type CtorRetType = ();
17
18    pub use crate::__ctor_call as ctor_call;
19    pub use crate::__ctor_entry as ctor_entry;
20    pub use crate::__ctor_link_section as ctor_link_section;
21    pub use crate::__ctor_link_section_attr as ctor_link_section_attr;
22    pub use crate::__ctor_parse as ctor_parse;
23    pub use crate::__dtor_entry as dtor_entry;
24    pub use crate::__dtor_parse as dtor_parse;
25    pub use crate::__if_has_feature as if_has_feature;
26    pub use crate::__if_unsafe as if_unsafe;
27    pub use crate::__get_priority as get_priority;
28    pub use crate::__unify_features as unify_features;
29}
30
31/// Parse a `#[ctor]`-annotated item as if it were a proc-macro.
32///
33/// This macro supports both the `fn` and `static` forms of the `#[ctor]`
34/// attribute, including attribute parameters.
35///
36/// ```rust
37/// # #[cfg(any())] // disabled due to code sharing between ctor/dtor
38/// # mod test {
39/// # use ctor::declarative::ctor;
40/// ctor! {
41///   /// Create a ctor with a link section
42/// # #[cfg(any())]
43///   #[ctor(link_section = ".ctors")]
44///   unsafe fn foo() { /* ... */ }
45/// }
46///
47/// ctor! {
48///   #[ctor]
49/// # #[cfg(any())]
50///   static FOO: std::collections::HashMap<u32, String> = unsafe {
51///     let mut m = std::collections::HashMap::new();
52///     m.insert(1, "foo".to_string());
53///     m
54///   };
55/// }
56/// # }
57/// ```
58#[doc(hidden)]
59#[macro_export]
60macro_rules! __ctor_parse {
61    (#[ctor $(($($meta:tt)*))?] $(#[$imeta:meta])* pub ( $($extra:tt)* ) $($item:tt)*) => {
62        $crate::__support::unify_features!(next=$crate::__support::ctor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[pub($($extra)*)], item=$($item)*);
63    };
64    (#[ctor $(($($meta:tt)*))?] $(#[$imeta:meta])* pub $($item:tt)*) => {
65        $crate::__support::unify_features!(next=$crate::__support::ctor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[pub], item=$($item)*);
66    };
67    (#[ctor $(($($meta:tt)*))?] $(#[$imeta:meta])* fn $($item:tt)*) => {
68        $crate::__support::unify_features!(next=$crate::__support::ctor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[], item=fn $($item)*);
69    };
70    (#[ctor $(($($meta:tt)*))?] $(#[$imeta:meta])* unsafe $($item:tt)*) => {
71        $crate::__support::unify_features!(next=$crate::__support::ctor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[], item=unsafe $($item)*);
72    };
73    (#[ctor $(($($meta:tt)*))?] $(#[$imeta:meta])* static $($item:tt)*) => {
74        $crate::__support::unify_features!(next=$crate::__support::ctor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[], item=static $($item)*);
75    };
76    // Reorder attributes that aren't `#[ctor]`
77    (#[$imeta:meta] $($rest:tt)*) => {
78        $crate::__support::ctor_parse!(__reorder__(#[$imeta],), $($rest)*);
79    };
80    (__reorder__($(#[$imeta:meta],)*), #[ctor $(($($meta:tt)*))?] $($rest:tt)*) => {
81        $crate::__support::ctor_parse!(#[ctor $(($($meta)*))?] $(#[$imeta])* $($rest)*);
82    };
83    (__reorder__($(#[$imeta:meta],)*), #[$imeta2:meta] $($rest:tt)*) => {
84        $crate::__support::ctor_parse!(__reorder__($(#[$imeta],)*#[$imeta2],), $($rest)*);
85    };
86}
87
88/// Parse a `#[dtor]`-annotated item as if it were a proc-macro.
89///
90/// ```rust
91/// # #[cfg(any())] mod test {
92/// dtor! {
93///   #[dtor]
94///   unsafe fn foo() { /* ... */ }
95/// }
96/// # }
97#[doc(hidden)]
98#[macro_export]
99macro_rules! __dtor_parse {
100    (#[dtor $(($($meta:tt)*))?] $(#[$imeta:meta])* pub ( $($extra:tt)* ) $($item:tt)*) => {
101        $crate::__support::unify_features!(next=$crate::__support::dtor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[pub($($extra)*)], item=$($item)*);
102    };
103    (#[dtor $(($($meta:tt)*))?] $(#[$imeta:meta])* pub $($item:tt)*) => {
104        $crate::__support::unify_features!(next=$crate::__support::dtor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[pub], item=$($item)*);
105    };
106    (#[dtor $(($($meta:tt)*))?] $(#[$imeta:meta])* fn $($item:tt)*) => {
107        $crate::__support::unify_features!(next=$crate::__support::dtor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[], item=fn $($item)*);
108    };
109    (#[dtor $(($($meta:tt)*))?] $(#[$imeta:meta])* unsafe $($item:tt)*) => {
110        $crate::__support::unify_features!(next=$crate::__support::dtor_entry, meta=[$($($meta)*)?], features=[], imeta=$(#[$imeta])*, vis=[], item=unsafe $($item)*);
111    };
112    // Reorder attributes that aren't `#[dtor]`
113    (#[$imeta:meta] $($rest:tt)*) => {
114        $crate::__support::dtor_parse!(__reorder__(#[$imeta],), $($rest)*);
115    };
116    (__reorder__($(#[$imeta:meta],)*), #[dtor $(($($meta:tt)*))?] $($rest:tt)*) => {
117        $crate::__support::dtor_parse!(#[dtor $(($($meta)*))?] $(#[$imeta])* $($rest)*);
118    };
119    (__reorder__($(#[$imeta:meta],)*), #[$imeta2:meta] $($rest:tt)*) => {
120        $crate::__support::dtor_parse!(__reorder__($(#[$imeta],)*#[$imeta2],), $($rest)*);
121    };
122}
123
124/// A macro that generates the appropriate feature extraction macros.
125macro_rules! declare_features {
126    ( $(#[doc = $doc1:literal])* crate = $crate_features:tt; $(#[doc = $doc2:literal])* attr = $attrs:tt; ) => {
127        declare_features!( __ crate $crate_features );
128    };
129
130    ( __ crate [$( 
131        $( #[doc = $doc:literal] )*
132        $feature_name:ident $feature_name_str:literal = $feature_include_macro:ident ; 
133    )*] ) => {
134        /// # Crate features
135        ///
136        $(
137            #[doc = concat!("<code>", stringify!($feature_name), "</code>: ")]
138            $( #[doc = $doc] )*
139            #[doc = "\n"]
140        )*
141        pub mod features {
142        }
143
144        $(
145        #[doc(hidden)]
146        #[macro_export]
147        #[cfg(feature = $feature_name_str)]
148        macro_rules! $feature_include_macro {
149            ($true:item $false:item) => {
150                $true
151            };
152        }
153        
154        #[doc(hidden)]
155        #[macro_export]
156        #[cfg(not(feature = $feature_name_str))]
157        macro_rules! $feature_include_macro {
158            ($true:item $false:item) => {
159                $false
160            };
161        }
162        )*
163    };
164}
165
166declare_features!(
167    /// Crate features: name/name as string/include macro.
168    crate = [
169        /// Enable support for the standard library. This is required for static ctor variables, but not for functions.
170        std "std" = __include_std_feature;
171        /// Mark all ctor functions with `used(linker)`.
172        used_linker "used_linker" = __include_used_linker_feature;
173        /// Enable support for the proc-macro `#[ctor]` and `#[dtor]` attributes.
174        proc_macro "proc_macro" = __include_proc_macro_feature;
175        /// Do not warn when a ctor or dtor is missing the `unsafe` keyword.
176        __no_warn_on_missing_unsafe "__no_warn_on_missing_unsafe" = __include_no_warn_on_missing_unsafe_feature;
177    ];
178
179    /// Attributes.
180    attr = [
181        /// Marks a ctor/dtor as unsafe. This will become a warning in 1.0.
182        unsafe = [unsafe];
183        /// Place the initialization function pointer in a custom link section. This may cause the initialization function
184        /// to fail to run or run earlier or later than other `ctor` functions.
185        link_section = [link_section($section:literal)];
186        /// Specify a custom crate path for the `ctor` crate. Used when re-exporting the ctor macro.
187        crate_path = [crate_path = $path:path];
188        /// Make the ctor function anonymous.
189        anonymous = [anonymous];
190        /// Mark this function with `used(linker)`.
191        used_linker = [used(linker)];
192        /// Set the ctor priority to a given value.
193        priority = [priority = $priority:literal];
194    ];
195);
196
197/// Extract #[ctor/dtor] attribute parameters and crate features and turn them
198/// into a unified feature array.
199///
200/// Supported attributes:
201///
202/// - `used(linker)` -> crate feature: `used_linker`
203/// - `std` -> crate feature: `std`
204/// - `link_section = ...` -> feature: `(link_section = ...)`
205/// - `crate_path = ...` -> feature: `(crate_path = ...)`
206#[doc(hidden)]
207#[macro_export]
208macro_rules! __unify_features {
209    // Entry
210    (next=$next_macro:path, meta=[$($meta:tt)*], features=[$($features:tt)*], $($rest:tt)*) => {
211        $crate::__support::unify_features!(std, next=$next_macro, meta=[$($meta)*], features=[$($features)*], $($rest)*);
212    };
213
214    // Add std feature if cfg(feature="std")
215    (std, next=$next_macro:path, meta=[$($meta:tt)*], features=[$($features:tt)*], $($rest:tt)*) => {
216        $crate::__include_std_feature!(
217            $crate::__support::unify_features!(used_linker, next=$next_macro, meta=[$($meta)*], features=[std,$($features)*], $($rest)*);
218            $crate::__support::unify_features!(used_linker, next=$next_macro, meta=[$($meta)*], features=[$($features)*], $($rest)*);
219        );
220    };
221
222    // Add used_linker feature if cfg(feature="used_linker")
223    (used_linker, next=$next_macro:path, meta=[$($meta:tt)*], features=[$($features:tt)*], $($rest:tt)*) => {
224        $crate::__include_used_linker_feature!(
225            $crate::__support::unify_features!(__no_warn_on_missing_unsafe, next=$next_macro, meta=[$($meta)*], features=[used_linker,$($features)*], $($rest)*);
226            $crate::__support::unify_features!(__no_warn_on_missing_unsafe, next=$next_macro, meta=[$($meta)*], features=[$($features)*], $($rest)*);
227        );
228    };
229    // Add __no_warn_on_missing_unsafe feature if cfg(feature="__no_warn_on_missing_unsafe")
230    (__no_warn_on_missing_unsafe, next=$next_macro:path, meta=[$($meta:tt)*], features=[$($features:tt)*], $($rest:tt)*) => {
231        $crate::__include_no_warn_on_missing_unsafe_feature!(
232            $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($meta)*], features=[__no_warn_on_missing_unsafe,$($features)*], $($rest)*);
233            $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($meta)*], features=[$($features)*], $($rest)*);
234        );
235    };
236
237    // Parse meta into features
238    (continue, next=$next_macro:path, meta=[used(linker) $(, $($meta:tt)* )?], features=[$($features:tt)*], $($rest:tt)*) => {
239        $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($($meta)*)?], features=[used_linker,$($features)*], $($rest)*);
240    };
241    (continue, next=$next_macro:path, meta=[link_section = $section:tt $(, $($meta:tt)* )?], features=[$($features:tt)*], $($rest:tt)*) => {
242        $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($($meta)*)?], features=[(link_section=($section)),$($features)*], $($rest)*);
243    };
244    (continue, next=$next_macro:path, meta=[crate_path = $path:path $(, $($meta:tt)* )?], features=[$($features:tt)*], $($rest:tt)*) => {
245        $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($($meta)*)?], features=[(crate_path=$path),$($features)*], $($rest)*);
246    };
247    (continue, next=$next_macro:path, meta=[anonymous $(, $($meta:tt)* )?], features=[$($features:tt)*], $($rest:tt)*) => {
248        $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($($meta)*)?], features=[anonymous,$($features)*], $($rest)*);
249    };
250    (continue, next=$next_macro:path, meta=[priority = $priority:literal $(, $($meta:tt)* )?], features=[$($features:tt)*], $($rest:tt)*) => {
251        $crate::__support::unify_features!(continue, next=$next_macro, meta=[$($($meta)*)?], features=[(priority=($priority)),$($features)*], $($rest)*);
252    };
253    (continue, next=$next_macro:path, meta=[$unknown_meta:meta $($meta:tt)*], features=[$($features:tt)*], $($rest:tt)*) => {
254        compile_error!(concat!("Unknown attribute parameter: ", stringify!($unknown_meta)));
255    };
256
257    (continue, next=$next_macro:path, meta=[], features=[$($features:tt)*], $($rest:tt)*) => {
258        $next_macro!(features=[$($features)*], $($rest)*);
259    };
260}
261
262/// If the features array contains the requested feature, generates `if_true`, else `if_false`.
263///
264/// This macro matches the features recursively.
265///
266/// Example: `[(link_section = ".ctors") , used_linker , __warn_on_missing_unsafe ,]`
267#[doc(hidden)]
268#[macro_export]
269#[allow(unknown_lints, edition_2024_expr_fragment_specifier)]
270macro_rules! __if_has_feature {
271    (std,                         [std,                             $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_true)* };
272    (used_linker,                 [used_linker,                     $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_true)* };
273    (__no_warn_on_missing_unsafe, [__no_warn_on_missing_unsafe,     $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_true)* };
274    (anonymous,                   [anonymous,                       $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_true)* };
275    ((link_section(c)),           [(link_section=($section:literal)), $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { #[link_section = $section] $($if_true)* };
276    ((priority(p)),               [(priority=($priority:literal)),    $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_true)* };
277
278    // Fallback rules
279    ($anything:tt, [$x:ident, $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $crate::__support::if_has_feature!($anything, [$($rest)*], {$($if_true)*}, {$($if_false)*}); };
280    ($anything:tt, [($x:ident=$y:tt), $($rest:tt)*], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $crate::__support::if_has_feature!($anything, [$($rest)*], {$($if_true)*}, {$($if_false)*}); };
281    ($anything:tt, [], {$($if_true:tt)*}, {$($if_false:tt)*}) => { $($if_false)* };
282}
283
284#[doc(hidden)]
285#[macro_export]
286macro_rules! __if_unsafe {
287    (, {$($if_unsafe:tt)*}, {$($if_safe:tt)*}) => { $($if_safe)* };
288    (unsafe, {$($if_unsafe:tt)*}, {$($if_safe:tt)*}) => { $($if_unsafe)* };
289}
290
291#[doc(hidden)]
292#[macro_export]
293macro_rules! __get_priority {
294    ($next:path, $args:tt, [(priority=($priority:literal)), $($rest:tt)*]) => { $next!($args, (".", $priority)); };
295    ($next:path, $args:tt, [$x:ident, $($rest:tt)*])                       => { $crate::__support::get_priority!($next, $args, [$($rest)*]); };
296    ($next:path, $args:tt, [($x:ident=$y:tt), $($rest:tt)*])               => { $crate::__support::get_priority!($next, $args, [$($rest)*]); };
297    ($next:path, $args:tt, [])                                             => { $next!($args, ("")); };
298}
299
300#[doc(hidden)]
301#[macro_export]
302#[allow(unknown_lints, edition_2024_expr_fragment_specifier)]
303macro_rules! __ctor_entry {
304    (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=unsafe fn $($item:tt)*) => {
305        $crate::__support::ctor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=unsafe, item=fn $($item)*);
306    };
307    (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=fn $($item:tt)*) => {
308        $crate::__support::ctor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=, item=fn $($item)*);
309    };
310    (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=static $ident:ident : $ty:ty = $(unsafe)? $({ $lit:literal })? $($lit2:literal)? ;) => {
311        compile_error!(concat!("Use `const ", stringify!($ident), " = ", stringify!($($lit)?$($lit2)?), ";` or `static ", stringify!($ident), ": ", stringify!($ty), " = ", stringify!($($lit)?$($lit2)?), ";` instead"));
312    };
313    (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=static $ident:ident : $ty:ty = unsafe $($item:tt)*) => {
314        $crate::__support::ctor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=unsafe, item=static $ident: $ty = $($item)*);
315    };
316    (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=static $ident:ident : $ty:ty = $($item:tt)*) => {
317        $crate::__support::ctor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=, item=static $ident: $ty = $($item)*);
318    };
319    (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
320        $crate::__support::if_has_feature!(anonymous, $features, {
321            $crate::__support::ctor_entry!(unnamed, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
322        }, {
323            $crate::__support::ctor_entry!(named, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
324        });
325    };
326    (unnamed,features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
327        const _: () = {
328            $crate::__support::ctor_entry!(named, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
329        };
330    };
331    (named, features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
332        $(#[$fnmeta])*
333        #[allow(unused)]
334        $($vis)* $($unsafe)? fn $ident() {
335            #[allow(unsafe_code)]
336            {
337                $crate::__support::if_unsafe!($($unsafe)?, {}, {
338                    $crate::__support::if_has_feature!( __warn_on_missing_unsafe, $features, {
339                        #[deprecated="ctor deprecation note:\n\n \
340                        Use of #[ctor] without `unsafe fn` is deprecated. As code execution before main\n\
341                        is unsupported by most Rust runtime functions, these functions must be marked\n\
342                        `unsafe`."]
343                            const fn ctor_without_unsafe_is_deprecated() {}
344                            #[allow(unused)]
345                            static UNSAFE_WARNING: () = ctor_without_unsafe_is_deprecated();
346                    }, {});
347                });
348
349                $crate::__support::ctor_call!(
350                    features=$features,
351                    { unsafe { $ident(); } }
352                );
353            }
354
355            #[cfg(target_family = "wasm")]
356            {
357                static __CTOR__INITILIZED: ::core::sync::atomic::AtomicBool = ::core::sync::atomic::AtomicBool::new(false);
358                if __CTOR__INITILIZED.swap(true, ::core::sync::atomic::Ordering::Relaxed) {
359                    return;
360                }
361            }
362
363            $block
364        }
365    };
366    (features=$features:tt, imeta=$(#[$imeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=static $ident:ident : $ty:ty = $block:block;) => {
367        $crate::__support::if_has_feature!(std, $features, {
368            $(#[$imeta])*
369            $($vis)* static $ident: $ident::Static<$ty> = $ident::Static::<$ty> {
370                _storage: {
371                    $crate::__support::ctor_call!(
372                        features=$features,
373                        { _ = &*$ident; }
374                    );
375
376                    ::std::sync::OnceLock::new()
377                }
378            };
379
380            impl ::core::ops::Deref for $ident::Static<$ty> {
381                type Target = $ty;
382                fn deref(&self) -> &$ty {
383                    fn init() -> $ty $block
384
385                    self._storage.get_or_init(move || {
386                        init()
387                    })
388                }
389            }
390
391            #[doc(hidden)]
392            #[allow(non_upper_case_globals, non_snake_case)]
393            #[allow(unsafe_code)]
394            mod $ident {
395                $crate::__support::if_unsafe!($($unsafe)?, {}, {
396                    $crate::__support::if_has_feature!( __no_warn_on_missing_unsafe, $features, {
397                        #[deprecated="ctor deprecation note:\n\n \
398                        Use of #[ctor] without `unsafe { ... }` is deprecated. As code execution before main\n\
399                        is unsupported by most Rust runtime functions, these functions must be marked\n\
400                        `unsafe`."]
401                            const fn ctor_without_unsafe_is_deprecated() {}
402                            #[allow(unused)]
403                            static UNSAFE_WARNING: () = ctor_without_unsafe_is_deprecated();
404                    }, {});
405                });
406
407                #[allow(non_camel_case_types, unreachable_pub)]
408                pub struct Static<T> {
409                    pub _storage: ::std::sync::OnceLock<T>
410                }
411            }
412        }, {
413            compile_error!("`#[ctor]` on `static` items requires the `std` feature");
414        });
415    };
416}
417
418// Code note:
419
420// You might wonder why we don't use `__attribute__((destructor))`/etc for
421// dtor. Unfortunately mingw doesn't appear to properly support section-based
422// hooks for shutdown, ie:
423
424// https://github.com/Alexpux/mingw-w64/blob/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-crt/crt/crtdll.c
425
426// In addition, OSX has removed support for section-based shutdown hooks after
427// warning about it for a number of years:
428
429// https://reviews.llvm.org/D45578
430
431#[doc(hidden)]
432#[macro_export]
433macro_rules! __dtor_entry {
434    (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=fn $ident:ident() $block:block) => {
435        $crate::__support::dtor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=, item=fn $ident() $block);
436    };
437    (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], item=unsafe fn $ident:ident() $block:block) => {
438        $crate::__support::dtor_entry!(features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=unsafe, item=fn $ident() $block);
439    };
440    (features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
441        $crate::__support::if_has_feature!(anonymous, $features, {
442            $crate::__support::dtor_entry!(unnamed, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
443        }, {
444            $crate::__support::dtor_entry!(named, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
445        });
446    };
447    (unnamed, features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
448        const _: () = {
449            $crate::__support::dtor_entry!(named, features=$features, imeta=$(#[$fnmeta])*, vis=[$($vis)*], unsafe=$($unsafe)?, item=fn $ident() $block);
450        };
451    };
452    (named, features=$features:tt, imeta=$(#[$fnmeta:meta])*, vis=[$($vis:tt)*], unsafe=$($unsafe:ident)?, item=fn $ident:ident() $block:block) => {
453        $(#[$fnmeta])*
454        #[allow(unused)]
455        $($vis)* $($unsafe)? fn $ident() {
456            #[allow(unsafe_code)]
457            {
458                $crate::__support::if_unsafe!($($unsafe)?, {}, {
459                    $crate::__support::if_has_feature!( __warn_on_missing_unsafe, $features, {
460                        #[deprecated="dtor deprecation note:\n\n \
461                        Use of #[dtor] without `unsafe fn` is deprecated. As code execution after main\n\
462                        is unsupported by most Rust runtime functions, these functions must be marked\n\
463                        `unsafe`."]
464                        const fn dtor_without_unsafe_is_deprecated() {}
465                        #[allow(unused)]
466                        static UNSAFE_WARNING: () = dtor_without_unsafe_is_deprecated();
467                    }, {});
468                });
469
470                $crate::__support::ctor_call!(
471                    features=$features,
472                    { unsafe { do_atexit(__dtor); } }
473                );
474
475                $crate::__support::ctor_link_section!(
476                    exit,
477                    features=$features,
478                    (""),
479
480                    /*unsafe*/ extern "C" fn __dtor(
481                        #[cfg(target_vendor = "apple")] _: *const u8
482                    ) { unsafe { $ident() } }
483                );
484
485                #[cfg(not(target_vendor = "apple"))]
486                #[inline(always)]
487                unsafe fn do_atexit(cb: unsafe extern fn()) {
488                    /*unsafe*/ extern "C" {
489                        fn atexit(cb: unsafe extern fn());
490                    }
491                    unsafe {
492                        atexit(cb);
493                    }
494                }
495
496                // For platforms that have __cxa_atexit, we register the dtor as scoped to dso_handle
497                #[cfg(target_vendor = "apple")]
498                #[inline(always)]
499                unsafe fn do_atexit(cb: /*unsafe*/ extern "C" fn(_: *const u8)) {
500                    /*unsafe*/ extern "C" {
501                        static __dso_handle: *const u8;
502                        fn __cxa_atexit(cb: /*unsafe*/ extern "C" fn(_: *const u8), arg: *const u8, dso_handle: *const u8);
503                    }
504                    unsafe {
505                        __cxa_atexit(cb, ::core::ptr::null(), __dso_handle);
506                    }
507                }
508            }
509
510            $block
511        }
512    };
513}
514
515/// Annotate a block with its appropriate link section.
516#[doc(hidden)]
517#[macro_export]
518macro_rules! __ctor_call {
519    (features=$features:tt, { $($block:tt)+ } ) => {
520        $crate::__support::get_priority!($crate::__support::ctor_call, [features=$features, { $($block)+ }], $features);
521    };
522    ([features=$features:tt, { $($block:tt)+ }], $priority:tt) => {
523        $crate::__support::ctor_link_section!(
524            array,
525            features=$features,
526            $priority,
527
528            #[allow(non_upper_case_globals, non_snake_case)]
529            #[doc(hidden)]
530            static f: /*unsafe*/ extern "C" fn() -> $crate::__support::CtorRetType =
531            {
532                $crate::__support::ctor_link_section!(
533                    startup,
534                    features=$features,
535                    (""),
536
537                    #[allow(non_snake_case)]
538                    /*unsafe*/ extern "C" fn f() -> $crate::__support::CtorRetType {
539                        $($block)+;
540                        ::core::default::Default::default()
541                    }
542                );
543
544                f
545            };
546        );
547    }
548}
549
550/// Annotate a block with its appropriate link section.
551#[doc(hidden)]
552#[macro_export]
553macro_rules! __ctor_link_section {
554    ($section:ident, features=$features:tt, $priority:tt, $($block:tt)+) => {
555        $crate::__support::if_has_feature!(used_linker, $features, {
556            $crate::__support::ctor_link_section_attr!($section, $features, used(linker), $priority, $($block)+);
557        }, {
558            $crate::__support::ctor_link_section_attr!($section, $features, used, $priority, $($block)+);
559        });
560
561        #[cfg(not(any(
562            target_os = "linux",
563            target_os = "android",
564            target_os = "freebsd",
565            target_os = "netbsd",
566            target_os = "openbsd",
567            target_os = "dragonfly",
568            target_os = "illumos",
569            target_os = "haiku",
570            target_vendor = "apple",
571            target_family = "wasm",
572            target_arch = "xtensa",
573            target_vendor = "pc"
574        )))]
575        compile_error!("#[ctor]/#[dtor] is not supported on the current target");
576    }
577}
578
579/// Apply either the default cfg-based link section attributes, or
580/// the overridden link_section attribute.
581#[doc(hidden)]
582#[macro_export]
583macro_rules! __ctor_link_section_attr {
584    (array, $features:tt, $used:meta, ($($priority:tt)*), $item:item) => {
585        $crate::__support::if_has_feature!((priority(p)), $features, {
586            #[cfg(target_vendor="apple")]
587            const _: () = {
588                #[deprecated(note = "The priority parameter is not supported on target_vendor = \"apple\"")]
589                const fn ctor_priority_unsupported() {}
590                ctor_priority_unsupported();
591            };
592        }, {});
593        $crate::__support::if_has_feature!((link_section(c)), $features, {
594            #[allow(unsafe_code)]
595            #[$used]
596            $item
597        }, {
598            #[allow(unsafe_code)]
599            $crate::__support::ctor_link_section_attr!(
600                [[any(
601                    target_os = "linux",
602                    target_os = "android",
603                    target_os = "freebsd",
604                    target_os = "netbsd",
605                    target_os = "openbsd",
606                    target_os = "dragonfly",
607                    target_os = "illumos",
608                    target_os = "haiku",
609                    target_family = "wasm"
610                ), (concat!(".init_array", $($priority)*))],
611                [target_arch = "xtensa", (concat!(".ctors", $($priority)*))],
612                // macOS/Darwin do not support the priority parameter in the link section
613                [target_vendor = "apple", (concat!("__DATA,__mod_init_func,mod_init_funcs"))],
614                [all(target_vendor = "pc", any(target_env = "gnu", target_env = "msvc")), (concat!(".CRT$XCU", $($priority)*))],
615                // cygwin support: rustc 1.85 does not like the explicit target_os = "cygwin" condition (https://github.com/mmastrac/rust-ctor/issues/356)
616                // We can work around this by excluding gnu and msvc target envs
617                [all(target_vendor = "pc", not(any(target_env = "gnu", target_env = "msvc"))), (concat!(".ctors", $($priority)*))]
618                ],
619                #[$used]
620                $item
621            );
622        });
623    };
624    (startup, $features:tt, $used:meta, $priority:tt, $item:item) => {
625        #[cfg(not(clippy))]
626        $crate::__support::ctor_link_section_attr!([[any(target_os = "linux", target_os = "android"), (".text.startup")]], $item);
627
628        #[cfg(clippy)]
629        $item
630    };
631    (exit, $features:tt, $used:meta, $priority:tt, $item:item) => {
632        #[cfg(not(clippy))]
633        $crate::__support::ctor_link_section_attr!([[any(target_os = "linux", target_os = "android"), (".text.exit")]], $item);
634
635        #[cfg(clippy)]
636        $item
637    };
638    ([$( [$cond:meta, ($($literal:tt)*) ] ),+], $item:item) => {
639        $( #[cfg_attr($cond, link_section = $($literal)*)] )+
640        $item
641    };
642}