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
use mun_abi as abi;
use std::{ffi::c_void, path::Path};

pub use temp_library::TempLibrary;

mod temp_library;

pub struct MunLibrary(TempLibrary);

impl MunLibrary {
    /// Loads a munlib library from disk.
    ///
    /// # Safety
    ///
    /// A munlib is simply a shared object. When a library is loaded, initialisation routines
    /// contained within it are executed. For the purposes of safety, the execution of these
    /// routines is conceptually the same calling an unknown foreign function and may impose
    /// arbitrary requirements on the caller for the call to be sound.
    ///
    /// Additionally, the callers of this function must also ensure that execution of the
    /// termination routines contained within the library is safe as well. These routines may be
    /// executed when the library is unloaded.
    ///
    /// See [`libloading::Library::new`] for more information.
    pub unsafe fn new(library_path: &Path) -> Result<Self, anyhow::Error> {
        // Although loading a library is technically unsafe, we assume here that this is not the
        // case for munlibs.
        let library = TempLibrary::new(library_path)?;

        // Verify that the `*.munlib` contains all required functions. Note that this is an unsafe
        // operation because the loaded symbols don't actually contain type information. Casting
        // is therefore unsafe.
        let _get_abi_version_fn: libloading::Symbol<'_, extern "C" fn() -> u32> =
            library.library().get(abi::GET_VERSION_FN_NAME.as_bytes())?;

        let _get_info_fn: libloading::Symbol<'_, extern "C" fn() -> abi::AssemblyInfo<'static>> =
            library.library().get(abi::GET_INFO_FN_NAME.as_bytes())?;

        let _set_allocator_handle_fn: libloading::Symbol<'_, extern "C" fn(*mut c_void)> = library
            .library()
            .get(abi::SET_ALLOCATOR_HANDLE_FN_NAME.as_bytes())?;

        Ok(MunLibrary(library))
    }

    pub fn into_inner(self) -> TempLibrary {
        self.0
    }

    /// Returns the ABI version of this mun library.
    ///
    /// # Safety
    ///
    /// This operations executes a function in the munlib. There is no guarantee that the execution
    /// of the function wont result in undefined behavior.
    pub unsafe fn get_abi_version(&self) -> u32 {
        let get_abi_version_fn: libloading::Symbol<'_, extern "C" fn() -> u32> = self
            .0
            .library()
            .get(abi::GET_VERSION_FN_NAME.as_bytes())
            .unwrap();

        get_abi_version_fn()
    }

    /// Returns the assembly info exported by the shared object.
    ///
    /// # Safety
    ///
    /// This operations executes a function in the munlib. There is no guarantee that the execution
    /// of the function wont result in undefined behavior.
    pub unsafe fn get_info(&self) -> abi::AssemblyInfo<'static> {
        let get_info_fn: libloading::Symbol<extern "C" fn() -> abi::AssemblyInfo<'static>> = self
            .0
            .library()
            .get(abi::GET_INFO_FN_NAME.as_bytes())
            .unwrap();

        get_info_fn()
    }

    /// Stores the allocator handle inside the shared object. This is used by the internals of the
    /// library to be able to allocate memory.
    ///
    /// # Safety
    ///
    /// This operations executes a function in the munlib. There is no guarantee that the execution
    /// of the function wont result in undefined behavior.
    pub unsafe fn set_allocator_handle(&mut self, allocator_ptr: *mut c_void) {
        let set_allocator_handle_fn: libloading::Symbol<'_, extern "C" fn(*mut c_void)> = self
            .0
            .library()
            .get(abi::SET_ALLOCATOR_HANDLE_FN_NAME.as_bytes())
            .unwrap();

        set_allocator_handle_fn(allocator_ptr);
    }
}