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
//! Implementation of shared objects as lovm2 modules
//!
//! Shared objects must be specifically compiled for lovm2 and export a function named
//! after `EXTERN_LOVM2_INITIALIZER`. It is responsible for registering the exported
//! functions in the given `HashMap`.
//!
//! See module [extend](crate::extend) for helper utilities and examples.

use libloading::{Error, Library, Symbol};
use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc;

use crate::code::{CallProtocol, CallableRef, CodeObject};
use crate::error::*;
use crate::module::{Module, Slots};
use crate::var::Variable;
use crate::vm::Vm;

/// Name of the unmangled function name to call when initializing module slots.
pub const EXTERN_LOVM2_INITIALIZER: &str = "lovm2_module_initialize";

/// Definition for dynamically linked function.
pub type ExternFunction = unsafe extern "C" fn(&mut Vm) -> Lovm2Result<()>;
/// Function signature of the extern module initializer.
pub type ExternInitializer = extern "C" fn(lib: Rc<Library>, &mut HashMap<Variable, CallableRef>);

fn load_slots(_name: &str, lib: Library) -> Lovm2Result<Slots> {
    unsafe {
        //let named_initializer = format!("{}_{}", EXTERN_LOVM2_INITIALIZER, name);

        let lib = Rc::new(lib);

        // try to lookup named initializer first, fallback to initializer without name
        let lookup: Result<Symbol<ExternInitializer>, Error> =
            lib.get(EXTERN_LOVM2_INITIALIZER.as_bytes());
        //.get(named_initializer.as_bytes());
        //.or_else(|_| lib.get(EXTERN_LOVM2_INITIALIZER.as_bytes()));

        match lookup {
            Ok(initializer) => {
                let mut slots = HashMap::new();
                initializer(lib.clone(), &mut slots);
                Ok(Slots::from(slots))
            }
            Err(_) => Err(Lovm2ErrorTy::Basic.into()),
        }
    }
}

/// Try loading a shared object from a file.
pub fn load_library_from_file<T>(path: T) -> Lovm2Result<Library>
where
    T: AsRef<Path>,
{
    // this fixes some segfault errors. https://github.com/nagisa/rust_libloading/issues/41
    // load and initialize library
    #[cfg(target_os = "linux")]
    let library: Result<Library, libloading::Error> = {
        // load library with `RTLD_NOW | RTLD_NODELETE` to fix a SIGSEGV
        ::libloading::os::unix::Library::open(Some(path.as_ref()), 0x2 | 0x1000).map(Library::from)
    };
    #[cfg(not(target_os = "linux"))]
    let library = Library::new(path.as_ref());

    library.or_else(err_from_string)
}

/// Turn the loaded shared object into a `lovm2` [Module].
pub fn module_from_library<T>(path: T, lib: Library) -> Lovm2Result<Module>
where
    T: AsRef<Path>,
{
    let name = path.as_ref().file_stem().unwrap().to_str().unwrap();

    let code_object = CodeObject {
        name: name.to_string(),
        loc: Some(path.as_ref().display().to_string()),
        ..CodeObject::default()
    };

    Ok(Module {
        code_object: Rc::new(code_object),
        slots: load_slots(name, lib)?,
    })
}

// As the `Library` is always valid for this structure, it should be fine to
// call `into_raw` on the loaded symbol and then use the function pointer afterwards.
/// Contains a function name, imported by `EXTERN_LOVM2_INITIALIZER`.
pub struct SharedObjectSlot(
    Rc<Library>,
    #[cfg(unix)] ::libloading::os::unix::Symbol<ExternFunction>,
    #[cfg(windows)] ::libloading::os::windows::Symbol<ExternFunction>,
);

impl SharedObjectSlot {
    pub fn new<T>(lib: Rc<Library>, name: T) -> Lovm2Result<Self>
    where
        T: AsRef<str>,
    {
        let name = name.as_ref();
        unsafe {
            let lookup: Result<Symbol<ExternFunction>, Error> = lib.get(name.as_bytes());
            match lookup {
                Ok(symbol) => Ok(Self(lib.clone(), symbol.into_raw())),
                Err(_) => err_symbol_not_found(name),
            }
        }
    }
}

impl CallProtocol for SharedObjectSlot {
    fn run(&self, vm: &mut Vm) -> Lovm2Result<()> {
        unsafe { self.1(vm) }
    }
}

impl std::fmt::Debug for SharedObjectSlot {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "<extern function>")
    }
}