1#![cfg(target_thread_local)]
2#![unstable(feature = "thread_local_internals", issue = "none")]
3
4//! Provides thread-local destructors without an associated "key", which
5//! can be more efficient.
6
7// Since what appears to be glibc 2.18 this symbol has been shipped which
8// GCC and clang both use to invoke destructors in thread_local globals, so
9// let's do the same!
10//
11// Note, however, that we run on lots older linuxes, as well as cross
12// compiling from a newer linux to an older linux, so we also have a
13// fallback implementation to use as well.
14#[cfg(any(
15 target_os = "linux",
16 target_os = "android",
17 target_os = "fuchsia",
18 target_os = "redox",
19 target_os = "hurd",
20 target_os = "netbsd",
21 target_os = "dragonfly"
22))]
23// FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
24// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
25#[no_sanitize(cfi, kcfi)]
26pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
27 use crate::mem;
28 use crate::sys_common::thread_local_dtor::register_dtor_fallback;
29
30 /// This is necessary because the __cxa_thread_atexit_impl implementation
31 /// std links to by default may be a C or C++ implementation that was not
32 /// compiled using the Clang integer normalization option.
33 #[cfg(sanitizer_cfi_normalize_integers)]
34 use core::ffi::c_int;
35 #[cfg(not(sanitizer_cfi_normalize_integers))]
36 #[cfi_encoding = "i"]
37 #[repr(transparent)]
38 pub struct c_int(#[allow(dead_code)] pub libc::c_int);
39
40 extern "C" {
41 #[linkage = "extern_weak"]
42 static __dso_handle: *mut u8;
43 #[linkage = "extern_weak"]
44 static __cxa_thread_atexit_impl: Option<
45 extern "C" fn(
46 unsafe extern "C" fn(*mut libc::c_void),
47 *mut libc::c_void,
48 *mut libc::c_void,
49 ) -> c_int,
50 >;
51 }
52
53 if let Some(f) = __cxa_thread_atexit_impl {
54 unsafe {
55 f(
56 mem::transmute::<
57 unsafe extern "C" fn(*mut u8),
58 unsafe extern "C" fn(*mut libc::c_void),
59 >(dtor),
60 t.cast(),
61 core::ptr::addr_of!(__dso_handle) as *mut _,
62 );
63 }
64 return;
65 }
66 register_dtor_fallback(t, dtor);
67}
68
69// This implementation is very similar to register_dtor_fallback in
70// sys_common/thread_local.rs. The main difference is that we want to hook into
71// macOS's analog of the above linux function, _tlv_atexit. OSX will run the
72// registered dtors before any TLS slots get freed, and when the main thread
73// exits.
74//
75// Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The
76// workaround below is to register, via _tlv_atexit, a custom DTOR list once per
77// thread. thread_local dtors are pushed to the DTOR list without calling
78// _tlv_atexit.
79#[cfg(any(
80 target_os = "macos",
81 target_os = "ios",
82 target_os = "watchos",
83 target_os = "visionos",
84 target_os = "tvos"
85))]
86pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
87 use crate::cell::{Cell, RefCell};
88 use crate::ptr;
89
90 #[thread_local]
91 static REGISTERED: Cell<bool> = Cell::new(false);
92
93 #[thread_local]
94 static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new());
95
96 if !REGISTERED.get() {
97 _tlv_atexit(run_dtors, ptr::null_mut());
98 REGISTERED.set(true);
99 }
100
101 extern "C" {
102 fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
103 }
104
105 match DTORS.try_borrow_mut() {
106 Ok(mut dtors) => dtors.push((t, dtor)),
107 Err(_) => rtabort!("global allocator may not use TLS"),
108 }
109
110 unsafe extern "C" fn run_dtors(_: *mut u8) {
111 let mut list = DTORS.take();
112 while !list.is_empty() {
113 for (ptr, dtor) in list {
114 dtor(ptr);
115 }
116 list = DTORS.take();
117 }
118 }
119}
120
121#[cfg(any(
122 target_os = "vxworks",
123 target_os = "horizon",
124 target_os = "emscripten",
125 target_os = "aix",
126 target_os = "freebsd",
127))]
128#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten)
129pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
130 use crate::sys_common::thread_local_dtor::register_dtor_fallback;
131 register_dtor_fallback(t, dtor);
132}
133