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)] |
27 | pub 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" ))] |
81 | pub 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) |
124 | pub 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 | |