Statically link WebView2Loader - single exe, no DLL needed
build.rs swaps the dynamic import library with the static archive from webview2-com-sys, so the WebView2 loader code is baked into the exe. msvc_compat.rs provides the MSVC CRT symbols (security cookie, thread-safe init, C++ operators) that the MSVC-compiled static library expects.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
mod config;
|
||||
#[cfg(all(windows, target_env = "gnu"))]
|
||||
mod msvc_compat;
|
||||
mod stats;
|
||||
mod timer;
|
||||
|
||||
|
||||
98
src-tauri/src/msvc_compat.rs
Normal file
98
src-tauri/src/msvc_compat.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
//! 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user