use std::sync::atomic::{AtomicPtr, Ordering};
#[allow(clippy::upper_case_acronyms)]
type BOOL = i32;
#[allow(clippy::upper_case_acronyms)]
type HANDLE = *mut core::ffi::c_void;
#[allow(clippy::upper_case_acronyms)]
type PWSTR = *mut u16;
#[allow(clippy::upper_case_acronyms)]
type PCSTR = *const u8;
#[allow(clippy::upper_case_acronyms)]
type PCWSTR = *const u16;
type WIN32_ERROR = u32;
#[repr(C)]
struct SECURITY_ATTRIBUTES {
nLength: u32,
lpSecurityDescriptor: *mut core::ffi::c_void,
bInheritHandle: BOOL,
}
const FALSE: BOOL = 0i32;
const TRUE: BOOL = 1i32;
const INFINITE: u32 = u32::MAX;
const WAIT_FAILED: u32 = 0xFFFFFFFF;
windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32);
windows_targets::link!("kernel32.dll" "system" fn CreateMutexA(lpmutexattributes: *const SECURITY_ATTRIBUTES, binitialowner: BOOL, lpname: PCSTR) -> HANDLE);
windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR);
windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> u32);
windows_targets::link!("kernel32.dll" "system" fn ReleaseMutex(hmutex: HANDLE) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn lstrlenW(lpstring : PCWSTR) -> i32);
windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE);
windows_targets::link!("dbghelp.dll" "system" fn SymInitializeW(hprocess: HANDLE, usersearchpath: PCWSTR, finvadeprocess: BOOL) -> BOOL);
windows_targets::link!("dbghelp.dll" "system" fn SymGetSearchPathW(hprocess: HANDLE, searchpatha: PWSTR, searchpathlength: u32) -> BOOL);
windows_targets::link!("dbghelp.dll" "system" fn SymSetSearchPathW(hprocess: HANDLE, searchpatha: PCWSTR) -> BOOL);
windows_targets::link!("dbghelp.dll" "system" fn EnumerateLoadedModulesW64(hprocess: HANDLE, enumloadedmodulescallback: Option<unsafe extern "system" fn(modulename: PCWSTR, modulebase: u64, modulesize: u32, usercontext: *const core::ffi::c_void) -> BOOL>, usercontext: *const core::ffi::c_void) -> BOOL);
static RUST_BACKTRACE_MUTEX: AtomicPtr<core::ffi::c_void> = AtomicPtr::new(std::ptr::null_mut());
#[no_mangle]
extern "C" fn RustBacktraceMutexInit() {
unsafe {
let name = format!("Local\\RustBacktraceMutex{:08X}\0", GetCurrentProcessId());
let mutex = CreateMutexA(std::ptr::null(), FALSE, name.as_ptr());
assert!(!mutex.is_null());
RUST_BACKTRACE_MUTEX.store(mutex, Ordering::Release);
}
init_dbghelp();
}
fn init_dbghelp() {
unsafe {
RustBacktraceMutexLock();
SymInitializeW(GetCurrentProcess(), std::ptr::null(), FALSE);
let mut paths = vec![0; 1024];
if SymGetSearchPathW(
GetCurrentProcess(),
paths.as_mut_ptr(),
paths.len().try_into().unwrap(),
) == TRUE
{
paths.truncate(lstrlenW(paths.as_ptr()).try_into().unwrap());
} else {
paths = vec!['.' as u16];
}
if EnumerateLoadedModulesW64(
GetCurrentProcess(),
Some(loaded_modules_callback),
(&mut paths as *mut Vec<u16>).cast(),
) == TRUE
{
paths.push(0); SymSetSearchPathW(GetCurrentProcess(), paths.as_ptr());
}
RustBacktraceMutexUnlock();
}
}
unsafe extern "system" fn loaded_modules_callback(
module_name: PCWSTR,
_module_base: u64,
_module_size: u32,
user_context: *const core::ffi::c_void,
) -> BOOL {
let path = unsafe {
std::slice::from_raw_parts(module_name, lstrlenW(module_name).try_into().unwrap())
};
let Some(last_separator) = path.iter().rposition(|&c| c == '/' as _ || c == '\\' as _) else {
return TRUE;
};
let dir = &path[..last_separator];
let paths = unsafe { &mut *user_context.cast::<Vec<u16>>().cast_mut() };
if paths.split(|&c| c == ';' as _).all(|slice| slice != dir) {
paths.push(';' as _);
paths.extend(dir);
}
TRUE }
#[no_mangle]
extern "C" fn RustBacktraceMutexLock() {
unsafe {
let mutex = RUST_BACKTRACE_MUTEX.load(Ordering::Acquire);
if !mutex.is_null() {
assert_ne!(
WaitForSingleObject(mutex, INFINITE),
WAIT_FAILED,
"{}",
GetLastError()
);
}
}
}
#[no_mangle]
extern "C" fn RustBacktraceMutexUnlock() {
unsafe {
let mutex = RUST_BACKTRACE_MUTEX.load(Ordering::Acquire);
if !mutex.is_null() {
assert_ne!(ReleaseMutex(mutex), 0, "{}", GetLastError());
}
}
}