Auto merge of #110152 - ChrisDenton:windows-sys, r=thomcc

Start using `windows sys` for Windows FFI bindings in std

Switch to using windows-sys for FFI. In order to avoid some currently contentious issues, this uses windows-bindgen to generate a smaller set of bindings instead of using the full crate.

Unlike the windows-sys crate, the generated bindings uses `*mut c_void` for handle types instead of `isize`. This to sidestep opsem concerns about mixing pointer types and integers between languages. Note that `SOCKET` remains defined as an integer but instead of being a usize, it's changed to fit the [standard library definition](a41fc00eaf/library/std/src/os/windows/raw.rs (L12-L16)):

```rust
#[cfg(target_pointer_width = "32")]
pub type SOCKET = u32;
#[cfg(target_pointer_width = "64")]
pub type SOCKET = u64;
```

The generated bindings also customizes the `#[link]` imports. I hope to switch to using raw-dylib but I don't want to tie that too closely with the switch to windows-sys.

---

Changes outside of the bindings are, for the most part, fairly minimal (e.g. some differences in `*mut` vs. `*const` or a few types differ). One issue is that our own bindings sometimes mix in higher level types, like `BorrowedHandle`. This is pretty adhoc though.
This commit is contained in:
bors 2023-05-09 05:20:41 +00:00
commit 7e7483d26e
23 changed files with 7180 additions and 3091 deletions

View file

