1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use std::ffi::CStr;
use super::symbol::Symbol;
use super::ptr_or_null::PtrOrNull;
use super::ptr_or_null_mut::PtrOrNullMut;
use super::super::raw::Library as RawLib;
use std::ffi::{CString, OsStr};
use std::ptr::{null, null_mut};

use super::super::err::Error;

/**
Safe wrapper around dynamic link library handle.

Methods of `Library` return only types that make the library borrowed. Therefore the problem with
dangling symbols is prevented.

**Note:**: It is recommended that you use certain methods in certain situations:

* `symbol()` - for obtaining functions and pointers (but only if you can't use references
    instead of pointers and you do not accept null value of a pointer).
* `reference()` and `reference_mut()` - for obtaining access to
    statically allocated objects - either constant or mutable.
* `ptr_or_null()` and `ptr_or_null_mut()` - for obtaining pointers if you accept null values of
pointers (in 99% of cases you should rather use previously mentioned methods).

#Example

```no_run
extern crate dlopen;
use dlopen::symbor::Library;

fn main(){
    let lib = Library::open("libexample.dylib").unwrap();
    let fun = unsafe{lib.symbol::<unsafe extern "C" fn()>("function")}.unwrap();
    unsafe{fun()};
    let glob_val: &mut u32 = unsafe{lib.reference_mut("glob_val")}.unwrap();
    *glob_val = 42;
    let ptr_or_null = unsafe{lib.ptr_or_null::<()>("void_ptr")}.unwrap();
    println!("Pointer is null: {}", ptr_or_null.is_null());
}
```
*/
pub struct Library {
    lib: RawLib,
}

impl Library {
    ///Open dynamic link library using provided file name or path.
    pub fn open<S>(name: S) -> Result<Library, Error>
    where
        S: AsRef<OsStr>,
    {
        Ok(Library {
            lib: RawLib::open(name)?,
        })
    }

    /// Open the program itself as library.
    ///
    /// This allows a shared library to load symbols of the program it was
    /// loaded into.
    pub fn open_self() -> Result<Library, Error> {
        Ok(Library {
            lib: RawLib::open_self()?,
        })
    }

    /// Obtain a symbol from library.
    ///
    /// This method is the most general one and allows obtaining basically everything assuming
    /// that the value of the given symbol cannot be null (use `ptr_or_null()` for this case).
    /// However the `reference()` and `reference_mut()` methods return a native reference and they
    /// are more programmer friendly when you try accessing statically allocated data in
    /// the library.
    pub unsafe fn symbol<T>(&self, name: &str) -> Result<Symbol<T>, Error> {
        Ok(Symbol::new(self.lib.symbol(name)?))
    }

    ///Equivalent of the `symbol()` method but takes `CStr` as a argument.
    pub unsafe fn symbol_cstr<T>(&self, name: &CStr) -> Result<Symbol<T>, Error> {
        Ok(Symbol::new(self.lib.symbol_cstr(name)?))
    }

    ///Obtain a const pointer from library.
    ///
    /// **Note:** This method is only recommended for data
    /// that can't be accessed as a reference and that can have a null pointer value
    /// (so not in 99% of cases).
    pub unsafe fn ptr_or_null<T>(&self, name: &str) -> Result<PtrOrNull<T>, Error> {
        let cname = CString::new(name)?;
        self.ptr_or_null_cstr(cname.as_ref())
    }

    ///Equivalent of the `pointer()` method but takes `CStr` as a argument.
    pub unsafe fn ptr_or_null_cstr<T>(&self, name: &CStr) -> Result<PtrOrNull<T>, Error> {
        let raw_ptr = match self.lib.symbol_cstr(name) {
            Ok(val) => val,
            Err(err) => match err {
                Error::NullSymbol => null(),
                _ => return Err(err),
            },
        };
        Ok(PtrOrNull::new(raw_ptr))
    }

    ///Obtain a mutable pointer from library.
    ///
    /// **Note:** This method is only recommended for data
    /// that can't be accessed as a reference and that can have a null pointer value
    /// (so not in 99% of cases).
    pub unsafe fn ptr_or_null_mut<T>(&self, name: &str) -> Result<PtrOrNullMut<T>, Error> {
        let cname = CString::new(name)?;
        self.ptr_or_null_mut_cstr(cname.as_ref())
    }

    ///Equivalent of the `pointer_mut()` method but takes `CStr` as a argument.
    pub unsafe fn ptr_or_null_mut_cstr<T>(&self, name: &CStr) -> Result<PtrOrNullMut<T>, Error> {
        let raw_ptr = match self.lib.symbol_cstr(name) {
            Ok(val) => val,
            Err(err) => match err {
                Error::NullSymbol => null_mut(),
                _ => return Err(err),
            },
        };
        Ok(PtrOrNullMut::new(raw_ptr))
    }

    ///Obtain const reference to statically allocated data in the library.
    pub unsafe fn reference<T>(&self, name: &str) -> Result<&T, Error> {
        self.lib.symbol(name)
    }

    ///Equivalent of the `reference()` method but takes `CStr` as a argument.
    pub unsafe fn reference_cstr<T>(&self, name: &CStr) -> Result<&T, Error> {
        self.lib.symbol_cstr(name)
    }

    ///Obtain mutable reference to statically allocated data in the library.
    pub unsafe fn reference_mut<T>(&self, name: &str) -> Result<&mut T, Error> {
        self.lib.symbol(name)
    }

    ///Equivalent of the `reference_mut()` method but takes `CStr` as a argument.
    pub unsafe fn reference_mut_cstr<T>(&self, name: &CStr) -> Result<&mut T, Error> {
        self.lib.symbol_cstr(name)
    }
}

unsafe impl Send for Library {}
unsafe impl Sync for Library {}