Skip to content

Commit 0b1309a

Browse files
committed
Add support for Native ARM64 and x64 if available
1 parent 29d95b5 commit 0b1309a

File tree

6 files changed

+166
-83
lines changed

6 files changed

+166
-83
lines changed

dev-tools/gen-windows-sys-binding/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ edition = "2018"
55
publish = false
66

77
[dependencies]
8-
windows-bindgen = "0.49"
8+
windows-bindgen = "0.53"

dev-tools/gen-windows-sys-binding/src/main.rs

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
use std::{
55
fs,
6-
io::{self, Write},
6+
io::{self, Read, Write},
77
};
88

99
/// This is printed to the file before the rest of the contents.
@@ -17,49 +17,46 @@ const PRELUDE: &str = r#"// This file is autogenerated.
1717
// ```
1818
"#;
1919

20-
const POSTLUDE: &str = r#"
21-
/// Adapted from
22-
/// [`core::ptr::invalid_mut()`](https://doc.rust-lang.org/src/core/ptr/mod.rs.html#600-607).
23-
///
24-
/// This function should actually use `core::mem::transmute` but due to msrv
25-
/// we use `as` casting instead.
26-
///
27-
/// Once msrv is bumped to 1.56, replace this with `core::mem::transmute` since
28-
/// it is const stablised in 1.56
29-
///
30-
/// NOTE that once supports `strict_provenance` we would also have to update
31-
/// this.
32-
const fn invalid_mut<T>(addr: usize) -> *mut T {
33-
addr as *mut T
34-
}
35-
"#;
36-
3720
fn main() -> io::Result<()> {
3821
let manifest_dir = env!("CARGO_MANIFEST_DIR");
39-
// Load the list of APIs
22+
let temp_output_path = std::env::temp_dir().join("windows_sys.rs");
23+
24+
// Common args to windows_bindgen.
25+
let mut args = vec![
26+
"--config",
27+
"std",
28+
"flatten",
29+
"--out",
30+
temp_output_path.to_str().unwrap(),
31+
"--filter",
32+
];
33+
34+
// Append the list of APIs
4035
let buffer = fs::read_to_string(format!("{manifest_dir}/windows_sys.list"))
41-
.expect("failed to read windows_sys.list");
42-
let names: Vec<&str> = buffer
43-
.lines()
44-
.filter_map(|line| {
45-
let line = line.trim();
46-
if line.is_empty() || line.starts_with("//") {
47-
None
48-
} else {
49-
Some(line)
50-
}
51-
})
52-
.collect();
36+
.expect("failed to read list");
37+
args.extend(buffer.lines().filter_map(|line| {
38+
let line = line.trim();
39+
if line.is_empty() || line.starts_with("//") {
40+
None
41+
} else {
42+
Some(line)
43+
}
44+
}));
45+
46+
// Generate bindings.
47+
windows_bindgen::bindgen(&args).expect("running bindgen failed");
5348

54-
// Write the bindings to windows-sys.rs
55-
let bindings =
56-
windows_bindgen::standalone_std(&names).replace("::core::ptr::invalid_mut", "invalid_mut");
49+
let mut bindings = String::new();
50+
fs::File::open(&temp_output_path)
51+
.expect("failed to open temp windows_sys.rs")
52+
.read_to_string(&mut bindings)
53+
.expect("failed to read temp windows_sys.rs");
54+
fs::remove_file(temp_output_path).expect("failed to remove temp windows_sys.rs");
5755

5856
let mut f = fs::File::create(format!("{manifest_dir}/../../src/windows/windows_sys.rs"))
5957
.expect("failed to create windows_sys.rs");
6058
f.write_all(PRELUDE.as_bytes())?;
6159
f.write_all(bindings.as_bytes())?;
62-
f.write_all(POSTLUDE.as_bytes())?;
6360

6461
Ok(())
6562
}

dev-tools/gen-windows-sys-binding/windows_sys.list

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Windows.Win32.Foundation.WAIT_OBJECT_0
1111
Windows.Win32.Foundation.WAIT_TIMEOUT
1212
Windows.Win32.Foundation.WAIT_FAILED
1313
Windows.Win32.Foundation.WAIT_ABANDONED
14+
Windows.Win32.Foundation.FreeLibrary
1415

