Skip to content

Commit e8053b9

Browse files
committed
auto merge of #13932 : MrAlert/rust/win-compat, r=brson
This addresses #12842 by offering fallback implementations for functions that aren't available. In this case, as Windows XP simply doesn't support symbolic links at all, the fallbacks simply return an error code indicating that the function hasn't been implemented. This should allow programs written in Rust to run under XP while still offering full support for symbolic links under newer versions of Windows with the same binary, but due to LLVM using stderror_s(), which isn't available in msvcrt.dll in XP, rustc itself will not. The fallback implementation is as follows: Calling the function instead calls to a mutable function pointer. This in and of itself would not constitute a performance hit because DLL calls are implemented in a similar manner (see Import Address Table). The function pointer initially points to a thunk which tries to get the address of the associated function and write it back to the function pointer. If it fails to find the function, it instead writes the address to a fallback. As this operation is idempotent, reading and writing the pointer simply needs to be atomic. Subsequent calls to the function should be as fast as any other DLL call, as the pointer will then point directly to either the correct function or a fallback.
2 parents edae0bd + facd127 commit e8053b9

File tree

4 files changed

+105
-16
lines changed

4 files changed

+105
-16
lines changed

src/liblibc/lib.rs

+4-11
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,6 @@ pub use funcs::bsd43::{shutdown};
252252
#[cfg(windows)] pub use funcs::extra::kernel32::{FlushFileBuffers, SetEndOfFile, CreateFileW};
253253
#[cfg(windows)] pub use funcs::extra::kernel32::{CreateDirectoryW, FindFirstFileW};
254254
#[cfg(windows)] pub use funcs::extra::kernel32::{FindNextFileW, FindClose, DeleteFileW};
255-
#[cfg(windows)] pub use funcs::extra::kernel32::{GetFinalPathNameByHandleW, CreateSymbolicLinkW};
256255
#[cfg(windows)] pub use funcs::extra::kernel32::{CreateHardLinkW, CreateEventW};
257256
#[cfg(windows)] pub use funcs::extra::kernel32::{FlushFileBuffers, CreateNamedPipeW};
258257
#[cfg(windows)] pub use funcs::extra::kernel32::{SetNamedPipeHandleState, WaitNamedPipeW};
@@ -1735,6 +1734,7 @@ pub mod consts {
17351734
pub static ERROR_INVALID_HANDLE : c_int = 6;
17361735
pub static ERROR_BROKEN_PIPE: c_int = 109;
17371736
pub static ERROR_DISK_FULL : c_int = 112;
1737+
pub static ERROR_CALL_NOT_IMPLEMENTED : c_int = 120;
17381738
pub static ERROR_INSUFFICIENT_BUFFER : c_int = 122;
17391739
pub static ERROR_INVALID_NAME : c_int = 123;
17401740
pub static ERROR_ALREADY_EXISTS : c_int = 183;
@@ -4189,9 +4189,9 @@ pub mod funcs {
41894189
LPSTARTUPINFO,
41904190
LPPROCESS_INFORMATION,
41914191
LPMEMORY_BASIC_INFORMATION,
4192-
LPSYSTEM_INFO, BOOLEAN,
4193-
HANDLE, LPHANDLE, LARGE_INTEGER,
4194-
PLARGE_INTEGER, LPFILETIME};
4192+
LPSYSTEM_INFO, HANDLE, LPHANDLE,
4193+
LARGE_INTEGER, PLARGE_INTEGER,
4194+
LPFILETIME};
41954195

