2121
2222use crate :: ffi:: { c_void, CStr } ;
2323use crate :: ptr:: NonNull ;
24- use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
24+ use crate :: sync:: atomic:: Ordering ;
2525use crate :: sys:: c;
2626
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+
2770/// Helper macro for creating CStrs from literals and symbol names.
2871macro_rules! ansi_str {
2972 ( sym $ident: ident) => { {
@@ -75,20 +118,6 @@ impl Module {
75118 NonNull :: new ( module) . map ( Self )
76119 }
77120
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-
92121 // Try to get the address of a function.
93122 pub fn proc_address ( self , name : & CStr ) -> Option < NonNull < c_void > > {
94123 // SAFETY:
@@ -182,14 +211,10 @@ macro_rules! compat_fn_optional {
182211
183212 #[ inline( always) ]
184213 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) } )
193218 }
194219 }
195220 ) +
@@ -205,17 +230,14 @@ pub(super) fn load_synch_functions() {
205230
206231 // Try loading the library and all the required functions.
207232 // 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 ) } ?;
209234 let wait_on_address = library. proc_address ( WAIT_ON_ADDRESS ) ?;
210235 let wake_by_address_single = library. proc_address ( WAKE_BY_ADDRESS_SINGLE ) ?;
211236
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 ) ;
214239 Some ( ( ) )
215240 }
216241
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 ( ) ;
221243}
0 commit comments