Skip to content

Commit

Permalink
Add support for Native ARM64 and x64 if available
Browse files Browse the repository at this point in the history
  • Loading branch information
dpaoliello committed Feb 22, 2024
1 parent 29d95b5 commit a8586d7
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 12 deletions.
7 changes: 7 additions & 0 deletions dev-tools/gen-windows-sys-binding/windows_sys.list
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ Windows.Win32.System.Com.COINIT_MULTITHREADED
Windows.Win32.System.Com.CoCreateInstance
Windows.Win32.System.Com.CoInitializeEx

Windows.Win32.System.LibraryLoader.FreeLibrary
Windows.Win32.System.LibraryLoader.GetProcAddress
Windows.Win32.System.LibraryLoader.LoadLibraryA

Windows.Win32.System.Pipes.PeekNamedPipe

Windows.Win32.System.Registry.RegCloseKey
Expand All @@ -31,9 +35,12 @@ 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.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
75 changes: 63 additions & 12 deletions src/windows/find_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, GetProcAddress, LoadLibraryA, UserEnabled, HMODULE, HRESULT,
IMAGE_FILE_MACHINE_AMD64, MACHINE_ATTRIBUTES, S_OK,
};
use std::convert::TryFrom;
use std::env;
use std::ffi::OsString;
Expand Down Expand Up @@ -199,6 +203,47 @@ mod impl_ {
include: Vec<PathBuf>,
}

struct LibraryHandle(HMODULE);

impl LibraryHandle {
fn new(name: &[u8]) -> Option<Self> {
let handle = unsafe { LoadLibraryA(name.as_ptr() as _) };
(!handle.is_null()).then(|| Self(handle))
}
}

impl Drop for LibraryHandle {
fn drop(&mut self) {
unsafe { FreeLibrary(self.0) };
}
}

fn is_amd64_emulation_supported() -> bool {
let kernel32 = LibraryHandle::new(b"kernel32.dll\0").unwrap();
// GetMachineTypeAttributes is only available on Win11 22000+.
if let Some(get_machine_type_attributes) =
unsafe { GetProcAddress(kernel32.0, b"GetMachineTypeAttributes\0".as_ptr() as _) }
{
// SAFETY: We are getting a function pointer to GetMachineTypeAttributes and then transmuting it to match
// the signature that windows-sys would generate: https://docs.rs/windows-sys/0.52.0/windows_sys/Win32/System/Threading/fn.GetMachineTypeAttributes.html
type GetMachineTypeAttributesFuncType =
unsafe extern "system" fn(u16, *mut MACHINE_ATTRIBUTES) -> HRESULT;
let get_machine_type_attributes: GetMachineTypeAttributesFuncType =
unsafe { std::mem::transmute_copy(&get_machine_type_attributes) };

let mut attributes = Default::default();
if unsafe { get_machine_type_attributes(IMAGE_FILE_MACHINE_AMD64, &mut attributes) }
== S_OK
{
(attributes & UserEnabled) != 0
} else {
false
}
} else {
false
}
}

impl MsvcTool {
fn new(tool: PathBuf) -> MsvcTool {
MsvcTool {
Expand Down Expand Up @@ -226,7 +271,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<bool> {
let vscmd_arch = env::var("VSCMD_ARG_TGT_ARCH").ok()?;
// Convert the Rust target arch to its VS arch equivalent.
Expand Down Expand Up @@ -483,34 +527,41 @@ mod impl_ {
let version = vs15plus_vc_read_version(instance_path)?;

let hosts = match host_arch() {
X86 => vec!["X86"],
X86_64 => vec!["X64"],
// Starting with VS 17.3, there is a natively hosted compiler on ARM64.
// On older versions of VS, we use the x86 toolchain under emulation.
// We don't want to overcomplicate compatibility checks, so we ignore x64 emulation.
AARCH64 => vec!["ARM64", "X86"],
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() {
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 = host_path.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 = host_path.join(&host.to_lowercase());
let lib_path = path.join("lib").join(&target);
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");
Some((
Expand Down
18 changes: 18 additions & 0 deletions src/windows/windows_sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ extern "system" {
) -> WIN32_ERROR;
}
#[link(name = "kernel32")]
extern "system" {
pub fn FreeLibrary(hlibmodule: HMODULE) -> BOOL;
}
#[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;
}
Expand Down Expand Up @@ -112,6 +124,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<unsafe extern "system" fn() -> isize>;
#[repr(C)]
pub struct FILETIME {
pub dwLowDateTime: u32,
Expand Down Expand Up @@ -149,10 +162,14 @@ impl ::core::clone::Clone for GUID {
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 type HMODULE = *mut ::core::ffi::c_void;
pub type HRESULT = i32;
pub type IMAGE_FILE_MACHINE = u16;
pub const IMAGE_FILE_MACHINE_AMD64: IMAGE_FILE_MACHINE = 34404u16;
pub type IUnknown = *mut ::core::ffi::c_void;
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;
Expand Down Expand Up @@ -191,6 +208,7 @@ pub const S_FALSE: HRESULT = 1i32;
pub const S_OK: HRESULT = 0i32;
pub type THREAD_ACCESS_RIGHTS = u32;
pub const THREAD_SYNCHRONIZE: THREAD_ACCESS_RIGHTS = 1048576u32;
pub const UserEnabled: MACHINE_ATTRIBUTES = 1i32;
pub const WAIT_ABANDONED: WIN32_ERROR = 128u32;
pub const WAIT_FAILED: WIN32_ERROR = 4294967295u32;
pub const WAIT_OBJECT_0: WIN32_ERROR = 0u32;
Expand Down

0 comments on commit a8586d7

Please sign in to comment.