//! Compatibility shims for MSVC CRT symbols required by WebView2LoaderStatic.lib. //! //! When statically linking the WebView2 loader on GNU/MinGW, the MSVC-compiled //! object code references these symbols. We provide minimal implementations so //! the linker can resolve them. use std::sync::atomic::{AtomicI32, Ordering}; // ── MSVC Buffer Security Check (/GS) ──────────────────────────────────────── // // MSVC's /GS flag instruments functions with stack canaries. These two symbols // implement the canary check. The cookie value is arbitrary -- real MSVC CRT // randomises it at startup, but for a statically-linked helper library this // fixed sentinel is sufficient. #[no_mangle] pub static __security_cookie: u64 = 0x00002B992DDFA232; #[no_mangle] pub unsafe extern "C" fn __security_check_cookie(cookie: u64) { if cookie != __security_cookie { std::process::abort(); } } // ── MSVC Thread-Safe Static Initialisation ─────────────────────────────────── // // C++11 guarantees that function-local statics are initialised exactly once, // even under concurrent access. MSVC implements this with an epoch counter and // a set of helper functions. The WebView2 loader uses a few statics internally. // // Simplified implementation: uses an atomic spin for the guard. This is safe // because WebView2 initialisation runs on the main thread in practice. #[no_mangle] pub static _Init_thread_epoch: AtomicI32 = AtomicI32::new(0); #[no_mangle] pub unsafe extern "C" fn _Init_thread_header(guard: *mut i32) { if guard.is_null() { return; } // Spin until we can claim the guard (-1 = uninitialized, 0 = done, 1 = in progress) loop { let val = guard.read_volatile(); if val == 0 { // Already initialised -- tell caller to skip return; } if val == -1 { // Not yet initialised -- try to claim it guard.write_volatile(1); return; } // val == 1: another thread is initialising -- yield and retry std::thread::yield_now(); } } #[no_mangle] pub unsafe extern "C" fn _Init_thread_footer(guard: *mut i32) { if !guard.is_null() { guard.write_volatile(0); // Mark initialisation complete _Init_thread_epoch.fetch_add(1, Ordering::Release); } } // ── MSVC C++ Runtime Operators (mangled names) ─────────────────────────────── // // The static library is compiled with MSVC, which uses its own C++ name mangling. // MinGW's libstdc++ exports the same operators but with GCC/Itanium mangling, // so the linker can't match them. We provide the MSVC-mangled versions here. /// `std::nothrow` -- MSVC-mangled `?nothrow@std@@3Unothrow_t@1@B` /// An empty struct constant used as a tag for nothrow `new`. #[export_name = "?nothrow@std@@3Unothrow_t@1@B"] pub static MSVC_STD_NOTHROW: u8 = 0; /// `operator new(size_t, const std::nothrow_t&)` -- nothrow allocation /// MSVC-mangled: `??2@YAPEAX_KAEBUnothrow_t@std@@@Z` #[export_name = "??2@YAPEAX_KAEBUnothrow_t@std@@@Z"] pub unsafe extern "C" fn msvc_operator_new_nothrow(size: usize, _nothrow: *const u8) -> *mut u8 { let size = if size == 0 { 1 } else { size }; let layout = std::alloc::Layout::from_size_align_unchecked(size, 8); let ptr = std::alloc::alloc(layout); ptr // null on failure -- nothrow semantics } /// `operator delete(void*, size_t)` -- sized deallocation /// MSVC-mangled: `??3@YAXPEAX_K@Z` #[export_name = "??3@YAXPEAX_K@Z"] pub unsafe extern "C" fn msvc_operator_delete_sized(ptr: *mut u8, size: usize) { if !ptr.is_null() { let size = if size == 0 { 1 } else { size }; let layout = std::alloc::Layout::from_size_align_unchecked(size, 8); std::alloc::dealloc(ptr, layout); } }