Skip to content

Commit 766fcb0

Browse files
Refactor dynamic library error checking on *nix
The old code was checking `dlerror` more often than necessary, since the return value of `dlopen` indicates whether an error occurred.
1 parent 527a685 commit 766fcb0

File tree

2 files changed

+61
-34
lines changed

2 files changed

+61
-34
lines changed

src/librustc_metadata/dynamic_lib.rs

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -51,51 +51,77 @@ mod tests;
5151

5252
#[cfg(unix)]
5353
mod dl {
54-
use std::ffi::{CStr, CString, OsStr};
54+
use std::ffi::{CString, OsStr};
5555
use std::os::unix::prelude::*;
56-
use std::ptr;
57-
use std::str;
5856

59-
pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> {
60-
check_for_errors_in(|| unsafe {
61-
let s = CString::new(filename.as_bytes()).unwrap();
62-
libc::dlopen(s.as_ptr(), libc::RTLD_LAZY) as *mut u8
63-
})
64-
}
57+
// `dlerror` is process global, so we can only allow a single thread at a
58+
// time to call `dlsym` and `dlopen` if we want to check the error message.
59+
mod error {
60+
use std::ffi::CStr;
61+
use std::lazy::SyncLazy;
62+
use std::sync::{Mutex, MutexGuard};
6563

66-
fn check_for_errors_in<T, F>(f: F) -> Result<T, String>
67-
where
68-
F: FnOnce() -> T,
69-
{
70-
use std::sync::{Mutex, Once};
71-
static INIT: Once = Once::new();
72-
static mut LOCK: *mut Mutex<()> = ptr::null_mut();
73-
unsafe {
74-
INIT.call_once(|| {
75-
LOCK = Box::into_raw(Box::new(Mutex::new(())));
76-
});
77-
// dlerror isn't thread safe, so we need to lock around this entire
78-
// sequence
79-
let _guard = (*LOCK).lock();
80-
let _old_error = libc::dlerror();
81-
82-
let result = f();
83-
84-
let last_error = libc::dlerror() as *const _;
85-
if ptr::null() == last_error {
86-
Ok(result)
87-
} else {
88-
let s = CStr::from_ptr(last_error).to_bytes();
89-
Err(str::from_utf8(s).unwrap().to_owned())
64+
pub fn lock() -> MutexGuard<'static, Guard> {
65+
static LOCK: SyncLazy<Mutex<Guard>> = SyncLazy::new(|| Mutex::new(Guard { _priv: () }));
66+
LOCK.lock().unwrap()
67+
}
68+
69+
pub struct Guard {
70+
_priv: (),
71+
}
72+
73+
impl Guard {
74+
pub fn get(&mut self) -> Result<(), String> {
75+
let msg = unsafe { libc::dlerror() };
76+
if msg.is_null() {
77+
Ok(())
78+
} else {
79+
let msg = unsafe { CStr::from_ptr(msg as *const _) };
80+
Err(msg.to_string_lossy().into_owned())
81+
}
9082
}
83+
84+
pub fn clear(&mut self) {
85+
let _ = unsafe { libc::dlerror() };
86+
}
87+
}
88+
}
89+
90+
pub(super) fn open(filename: &OsStr) -> Result<*mut u8, String> {
91+
let s = CString::new(filename.as_bytes()).unwrap();
92+
93+
let mut dlerror = error::lock();
94+
let ret = unsafe { libc::dlopen(s.as_ptr(), libc::RTLD_LAZY) } as *mut u8;
95+
96+
if !ret.is_null() {
97+
return Ok(ret);
9198
}
99+
100+
// A NULL return from `dlopen` indicates that an error has
101+
// definitely occurred, so if nothing is in `dlerror`, we are
102+
// racing with another thread that has stolen our error message.
103+
dlerror.get().and_then(|()| Err("Unknown error".to_string()))
92104
}
93105

94106
pub(super) unsafe fn symbol(
95107
handle: *mut u8,
96108
symbol: *const libc::c_char,
97109
) -> Result<*mut u8, String> {
98-
check_for_errors_in(|| libc::dlsym(handle as *mut libc::c_void, symbol) as *mut u8)
110+
let mut dlerror = error::lock();
111+
112+
// Flush `dlerror` since we need to use it to determine whether the subsequent call to
113+
// `dlsym` is successful.
114+
dlerror.clear();
115+
116+
let ret = libc::dlsym(handle as *mut libc::c_void, symbol) as *mut u8;
117+
118+
// A non-NULL return value *always* indicates success. There's no need
119+
// to check `dlerror`.
120+
if !ret.is_null() {
121+
return Ok(ret);
122+
}
123+
124+
dlerror.get().map(|()| ret)
99125
}
100126

101127
pub(super) unsafe fn close(handle: *mut u8) {

src/librustc_metadata/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![feature(drain_filter)]
66
#![feature(in_band_lifetimes)]
77
#![feature(nll)]
8+
#![feature(once_cell)]
89
#![feature(or_patterns)]
910
#![feature(proc_macro_internals)]
1011
#![feature(min_specialization)]

0 commit comments

Comments
 (0)