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