about summary refs log tree commit diff
path: root/src/libnative
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-05-12 09:12:04 -0700
committerbors <bors@rust-lang.org>2014-05-12 09:12:04 -0700
commite8053b9a7ff91b6a69238c1d7dea99be34bb7a3b (patch)
treed5ab9f71437bebfc7a8d01b1ee50bb218e75f8cf /src/libnative
parentedae0bdabf75b6acf4f0f0eb5bc2bd3b29d58b81 (diff)
parentfacd1270c636beb7c2c8153424ffd6dda4179d59 (diff)
downloadrust-e8053b9a7ff91b6a69238c1d7dea99be34bb7a3b.tar.gz
rust-e8053b9a7ff91b6a69238c1d7dea99be34bb7a3b.zip
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.
Diffstat (limited to 'src/libnative')
-rw-r--r--src/libnative/io/c_win32.rs93
-rw-r--r--src/libnative/io/file_win32.rs12
-rw-r--r--src/libnative/lib.rs1
3 files changed, 101 insertions, 5 deletions
diff --git a/src/libnative/io/c_win32.rs b/src/libnative/io/c_win32.rs
index 151111af3df..93b3ec7ccef 100644
--- a/src/libnative/io/c_win32.rs
+++ b/src/libnative/io/c_win32.rs
@@ -65,3 +65,96 @@ extern "system" {
     pub fn CancelIoEx(hFile: libc::HANDLE,
                       lpOverlapped: libc::LPOVERLAPPED) -> libc::BOOL;
 }
+
+pub mod compat {
+    use std::intrinsics::{atomic_store_relaxed, transmute};
+    use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
+    use std::os::win32::as_utf16_p;
+
+    extern "system" {
+        fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
+        fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID;
+    }
+
+    // store_func() is idempotent, so using relaxed ordering for the atomics should be enough.
+    // This way, calling a function in this compatibility layer (after it's loaded) shouldn't
+    // be any slower than a regular DLL call.
+    unsafe fn store_func<T: Copy>(ptr: *mut T, module: &str, symbol: &str, fallback: T) {
+        as_utf16_p(module, |module| {
+            symbol.with_c_str(|symbol| {
+                let handle = GetModuleHandleW(module);
+                let func: Option<T> = transmute(GetProcAddress(handle, symbol));
+                atomic_store_relaxed(ptr, func.unwrap_or(fallback))
+            })
+        })
+    }
+
+    /// Macro for creating a compatibility fallback for a Windows function
+    ///
+    /// # Example
+    /// ```
+    /// compat_fn!(adll32::SomeFunctionW(_arg: LPCWSTR) {
+    ///     // Fallback implementation
+    /// })
+    /// ```
+    ///
+    /// Note that arguments unused by the fallback implementation should not be called `_` as
+    /// they are used to be passed to the real function if available.
+    macro_rules! compat_fn(
+        ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*)
+                                      -> $rettype:ty $fallback:block) => (
+            #[inline(always)]
+            pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
+                static mut ptr: extern "system" fn($($argname: $argtype),*) -> $rettype = thunk;
+
+                extern "system" fn thunk($($argname: $argtype),*) -> $rettype {
+                    unsafe {
+                        ::io::c::compat::store_func(&mut ptr,
+                                                             stringify!($module),
+                                                             stringify!($symbol),
+                                                             fallback);
+                        ::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
+                    }
+                }
+
+                extern "system" fn fallback($($argname: $argtype),*) -> $rettype $fallback
+
+                ::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
+            }
+        );
+
+        ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) $fallback:block) => (
+            compat_fn!($module::$symbol($($argname: $argtype),*) -> () $fallback)
+        )
+    )
+
+    /// Compatibility layer for functions in `kernel32.dll`
+    ///
+    /// Latest versions of Windows this is needed for:
+    ///
+    /// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003
+    /// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003
+    pub mod kernel32 {
+        use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE};
+        use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
+
+        extern "system" {
+            fn SetLastError(dwErrCode: DWORD);
+        }
+
+        compat_fn!(kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
+                                                 _lpTargetFileName: LPCWSTR,
+                                                 _dwFlags: DWORD) -> BOOLEAN {
+            unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); }
+            0
+        })
+
+        compat_fn!(kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE,
+                                                       _lpszFilePath: LPCWSTR,
+                                                       _cchFilePath: DWORD,
+                                                       _dwFlags: DWORD) -> DWORD {
+            unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); }
+            0
+        })
+    }
+}
diff --git a/src/libnative/io/file_win32.rs b/src/libnative/io/file_win32.rs
index fc08a7f128f..707b0c0cf3f 100644
--- a/src/libnative/io/file_win32.rs
+++ b/src/libnative/io/file_win32.rs
@@ -422,6 +422,7 @@ pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> {
 
 pub fn readlink(p: &CString) -> IoResult<Path> {
     // FIXME: I have a feeling that this reads intermediate symlinks as well.
+    use io::c::compat::kernel32::GetFinalPathNameByHandleW;
     let handle = unsafe {
         as_utf16_p(p.as_str().unwrap(), |p| {
             libc::CreateFileW(p,
@@ -439,10 +440,10 @@ pub fn readlink(p: &CString) -> IoResult<Path> {
     // Specify (sz - 1) because the documentation states that it's the size
     // without the null pointer
     let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe {
-        libc::GetFinalPathNameByHandleW(handle,
-                                        buf as *u16,
-                                        sz - 1,
-                                        libc::VOLUME_NAME_DOS)
+        GetFinalPathNameByHandleW(handle,
+                                  buf as *u16,
+                                  sz - 1,
+                                  libc::VOLUME_NAME_DOS)
     });
     let ret = match ret {
         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> {
 }
 
 pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
+    use io::c::compat::kernel32::CreateSymbolicLinkW;
     super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
         as_utf16_p(dst.as_str().unwrap(), |dst| {
-            unsafe { libc::CreateSymbolicLinkW(dst, src, 0) }
+            unsafe { CreateSymbolicLinkW(dst, src, 0) }
         }) as libc::BOOL
     }))
 }
diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs
index 0df45f7d5a0..3d5f4151a4b 100644
--- a/src/libnative/lib.rs
+++ b/src/libnative/lib.rs
@@ -50,6 +50,7 @@
        html_root_url = "http://static.rust-lang.org/doc/master")]
 #![deny(unused_result, unused_must_use)]
 #![allow(non_camel_case_types)]
+#![feature(macro_rules)]
 
 // NB this crate explicitly does *not* allow glob imports, please seriously
 //    consider whether they're needed before adding that feature here (the