1extern crate libc;
16
17use std::env;
18use std::ffi::{CString, OsString};
19use std::mem;
20use std::path::{Path, PathBuf};
21
22pub struct DynamicLibrary {
23 handle: *mut u8,
24}
25
26impl Drop for DynamicLibrary {
27 fn drop(&mut self) {
28 match dl::check_for_errors_in(|| unsafe { dl::close(self.handle) }) {
29 Ok(()) => {}
30 Err(str) => panic!("{}", str),
31 }
32 }
33}
34
35impl DynamicLibrary {
36 pub fn open(filename: Option<&Path>) -> Result<DynamicLibrary, String> {
47 let maybe_library = dl::open(filename.map(|path| path.as_os_str()));
48
49 match maybe_library {
53 Err(err) => Err(err),
54 Ok(handle) => Ok(DynamicLibrary { handle: handle }),
55 }
56 }
57
58 pub fn prepend_search_path(path: &Path) {
60 let mut search_path = DynamicLibrary::search_path();
61 search_path.insert(0, path.to_path_buf());
62 env::set_var(
63 DynamicLibrary::envvar(),
64 &DynamicLibrary::create_path(&search_path),
65 );
66 }
67
68 pub fn create_path(path: &[PathBuf]) -> OsString {
71 let mut newvar = OsString::new();
72 for (i, path) in path.iter().enumerate() {
73 if i > 0 {
74 newvar.push(DynamicLibrary::separator());
75 }
76 newvar.push(path);
77 }
78 newvar
79 }
80
81 pub fn envvar() -> &'static str {
84 if cfg!(windows) {
85 "PATH"
86 } else if cfg!(target_os = "macos") {
87 "DYLD_LIBRARY_PATH"
88 } else {
89 "LD_LIBRARY_PATH"
90 }
91 }
92
93 fn separator() -> &'static str {
94 if cfg!(windows) { ";" } else { ":" }
95 }
96
97 pub fn search_path() -> Vec<PathBuf> {
100 match env::var_os(DynamicLibrary::envvar()) {
101 Some(var) => env::split_paths(&var).collect(),
102 None => Vec::new(),
103 }
104 }
105
106 pub unsafe fn symbol<T>(&self, symbol: &str) -> Result<*mut T, String> {
108 let raw_string = CString::new(symbol).unwrap();
112 let maybe_symbol_value =
113 dl::check_for_errors_in(|| dl::symbol(self.handle, raw_string.as_ptr()));
114
115 match maybe_symbol_value {
118 Err(err) => Err(err),
119 Ok(symbol_value) => Ok(mem::transmute(symbol_value)),
120 }
121 }
122}
123
124#[cfg(all(test, not(target_os = "ios")))]
125mod test {
126 use super::*;
127 use std::mem;
128 use path::Path;
129
130 #[test]
131 #[cfg_attr(any(windows, target_os = "android"), ignore)] fn test_loading_cosine() {
133 let libm = match DynamicLibrary::open(None) {
136 Err(error) => panic!("Could not load self as module: {}", error),
137 Ok(libm) => libm,
138 };
139
140 let cosine: extern "C" fn(libc::c_double) -> libc::c_double = unsafe {
141 match libm.symbol("cos") {
142 Err(error) => panic!("Could not load function cos: {}", error),
143 Ok(cosine) => mem::transmute::<*mut u8, _>(cosine),
144 }
145 };
146
147 let argument = 0.0;
148 let expected_result = 1.0;
149 let result = cosine(argument);
150 if result != expected_result {
151 panic!(
152 "cos({}) != {} but equaled {} instead",
153 argument,
154 expected_result,
155 result
156 )
157 }
158 }
159
160 #[test]
161 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd",
162 target_os = "dragonfly", target_os = "bitrig", target_os = "openbsd"))]
163 fn test_errors_do_not_crash() {
164 let path = Path::new("/dev/null");
167 match DynamicLibrary::open(Some(&path)) {
168 Err(_) => {}
169 Ok(_) => panic!("Successfully opened the empty library."),
170 }
171 }
172}
173
174#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios",
175 target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig",
176 target_os = "openbsd"))]
177mod dl {
178 use std::ffi::{CStr, OsStr, CString};
179 use std::os::unix::ffi::OsStrExt;
180 use std::str;
181 use std::ptr;
182 use libc;
183
184 pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
185 check_for_errors_in(|| unsafe {
186 match filename {
187 Some(filename) => open_external(filename),
188 None => open_internal(),
189 }
190 })
191 }
192
193 const LAZY: libc::c_int = 1;
194
195 unsafe fn open_external(filename: &OsStr) -> *mut u8 {
196 let s = CString::new(filename.as_bytes()).unwrap(); dlopen(s.as_ptr(), LAZY) as *mut u8
198 }
199
200 unsafe fn open_internal() -> *mut u8 {
201 dlopen(ptr::null(), LAZY) as *mut u8
202 }
203
204 pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String>
205 where
206 F: FnOnce() -> T,
207 {
208 unsafe {
209 let result = f();
210
211 let last_error = dlerror() as *const _;
212 if last_error.is_null() {
213 Ok(result)
214 } else {
215 let s = CStr::from_ptr(last_error).to_bytes();
216 Err(str::from_utf8(s).unwrap().to_string())
217 }
218 }
219 }
220
221 pub unsafe fn symbol(handle: *mut u8, symbol: *const libc::c_char) -> *mut u8 {
222 dlsym(handle as *mut libc::c_void, symbol) as *mut u8
223 }
224 pub unsafe fn close(handle: *mut u8) {
225 dlclose(handle as *mut libc::c_void);
226 ()
227 }
228
229 extern "C" {
230 fn dlopen(filename: *const libc::c_char, flag: libc::c_int) -> *mut libc::c_void;
231 fn dlerror() -> *mut libc::c_char;
232 fn dlsym(handle: *mut libc::c_void, symbol: *const libc::c_char) -> *mut libc::c_void;
233 fn dlclose(handle: *mut libc::c_void) -> libc::c_int;
234 }
235}
236
237#[cfg(target_os = "windows")]
238mod dl {
239 use std::ffi::OsStr;
240 use std::iter::Iterator;
241 use std::libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
242 use std::ops::FnOnce;
243 use std::sys::os;
244 use std::os::windows::prelude::*;
245 use std::option::Option::{self, Some, None};
246 use std::ptr;
247 use std::result::Result;
248 use std::result::Result::{Ok, Err};
249 use std::string::String;
250 use std::vec::Vec;
251 use std::sys::c::compat::kernel32::SetThreadErrorMode;
252
253 pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
254 let mut use_thread_mode = true;
256 let prev_error_mode = unsafe {
257 let new_error_mode = 1;
259 let mut prev_error_mode = 0;
260 let result = SetThreadErrorMode(new_error_mode, &mut prev_error_mode);
262 if result == 0 {
263 let err = os::errno();
264 if err as libc::c_int == ERROR_CALL_NOT_IMPLEMENTED {
265 use_thread_mode = false;
266 prev_error_mode = SetErrorMode(new_error_mode);
272 }
273 }
274 prev_error_mode
275 };
276
277 unsafe {
278 SetLastError(0);
279 }
280
281 let result = match filename {
282 Some(filename) => {
283 let filename_str: Vec<_> =
284 filename.encode_wide().chain(Some(0).into_iter()).collect();
285 let result = unsafe { LoadLibraryW(filename_str.as_ptr() as *const libc::c_void) };
286 if result == ptr::null_mut() {
289 let errno = os::errno();
290 Err(os::error_string(errno))
291 } else {
292 Ok(result as *mut u8)
293 }
294 }
295 None => {
296 let mut handle = ptr::null_mut();
297 let succeeded =
298 unsafe { GetModuleHandleExW(0 as libc::DWORD, ptr::null(), &mut handle) };
299 if succeeded == libc::FALSE {
300 let errno = os::errno();
301 Err(os::error_string(errno))
302 } else {
303 Ok(handle as *mut u8)
304 }
305 }
306 };
307
308 unsafe {
309 if use_thread_mode {
310 SetThreadErrorMode(prev_error_mode, ptr::null_mut());
311 } else {
312 SetErrorMode(prev_error_mode);
313 }
314 }
315
316 result
317 }
318
319 pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String>
320 where
321 F: FnOnce() -> T,
322 {
323 unsafe {
324 SetLastError(0);
325
326 let result = f();
327
328 let error = os::errno();
329 if 0 == error {
330 Ok(result)
331 } else {
332 Err(format!("Error code {}", error))
333 }
334 }
335 }
336
337 pub unsafe fn symbol(handle: *mut u8, symbol: *const libc::c_char) -> *mut u8 {
338 GetProcAddress(handle as *mut libc::c_void, symbol) as *mut u8
339 }
340 pub unsafe fn close(handle: *mut u8) {
341 FreeLibrary(handle as *mut libc::c_void);
342 ()
343 }
344
345 #[allow(non_snake_case)]
346 extern "system" {
347 fn SetLastError(error: libc::size_t);
348 fn LoadLibraryW(name: *const libc::c_void) -> *mut libc::c_void;
349 fn GetModuleHandleExW(
350 dwFlags: libc::DWORD,
351 name: *const u16,
352 handle: *mut *mut libc::c_void,
353 ) -> libc::BOOL;
354 fn GetProcAddress(
355 handle: *mut libc::c_void,
356 name: *const libc::c_char,
357 ) -> *mut libc::c_void;
358 fn FreeLibrary(handle: *mut libc::c_void);
359 fn SetErrorMode(uMode: libc::c_uint) -> libc::c_uint;
360 }
361}