Skip to content

std::dynamic_lib: Fix Windows error handling #20874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 19, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 98 additions & 43 deletions src/libstd/dynamic_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,14 @@ impl DynamicLibrary {
/// Lazily open a dynamic library. When passed None it gives a
/// handle to the calling process
pub fn open(filename: Option<&Path>) -> Result<DynamicLibrary, String> {
unsafe {
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
// an error opening the library so the destructor does not
// run.
match maybe_library {
Err(err) => Err(err),
Ok(handle) => Ok(DynamicLibrary { handle: handle })
}
let maybe_library = dl::open(filename.map(|path| path.as_vec()));

// The dynamic library must not be constructed if there is
// an error opening the library so the destructor does not
// run.
match maybe_library {
Err(err) => Err(err),
Ok(handle) => Ok(DynamicLibrary { handle: handle })
}
}

Expand Down Expand Up @@ -198,22 +191,34 @@ mod test {
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
pub mod dl {
pub use self::Rtld::*;
mod dl {
use prelude::v1::*;

use ffi::{self, CString};
use str;
use libc;
use ptr;

pub unsafe fn open_external(filename: &[u8]) -> *mut u8 {
pub fn open(filename: Option<&[u8]>) -> Result<*mut u8, String> {
check_for_errors_in(|| {
unsafe {
match filename {
Some(filename) => open_external(filename),
None => open_internal(),
}
}
})
}

const LAZY: libc::c_int = 1;

unsafe fn open_external(filename: &[u8]) -> *mut u8 {
let s = CString::from_slice(filename);
dlopen(s.as_ptr(), Lazy as libc::c_int) as *mut u8
dlopen(s.as_ptr(), LAZY) as *mut u8
}

pub unsafe fn open_internal() -> *mut u8 {
dlopen(ptr::null(), Lazy as libc::c_int) 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
Expand Down Expand Up @@ -249,14 +254,6 @@ pub mod dl {
dlclose(handle as *mut libc::c_void); ()
}

#[derive(Copy)]
pub enum Rtld {
Lazy = 1,
Now = 2,
Global = 256,
Local = 0,
}

#[link_name = "dl"]
extern {
fn dlopen(filename: *const libc::c_char,
Expand All @@ -269,11 +266,13 @@ pub mod dl {
}

#[cfg(target_os = "windows")]
pub mod dl {
mod dl {
use iter::IteratorExt;
use libc;
use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
use ops::FnOnce;
use os;
use option::Option::{self, Some, None};
use ptr;
use result::Result;
use result::Result::{Ok, Err};
Expand All @@ -282,19 +281,75 @@ pub mod dl {
use str;
use string::String;
use vec::Vec;
use sys::c::compat::kernel32::SetThreadErrorMode;

pub fn open(filename: Option<&[u8]>) -> Result<*mut u8, String> {
// 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
};

pub unsafe fn open_external(filename: &[u8]) -> *mut u8 {
// Windows expects Unicode data
let filename_str = str::from_utf8(filename).unwrap();
let mut filename_str: Vec<u16> = filename_str.utf16_units().collect();
filename_str.push(0);
LoadLibraryW(filename_str.as_ptr() as *const libc::c_void) as *mut u8
}
unsafe {
SetLastError(0);
}

let result = match filename {
Some(filename) => {
let filename_str = str::from_utf8(filename).unwrap();
let mut filename_str: Vec<u16> = filename_str.utf16_units().collect();
filename_str.push(0);
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 {
let mut handle = ptr::null_mut();
GetModuleHandleExW(0 as libc::DWORD, ptr::null(), &mut handle);
handle as *mut u8
result
}

pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
Expand Down Expand Up @@ -326,10 +381,10 @@ pub mod dl {
fn SetLastError(error: libc::size_t);
fn LoadLibraryW(name: *const libc::c_void) -> *mut libc::c_void;
fn GetModuleHandleExW(dwFlags: libc::DWORD, name: *const u16,
handle: *mut *mut libc::c_void)
-> *mut libc::c_void;
handle: *mut *mut libc::c_void) -> libc::BOOL;
fn GetProcAddress(handle: *mut libc::c_void,
name: *const libc::c_char) -> *mut libc::c_void;
fn FreeLibrary(handle: *mut libc::c_void);
fn SetErrorMode(uMode: libc::c_uint) -> libc::c_uint;
}
}
7 changes: 7 additions & 0 deletions src/libstd/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ pub mod compat {
/// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003
/// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003
pub mod kernel32 {
use libc::c_uint;
use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE};
use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;

Expand All @@ -249,6 +250,12 @@ pub mod compat {
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 }
}
}
}
}

Expand Down