Skip to main content

dtor/
lib.rs

1#![recursion_limit = "256"]
2#![no_std]
3#![doc = include_str!("../docs/BUILD.md")]
4//! # dtor
5#![doc = include_str!("../docs/PREAMBLE.md")]
6#![doc = include_str!("../docs/REEXPORT.md")]
7#![doc = include_str!("../docs/GENERATED.md")]
8
9#[cfg(feature = "std")]
10extern crate std;
11
12mod macros;
13mod native;
14mod parse;
15
16#[doc = include_str!("../docs/LIFE_BEFORE_MAIN.md")]
17pub mod life_before_main {}
18
19pub use native::*;
20
21/// Marks a function as a library/executable destructor. This uses OS-specific
22/// linker sections to call a specific function at termination time.
23///
24/// Multiple shutdown functions are supported, but the invocation order is not
25/// guaranteed.
26///
27/// The `dtor` crate assumes it is available as a direct dependency. If you
28/// re-export `dtor` items as part of your crate, you can use the `crate_path`
29/// parameter to redirect the macro's output to the correct crate, or use the
30/// [`declarative::dtor`] form.
31///
32/// ```rust,ignore
33/// # use dtor::dtor;
34/// # fn main() {}
35///
36/// #[dtor(unsafe)]
37/// fn shutdown() {
38///   /* ... */
39/// }
40/// ```
41#[doc(inline)]
42#[cfg(feature = "proc_macro")]
43pub use linktime_proc_macro::dtor;
44
45/// Declarative forms of the `#[dtor]` macro.
46///
47/// The declarative forms wrap and parse a proc_macro-like syntax like so, and
48/// are identical in expansion to the undecorated procedural macros. The
49/// declarative forms support the same attribute parameters as the procedural
50/// macros.
51///
52/// ```rust
53/// # #[cfg(any())] mod test { use dtor::*; use libc_print::*;
54/// dtor::declarative::dtor! {
55///   #[dtor(unsafe)]
56///   fn foo() {
57///     libc_println!("Goodbye, world!");
58///   }
59/// }
60/// # }
61///
62/// // ... the above is identical to:
63///
64/// # #[cfg(any())] mod test_2 { use dtor::*; use libc_print::*;
65/// #[dtor(unsafe)]
66/// fn foo() {
67///   libc_println!("Goodbye, world!");
68/// }
69/// # }
70/// ```
71pub mod declarative {
72    #[doc(inline)]
73    pub use crate::__dtor_parse as dtor;
74}
75
76#[doc(hidden)]
77#[allow(unused)]
78pub mod __support {
79    use crate::macros::*;
80
81    // Required for proc_macro.
82    pub use crate::__dtor_parse as dtor_parse;
83
84    pub use crate::native::*;
85}
86
87__declare_features!(
88    dtor: __dtor_features;
89
90    /// Do not give the destructor's registration entry a name in the generated
91    /// code (allows for multiple items with the same name). Equivalent to
92    /// wrapping the registration in an anonymous const (i.e.: `const _ = { ... };`).
93    anonymous {
94        attr: [(anonymous) => (anonymous)];
95    };
96    /// The path to the `dtor` crate containing the support macros. If you
97    /// re-export `dtor` items as part of your crate, you can use this to
98    /// redirect the macro's output to the correct crate.
99    ///
100    /// Using the declarative [`dtor!`][d] form is
101    /// preferred over this parameter.
102    ///
103    /// [d]: crate::declarative::dtor!
104    crate_path {
105        attr: [(crate_path = $path:pat) => (($path))];
106        example: "crate_path = ::path::to::dtor::crate";
107    };
108    /// Specify a custom export name prefix for the generated constructor
109    /// function.
110    ///
111    /// If specified, an export with the given prefix will be generated in the
112    /// form:
113    ///
114    /// `<prefix>_<unique_id>`
115    ctor_export_name_prefix {
116        attr: [(ctor(export_name_prefix = $ctor_export_name_prefix_str:literal)) => ($ctor_export_name_prefix_str)];
117        example: "ctor(export_name_prefix = \"ctor_\")";
118        default {
119            (target_os = "aix") => "__sinit80000000",
120            _ => (),
121        }
122    };
123    /// Place the generated registration constructor's function pointer in a
124    /// custom link section.
125    ctor_link_section {
126        attr: [(ctor(link_section = $ctor_link_section_name:literal)) => ($ctor_link_section_name)];
127        example: "ctor(link_section = \".ctors\")";
128        // Historical note: GCC 4.7 stopped providing .ctors/.dtors compatible
129        // crt files. Modern compilers, with the exception of Apple, MSVC, and
130        // AIX, will use `.init_array`.
131        default {
132            (target_vendor = "apple") => "__DATA,__mod_init_func,mod_init_funcs",
133            // Most LLVM/GCC targets can use .init_array
134            (any(
135                target_os = "linux",
136                target_os = "android",
137                target_os = "freebsd",
138                target_os = "netbsd",
139                target_os = "openbsd",
140                target_os = "dragonfly",
141                target_os = "illumos",
142                target_os = "haiku",
143                target_os = "vxworks",
144                target_os = "nto",
145                target_family = "wasm"
146            )) => ".init_array",
147            // No OS
148            (target_os = "none") => ".init_array",
149            // xtensa targets: .ctors
150            (target_arch = "xtensa") => ".ctors",
151            // Windows targets: .CRT$XCU
152            (all(target_os = "windows", any(target_env = "gnu", target_env = "msvc"))) => ".CRT$XCU",
153            // ... except mingw32 (https://llvm.googlesource.com/clang/+/1a209b667f83588866326a0384fa943ea2287b6c)
154            (all(target_os = "windows", not(any(target_env = "gnu", target_env = "msvc")))) => ".ctors",
155            // Research suggests that MSVC will use .CRT$XCU for UEFI targets, but won't actually
156            // run them. The gnu-efi project _does_ at least document .init_array support:
157            // https://github.com/vathpela/gnu-efi/blob/master/gnuefi/elf_x86_64_efi.lds
158            (target_os = "uefi") => ".init_array",
159            (target_os = "aix") => (), // AIX uses export_name_prefix
160            // Fall back to .init_array which is effectively the gold standard
161            // for LLVM/GCC targets moving forward
162            #[warn("Falling back to .init_array for unsupported target. If this \
163            works for you, please file an issue to add support for your target \
164            at https://github.com/mmastrac/linktime/issues")]
165            _ => ".init_array",
166        }
167    };
168    /// The default method used for running a `dtor` on termination. This is
169    /// generally not recommended as code may be unloaded before the dtor is
170    /// called.
171    ///
172    /// This is only used if the specified `dtor` method is `term`.
173    ///
174    /// All platforms use `at_binary_exit` except Windows, which uses
175    /// `at_module_exit`.
176    default_term_method {
177        default {
178            (target_os = "windows") => at_module_exit,
179            _ => at_binary_exit,
180        }
181    };
182    /// The default method used for running a `dtor` on module unload.
183    ///
184    /// This is only used if the `method` attribute is not specified, or if the
185    /// method is `unload`.
186    default_unload_method {
187        default {
188            _ => at_module_exit,
189        }
190    };
191    /// Specify a custom export name prefix for the destructor function.
192    ///
193    /// If specified, an export with the given prefix will be generated in the form:
194    ///
195    /// `<prefix>_<unique_id>`
196    export_name_prefix {
197        attr: [(export_name_prefix = $export_name_prefix_str:literal) => ($export_name_prefix_str)];
198        example: "export_name_prefix = \"ctor_\"";
199        default {
200            (target_os = "aix") => "__sterm80000000",
201            _ => (),
202        }
203    };
204    /// Place the destructor function pointer in a custom link section.
205    link_section {
206        attr: [(link_section = $section:literal) => ($section)];
207        example: "link_section = \".dtors\"";
208        default {
209            // This is no longer supported by Apple
210            (target_vendor = "apple") => "__DATA,__mod_term_func,mod_term_funcs",
211            // Most LLVM/GCC targets can use .fini_array
212            (any(
213                target_os = "linux",
214                target_os = "android",
215                target_os = "freebsd",
216                target_os = "netbsd",
217                target_os = "dragonfly",
218                target_os = "illumos",
219                target_os = "haiku",
220                target_os = "vxworks",
221                target_os = "nto",
222                target_family = "wasm"
223            )) => ".fini_array",
224            // OpenBSD only seems to support .fini_array for binaries
225            (target_os = "openbsd") => ".dtors",
226            // No OS
227            (target_os = "none") => ".fini_array",
228            // xtensa targets: .dtors
229            (target_arch = "xtensa") => ".dtors",
230            // Windows targets: .CRT$XPU (requires static CRT)
231            (all(target_os = "windows", any(target_env = "gnu", target_env = "msvc"))) => ".CRT$XPU",
232            // ... except GNU
233            (all(target_os = "windows", not(any(target_env = "gnu", target_env = "msvc")))) => ".dtors",
234            // The gnu-efi project documents .fini_array support:
235            // https://github.com/vathpela/gnu-efi/blob/master/gnuefi/elf_x86_64_efi.lds
236            (target_os = "uefi") => ".fini_array",
237            (target_os = "aix") => (), // AIX uses export_name_prefix
238            // Fall back to .fini_array which is effectively the gold standard
239            // for LLVM/GCC targets moving forward
240            #[warn("Falling back to .fini_array for unsupported target. If this \
241            works for you, please file an issue to add support for your target \
242            at https://github.com/mmastrac/linktime/issues")]
243            _ => ".fini_array",
244        }
245    };
246    /// Specify the dtor method.
247    ///
248    ///  - `term`: Run the dtor on binary termination using the platform's
249    ///    [default_term_method](#default_term_method). Not recommended as code
250    ///    may be unloaded before the dtor is called.
251    ///  - `unload`: Run the dtor on module unload (library or binary) using the
252    ///    platform's [default_unload_method](#default_unload_method).
253    ///  - `at_module_exit`: Run the dtor using the platform's
254    ///    [`at_module_exit`][at_module_exit] (`__cxa_atexit` on all platforms
255    ///    other than Windows, `atexit` on Windows).
256    ///  - `at_binary_exit`: Run the dtor using the platform's
257    ///    [`at_binary_exit`][at_binary_exit] (unsupported on Windows
258    ///    platforms).
259    ///  - `linker`: Register the dtor using the platform's
260    ///    [link_section](#link_section) or
261    ///    [export_name_prefix](#export_name_prefix) (unsupported on Apple
262    ///    platforms).
263    ///
264    /// [at_module_exit]: crate::native::at_module_exit
265    /// [at_binary_exit]: crate::native::at_binary_exit
266    method {
267        attr: [(method = $method_id:ident) => ($method_id)];
268        example: "method = term|unload|at_module_exit|at_binary_exit|linker";
269        validate: [(term), (unload), (at_module_exit), (at_binary_exit), (linker)];
270        default {
271            (target_vendor = "apple") => at_module_exit,
272            (target_os = "windows") => at_module_exit,
273            // WASI/Emscripten support atexit only
274            // For wasm-unknown-unknown, you'll need to provide one
275            (target_family = "wasm") => at_binary_exit,
276            // OpenBSD's linker support is inconsistent
277            (target_os = "openbsd") => at_module_exit,
278            _ => linker,
279        }
280    };
281    /// Enable support for the proc-macro `#[dtor]` attribute. The declarative
282    /// form (`dtor!(...)`) is always available. It is recommended that crates
283    /// re-exporting the `dtor` macro disable this feature and only use the
284    /// declarative form.
285    proc_macro {
286        feature: "proc_macro";
287    };
288    /// Enable support for the standard library.
289    std {
290        feature: "std";
291    };
292    r#unsafe {
293        /// attr
294        ///
295        /// Marks a dtor as unsafe. Required.
296        ///
297        /// The `dtor` crate rejects `#[dtor]` without marking the item unsafe;
298        /// that error can be suppressed by passing
299        /// `RUSTFLAGS="--cfg linktime_no_fail_on_missing_unsafe"` to Cargo.
300        attr: [(unsafe) => (no_fail_on_missing_unsafe)];
301        default {
302            (linktime_no_fail_on_missing_unsafe) => (no_fail_on_missing_unsafe),
303            _ => (),
304        }
305    };
306    used_linker {
307        /// attr
308        ///
309        /// Mark generated function pointers `used(linker)`. Requires nightly
310        /// for the nightly-only feature `feature(used_with_arg)` (see
311        /// <https://github.com/rust-lang/rust/issues/93798>).
312        ///
313        /// This can be made the default by using the `cfg` flag
314        /// `linktime_used_linker` (`RUSTFLAGS="--cfg linktime_used_linker"`).
315        ///
316        /// For a crate using this macro to function correctly with and without
317        /// this flag, it is recommended to add the following line to the top of
318        /// lib.rs in the crate root:
319        ///
320        /// `#![cfg_attr(linktime_used_linker, feature(used_with_arg))]`
321        ///
322        attr: [(used(linker)) => (used_linker)];
323        default {
324            (linktime_used_linker) => used_linker,
325            _ => (),
326        }
327    };
328);
329
330#[cfg(doc)]
331__generate_docs!(__dtor_features);