Reinstate preloading of some dll imports
This commit is contained in:
parent
48853a361a
commit
7bb47a6f38
@ -228,8 +228,6 @@ pub const IPV6_ADD_MEMBERSHIP: c_int = 12;
|
||||
pub const IPV6_DROP_MEMBERSHIP: c_int = 13;
|
||||
pub const MSG_PEEK: c_int = 0x2;
|
||||
|
||||
pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 0x800;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct linger {
|
||||
@ -1032,7 +1030,6 @@ extern "system" {
|
||||
pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void;
|
||||
pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE;
|
||||
pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
|
||||
pub fn LoadLibraryExA(lplibfilename: *const i8, hfile: HANDLE, dwflags: u32) -> HINSTANCE;
|
||||
|
||||
pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME);
|
||||
pub fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO);
|
||||
|
@ -21,9 +21,52 @@
|
||||
|
||||
use crate::ffi::{c_void, CStr};
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||
use crate::sync::atomic::Ordering;
|
||||
use crate::sys::c;
|
||||
|
||||
// This uses a static initializer to preload some imported functions.
|
||||
// The CRT (C runtime) executes static initializers before `main`
|
||||
// is called (for binaries) and before `DllMain` is called (for DLLs).
|
||||
//
|
||||
// It works by contributing a global symbol to the `.CRT$XCT` section.
|
||||
// The linker builds a table of all static initializer functions.
|
||||
// The CRT startup code then iterates that table, calling each
|
||||
// initializer function.
|
||||
//
|
||||
// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer.
|
||||
// If you're reading this and would like a guarantee here, please
|
||||
// file an issue for discussion; currently we don't guarantee any functionality
|
||||
// before main.
|
||||
// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
|
||||
#[used]
|
||||
#[link_section = ".CRT$XCT"]
|
||||
static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
|
||||
|
||||
/// Preload some imported functions.
|
||||
///
|
||||
/// Note that any functions included here will be unconditionally loaded in
|
||||
/// the final binary, regardless of whether or not they're actually used.
|
||||
///
|
||||
/// Therefore, this should be limited to `compat_fn_optional` functions which
|
||||
/// must be preloaded or any functions where lazier loading demonstrates a
|
||||
/// negative performance impact in practical situations.
|
||||
///
|
||||
/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`.
|
||||
unsafe extern "C" fn init() {
|
||||
// In an exe this code is executed before main() so is single threaded.
|
||||
// In a DLL the system's loader lock will be held thereby synchronizing
|
||||
// access. So the same best practices apply here as they do to running in DllMain:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
|
||||
//
|
||||
// DO NOT do anything interesting or complicated in this function! DO NOT call
|
||||
// any Rust functions or CRT functions if those functions touch any global state,
|
||||
// because this function runs during global initialization. For example, DO NOT
|
||||
// do any dynamic allocation, don't call LoadLibrary, etc.
|
||||
|
||||
// Attempt to preload the synch functions.
|
||||
load_synch_functions();
|
||||
}
|
||||
|
||||
/// Helper macro for creating CStrs from literals and symbol names.
|
||||
macro_rules! ansi_str {
|
||||
(sym $ident:ident) => {{
|
||||
@ -75,20 +118,6 @@ impl Module {
|
||||
NonNull::new(module).map(Self)
|
||||
}
|
||||
|
||||
/// Load the library (if not already loaded)
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The module must not be unloaded.
|
||||
pub unsafe fn load_system_library(name: &CStr) -> Option<Self> {
|
||||
let module = c::LoadLibraryExA(
|
||||
name.as_ptr(),
|
||||
crate::ptr::null_mut(),
|
||||
c::LOAD_LIBRARY_SEARCH_SYSTEM32,
|
||||
);
|
||||
NonNull::new(module).map(Self)
|
||||
}
|
||||
|
||||
// Try to get the address of a function.
|
||||
pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
|
||||
// SAFETY:
|
||||
@ -182,14 +211,10 @@ macro_rules! compat_fn_optional {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn option() -> Option<F> {
|
||||
let f = PTR.load(Ordering::Acquire);
|
||||
if !f.is_null() { Some(unsafe { mem::transmute(f) }) } else { try_load() }
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn try_load() -> Option<F> {
|
||||
$load_functions;
|
||||
NonNull::new(PTR.load(Ordering::Acquire)).map(|f| unsafe { mem::transmute(f) })
|
||||
// Miri does not understand the way we do preloading
|
||||
// therefore load the function here instead.
|
||||
#[cfg(miri)] $load_functions;
|
||||
NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) })
|
||||
}
|
||||
}
|
||||
)+
|
||||
@ -205,17 +230,14 @@ pub(super) fn load_synch_functions() {
|
||||
|
||||
// Try loading the library and all the required functions.
|
||||
// If any step fails, then they all fail.
|
||||
let library = unsafe { Module::load_system_library(MODULE_NAME) }?;
|
||||
let library = unsafe { Module::new(MODULE_NAME) }?;
|
||||
let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?;
|
||||
let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?;
|
||||
|
||||
c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Release);
|
||||
c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Release);
|
||||
c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed);
|
||||
c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed);
|
||||
Some(())
|
||||
}
|
||||
|
||||
// Try to load the module but skip loading if a previous attempt failed.
|
||||
static LOAD_MODULE: AtomicBool = AtomicBool::new(true);
|
||||
let module_loaded = LOAD_MODULE.load(Ordering::Acquire) && try_load().is_some();
|
||||
LOAD_MODULE.store(module_loaded, Ordering::Release)
|
||||
try_load();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user