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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
use super::super::err::Error;
use std::ffi::{CStr, CString, OsStr};

//choose the right platform implementation here
#[cfg(unix)]
use super::unix::{close_lib, get_sym, open_self, open_lib, addr_info_obtain, addr_info_init, addr_info_cleanup, Handle};
#[cfg(windows)]
use super::windows::{close_lib, get_sym, open_self, open_lib, addr_info_obtain, addr_info_init, addr_info_cleanup, Handle};

use std::mem::{size_of, transmute_copy};

/**
Main interface for opening and working with a dynamic link library.

**Note:** Several methods have their "*_cstr" equivalents. This is because all native OS
interfaces actually use C-strings. If you pass
[`CStr`](https://doc.rust-lang.org/std/ffi/struct.CStr.html)
as an argument, Library doesn't need to perform additional conversion from Rust string to
C-string.. This makes `*_cstr" functions slightly more optimal than their normal equivalents.
It is recommended that you use
[const-cstr](https://github.com/abonander/const-cstr) crate to create statically allocated
C-strings.

**Note:** The handle to the library gets released when the library object gets dropped.
Unless your application opened the library multiple times, this is the moment when symbols
obtained from the library become dangling symbols.
*/
#[derive(Debug)]
pub struct Library {
    handle: Handle,
}

impl Library {
    /**
    Open a dynamic library.

    **Note:** different platforms search for libraries in different directories.
    Therefore this function cannot be 100% platform independent.
    However it seems that all platforms support the full path and
    searching in default os directories if you provide only the file name.
    Please refer to your operating system guide for precise information about the directories
    where the operating system searches for dynamic link libraries.

    # Example

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

    fn main() {
        //use full path
        let lib = Library::open("/lib/i386-linux-gnu/libm.so.6").unwrap();
        //use only file name
        let lib = Library::open("libm.so.6").unwrap();
    }
    ```
    */
    pub fn open<S>(name: S) -> Result<Library, Error>
    where
        S: AsRef<OsStr>,
    {
        Ok(Self {
            handle: unsafe { open_lib(name.as_ref()) }?,
        })
    }

    /**
    Open the main program itself as a library.

    This allows a shared library to load symbols of the program it was loaded
    into.
    */
    pub fn open_self() -> Result<Library, Error> {
        Ok(Self {
            handle: unsafe { open_self() }?,
        })
    }

    /**
    Obtain symbol from opened library.

    **Note:** the `T` template type needs to have a size of a pointer.
    Because Rust does not support static casts at the moment, the size of the type
    is checked in runtime and causes panic if it doesn't match.

    **Note:** It is legal for a library to export null symbols.
    However this is something that almost nobody expects.
    Therefore allowing it here would bring many problems, especially if user obtains references
    or functions.
    This method checks the address value and returns `Error::NullSymbol` error if the value is null.
    If your code does require obtaining symbols with null value, please do something like this:

    # Example

    ```no_run
    extern crate dlopen;
    use dlopen::raw::Library;
    use dlopen::Error;
    use std::ptr::null;
    fn main(){
        let lib = Library::open("libyourlib.so").unwrap();
        let ptr_or_null: * const i32 = match unsafe{ lib.symbol("symbolname") } {
            Ok(val) => val,
            Err(err) => match err {
                Error::NullSymbol => null(),
                _ => panic!("Could not obtain the symbol")
            }
        };
        //do something with the symbol
    }
    ```
    */
    pub unsafe fn symbol<T>(&self, name: &str) -> Result<T, Error> {
        let cname = CString::new(name)?;
        self.symbol_cstr(cname.as_ref())
    }
    ///Equivalent of the `symbol` method but takes `CStr` as a argument.
    pub unsafe fn symbol_cstr<T>(&self, name: &CStr) -> Result<T, Error> {
        //TODO: convert it to some kind of static assertion (not yet supported in Rust)
        //this comparison should be calculated by compiler at compilation time - zero cost
        if size_of::<T>() != size_of::<*mut ()>() {
            panic!(
                "The type passed to dlopen::Library::symbol() function has a different size than a \
                 pointer - cannot transmute"
            );
        }
        let raw = get_sym(self.handle, name)?;
        if raw.is_null() {
            return Err(Error::NullSymbol);
        } else {
            Ok(transmute_copy(&raw))
        }
    }
}

impl Drop for Library {
    fn drop(&mut self) {
        self.handle = close_lib(self.handle);
    }
}

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

///Container for information about overlapping symbol from dynamic load library.
#[derive(Debug)]
pub struct OverlappingSymbol{
    ///Overlapping symbol name
    pub name: String,
    /// Overlapping symbol address
    pub addr: * const ()
}

/// Container for information about an address obtained from dynamic load library.
#[derive(Debug)]
pub struct AddressInfo {
    /// Path to the library that is the source of this symbol.
    pub dll_path: String,
    /// Base address of the library that is the source of this symbol.
    pub dll_base_addr: * const (),
    /// Information about the overlapping symbol from the dynamic load library.
    ///
    /// The information is optional since the given address may not overlap with any symbol.
    pub overlapping_symbol: Option<OverlappingSymbol>
}

///Obtains information about an address previously loaded from a dynamic load library.
pub struct AddressInfoObtainer {
}

impl AddressInfoObtainer {
    pub fn new() -> AddressInfoObtainer {
        unsafe{addr_info_init()};
        AddressInfoObtainer{}
    }

    /**
    Obtains information about an address previously loaded from a dynamic load library.

    # Example

    ```no_run
    extern crate dlopen;
    use dlopen::raw::{Library, AddressInfoObtainer};
    fn main() {
        let lib = Library::open("libyourlib.so").unwrap();
        let ptr: * const i32 = unsafe{ lib.symbol("symbolname") }.unwrap();

        // now we can obtain information about the symbol - library, base address etc.
        let aio = AddressInfoObtainer::new();
        let addr_info = aio.obtain(ptr as * const ()).unwrap();
        println!("Library path: {}", &addr_info.dll_path);
        println!("Library base address: {:?}", addr_info.dll_base_addr);
        if let Some(os) = addr_info.overlapping_symbol{
            println!("Overlapping symbol name: {}", &os.name);
            println!("Overlapping symbol address: {:?}", os.addr);
        }

    }
    ```
    */
    pub fn obtain(&self, addr: * const ()) -> Result<AddressInfo, Error>{
        unsafe {addr_info_obtain(addr)}
    }
}

impl Drop for AddressInfoObtainer{
    fn drop(&mut self) {
        unsafe{addr_info_cleanup()}
    }
}