Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -817,7 +817,6 @@ dependencies = [
"thiserror 2.0.16",
"tracelimit",
"tracing",
"winapi",
"windows-sys 0.61.0",
"zerocopy 0.8.27",
]
Expand Down Expand Up @@ -5491,6 +5490,8 @@ dependencies = [
"widestring",
"winapi",
"windows 0.62.0",
"windows-result 0.4.0",
"windows-sys 0.61.0",
]

[[package]]
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@ win_etw_provider = "0.1.11"
win_etw_tracing = "0.1.4"
winapi = "0.3"
windows = "0.62"
windows-result = "0.4"
windows-service = "0.8"
windows-sys = "0.61"
windows-version = "0.1.4"
Expand Down
9 changes: 8 additions & 1 deletion support/pal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pal_event.workspace = true
ntapi = { workspace = true, features = ["impl-default"] }
socket2 = { workspace = true, features = ["all"] }
widestring.workspace = true
windows-result.workspace = true

[target.'cfg(windows)'.dependencies.winapi]
workspace = true
Expand Down Expand Up @@ -54,7 +55,13 @@ workspace = true
features = [
"Wdk_Foundation",
"Win32_Foundation",
"Win32_Security"
"Win32_Security",
]

[target.'cfg(windows)'.dependencies.windows-sys]
workspace = true
features = [
Comment thread
damanm24 marked this conversation as resolved.
"Win32_System_LibraryLoader",
]

[target.'cfg(unix)'.dependencies]
Expand Down
9 changes: 9 additions & 0 deletions support/pal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ pub mod process;
pub mod unix;
pub mod windows;

// Re-export windows_sys and windows_result so that the delayload! macro works
// when used in other crates
#[cfg(windows)]
#[doc(hidden)]
pub use windows_result;
Comment thread
damanm24 marked this conversation as resolved.
#[cfg(windows)]
#[doc(hidden)]
pub use windows_sys;

pub use sys::close_stdout;
pub use sys::pipe::pair as pipe_pair;

