Skip to content

Commit 40defd6

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

File tree

10 files changed

+201
-91
lines changed

10 files changed

+201
-91
lines changed

dev-tools/cc-test/msvc2.exe

9 KB
Binary file not shown.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[package]
22
name = "gen-windows-sys-binding"
33
version = "0.0.0"
4-
edition = "2018"
4+
edition = "2021"
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: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,64 +2,78 @@
22
//! https://github.com/rust-lang/rust/blob/master/src/tools/generate-windows-sys/src/main.rs
33
44
use std::{
5-
fs,
6-
io::{self, Write},
5+
fs::{self, OpenOptions},
6+
io::{self, Read, Seek, SeekFrom, Write},
77
};
88

99
/// This is printed to the file before the rest of the contents.
1010
const PRELUDE: &str = r#"// This file is autogenerated.
1111
//
12-
// To add bindings, edit windows_sys.lst then run:
12+
// To add bindings, edit windows_sys.lst or windows_sys_no_link.lst then run:
1313
//
1414
// ```
1515
// cd generate-windows-sys/
1616
// cargo run
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
20+
fn generate_bindings<P, F>(api_list: P, out_path: &str, modify_contents: F) -> io::Result<()>
21+
where
22+
P: AsRef<std::path::Path>,
23+
F: FnOnce(String) -> String,
24+
{
25+
// Load the list of APIs
26+
let buffer = fs::read_to_string(api_list).expect("failed to read list");
27+
let names = buffer.lines().filter_map(|line| {
28+
let line = line.trim();
29+
if line.is_empty() || line.starts_with("//") {
30+
None
31+
} else {
32+
Some(line)
33+
}
34+
});
35+
36+
windows_bindgen::bindgen(
37+
["--config", "std", "flatten", "--out", out_path, "--filter"]
38+
.into_iter()
39+
.chain(names),
40+
)
41+
.expect("running bindgen failed");
42+
43+
let mut out_file = OpenOptions::new()
44+
.read(true)
45+
.write(true)
46+
.create(false)
47+
.open(out_path)?;
48+
let mut contents = String::new();
49+
out_file.read_to_string(&mut contents)?;
50+
51+
let contents = modify_contents(contents);
52+
53+
out_file.seek(SeekFrom::Start(0))?;
54+
out_file.set_len(0)?;
55+
out_file.write_all(PRELUDE.as_bytes())?;
56+
out_file.write_all(contents.as_bytes())?;
57+
58+
Ok(())
3459
}
35-
"#;
3660

3761
fn main() -> io::Result<()> {
3862
let manifest_dir = env!("CARGO_MANIFEST_DIR");
39-
// Load the list of APIs
40-
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();
53-
54-
// Write the bindings to windows-sys.rs
55-
let bindings =
56-
windows_bindgen::standalone_std(&names).replace("::core::ptr::invalid_mut", "invalid_mut");
63+
// Generate regular bindings.
64+
generate_bindings(
65+
format!("{manifest_dir}/windows_sys.list"),
66+
&format!("{manifest_dir}/../../src/windows/windows_sys.rs"),
67+
|contents| contents,
68+
)?;
5769

58-
let mut f = fs::File::create(format!("{manifest_dir}/../../src/windows/windows_sys.rs"))
59-
.expect("failed to create windows_sys.rs");
60-
f.write_all(PRELUDE.as_bytes())?;
61-
f.write_all(bindings.as_bytes())?;
62-
f.write_all(POSTLUDE.as_bytes())?;
70+
// Generate bindings without #[link] attributes - this is useful if you want the signature of a
71+
// function (e.g., for GetProcAddress) but don't want to link to it.
72+
generate_bindings(
73+
format!("{manifest_dir}/windows_sys_no_link.list"),
74+
&format!("{manifest_dir}/../../src/windows/windows_sys_no_link.rs"),
75+
|contents| contents.replace("#[link(", "// #[link("),
76+
)?;
6377

6478
Ok(())
6579
}

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

Lines changed: 7 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,12 @@ 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+
3440
Windows.Win32.System.Threading.ReleaseSemaphore
3541
Windows.Win32.System.Threading.WaitForSingleObject
3642
Windows.Win32.System.Threading.SEMAPHORE_MODIFY_STATE
3743
Windows.Win32.System.Threading.THREAD_SYNCHRONIZE
44+
Windows.Win32.System.Threading.UserEnabled
3845

