diff --git a/dev-tools/gen-windows-sys-binding/Cargo.toml b/dev-tools/gen-windows-sys-binding/Cargo.toml index a1ef503cd..9166030e0 100644 --- a/dev-tools/gen-windows-sys-binding/Cargo.toml +++ b/dev-tools/gen-windows-sys-binding/Cargo.toml @@ -5,4 +5,5 @@ edition = "2018" publish = false [dependencies] -windows-bindgen = "0.49" +windows-bindgen = "0.53" +tempfile = "3" diff --git a/dev-tools/gen-windows-sys-binding/src/main.rs b/dev-tools/gen-windows-sys-binding/src/main.rs index 01b4b975f..2a5444f92 100644 --- a/dev-tools/gen-windows-sys-binding/src/main.rs +++ b/dev-tools/gen-windows-sys-binding/src/main.rs @@ -3,7 +3,7 @@ use std::{ fs, - io::{self, Write}, + io::{self, Read, Write}, }; /// This is printed to the file before the rest of the contents. @@ -17,49 +17,48 @@ const PRELUDE: &str = r#"// This file is autogenerated. // ``` "#; -const POSTLUDE: &str = r#" -/// Adapted from -/// [`core::ptr::invalid_mut()`](https://doc.rust-lang.org/src/core/ptr/mod.rs.html#600-607). -/// -/// This function should actually use `core::mem::transmute` but due to msrv -/// we use `as` casting instead. -/// -/// Once msrv is bumped to 1.56, replace this with `core::mem::transmute` since -/// it is const stablised in 1.56 -/// -/// NOTE that once supports `strict_provenance` we would also have to update -/// this. -const fn invalid_mut(addr: usize) -> *mut T { - addr as *mut T -} -"#; - fn main() -> io::Result<()> { let manifest_dir = env!("CARGO_MANIFEST_DIR"); - // Load the list of APIs + let temp_file = tempfile::Builder::new() + .suffix(".rs") + .tempfile() + .expect("failed to create temp file"); + + // Common args to windows_bindgen. + let mut args = vec![ + "--config", + "std", + "flatten", + "--out", + temp_file.path().to_str().unwrap(), + "--filter", + ]; + + // Append the list of APIs let buffer = fs::read_to_string(format!("{manifest_dir}/windows_sys.list")) - .expect("failed to read windows_sys.list"); - let names: Vec<&str> = buffer - .lines() - .filter_map(|line| { - let line = line.trim(); - if line.is_empty() || line.starts_with("//") { - None - } else { - Some(line) - } - }) - .collect(); + .expect("failed to read list"); + args.extend(buffer.lines().filter_map(|line| { + let line = line.trim(); + if line.is_empty() || line.starts_with("//") { + None + } else { + Some(line) + } + })); + + // Generate bindings. + windows_bindgen::bindgen(&args).expect("running bindgen failed"); - // Write the bindings to windows-sys.rs - let bindings = - windows_bindgen::standalone_std(&names).replace("::core::ptr::invalid_mut", "invalid_mut"); + let mut bindings = String::new(); + fs::File::open(temp_file.path()) + .expect("failed to open temp windows_sys.rs") + .read_to_string(&mut bindings) + .expect("failed to read temp windows_sys.rs"); let mut f = fs::File::create(format!("{manifest_dir}/../../src/windows/windows_sys.rs")) .expect("failed to create windows_sys.rs"); f.write_all(PRELUDE.as_bytes())?; f.write_all(bindings.as_bytes())?; - f.write_all(POSTLUDE.as_bytes())?; Ok(()) } diff --git a/dev-tools/gen-windows-sys-binding/windows_sys.list b/dev-tools/gen-windows-sys-binding/windows_sys.list index ec8b02793..327bed830 100644 --- a/dev-tools/gen-windows-sys-binding/windows_sys.list +++ b/dev-tools/gen-windows-sys-binding/windows_sys.list @@ -11,6 +11,7 @@ Windows.Win32.Foundation.WAIT_OBJECT_0 Windows.Win32.Foundation.WAIT_TIMEOUT Windows.Win32.Foundation.WAIT_FAILED Windows.Win32.Foundation.WAIT_ABANDONED +Windows.Win32.Foundation.FreeLibrary Windows.Win32.System.Com.SAFEARRAY Windows.Win32.System.Com.SAFEARRAYBOUND @@ -19,6 +20,9 @@ Windows.Win32.System.Com.COINIT_MULTITHREADED Windows.Win32.System.Com.CoCreateInstance Windows.Win32.System.Com.CoInitializeEx +Windows.Win32.System.LibraryLoader.GetProcAddress +Windows.Win32.System.LibraryLoader.LoadLibraryA + Windows.Win32.System.Pipes.PeekNamedPipe Windows.Win32.System.Registry.RegCloseKey @@ -31,9 +35,13 @@ Windows.Win32.System.Registry.KEY_READ Windows.Win32.System.Registry.KEY_WOW64_32KEY Windows.Win32.System.Registry.REG_SZ +Windows.Win32.System.SystemInformation.IMAGE_FILE_MACHINE_AMD64 + +Windows.Win32.System.Threading.GetMachineTypeAttributes Windows.Win32.System.Threading.ReleaseSemaphore Windows.Win32.System.Threading.WaitForSingleObject Windows.Win32.System.Threading.SEMAPHORE_MODIFY_STATE Windows.Win32.System.Threading.THREAD_SYNCHRONIZE +Windows.Win32.System.Threading.UserEnabled Windows.Win32.System.WindowsProgramming.OpenSemaphoreA diff --git a/src/windows/com.rs b/src/windows/com.rs index e85348d7a..e81bb1d3c 100644 --- a/src/windows/com.rs +++ b/src/windows/com.rs @@ -15,6 +15,7 @@ use crate::windows::{ }, }; use std::{ + convert::TryInto, ffi::{OsStr, OsString}, mem::ManuallyDrop, ops::Deref, @@ -24,7 +25,7 @@ use std::{ }; pub fn initialize() -> Result<(), HRESULT> { - let err = unsafe { CoInitializeEx(null(), COINIT_MULTITHREADED) }; + let err = unsafe { CoInitializeEx(null(), COINIT_MULTITHREADED.try_into().unwrap()) }; if err != S_OK && err != S_FALSE { // S_FALSE just means COM is already initialized Err(err) diff --git a/src/windows/find_tools.rs b/src/windows/find_tools.rs index 23348cacf..9c6511555 100644 --- a/src/windows/find_tools.rs +++ b/src/windows/find_tools.rs @@ -163,6 +163,10 @@ mod impl_ { use crate::windows::registry::{RegistryKey, LOCAL_MACHINE}; use crate::windows::setup_config::SetupConfiguration; use crate::windows::vs_instances::{VsInstances, VswhereInstance}; + use crate::windows::windows_sys::{ + FreeLibrary, GetMachineTypeAttributes, GetProcAddress, LoadLibraryA, UserEnabled, HMODULE, + IMAGE_FILE_MACHINE_AMD64, MACHINE_ATTRIBUTES, S_OK, + }; use std::convert::TryFrom; use std::env; use std::ffi::OsString; @@ -173,6 +177,8 @@ mod impl_ { use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Once; use super::MSVC_FAMILY; use crate::Tool; @@ -199,6 +205,71 @@ mod impl_ { include: Vec, } + struct LibraryHandle(HMODULE); + + impl LibraryHandle { + fn new(name: &[u8]) -> Option { + let handle = unsafe { LoadLibraryA(name.as_ptr() as _) }; + (!handle.is_null()).then(|| Self(handle)) + } + + /// Get a function pointer to a function in the library. + /// SAFETY: The caller must ensure that the function signature matches the actual function. + /// The easiest way to do this is to add an entry to windows_sys_no_link.list and use the + /// generated function for `func_signature`. + unsafe fn get_proc_address(&self, name: &[u8]) -> Option { + let symbol = unsafe { GetProcAddress(self.0, name.as_ptr() as _) }; + symbol.map(|symbol| unsafe { mem::transmute_copy(&symbol) }) + } + } + + impl Drop for LibraryHandle { + fn drop(&mut self) { + unsafe { FreeLibrary(self.0) }; + } + } + + type GetMachineTypeAttributesFuncType = + unsafe extern "system" fn(u16, *mut MACHINE_ATTRIBUTES) -> i32; + const _: () = { + // Ensure that our hand-written signature matches the actual function signature. + // We can't use `GetMachineTypeAttributes` outside of a const scope otherwise we'll end up statically linking to + // it, which will fail to load on older versions of Windows. + let _: GetMachineTypeAttributesFuncType = GetMachineTypeAttributes; + }; + + fn is_amd64_emulation_supported_inner() -> Option { + // GetMachineTypeAttributes is only available on Win11 22000+, so dynamically load it. + let kernel32 = LibraryHandle::new(b"kernel32.dll\0")?; + // SAFETY: GetMachineTypeAttributesFuncType is checked to match the real function signature. + let get_machine_type_attributes = unsafe { + kernel32 + .get_proc_address::(b"GetMachineTypeAttributes\0") + }?; + let mut attributes = Default::default(); + if unsafe { get_machine_type_attributes(IMAGE_FILE_MACHINE_AMD64, &mut attributes) } == S_OK + { + Some((attributes & UserEnabled) != 0) + } else { + Some(false) + } + } + + fn is_amd64_emulation_supported() -> bool { + // TODO: Replace with a OnceLock once MSRV is 1.70. + static LOAD_VALUE: Once = Once::new(); + static IS_SUPPORTED: AtomicBool = AtomicBool::new(false); + + // Using Relaxed ordering since the Once is providing synchronization. + LOAD_VALUE.call_once(|| { + IS_SUPPORTED.store( + is_amd64_emulation_supported_inner().unwrap_or(false), + Ordering::Relaxed, + ); + }); + IS_SUPPORTED.load(Ordering::Relaxed) + } + impl MsvcTool { fn new(tool: PathBuf) -> MsvcTool { MsvcTool { @@ -226,7 +297,6 @@ mod impl_ { /// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the /// given target's arch. Returns `None` if the variable does not exist. - #[cfg(windows)] fn is_vscmd_target(target: TargetArch<'_>) -> Option { let vscmd_arch = env::var("VSCMD_ARG_TGT_ARCH").ok()?; // Convert the Rust target arch to its VS arch equivalent. @@ -482,27 +552,41 @@ mod impl_ { ) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, Option, PathBuf)> { let version = vs15plus_vc_read_version(instance_path)?; - let host = match host_arch() { - X86 => "X86", - X86_64 => "X64", - // There is no natively hosted compiler on ARM64. - // Instead, use the x86 toolchain under emulation (there is no x64 emulation). - AARCH64 => "X86", + let hosts = match host_arch() { + X86 => &["X86"], + X86_64 => &["X64"], + // Starting with VS 17.4, there is a natively hosted compiler on ARM64: + // https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/ + // On older versions of VS, we use x64 if running under emulation is supported, + // otherwise use x86. + AARCH64 => { + if is_amd64_emulation_supported() { + &["ARM64", "X64", "X86"][..] + } else { + &["ARM64", "X86"] + } + } _ => return None, }; let target = lib_subdir(target)?; // The directory layout here is MSVC/bin/Host$host/$target/ let path = instance_path.join(r"VC\Tools\MSVC").join(version); + // We use the first available host architecture that can build for the target + let (host_path, host) = hosts.iter().find_map(|&x| { + let candidate = path.join("bin").join(format!("Host{}", x)); + if candidate.join(target).exists() { + Some((candidate, x)) + } else { + None + } + })?; // This is the path to the toolchain for a particular target, running // on a given host - let bin_path = path.join("bin").join(format!("Host{}", host)).join(target); + let bin_path = host_path.join(target); // But! we also need PATH to contain the target directory for the host // architecture, because it contains dlls like mspdb140.dll compiled for // the host architecture. - let host_dylib_path = path - .join("bin") - .join(format!("Host{}", host)) - .join(host.to_lowercase()); + let host_dylib_path = host_path.join(host.to_lowercase()); let lib_path = path.join("lib").join(target); let alt_lib_path = (target == "arm64ec").then(|| path.join("lib").join("arm64ec")); let include_path = path.join("include"); diff --git a/src/windows/windows_sys.rs b/src/windows/windows_sys.rs index 88ca76730..8b98ce97f 100644 --- a/src/windows/windows_sys.rs +++ b/src/windows/windows_sys.rs @@ -6,7 +6,7 @@ // cd generate-windows-sys/ // cargo run // ``` -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `windows-bindgen` 0.53.0 #![allow( non_snake_case, @@ -54,6 +54,25 @@ extern "system" { ) -> WIN32_ERROR; } #[link(name = "kernel32")] +extern "system" { + pub fn FreeLibrary(hlibmodule: HMODULE) -> BOOL; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetMachineTypeAttributes( + machine: u16, + machinetypeattributes: *mut MACHINE_ATTRIBUTES, + ) -> HRESULT; +} +#[link(name = "kernel32")] +extern "system" { + pub fn GetProcAddress(hmodule: HMODULE, lpprocname: PCSTR) -> FARPROC; +} +#[link(name = "kernel32")] +extern "system" { + pub fn LoadLibraryA(lplibfilename: PCSTR) -> HMODULE; +} +#[link(name = "kernel32")] extern "system" { pub fn OpenSemaphoreA(dwdesiredaccess: u32, binherithandle: BOOL, lpname: PCSTR) -> HANDLE; } @@ -78,13 +97,13 @@ extern "system" { } #[link(name = "kernel32")] extern "system" { - pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WIN32_ERROR; + pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WAIT_EVENT; } #[link(name = "ole32")] extern "system" { pub fn CoCreateInstance( rclsid: *const GUID, - punkouter: IUnknown, + punkouter: *mut ::core::ffi::c_void, dwclscontext: CLSCTX, riid: *const GUID, ppv: *mut *mut ::core::ffi::c_void, @@ -92,11 +111,11 @@ extern "system" { } #[link(name = "ole32")] extern "system" { - pub fn CoInitializeEx(pvreserved: *const ::core::ffi::c_void, dwcoinit: COINIT) -> HRESULT; + pub fn CoInitializeEx(pvreserved: *const ::core::ffi::c_void, dwcoinit: u32) -> HRESULT; } #[link(name = "oleaut32")] extern "system" { - pub fn SysFreeString(bstrstring: BSTR) -> (); + pub fn SysFreeString(bstrstring: BSTR); } #[link(name = "oleaut32")] extern "system" { @@ -112,6 +131,7 @@ pub const COINIT_MULTITHREADED: COINIT = 0i32; pub const ERROR_NO_MORE_ITEMS: WIN32_ERROR = 259u32; pub const ERROR_SUCCESS: WIN32_ERROR = 0u32; pub const FALSE: BOOL = 0i32; +pub type FARPROC = ::core::option::Option isize>; #[repr(C)] pub struct FILETIME { pub dwLowDateTime: u32, @@ -130,6 +150,12 @@ pub struct GUID { pub data3: u16, pub data4: [u8; 8], } +impl ::core::marker::Copy for GUID {} +impl ::core::clone::Clone for GUID { + fn clone(&self) -> Self { + *self + } +} impl GUID { pub const fn from_u128(uuid: u128) -> Self { Self { @@ -140,19 +166,16 @@ impl GUID { } } } -impl ::core::marker::Copy for GUID {} -impl ::core::clone::Clone for GUID { - fn clone(&self) -> Self { - *self - } -} pub type HANDLE = *mut ::core::ffi::c_void; pub type HKEY = *mut ::core::ffi::c_void; -pub const HKEY_LOCAL_MACHINE: HKEY = invalid_mut(-2147483646i32 as _); +pub const HKEY_LOCAL_MACHINE: HKEY = -2147483646i32 as _; +pub type HMODULE = *mut ::core::ffi::c_void; pub type HRESULT = i32; -pub type IUnknown = *mut ::core::ffi::c_void; +pub type IMAGE_FILE_MACHINE = u16; +pub const IMAGE_FILE_MACHINE_AMD64: IMAGE_FILE_MACHINE = 34404u16; pub const KEY_READ: REG_SAM_FLAGS = 131097u32; pub const KEY_WOW64_32KEY: REG_SAM_FLAGS = 512u32; +pub type MACHINE_ATTRIBUTES = i32; pub type PCSTR = *const u8; pub type PCWSTR = *const u16; pub type PWSTR = *mut u16; @@ -187,27 +210,14 @@ impl ::core::clone::Clone for SAFEARRAYBOUND { } pub const SEMAPHORE_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32; pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32; -pub const S_FALSE: HRESULT = 1i32; -pub const S_OK: HRESULT = 0i32; +pub const S_FALSE: HRESULT = 0x1_u32 as _; +pub const S_OK: HRESULT = 0x0_u32 as _; pub type THREAD_ACCESS_RIGHTS = u32; pub const THREAD_SYNCHRONIZE: THREAD_ACCESS_RIGHTS = 1048576u32; -pub const WAIT_ABANDONED: WIN32_ERROR = 128u32; -pub const WAIT_FAILED: WIN32_ERROR = 4294967295u32; -pub const WAIT_OBJECT_0: WIN32_ERROR = 0u32; -pub const WAIT_TIMEOUT: WIN32_ERROR = 258u32; +pub const UserEnabled: MACHINE_ATTRIBUTES = 1i32; +pub const WAIT_ABANDONED: WAIT_EVENT = 128u32; +pub type WAIT_EVENT = u32; +pub const WAIT_FAILED: WAIT_EVENT = 4294967295u32; +pub const WAIT_OBJECT_0: WAIT_EVENT = 0u32; +pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32; pub type WIN32_ERROR = u32; - -/// Adapted from -/// [`core::ptr::invalid_mut()`](https://doc.rust-lang.org/src/core/ptr/mod.rs.html#600-607). -/// -/// This function should actually use `core::mem::transmute` but due to msrv -/// we use `as` casting instead. -/// -/// Once msrv is bumped to 1.56, replace this with `core::mem::transmute` since -/// it is const stablised in 1.56 -/// -/// NOTE that once supports `strict_provenance` we would also have to update -/// this. -const fn invalid_mut(addr: usize) -> *mut T { - addr as *mut T -}