Expand Down
123 changes: 102 additions & 21 deletions support/pal/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1006,24 +1006,29 @@ macro_rules! delayload {
$visibility:vis fn $name:ident($($params:ident : $types:ty),* $(,)?) -> $result:ty;
)*
}} => {
fn get_module() -> Result<::winapi::shared::minwindef::HINSTANCE, ::winapi::shared::minwindef::DWORD> {
fn get_module() -> Result<$crate::windows_sys::Win32::Foundation::HMODULE, $crate::windows_sys::Win32::Foundation::WIN32_ERROR> {
use ::std::ffi::c_void;
use ::std::ptr::null_mut;
use ::std::sync::atomic::{AtomicPtr, Ordering};
use ::winapi::um::{
errhandlingapi::GetLastError,
libloaderapi::{FreeLibrary, LoadLibraryA},
use $crate::windows_sys::Win32::{
Foundation::{FreeLibrary, GetLastError},
System::LibraryLoader::{LoadLibraryA},
};

static MODULE: AtomicPtr<::winapi::shared::minwindef::HINSTANCE__> = AtomicPtr::new(null_mut());
let mut module = MODULE.load(Ordering::Relaxed);
static MODULE: AtomicPtr<c_void> = AtomicPtr::new(null_mut());
let mut module = MODULE.load(Ordering::Acquire);
if module.is_null() {
module = unsafe { LoadLibraryA(concat!($dll, "\0").as_ptr() as *const i8) };
if module.is_null() {
let new_module = unsafe { LoadLibraryA(concat!($dll, "\0").as_ptr().cast::<u8>()) };
if new_module.is_null() {
return Err(unsafe { GetLastError() });
}
Comment thread
damanm24 marked this conversation as resolved.
let old_module = MODULE.swap(module, Ordering::Relaxed);
if !old_module.is_null() {
unsafe { FreeLibrary(old_module) };
match MODULE.compare_exchange(null_mut(), new_module, Ordering::Release, Ordering::Acquire) {
Ok(_) => module = new_module,
Err(old_module) => {
// Another thread won the race, use their module and free ours
unsafe { FreeLibrary(new_module) };
module = old_module;
}
}
}
Ok(module)
Expand All @@ -1033,12 +1038,12 @@ macro_rules! delayload {
#![expect(non_snake_case)]
$(
$(#[$a])*
pub fn $name() -> Result<usize, ::winapi::shared::minwindef::DWORD> {
pub fn $name() -> Result<usize, $crate::windows_sys::Win32::Foundation::WIN32_ERROR> {
use ::std::concat;
use ::std::sync::atomic::{AtomicUsize, Ordering};
use ::winapi::{
shared::winerror::ERROR_PROC_NOT_FOUND,
um::libloaderapi::GetProcAddress,
use $crate::windows_sys::Win32::{
Foundation::ERROR_PROC_NOT_FOUND,
System::LibraryLoader::GetProcAddress,
};

// A FNCELL value 0 denotes that GetProcAddress has never been
Expand All @@ -1053,8 +1058,9 @@ macro_rules! delayload {
let module = super::get_module()?;
fnval = unsafe { GetProcAddress(
module,
concat!(stringify!($name), "\0").as_ptr() as *const i8) }
as usize;
concat!(stringify!($name), "\0").as_ptr().cast::<u8>()) }
.map(|f| f as usize)
.unwrap_or(0);
if fnval == 0 {
fnval = 1;
}
Expand Down Expand Up @@ -1086,8 +1092,10 @@ macro_rules! delayload {
match funcs::$name() {
Ok(fnval) => {
type FnType = unsafe extern "system" fn($($params: $types,)*) -> $result;
let fnptr: FnType = ::std::mem::transmute(fnval);
fnptr($($params,)*)
// SAFETY: fnval is a valid function pointer obtained from GetProcAddress
let fnptr: FnType = unsafe { ::std::mem::transmute(fnval) };
// SAFETY: The function pointer is valid and the caller must uphold the function's safety contract
unsafe { fnptr($($params,)*) }
},
Err(win32) => {
$crate::delayload!(@result_from_win32(($result), win32))
Expand All @@ -1097,10 +1105,10 @@ macro_rules! delayload {
)*
};

(@result_from_win32((i32), $val:expr)) => { ::winapi::shared::winerror::HRESULT_FROM_WIN32($val) };
(@result_from_win32((i32), $val:expr)) => { $crate::windows_result::HRESULT::from_win32($val) };
(@result_from_win32((u32), $val:expr)) => { $val };
(@result_from_win32((DWORD), $val:expr)) => { $val };
(@result_from_win32((HRESULT), $val:expr)) => { ::winapi::shared::winerror::HRESULT_FROM_WIN32($val) };
(@result_from_win32((HRESULT), $val:expr)) => { $crate::windows_result::HRESULT::from_win32($val) };
(@result_from_win32(($t:tt), $val:expr)) => { panic!("could not load: {}", $val) };
}

Expand Down Expand Up @@ -1155,4 +1163,77 @@ mod tests {
let s: UnicodeString = "abc".try_into().unwrap();
assert!(s.as_slice().iter().copied().eq("abc".encode_utf16()));
}

#[test]
fn test_delayload() {
// Test delayloading kernel32.dll functions that are guaranteed to exist
mod kernel32_delayload {
delayload! { "kernel32.dll" {
pub fn GetCurrentProcessId() -> u32;
pub fn GetCurrentThreadId() -> u32;
}}
}

// Test is_supported for functions that exist
assert!(kernel32_delayload::is_supported::GetCurrentProcessId());
assert!(kernel32_delayload::is_supported::GetCurrentThreadId());

// Test that the functions work correctly
unsafe {
let pid = kernel32_delayload::GetCurrentProcessId();
assert_ne!(pid, 0, "Process ID should not be zero");

let tid = kernel32_delayload::GetCurrentThreadId();
assert_ne!(tid, 0, "Thread ID should not be zero");
}
}

#[test]
fn test_delayload_missing_function() {
// Test delayloading a function that doesn't exist
mod kernel32_missing {
delayload! { "kernel32.dll" {
#[allow(dead_code)]
pub fn NonExistentFunction() -> u32;
}}
}

// Test that is_supported correctly identifies missing functions
assert!(!kernel32_missing::is_supported::NonExistentFunction());
}

#[test]
fn test_delayload_missing_dll() {
// Test delayloading from a DLL that doesn't exist
mod missing_dll {
delayload! { "this_dll_does_not_exist_12345.dll" {
#[allow(dead_code)]
pub fn SomeFunction() -> u32;
}}
}

// Test that is_supported returns false when the DLL doesn't exist
assert!(!missing_dll::is_supported::SomeFunction());
}

#[test]
fn test_delayload_multiple_calls() {
// Test that multiple calls to the same delayloaded function work
// and that the function pointer is cached correctly
mod kernel32_cached {
delayload! { "kernel32.dll" {
pub fn GetCurrentProcessId() -> u32;
}}
}

unsafe {
let pid1 = kernel32_cached::GetCurrentProcessId();
let pid2 = kernel32_cached::GetCurrentProcessId();
let pid3 = kernel32_cached::GetCurrentProcessId();

// All calls should return the same PID
assert_eq!(pid1, pid2);
assert_eq!(pid2, pid3);
}
}
}
1 change: 0 additions & 1 deletion vm/devices/net/net_consomme/consomme/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ resolv-conf.workspace = true
[target.'cfg(windows)'.dependencies]
pal.workspace = true
slab.workspace = true
winapi.workspace = true
windows-sys = { workspace = true, features = ["Win32_Foundation", "Win32_System_IO", "Win32_NetworkManagement_Dns", "Win32_NetworkManagement_IpHelper", "Win32_NetworkManagement_Ndis", "Win32_Networking_WinSock", "Win32_System_LibraryLoader"] }

[lints]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
// Licensed under the MIT License.

//! Raw DNS API bindings with delay-loading support.

// Ensure winapi dependency is recognized as used (required for pal::delayload!)
use winapi as _;

use windows_sys::Win32::NetworkManagement::Dns::DNS_QUERY_RAW_CANCEL;
use windows_sys::Win32::NetworkManagement::Dns::DNS_QUERY_RAW_REQUEST;
use windows_sys::Win32::NetworkManagement::Dns::DNS_QUERY_RAW_RESULT;
Expand Down
1 change: 0 additions & 1 deletion vm/devices/net/net_consomme/consomme/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use socket2::Socket;
use std::net::Ipv6Addr;
use std::os::windows::io::AsRawSocket;
use std::ptr::null_mut;
use winapi as _;
use windows_sys::Win32::Foundation::ERROR_SUCCESS;
use windows_sys::Win32::NetworkManagement::IpHelper::MIB_UNICASTIPADDRESS_TABLE;
use windows_sys::Win32::Networking::WinSock;
Expand Down