41964196
extern "system" {
41974197
pub fn GetEnvironmentVariableW(n: LPCWSTR,
@@ -4301,9 +4301,6 @@ pub mod funcs {
43014301
pub fn MoveFileExW(lpExistingFileName: LPCWSTR,
43024302
lpNewFileName: LPCWSTR,
43034303
dwFlags: DWORD) -> BOOL;
4304-
pub fn CreateSymbolicLinkW(lpSymlinkFileName: LPCWSTR,
4305-
lpTargetFileName: LPCWSTR,
4306-
dwFlags: DWORD) -> BOOLEAN;
43074304
pub fn CreateHardLinkW(lpSymlinkFileName: LPCWSTR,
43084305
lpTargetFileName: LPCWSTR,
43094306
lpSecurityAttributes: LPSECURITY_ATTRIBUTES)
@@ -4316,10 +4313,6 @@ pub mod funcs {
43164313
dwCreationDisposition: DWORD,
43174314
dwFlagsAndAttributes: DWORD,
43184315
hTemplateFile: HANDLE) -> HANDLE;
4319-
pub fn GetFinalPathNameByHandleW(hFile: HANDLE,
4320-
lpszFilePath: LPCWSTR,
4321-
cchFilePath: DWORD,
4322-
dwFlags: DWORD) -> DWORD;
43234316
pub fn ReadFile(hFile: HANDLE,
43244317
lpBuffer: LPVOID,
43254318
nNumberOfBytesToRead: DWORD,

src/libnative/io/c_win32.rs

+93
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,96 @@ extern "system" {
6565
pub fn CancelIoEx(hFile: libc::HANDLE,
6666
lpOverlapped: libc::LPOVERLAPPED) -> libc::BOOL;
6767
}
68+
69+
pub mod compat {
70+
use std::intrinsics::{atomic_store_relaxed, transmute};
71+
use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
72+
use std::os::win32::as_utf16_p;
73+
74+
extern "system" {
75+
fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
76+
fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID;
77+
}
78+
79+
// store_func() is idempotent, so using relaxed ordering for the atomics should be enough.
80+
// This way, calling a function in this compatibility layer (after it's loaded) shouldn't
81+
// be any slower than a regular DLL call.
82+
unsafe fn store_func<T: Copy>(ptr: *mut T, module: &str, symbol: &str, fallback: T) {
83+
as_utf16_p(module, |module| {
84+
symbol.with_c_str(|symbol| {
85+
let handle = GetModuleHandleW(module);
86+
let func: Option<T> = transmute(GetProcAddress(handle, symbol));
87+
atomic_store_relaxed(ptr, func.unwrap_or(fallback))
88+
})
89+
})
90+
}
91+
92+
/// Macro for creating a compatibility fallback for a Windows function
93+
///
94+
/// # Example
95+
/// ```
96+
/// compat_fn!(adll32::SomeFunctionW(_arg: LPCWSTR) {
97+
/// // Fallback implementation
98+
/// })
99+
/// ```
100+
///
101+
/// Note that arguments unused by the fallback implementation should not be called `_` as
102+
/// they are used to be passed to the real function if available.
103+
macro_rules! compat_fn(
104+
($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*)
105+
-> $rettype:ty $fallback:block) => (
106+
#[inline(always)]
107+
pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
108+
static mut ptr: extern "system" fn($($argname: $argtype),*) -> $rettype = thunk;
109+
110+
extern "system" fn thunk($($argname: $argtype),*) -> $rettype {
111+
unsafe {
112+
::io::c::compat::store_func(&mut ptr,
113+
stringify!($module),
114+
stringify!($symbol),
115+
fallback);
116+
::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
117+
}
118+
}
119+
120+
extern "system" fn fallback($($argname: $argtype),*) -> $rettype $fallback
121+
122+
::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
123+
}
124+
);
125+
126+
($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) $fallback:block) => (
127+
compat_fn!($module::$symbol($($argname: $argtype),*) -> () $fallback)
128+
)
129+
)
130+
131+
/// Compatibility layer for functions in `kernel32.dll`
132+
///
133+
/// Latest versions of Windows this is needed for:
134+
///
135+
/// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003
136+
/// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003
137+
pub mod kernel32 {
138+
use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE};
139+
use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
140+
141+
extern "system" {
142+
fn SetLastError(dwErrCode: DWORD);
143+
}
144+
145+
compat_fn!(kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
146+
_lpTargetFileName: LPCWSTR,
147+
_dwFlags: DWORD) -> BOOLEAN {
148+
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); }
149+
0
150+
})
151+
152+
compat_fn!(kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE,
153+
_lpszFilePath: LPCWSTR,
154+
_cchFilePath: DWORD,
155+
_dwFlags: DWORD) -> DWORD {
156+
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); }
157+
0
158+
})
159+
}
160+
}

src/libnative/io/file_win32.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> {
422422

423423
pub fn readlink(p: &CString) -> IoResult<Path> {
424424
// FIXME: I have a feeling that this reads intermediate symlinks as well.
425+
use io::c::compat::kernel32::GetFinalPathNameByHandleW;
425426
let handle = unsafe {
426427
as_utf16_p(p.as_str().unwrap(), |p| {
427428
libc::CreateFileW(p,
@@ -439,10 +440,10 @@ pub fn readlink(p: &CString) -> IoResult<Path> {
439440
// Specify (sz - 1) because the documentation states that it's the size
440441
// without the null pointer
441442
let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe {
442-
libc::GetFinalPathNameByHandleW(handle,
443-
buf as *u16,
444-
sz - 1,
445-
libc::VOLUME_NAME_DOS)
443+
GetFinalPathNameByHandleW(handle,
444+
buf as *u16,
445+
sz - 1,
446+
libc::VOLUME_NAME_DOS)
446447
});
447448
let ret = match ret {
448449
Some(ref s) if s.starts_with(r"\\?\") => Ok(Path::new(s.slice_from(4))),
@@ -454,9 +455,10 @@ pub fn readlink(p: &CString) -> IoResult<Path> {
454455
}
455456

456457
pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
458+
use io::c::compat::kernel32::CreateSymbolicLinkW;
457459
super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
458460
as_utf16_p(dst.as_str().unwrap(), |dst| {
459-
unsafe { libc::CreateSymbolicLinkW(dst, src, 0) }
461+
unsafe { CreateSymbolicLinkW(dst, src, 0) }
460462
}) as libc::BOOL
461463
}))
462464
}

src/libnative/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
html_root_url = "http://static.rust-lang.org/doc/master")]
5151
#![deny(unused_result, unused_must_use)]
5252
#![allow(non_camel_case_types)]
53+
#![feature(macro_rules)]
5354

5455
// NB this crate explicitly does *not* allow glob imports, please seriously
5556
// consider whether they're needed before adding that feature here (the

0 commit comments

Comments
 (0)