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);