libloading

repository·master·Indexed 23 days ago

https://github.com/nagisa/rust_libloading

A Rust library providing safe bindings for loading dynamic/shared libraries and accessing their functions and static variables. It focuses on improved memory safety by using Rust's lifetime system to prevent dangling Symbols when a Library is unloaded. Version 0.9.0.

Tokens
2.4K
Snippets
7
Records
11
Agent score
79%

What's inside libloading

  1. Overview of libloading

    master
    libloading provides bindings around platform-specific dynamic library loading primitives (shared libraries) with a focus on improved memory safety. Its primary safety guarantee is preventing dangling Symbols that could otherwise occur if a Library is unloaded while symbols from that library are still in use.
  2. How `Symbol` manages lifetimes

    master

    A Symbol<'lib, T> is a safe wrapper around a dynamically loaded symbol. It uses Rust's lifetime system to ensure that the symbol cannot be used after the Library it was loaded from has been unloaded.

    Key Features

    • Deref: You can use a Symbol directly as if it were the underlying function or variable thanks to the Deref implementation.
    • Lifetimes: The 'lib lifetime parameter ties the symbol to the existence of the Library.
    • Lifting Options: If you are loading a symbol that might be a null pointer (represented as Option<T>), you can use lift_option() to convert a Symbol<'lib, Option<T>> into a Symbol<'lib, T> if the value is present.
    # use ::libloading::{Library, Symbol};
    # let lib = unsafe {
    #     Library::new("/path/to/awesome.module").unwrap()
    # };
    unsafe {
        let awesome_function: Symbol<unsafe extern "C" fn(f64) -> f64> =
            lib.get(b"awesome_function\0").unwrap();
        awesome_function(0.42);
    }
  3. Inspect underlying OS error sources

    master

    When an Error is returned, you can inspect the underlying system error using the .source() method. This is useful for debugging specific OS-level failures.

    • For Unix-like systems, the source is a DlError, which wraps a CString containing the error message from dlerror().
    • For Windows, the source is a WindowsError, which wraps an i32 error code. If the std feature is enabled, WindowsError can be converted into a standard std::io::Error for more detailed reporting.
  4. Load a dynamic library and call a function

    master

    To load a library and access its symbols, use Library::new to load the file and Library::get to retrieve a symbol.

    Safety Note: Accessing symbols is an unsafe operation. However, libloading uses Rust's lifetime system to ensure that the loaded Symbol cannot outlive the Library instance, preventing common use-after-free errors associated with dynamic loading.

    fn call_dynamic() -> Result<u32, Box<dyn std::error::Error>> {
        unsafe {
            let lib = libloading::Library::new("/path/to/liblibrary.so")?;
            let func: libloading::Symbol<unsafe extern "C" fn() -> u32> = lib.get(b"my_func")?;
            Ok(func())
        }
    }
  5. Convert a `Symbol` to a raw pointer

    master

    If you need to bypass the lifetime guarantees provided by the Symbol wrapper, you can use into_raw() or try_as_raw_ptr().

    Safety

    Warning: Using these methods is unsafe. You relinquish all lifetime guarantees and must manually ensure that the resulting raw pointer is not used after the Library that provided it has been unloaded.

    # use ::libloading::{Library, Symbol};
    # let lib = unsafe { Library::new("/path/to/awesome.module").unwrap() };
    unsafe {
        let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
        let symbol = symbol.into_raw();
    }
  6. Get a symbol from a library with `Library::get`

    master

    Use Library::get to retrieve a pointer to a function or static variable by its symbol name. The symbol name is interpreted as-is without mangling.

    Safety

    Users must specify the correct type (T) for the function or variable being loaded. Incorrect types will lead to undefined behavior.

    Platform-specifics

    On some POSIX implementations (like FreeBSD), if dlsym returns a null pointer, this function will unconditionally return an error due to dlerror not being MT-safe. If you need to support genuine null pointers, use the platform-specific get_singlethreaded method.

    # use ::libloading::{Library, Symbol};
    # let lib = unsafe {
    #     Library::new("/path/to/awesome.module").unwrap()
    # };
    unsafe {
        let awesome_function: Symbol<unsafe extern "C" fn(f64) -> f64> =
            lib.get(b"awesome_function\0").unwrap();
        awesome_function(0.42);
    }
  7. Unload a library with `Library::close`

    master
    Manually unload the library. This is useful if you want to handle errors that might arise during the unloading process. If you do not call close, the library will be unloaded when the Library instance is dropped, but any errors occurring during that automatic drop will be ignored.
  8. Generate platform-specific library filenames with library_filename

    master

    The library_filename function converts a base library name into a filename appropriate for the current operating system by prepending the correct prefix (e.g., lib) and appending the correct suffix (e.g., .so, .dylib, or .dll).

    This is useful for loading global libraries in a platform-independent way.

    use libloading::{Library, library_filename};
    // Will attempt to load `libLLVM.so` on Linux, `libLLVM.dylib` on macOS and `LLVM.dll` on Windows.
    let library = unsafe {
        Library::new(library_filename("LLVM"))
    };
  9. Load a dynamic library with `Library::new`

    master

    Use Library::new to find and load a dynamic library from a filename, absolute path, or relative path.

    Safety

    This function is unsafe because:

    • Loading a library executes its initialization routines, which are treated as calling unknown foreign functions.
    • The caller must ensure that the execution of termination routines (executed when the library is unloaded) is safe.

    Thread Safety

    While the implementation strives for MT-safety, certain platforms have limitations (e.g., dlerror on some UNIX targets may not be MT-safe). Additionally, calling this function from multiple threads is not MT-safe if the library search path is being modified (e.g., via SetDllDirectory on Windows or LD_LIBRARY_PATH on UNIX).

    Tips

    • To improve portability, distribute libraries under a common filename (e.g., awesome.module) to avoid platform-specific extensions.
    • Use absolute or relative paths whenever possible to avoid flakiness caused by platform-dependent search locations.
    # use ::libloading::Library;
    // Any of the following are valid.
    unsafe {
        let _ = Library::new("/path/to/awesome.module").unwrap();
        let _ = Library::new("../awesome.module").unwrap();
        let _ = Library::new("libsomelib.so.1").unwrap();
    }
  10. Handle errors in libloading

    master

    The libloading::Error enum represents all possible errors encountered during dynamic library loading and symbol lookup. Errors are categorized by the underlying OS operation that failed (e.g., dlopen on Unix-like systems or LoadLibraryExW on Windows).

    Error Categories

    Unix-like Systems (dlopen/dlsym/dlclose)

    • DlOpen: Failed to load a library. Contains a DlError source.
    • DlOpenUnknown: Failed to load a library, but the system did not provide an error string.
    • DlSym: Failed to find a symbol. Contains a DlError source.
    • DlSymUnknown: Failed to find a symbol, but the system did not provide an error string.
    • DlClose: Failed to unload a library. Contains a DlError source.
    • DlCloseUnknown: Failed to unload a library, but the system did not provide an error string.

    Windows (LoadLibraryExW/GetModuleHandleExW/GetProcAddress/FreeLibrary)

    • LoadLibraryExW: Failed to load a library. Contains a WindowsError source.
    • LoadLibraryExWUnknown: Failed to load a library, but the system did not provide an error code.
    • GetModuleHandleExW: Failed to get a module handle. Contains a WindowsError source.
    • GetModuleHandleExWUnknown: Failed to get a module handle, but the system did not provide an error code.
    • GetProcAddress: Failed to find a symbol. Contains a WindowsError source.
    • GetProcAddressUnknown: Failed to find a symbol, but the system did not provide an error code.
    • FreeLibrary: Failed to unload a library. Contains a WindowsError source.
    • FreeLibraryUnknown: Failed to unload a library, but the system did not provide an error code.

    General Errors

    • IncompatibleSize: The requested type size is incompatible with the loaded symbol.
    • InteriorZeroElements: The input filename or symbol name contains null bytes (\0) internally.