@ -1443,6 +1443,13 @@ dependencies = [
"serde_json",
]
[[package]]
name = "generate-windows-sys"
version = "0.1.0"
dependencies = [
"windows-bindgen",
]
[[package]]
name = "generic-array"
version = "0.14.4"
@ -5509,6 +5516,22 @@ dependencies = [
"windows-targets 0.48.0",
]
[[package]]
name = "windows-bindgen"
version = "0.49.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6935fb09b84ee57929ae92518b475f5dfdfbeb87c5334756acc28ee8e202b60"
dependencies = [
"windows-metadata",
"windows-tokens",
]
[[package]]
name = "windows-metadata"
version = "0.49.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f5bca94a32bf1e6a376522b6601275a3b611ee885ec0f1b6a05f17e8cfd3385"
[[package]]
name = "windows-sys"
version = "0.42.0"
@ -5572,6 +5595,12 @@ dependencies = [
"windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows-tokens"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b34c9a3b28cb41db7385546f7f9a8179348dffc89923dde66857b1ba5312f6b4"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"

View file

@ -39,6 +39,7 @@ members = [
"src/tools/collect-license-metadata",
"src/tools/generate-copyright",
"src/tools/suggest-tests",
"src/tools/generate-windows-sys",
]
exclude = [

View file

@ -110,7 +110,7 @@ impl BorrowedSocket<'_> {
/// object as the existing `BorrowedSocket` instance.
#[stable(feature = "io_safety", since = "1.63.0")]
pub fn try_clone_to_owned(&self) -> io::Result<OwnedSocket> {
let mut info = unsafe { mem::zeroed::<c::WSAPROTOCOL_INFO>() };
let mut info = unsafe { mem::zeroed::<c::WSAPROTOCOL_INFOW>() };
let result = unsafe {
c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info)
};

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -114,17 +114,20 @@ impl Module {
/// (e.g. kernel32 and ntdll).
pub unsafe fn new(name: &CStr) -> Option<Self> {
// SAFETY: A CStr is always null terminated.
let module = c::GetModuleHandleA(name.as_ptr());
let module = c::GetModuleHandleA(name.as_ptr().cast::<u8>());
NonNull::new(module).map(Self)
}
// Try to get the address of a function.
pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
// SAFETY:
// `self.0` will always be a valid module.
// A CStr is always null terminated.
let proc = unsafe { c::GetProcAddress(self.0.as_ptr(), name.as_ptr()) };
NonNull::new(proc)
unsafe {
// SAFETY:
// `self.0` will always be a valid module.
// A CStr is always null terminated.
let proc = c::GetProcAddress(self.0.as_ptr(), name.as_ptr().cast::<u8>());
// SAFETY: `GetProcAddress` returns None on null.
proc.map(|p| NonNull::new_unchecked(p as *mut c_void))
}
}
}
@ -199,6 +202,7 @@ macro_rules! compat_fn_optional {
)+) => (
$(
pub mod $symbol {
#[allow(unused_imports)]
use super::*;
use crate::ffi::c_void;
use crate::mem;

View file

@ -89,6 +89,12 @@ pub struct FileTimes {
accessed: Option<c::FILETIME>,
modified: Option<c::FILETIME>,
}
impl core::fmt::Debug for c::FILETIME {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let time = ((self.dwHighDateTime as u64) << 32) | self.dwLowDateTime as u64;
f.debug_tuple("FILETIME").field(&time).finish()
}
}
#[derive(Debug)]
pub struct DirBuilder;
@ -290,6 +296,7 @@ impl File {
ptr::null_mut(),
)
};
let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) };
if let Ok(handle) = handle.try_into() {
Ok(File { handle: Handle::from_inner(handle) })
} else {
@ -501,7 +508,8 @@ impl File {
}
fn readlink(&self) -> io::Result<PathBuf> {
let mut space = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let mut space =
Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
let (_bytes, buf) = self.reparse_point(&mut space)?;
unsafe {
let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
@ -589,7 +597,11 @@ impl File {
));
}
cvt(unsafe {
c::SetFileTime(self.as_handle(), None, times.accessed.as_ref(), times.modified.as_ref())
let accessed =
times.accessed.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
let modified =
times.modified.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
c::SetFileTime(self.as_raw_handle(), ptr::null_mut(), accessed, modified)
})?;
Ok(())
}
@ -618,9 +630,9 @@ impl File {
/// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.
fn posix_delete(&self) -> io::Result<()> {
let mut info = c::FILE_DISPOSITION_INFO_EX {
Flags: c::FILE_DISPOSITION_DELETE
| c::FILE_DISPOSITION_POSIX_SEMANTICS
| c::FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
Flags: c::FILE_DISPOSITION_FLAG_DELETE
| c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
| c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE,
};
let size = mem::size_of_val(&info);
cvt(unsafe {
@ -791,15 +803,15 @@ fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<
// See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
unsafe {
let mut handle = ptr::null_mut();
let mut io_status = c::IO_STATUS_BLOCK::default();
let name_str = c::UNICODE_STRING::from_ref(name);
let mut io_status = c::IO_STATUS_BLOCK::PENDING;
let mut name_str = c::UNICODE_STRING::from_ref(name);
use crate::sync::atomic::{AtomicU32, Ordering};
// The `OBJ_DONT_REPARSE` attribute ensures that we haven't been
// tricked into following a symlink. However, it may not be available in
// earlier versions of Windows.
static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE);
let object = c::OBJECT_ATTRIBUTES {
ObjectName: &name_str,
let mut object = c::OBJECT_ATTRIBUTES {
ObjectName: &mut name_str,
RootDirectory: parent.as_raw_handle(),
Attributes: ATTRIBUTES.load(Ordering::Relaxed),
..c::OBJECT_ATTRIBUTES::default()
@ -807,7 +819,7 @@ fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<
let status = c::NtCreateFile(
&mut handle,
access,
&object,
&mut object,
&mut io_status,
crate::ptr::null_mut(),
0,
@ -1368,7 +1380,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
_dwCallbackReason: c::DWORD,
_hSourceFile: c::HANDLE,
_hDestinationFile: c::HANDLE,
lpData: c::LPVOID,
lpData: c::LPCVOID,
) -> c::DWORD {
if dwStreamNumber == 1 {
*(lpData as *mut i64) = StreamBytesTransferred;
@ -1415,9 +1427,10 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
let f = File::open(junction, &opts)?;
let h = f.as_inner().as_raw_handle();
unsafe {
let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let mut data =
Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
let data_ptr = data.0.as_mut_ptr();
let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize);
let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
// Zero the header to ensure it's fully initialized, including reserved parameters.
*db = mem::zeroed();

View file

@ -144,7 +144,7 @@ impl Handle {
let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
let mut amt = 0;
let res = cvt(c::ReadFile(
self.as_handle(),
self.as_raw_handle(),
buf.as_ptr() as c::LPVOID,
len,
&mut amt,
@ -235,7 +235,7 @@ impl Handle {
len: usize,
offset: Option<u64>,
) -> io::Result<usize> {
let mut io_status = c::IO_STATUS_BLOCK::default();
let mut io_status = c::IO_STATUS_BLOCK::PENDING;
// The length is clamped at u32::MAX.
let len = cmp::min(len, c::DWORD::MAX as usize) as c::DWORD;
@ -283,7 +283,7 @@ impl Handle {
///
/// If `offset` is `None` then the current file position is used.
fn synchronous_write(&self, buf: &[u8], offset: Option<u64>) -> io::Result<usize> {
let mut io_status = c::IO_STATUS_BLOCK::default();
let mut io_status = c::IO_STATUS_BLOCK::PENDING;
// The length is clamped at u32::MAX.
let len = cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;

View file

@ -17,10 +17,7 @@ impl<'a> IoSlice<'a> {
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
assert!(buf.len() <= c::ULONG::MAX as usize);
IoSlice {
vec: c::WSABUF {
len: buf.len() as c::ULONG,
buf: buf.as_ptr() as *mut u8 as *mut c::CHAR,
},
vec: c::WSABUF { len: buf.len() as c::ULONG, buf: buf.as_ptr() as *mut u8 },
_p: PhantomData,
}
}
@ -54,7 +51,7 @@ impl<'a> IoSliceMut<'a> {
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
assert!(buf.len() <= c::ULONG::MAX as usize);
IoSliceMut {
vec: c::WSABUF { len: buf.len() as c::ULONG, buf: buf.as_mut_ptr() as *mut c::CHAR },
vec: c::WSABUF { len: buf.len() as c::ULONG, buf: buf.as_mut_ptr() },
_p: PhantomData,
}
}

View file

@ -263,7 +263,7 @@ impl Socket {
&mut nread,
&mut flags,
ptr::null_mut(),
ptr::null_mut(),
None,
)
};
@ -347,7 +347,7 @@ impl Socket {
&mut nwritten,
0,
ptr::null_mut(),
ptr::null_mut(),
None,
)
};
cvt(result).map(|_| nwritten as usize)

View file

@ -373,7 +373,7 @@ impl AnonPipe {
// Asynchronous read of the pipe.
// If successful, `callback` will be called once it completes.
let result = io(self.inner.as_handle(), buf, len, &mut overlapped, callback);
let result = io(self.inner.as_handle(), buf, len, &mut overlapped, Some(callback));
if result == c::FALSE {
// We can return here because the call failed.
// After this we must not return until the I/O completes.

View file

@ -308,7 +308,7 @@ impl Command {
let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?;
let mut si = zeroed_startupinfo();
si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
si.cb = mem::size_of::<c::STARTUPINFOW>() as c::DWORD;
// If at least one of stdin, stdout or stderr are set (i.e. are non null)
// then set the `hStd` fields in `STARTUPINFO`.
@ -332,7 +332,7 @@ impl Command {
flags,
envp,
dirp,
&mut si,
&si,
&mut pi,
))
}?;
@ -720,8 +720,8 @@ impl From<u32> for ExitCode {
}
}
fn zeroed_startupinfo() -> c::STARTUPINFO {
c::STARTUPINFO {
fn zeroed_startupinfo() -> c::STARTUPINFOW {
c::STARTUPINFOW {
cb: 0,
lpReserved: ptr::null_mut(),
lpDesktop: ptr::null_mut(),
@ -731,7 +731,7 @@ fn zeroed_startupinfo() -> c::STARTUPINFO {
dwXSize: 0,
dwYSize: 0,
dwXCountChars: 0,
dwYCountCharts: 0,
dwYCountChars: 0,
dwFillAttribute: 0,
dwFlags: 0,
wShowWindow: 0,

View file

@ -1,3 +1,4 @@
use crate::ffi::c_void;
use crate::io;
use crate::mem;
use crate::ptr;
@ -25,8 +26,9 @@ pub fn hashmap_random_keys() -> (u64, u64) {
#[inline(never)]
fn fallback_rng() -> (u64, u64) {
let mut v = (0, 0);
let ret =
unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) };
let ret = unsafe {
c::RtlGenRandom(&mut v as *mut _ as *mut c_void, mem::size_of_val(&v) as c::ULONG)
};
if ret != 0 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) }
}

View file

@ -18,7 +18,7 @@ impl Handler {
}
}
extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> c::LONG {
unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> c::LONG {
unsafe {
let rec = &(*(*ExceptionInfo).ExceptionRecord);
let code = rec.ExceptionCode;
@ -34,7 +34,7 @@ extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -
}
pub unsafe fn init() {
if c::AddVectoredExceptionHandler(0, vectored_handler).is_null() {
if c::AddVectoredExceptionHandler(0, Some(vectored_handler)).is_null() {
panic!("failed to install exception handler");
}
// Set the thread stack guarantee for the main thread.

View file

@ -180,7 +180,7 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usiz
let result = c::MultiByteToWideChar(
c::CP_UTF8, // CodePage
c::MB_ERR_INVALID_CHARS, // dwFlags
utf8.as_ptr() as c::LPCCH, // lpMultiByteStr
utf8.as_ptr(), // lpMultiByteStr
utf8.len() as c::c_int, // cbMultiByte
utf16.as_mut_ptr() as c::LPWSTR, // lpWideCharStr
utf16.len() as c::c_int, // cchWideChar
@ -344,7 +344,7 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit<u16>]) -> io::Result<usiz
// See #38274 and https://stackoverflow.com/questions/43836040/win-api-readconsole.
const CTRL_Z: u16 = 0x1A;
const CTRL_Z_MASK: c::ULONG = 1 << CTRL_Z;
let mut input_control = c::CONSOLE_READCONSOLE_CONTROL {
let input_control = c::CONSOLE_READCONSOLE_CONTROL {
nLength: crate::mem::size_of::<c::CONSOLE_READCONSOLE_CONTROL>() as c::ULONG,
nInitialChars: 0,
dwCtrlWakeupMask: CTRL_Z_MASK,
@ -360,7 +360,7 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit<u16>]) -> io::Result<usiz
buf.as_mut_ptr() as c::LPVOID,
buf.len() as u32,
&mut amount,
&mut input_control as c::PCONSOLE_READCONSOLE_CONTROL,
&input_control,
)
})?;
@ -385,14 +385,14 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result<usize> {
let result = unsafe {
c::WideCharToMultiByte(
c::CP_UTF8, // CodePage
c::WC_ERR_INVALID_CHARS, // dwFlags
utf16.as_ptr(), // lpWideCharStr
utf16.len() as c::c_int, // cchWideChar
utf8.as_mut_ptr() as c::LPSTR, // lpMultiByteStr
utf8.len() as c::c_int, // cbMultiByte
ptr::null(), // lpDefaultChar
ptr::null_mut(), // lpUsedDefaultChar
c::CP_UTF8, // CodePage
c::WC_ERR_INVALID_CHARS, // dwFlags
utf16.as_ptr(), // lpWideCharStr
utf16.len() as c::c_int, // cchWideChar
utf8.as_mut_ptr(), // lpMultiByteStr
utf8.len() as c::c_int, // cbMultiByte
ptr::null(), // lpDefaultChar
ptr::null_mut(), // lpUsedDefaultChar
)
};
if result == 0 {

View file

@ -2,6 +2,7 @@ use crate::ffi::CStr;
use crate::io;
use crate::num::NonZeroUsize;
use crate::os::windows::io::AsRawHandle;
use crate::os::windows::io::HandleOrNull;
use crate::ptr;
use crate::sys::c;
use crate::sys::handle::Handle;
@ -32,12 +33,12 @@ impl Thread {
let ret = c::CreateThread(
ptr::null_mut(),
stack,
thread_start,
Some(thread_start),
p as *mut _,
c::STACK_SIZE_PARAM_IS_A_RESERVATION,
ptr::null_mut(),
);
let ret = HandleOrNull::from_raw_handle(ret);
return if let Ok(handle) = ret.try_into() {
Ok(Thread { handle: Handle::from_inner(handle) })
} else {

View file

@ -838,6 +838,7 @@ impl<'a> Builder<'a> {
run::Miri,
run::CollectLicenseMetadata,
run::GenerateCopyright,
run::GenerateWindowsSys,
),
Kind::Setup => describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode),
Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),

View file

@ -253,3 +253,25 @@ impl Step for GenerateCopyright {
dest
}
}
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
pub struct GenerateWindowsSys;
impl Step for GenerateWindowsSys {
type Output = ();
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/generate-windows-sys")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(GenerateWindowsSys);
}
fn run(self, builder: &Builder<'_>) {
let mut cmd = builder.tool_cmd(Tool::GenerateWindowsSys);
cmd.arg(&builder.src);
builder.run(&mut cmd);
}
}

View file

@ -301,6 +301,7 @@ bootstrap_tool!(
CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
SuggestTests, "src/tools/suggest-tests", "suggest-tests";
GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys";
);
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]

View file

@ -0,0 +1,7 @@
[package]
name = "generate-windows-sys"
version = "0.1.0"
edition = "2021"
[dependencies.windows-bindgen]
version = "0.49"

View file

@ -0,0 +1,37 @@
use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;
/// This is printed to the file before the rest of the contents.
const PRELUDE: &str = r#"// This file is autogenerated.
//
// To add bindings, edit windows_sys.lst then use `./x run generate-windows-sys` to
// regenerate the bindings.
//
// ignore-tidy-filelength
"#;
fn main() -> io::Result<()> {
let mut path: PathBuf =
std::env::args_os().nth(1).expect("a path to the rust repository is required").into();
path.push("library/std/src/sys/windows/c/windows_sys.lst");
// Load the list of APIs
let buffer = fs::read_to_string(&path)?;
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();
// Write the bindings to windows-sys.rs
let bindings = windows_bindgen::standalone_std(&names);
path.set_extension("rs");
let mut f = std::fs::File::create(&path)?;
f.write_all(PRELUDE.as_bytes())?;
f.write_all(bindings.as_bytes())?;
Ok(())
}