1516
Windows.Win32.System.Com.SAFEARRAY
1617
Windows.Win32.System.Com.SAFEARRAYBOUND
@@ -19,6 +20,9 @@ Windows.Win32.System.Com.COINIT_MULTITHREADED
1920
Windows.Win32.System.Com.CoCreateInstance
2021
Windows.Win32.System.Com.CoInitializeEx
2122

23+
Windows.Win32.System.LibraryLoader.GetProcAddress
24+
Windows.Win32.System.LibraryLoader.LoadLibraryA
25+
2226
Windows.Win32.System.Pipes.PeekNamedPipe
2327

2428
Windows.Win32.System.Registry.RegCloseKey
@@ -31,9 +35,13 @@ Windows.Win32.System.Registry.KEY_READ
3135
Windows.Win32.System.Registry.KEY_WOW64_32KEY
3236
Windows.Win32.System.Registry.REG_SZ
3337

38+
Windows.Win32.System.SystemInformation.IMAGE_FILE_MACHINE_AMD64
39+
40+
Windows.Win32.System.Threading.GetMachineTypeAttributes
3441
Windows.Win32.System.Threading.ReleaseSemaphore
3542
Windows.Win32.System.Threading.WaitForSingleObject
3643
Windows.Win32.System.Threading.SEMAPHORE_MODIFY_STATE
3744
Windows.Win32.System.Threading.THREAD_SYNCHRONIZE
45+
Windows.Win32.System.Threading.UserEnabled
3846

3947
Windows.Win32.System.WindowsProgramming.OpenSemaphoreA

src/windows/com.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use std::{
2424
};
2525