3946
Windows.Win32.System.WindowsProgramming.OpenSemaphoreA
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Windows.Win32.System.Threading.GetMachineTypeAttributes

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: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ 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, GetProcAddress, LoadLibraryA, UserEnabled, HMODULE, IMAGE_FILE_MACHINE_AMD64,
168+
S_OK,
169+
};
170+
use crate::windows::windows_sys_no_link::GetMachineTypeAttributes;
166171
use std::convert::TryFrom;
167172
use std::env;
168173
use std::ffi::OsString;
@@ -199,6 +204,52 @@ mod impl_ {
199204
include: Vec<PathBuf>,
200205
}
201206

207+
struct LibraryHandle(HMODULE);
208+
209+
impl LibraryHandle {
210+
fn new(name: &[u8]) -> Option<Self> {
211+
let handle = unsafe { LoadLibraryA(name.as_ptr() as _) };
212+
(!handle.is_null()).then(|| Self(handle))
213+
}
214+
215+
/// Get a function pointer to a function in the library.
216+
/// SAFETY: The caller must ensure that the function signature matches the actual function.
217+
/// The easiest way to do this is to add an entry to windows_sys_no_link.list and use the
218+
/// generated function for `func_signature`.
219+
unsafe fn get_proc_address<F>(&self, name: &[u8], _func_signature: &F) -> Option<F> {
220+
let symbol = unsafe { GetProcAddress(self.0, name.as_ptr() as _) };
221+
symbol.map(|symbol| unsafe { mem::transmute_copy(&symbol) })
222+
}
223+
}
224+
225+
impl Drop for LibraryHandle {
226+
fn drop(&mut self) {
227+
unsafe { FreeLibrary(self.0) };
228+
}
229+
}
230+
231+
fn is_amd64_emulation_supported() -> bool {
232+
if let Some(kernel32) = LibraryHandle::new(b"kernel32.dll\0") {
233+
// GetMachineTypeAttributes is only available on Win11 22000+.
234+
if let Some(get_machine_type_attributes) = unsafe {
235+
kernel32.get_proc_address(b"GetMachineTypeAttributes\0", &GetMachineTypeAttributes)
236+
} {
237+
let mut attributes = Default::default();
238+
if unsafe { get_machine_type_attributes(IMAGE_FILE_MACHINE_AMD64, &mut attributes) }
239+
== S_OK
240+
{
241+
(attributes & UserEnabled) != 0
242+
} else {
243+
false
244+
}
245+
} else {
246+
false
247+
}
248+
} else {
249+
false
250+
}
251+
}
252+
202253
impl MsvcTool {
203254
fn new(tool: PathBuf) -> MsvcTool {
204255
MsvcTool {
@@ -226,7 +277,6 @@ mod impl_ {
226277

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

485535
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"],
536+
X86 => &["X86"],
537+
X86_64 => &["X64"],
538+
// Starting with VS 17.4, there is a natively hosted compiler on ARM64:
539+
// https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/
540+
// On older versions of VS, we use x64 if running under emulation is supported,
541+
// otherwise use x86.
542+
AARCH64 => {
543+
if is_amd64_emulation_supported() {
544+
&["ARM64", "X64", "X86"][..]
545+
} else {
546+
&["ARM64", "X86"]
547+
}
548+
}
492549
_ => return None,
493550
};
494551
let target = lib_subdir(target)?;
495552
// The directory layout here is MSVC/bin/Host$host/$target/
496553
let path = instance_path.join(r"VC\Tools\MSVC").join(version);
497554
// We use the first available host architecture that can build for the target
498555
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() {
556+
let candidate = path.join("bin").join(format!("Host{}", x));
557+
if candidate.join(target).exists() {
501558
Some((candidate, x))
502559
} else {
503560
None
504561
}
505562
})?;
506563
// This is the path to the toolchain for a particular target, running
507564
// on a given host
508-
let bin_path = host_path.join(&target);
565+
let bin_path = host_path.join(target);
509566
// But! we also need PATH to contain the target directory for the host
510567
// architecture, because it contains dlls like mspdb140.dll compiled for
511568
// the host architecture.
512-
let host_dylib_path = host_path.join(&host.to_lowercase());
513-
let lib_path = path.join("lib").join(&target);
569+
let host_dylib_path = host_path.join(host.to_lowercase());
570+
let lib_path = path.join("lib").join(target);
514571
let alt_lib_path = (target == "arm64ec").then(|| path.join("lib").join("arm64ec"));
515572
let include_path = path.join("include");
516573
Some((

src/windows/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub mod find_tools;
66

77
#[cfg(windows)]
88
pub(crate) mod windows_sys;
9+
#[cfg(windows)]
10+
pub(crate) mod windows_sys_no_link;
911

1012
#[cfg(windows)]
1113
mod registry;

0 commit comments

Comments
 (0)