std::dynamic_lib: Fix Windows error handling
This is a [breaking-change] since `std::dynamic_lib::dl` is now private. When `LoadLibraryW()` fails, original code called `errno()` to get error code. However, there was local allocation of `Vec` before `LoadLibraryW()`, and it drops before `errno()`, and the drop (deallocation) changed `errno`! Therefore `dynamic_lib::open()` thought it always succeeded. This commit fixes the issue. This commit also sets Windows error mode during `LoadLibrary()` to prevent "dll load failed" dialog.
This commit is contained in:
@@ -52,13 +52,7 @@ impl DynamicLibrary {
|
|||||||
/// Lazily open a dynamic library. When passed None it gives a
|
/// Lazily open a dynamic library. When passed None it gives a
|
||||||
/// handle to the calling process
|
/// handle to the calling process
|
||||||
pub fn open(filename: Option<&Path>) -> Result<DynamicLibrary, String> {
|
pub fn open(filename: Option<&Path>) -> Result<DynamicLibrary, String> {
|
||||||
unsafe {
|
let maybe_library = dl::open(filename.map(|path| path.as_vec()));
|
||||||
let maybe_library = dl::check_for_errors_in(|| {
|
|
||||||
match filename {
|
|
||||||
Some(name) => dl::open_external(name.as_vec()),
|
|
||||||
None => dl::open_internal()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// The dynamic library must not be constructed if there is
|
// The dynamic library must not be constructed if there is
|
||||||
// an error opening the library so the destructor does not
|
// an error opening the library so the destructor does not
|
||||||
@@ -68,7 +62,6 @@ impl DynamicLibrary {
|
|||||||
Ok(handle) => Ok(DynamicLibrary { handle: handle })
|
Ok(handle) => Ok(DynamicLibrary { handle: handle })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepends a path to this process's search path for dynamic libraries
|
/// Prepends a path to this process's search path for dynamic libraries
|
||||||
pub fn prepend_search_path(path: &Path) {
|
pub fn prepend_search_path(path: &Path) {
|
||||||
@@ -198,8 +191,7 @@ mod test {
|
|||||||
target_os = "ios",
|
target_os = "ios",
|
||||||
target_os = "freebsd",
|
target_os = "freebsd",
|
||||||
target_os = "dragonfly"))]
|
target_os = "dragonfly"))]
|
||||||
pub mod dl {
|
mod dl {
|
||||||
pub use self::Rtld::*;
|
|
||||||
use prelude::v1::*;
|
use prelude::v1::*;
|
||||||
|
|
||||||
use ffi::{self, CString};
|
use ffi::{self, CString};
|
||||||
@@ -207,13 +199,26 @@ pub mod dl {
|
|||||||
use libc;
|
use libc;
|
||||||
use ptr;
|
use ptr;
|
||||||
|
|
||||||
pub unsafe fn open_external(filename: &[u8]) -> *mut u8 {
|
pub fn open(filename: Option<&[u8]>) -> Result<*mut u8, String> {
|
||||||
let s = CString::from_slice(filename);
|
check_for_errors_in(|| {
|
||||||
dlopen(s.as_ptr(), Lazy as libc::c_int) as *mut u8
|
unsafe {
|
||||||
|
match filename {
|
||||||
|
Some(filename) => open_external(filename),
|
||||||
|
None => open_internal(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn open_internal() -> *mut u8 {
|
const LAZY: libc::c_int = 1;
|
||||||
dlopen(ptr::null(), Lazy as libc::c_int) as *mut u8
|
|
||||||
|
unsafe fn open_external(filename: &[u8]) -> *mut u8 {
|
||||||
|
let s = CString::from_slice(filename);
|
||||||
|
dlopen(s.as_ptr(), LAZY) as *mut u8
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn open_internal() -> *mut u8 {
|
||||||
|
dlopen(ptr::null(), LAZY) as *mut u8
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
|
pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
|
||||||
@@ -249,14 +254,6 @@ pub mod dl {
|
|||||||
dlclose(handle as *mut libc::c_void); ()
|
dlclose(handle as *mut libc::c_void); ()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy)]
|
|
||||||
pub enum Rtld {
|
|
||||||
Lazy = 1,
|
|
||||||
Now = 2,
|
|
||||||
Global = 256,
|
|
||||||
Local = 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[link_name = "dl"]
|
#[link_name = "dl"]
|
||||||
extern {
|
extern {
|
||||||
fn dlopen(filename: *const libc::c_char,
|
fn dlopen(filename: *const libc::c_char,
|
||||||
@@ -269,11 +266,13 @@ pub mod dl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub mod dl {
|
mod dl {
|
||||||
use iter::IteratorExt;
|
use iter::IteratorExt;
|
||||||
use libc;
|
use libc;
|
||||||
|
use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
|
||||||
use ops::FnOnce;
|
use ops::FnOnce;
|
||||||
use os;
|
use os;
|
||||||
|
use option::Option::{self, Some, None};
|
||||||
use ptr;
|
use ptr;
|
||||||
use result::Result;
|
use result::Result;
|
||||||
use result::Result::{Ok, Err};
|
use result::Result::{Ok, Err};
|
||||||
@@ -282,19 +281,75 @@ pub mod dl {
|
|||||||
use str;
|
use str;
|
||||||
use string::String;
|
use string::String;
|
||||||
use vec::Vec;
|
use vec::Vec;
|
||||||
|
use sys::c::compat::kernel32::SetThreadErrorMode;
|
||||||
|
|
||||||
pub unsafe fn open_external(filename: &[u8]) -> *mut u8 {
|
pub fn open(filename: Option<&[u8]>) -> Result<*mut u8, String> {
|
||||||
// Windows expects Unicode data
|
// disable "dll load failed" error dialog.
|
||||||
|
let mut use_thread_mode = true;
|
||||||
|
let prev_error_mode = unsafe {
|
||||||
|
// SEM_FAILCRITICALERRORS 0x01
|
||||||
|
let new_error_mode = 1;
|
||||||
|
let mut prev_error_mode = 0;
|
||||||
|
// Windows >= 7 supports thread error mode.
|
||||||
|
let result = SetThreadErrorMode(new_error_mode, &mut prev_error_mode);
|
||||||
|
if result == 0 {
|
||||||
|
let err = os::errno();
|
||||||
|
if err as libc::c_int == ERROR_CALL_NOT_IMPLEMENTED {
|
||||||
|
use_thread_mode = false;
|
||||||
|
// SetThreadErrorMode not found. use fallback solution: SetErrorMode()
|
||||||
|
// Note that SetErrorMode is process-wide so this can cause race condition!
|
||||||
|
// However, since even Windows APIs do not care of such problem (#20650),
|
||||||
|
// we just assume SetErrorMode race is not a great deal.
|
||||||
|
prev_error_mode = SetErrorMode(new_error_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_error_mode
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
SetLastError(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = match filename {
|
||||||
|
Some(filename) => {
|
||||||
let filename_str = str::from_utf8(filename).unwrap();
|
let filename_str = str::from_utf8(filename).unwrap();
|
||||||
let mut filename_str: Vec<u16> = filename_str.utf16_units().collect();
|
let mut filename_str: Vec<u16> = filename_str.utf16_units().collect();
|
||||||
filename_str.push(0);
|
filename_str.push(0);
|
||||||
LoadLibraryW(filename_str.as_ptr() as *const libc::c_void) as *mut u8
|
let result = unsafe {
|
||||||
|
LoadLibraryW(filename_str.as_ptr() as *const libc::c_void)
|
||||||
|
};
|
||||||
|
// beware: Vec/String may change errno during drop!
|
||||||
|
// so we get error here.
|
||||||
|
if result == ptr::null_mut() {
|
||||||
|
let errno = os::errno();
|
||||||
|
Err(os::error_string(errno))
|
||||||
|
} else {
|
||||||
|
Ok(result as *mut u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let mut handle = ptr::null_mut();
|
||||||
|
let succeeded = unsafe {
|
||||||
|
GetModuleHandleExW(0 as libc::DWORD, ptr::null(), &mut handle)
|
||||||
|
};
|
||||||
|
if succeeded == libc::FALSE {
|
||||||
|
let errno = os::errno();
|
||||||
|
Err(os::error_string(errno))
|
||||||
|
} else {
|
||||||
|
Ok(handle as *mut u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if use_thread_mode {
|
||||||
|
SetThreadErrorMode(prev_error_mode, ptr::null_mut());
|
||||||
|
} else {
|
||||||
|
SetErrorMode(prev_error_mode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn open_internal() -> *mut u8 {
|
result
|
||||||
let mut handle = ptr::null_mut();
|
|
||||||
GetModuleHandleExW(0 as libc::DWORD, ptr::null(), &mut handle);
|
|
||||||
handle as *mut u8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
|
pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
|
||||||
@@ -326,10 +381,10 @@ pub mod dl {
|
|||||||
fn SetLastError(error: libc::size_t);
|
fn SetLastError(error: libc::size_t);
|
||||||
fn LoadLibraryW(name: *const libc::c_void) -> *mut libc::c_void;
|
fn LoadLibraryW(name: *const libc::c_void) -> *mut libc::c_void;
|
||||||
fn GetModuleHandleExW(dwFlags: libc::DWORD, name: *const u16,
|
fn GetModuleHandleExW(dwFlags: libc::DWORD, name: *const u16,
|
||||||
handle: *mut *mut libc::c_void)
|
handle: *mut *mut libc::c_void) -> libc::BOOL;
|
||||||
-> *mut libc::c_void;
|
|
||||||
fn GetProcAddress(handle: *mut libc::c_void,
|
fn GetProcAddress(handle: *mut libc::c_void,
|
||||||
name: *const libc::c_char) -> *mut libc::c_void;
|
name: *const libc::c_char) -> *mut libc::c_void;
|
||||||
fn FreeLibrary(handle: *mut libc::c_void);
|
fn FreeLibrary(handle: *mut libc::c_void);
|
||||||
|
fn SetErrorMode(uMode: libc::c_uint) -> libc::c_uint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,6 +226,7 @@ pub mod compat {
|
|||||||
/// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003
|
/// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003
|
||||||
/// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003
|
/// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003
|
||||||
pub mod kernel32 {
|
pub mod kernel32 {
|
||||||
|
use libc::c_uint;
|
||||||
use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE};
|
use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE};
|
||||||
use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
|
use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
|
||||||
|
|
||||||
@@ -249,6 +250,12 @@ pub mod compat {
|
|||||||
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 }
|
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compat_fn! {
|
||||||
|
kernel32::SetThreadErrorMode(_dwNewMode: DWORD, _lpOldMode: *mut DWORD) -> c_uint {
|
||||||
|
unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user