2626
pub fn initialize() -> Result<(), HRESULT> {
27-
let err = unsafe { CoInitializeEx(null(), COINIT_MULTITHREADED) };
27+
let err = unsafe { CoInitializeEx(null(), COINIT_MULTITHREADED as u32) };
2828
if err != S_OK && err != S_FALSE {
2929
// S_FALSE just means COM is already initialized
3030
Err(err)

src/windows/find_tools.rs

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ mod impl_ {
163163
use crate::windows::registry::{RegistryKey, LOCAL_MACHINE};
164164
use crate::windows::setup_config::SetupConfiguration;
165165
use crate::windows::vs_instances::{VsInstances, VswhereInstance};
166+
use crate::windows::windows_sys::{
167+
FreeLibrary, GetMachineTypeAttributes, GetProcAddress, LoadLibraryA, UserEnabled, HMODULE,
168+
IMAGE_FILE_MACHINE_AMD64, MACHINE_ATTRIBUTES, S_OK,
169+
};
166170
use std::convert::TryFrom;
167171
use std::env;
168172
use std::ffi::OsString;
@@ -199,6 +203,64 @@ mod impl_ {
199203
include: Vec<PathBuf>,
200204
}
201205

206+
struct LibraryHandle(HMODULE);
207+
208+
impl LibraryHandle {
209+
fn new(name: &[u8]) -> Option<Self> {
210+
let handle = unsafe { LoadLibraryA(name.as_ptr() as _) };
211+
(!handle.is_null()).then(|| Self(handle))
212+
}
213+
214+
/// Get a function pointer to a function in the library.
215+
/// SAFETY: The caller must ensure that the function signature matches the actual function.
216+
/// The easiest way to do this is to add an entry to windows_sys_no_link.list and use the
217+
/// generated function for `func_signature`.
218+
unsafe fn get_proc_address<F>(&self, name: &[u8]) -> Option<F> {
219+
let symbol = unsafe { GetProcAddress(self.0, name.as_ptr() as _) };
220+
symbol.map(|symbol| unsafe { mem::transmute_copy(&symbol) })
221+
}
222+
}
223+
224+
impl Drop for LibraryHandle {
225+
fn drop(&mut self) {
226+
unsafe { FreeLibrary(self.0) };
227+
}
228+
}
229+
230+
type GetMachineTypeAttributesFuncType =
231+
unsafe extern "system" fn(u16, *mut MACHINE_ATTRIBUTES) -> i32;
232+
const _: () = {
233+
// Ensure that our hand-written signature matches the actual function signature.
234+
// We can't use `GetMachineTypeAttributes` outside of a const scope otherwise we'll end up statically linking to
235+
// it, which will fail to load on older versions of Windows.
236+
let _: GetMachineTypeAttributesFuncType = GetMachineTypeAttributes;
237+
};
238+
239+
fn is_amd64_emulation_supported() -> bool {
240+
// GetMachineTypeAttributes is only available on Win11 22000+, so dynamically load it.
241+
if let Some(kernel32) = LibraryHandle::new(b"kernel32.dll\0") {
242+
// SAFETY: GetMachineTypeAttributesFuncType is checked to match the real function signature.
243+
if let Some(get_machine_type_attributes) = unsafe {
244+
kernel32.get_proc_address::<GetMachineTypeAttributesFuncType>(
245+
b"GetMachineTypeAttributes\0",
246+
)
247+
} {
248+
let mut attributes = Default::default();
249+
if unsafe { get_machine_type_attributes(IMAGE_FILE_MACHINE_AMD64, &mut attributes) }
250+
== S_OK
251+
{
252+
(attributes & UserEnabled) != 0
253+
} else {
254+
false
255+
}
256+
} else {
257+
false
258+
}
259+
} else {
260+
false
261+
}
262+
}
263+
202264
impl MsvcTool {
203265
fn new(tool: PathBuf) -> MsvcTool {
204266
MsvcTool {
@@ -226,7 +288,6 @@ mod impl_ {
226288

227289
/// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the
228290
/// given target's arch. Returns `None` if the variable does not exist.
229-
#[cfg(windows)]
230291
fn is_vscmd_target(target: TargetArch<'_>) -> Option<bool> {
231292
let vscmd_arch = env::var("VSCMD_ARG_TGT_ARCH").ok()?;
232293
// Convert the Rust target arch to its VS arch equivalent.
@@ -483,34 +544,41 @@ mod impl_ {
483544
let version = vs15plus_vc_read_version(instance_path)?;
484545

485546
let hosts = match host_arch() {
486-
X86 => vec!["X86"],
487-
X86_64 => vec!["X64"],
488-
// Starting with VS 17.3, there is a natively hosted compiler on ARM64.
489-
// On older versions of VS, we use the x86 toolchain under emulation.
490-
// We don't want to overcomplicate compatibility checks, so we ignore x64 emulation.
491-
AARCH64 => vec!["ARM64", "X86"],
547+
X86 => &["X86"],
548+
X86_64 => &["X64"],
549+
// Starting with VS 17.4, there is a natively hosted compiler on ARM64:
550+
// https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/
551+
// On older versions of VS, we use x64 if running under emulation is supported,
552+
// otherwise use x86.
553+
AARCH64 => {
554+
if is_amd64_emulation_supported() {
555+
&["ARM64", "X64", "X86"][..]
556+
} else {
557+
&["ARM64", "X86"]
558+
}
559+
}
492560
_ => return None,
493561
};
494562
let target = lib_subdir(target)?;
495563
// The directory layout here is MSVC/bin/Host$host/$target/
496564
let path = instance_path.join(r"VC\Tools\MSVC").join(version);
497565
// We use the first available host architecture that can build for the target
498566
let (host_path, host) = hosts.iter().find_map(|&x| {
499-
let candidate = path.join("bin").join(&format!("Host{}", x));
500-
if candidate.join(&target).exists() {
567+
let candidate = path.join("bin").join(format!("Host{}", x));
568+
if candidate.join(target).exists() {
501569
Some((candidate, x))
502570
} else {
503571
None
504572
}
505573
})?;
506574
// This is the path to the toolchain for a particular target, running
507575
// on a given host
508-
let bin_path = host_path.join(&target);
576+
let bin_path = host_path.join(target);
509577
// But! we also need PATH to contain the target directory for the host
510578
// architecture, because it contains dlls like mspdb140.dll compiled for
511579
// the host architecture.
512-
let host_dylib_path = host_path.join(&host.to_lowercase());
513-
let lib_path = path.join("lib").join(&target);
580+
let host_dylib_path = host_path.join(host.to_lowercase());
581+
let lib_path = path.join("lib").join(target);
514582
let alt_lib_path = (target == "arm64ec").then(|| path.join("lib").join("arm64ec"));
515583
let include_path = path.join("include");
516584
Some((

0 commit comments

Comments
 (0)