21
21
22
22
use crate :: ffi:: { c_void, CStr } ;
23
23
use crate :: ptr:: NonNull ;
24
- use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
24
+ use crate :: sync:: atomic:: Ordering ;
25
25
use crate :: sys:: c;
26
26
27
+ // This uses a static initializer to preload some imported functions.
28
+ // The CRT (C runtime) executes static initializers before `main`
29
+ // is called (for binaries) and before `DllMain` is called (for DLLs).
30
+ //
31
+ // It works by contributing a global symbol to the `.CRT$XCT` section.
32
+ // The linker builds a table of all static initializer functions.
33
+ // The CRT startup code then iterates that table, calling each
34
+ // initializer function.
35
+ //
36
+ // NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer.
37
+ // If you're reading this and would like a guarantee here, please
38
+ // file an issue for discussion; currently we don't guarantee any functionality
39
+ // before main.
40
+ // See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
41
+ #[ used]
42
+ #[ link_section = ".CRT$XCT" ]
43
+ static INIT_TABLE_ENTRY : unsafe extern "C" fn ( ) = init;
44
+
45
+ /// Preload some imported functions.
46
+ ///
47
+ /// Note that any functions included here will be unconditionally loaded in
48
+ /// the final binary, regardless of whether or not they're actually used.
49
+ ///
50
+ /// Therefore, this should be limited to `compat_fn_optional` functions which
51
+ /// must be preloaded or any functions where lazier loading demonstrates a
52
+ /// negative performance impact in practical situations.
53
+ ///
54
+ /// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`.
55
+ unsafe extern "C" fn init ( ) {
56
+ // In an exe this code is executed before main() so is single threaded.
57
+ // In a DLL the system's loader lock will be held thereby synchronizing
58
+ // access. So the same best practices apply here as they do to running in DllMain:
59
+ // https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
60
+ //
61
+ // DO NOT do anything interesting or complicated in this function! DO NOT call
62
+ // any Rust functions or CRT functions if those functions touch any global state,
63
+ // because this function runs during global initialization. For example, DO NOT
64
+ // do any dynamic allocation, don't call LoadLibrary, etc.
65
+
66
+ // Attempt to preload the synch functions.
67
+ load_synch_functions ( ) ;
68
+ }
69
+
27
70
/// Helper macro for creating CStrs from literals and symbol names.
28
71
macro_rules! ansi_str {
29
72
( sym $ident: ident) => { {
@@ -75,20 +118,6 @@ impl Module {
75
118
NonNull :: new ( module) . map ( Self )
76
119
}
77
120
78
- /// Load the library (if not already loaded)
79
- ///
80
- /// # Safety
81
- ///
82
- /// The module must not be unloaded.
83
- pub unsafe fn load_system_library ( name : & CStr ) -> Option < Self > {
84
- let module = c:: LoadLibraryExA (
85
- name. as_ptr ( ) ,
86
- crate :: ptr:: null_mut ( ) ,
87
- c:: LOAD_LIBRARY_SEARCH_SYSTEM32 ,
88
- ) ;
89
- NonNull :: new ( module) . map ( Self )
90
- }
91
-
92
121
// Try to get the address of a function.
93
122
pub fn proc_address ( self , name : & CStr ) -> Option < NonNull < c_void > > {
94
123
// SAFETY:
@@ -182,14 +211,10 @@ macro_rules! compat_fn_optional {
182
211
183
212
#[ inline( always) ]
184
213
pub fn option( ) -> Option <F > {
185
- let f = PTR . load( Ordering :: Acquire ) ;
186
- if !f. is_null( ) { Some ( unsafe { mem:: transmute( f) } ) } else { try_load( ) }
187
- }
188
-
189
- #[ cold]
190
- fn try_load( ) -> Option <F > {
191
- $load_functions;
192
- NonNull :: new( PTR . load( Ordering :: Acquire ) ) . map( |f| unsafe { mem:: transmute( f) } )
214
+ // Miri does not understand the way we do preloading
215
+ // therefore load the function here instead.
216
+ #[ cfg( miri) ] $load_functions;
217
+ NonNull :: new( PTR . load( Ordering :: Relaxed ) ) . map( |f| unsafe { mem:: transmute( f) } )
193
218
}
194
219
}
195
220
) +
@@ -205,17 +230,14 @@ pub(super) fn load_synch_functions() {
205
230
206
231
// Try loading the library and all the required functions.
207
232
// If any step fails, then they all fail.
208
- let library = unsafe { Module :: load_system_library ( MODULE_NAME ) } ?;
233
+ let library = unsafe { Module :: new ( MODULE_NAME ) } ?;
209
234
let wait_on_address = library. proc_address ( WAIT_ON_ADDRESS ) ?;
210
235
let wake_by_address_single = library. proc_address ( WAKE_BY_ADDRESS_SINGLE ) ?;
211
236
212
- c:: WaitOnAddress :: PTR . store ( wait_on_address. as_ptr ( ) , Ordering :: Release ) ;
213
- c:: WakeByAddressSingle :: PTR . store ( wake_by_address_single. as_ptr ( ) , Ordering :: Release ) ;
237
+ c:: WaitOnAddress :: PTR . store ( wait_on_address. as_ptr ( ) , Ordering :: Relaxed ) ;
238
+ c:: WakeByAddressSingle :: PTR . store ( wake_by_address_single. as_ptr ( ) , Ordering :: Relaxed ) ;
214
239
Some ( ( ) )
215
240
}
216
241
217
- // Try to load the module but skip loading if a previous attempt failed.
218
- static LOAD_MODULE : AtomicBool = AtomicBool :: new ( true ) ;
219
- let module_loaded = LOAD_MODULE . load ( Ordering :: Acquire ) && try_load ( ) . is_some ( ) ;
220
- LOAD_MODULE . store ( module_loaded, Ordering :: Release )
242
+ try_load ( ) ;
221
243
}
0 commit comments