Crate sharedlib [] [src]

A simple cross-platform library loader.

(crates.io) (github)

Based on libloading by Simonas Kazlauskas.

Loading a library

To load a library you can use any of the Lib, LibTracked, or LibUnsafe structs. Each of these structs provides different guarantees. For more information about the guarantees they provide, see the chosing your guarantees section, below. We use Lib for the examples below.

Calling a function in another library

unsafe {
    let path_to_lib = "examplelib.dll";
    let lib = try!(Lib::new(path_to_lib));
    let hello_world_symbol: Func<extern "C" fn()> = try!(lib.find_func("hello_world"));
    let hello_world = hello_world_symbol.get();
    hello_world();
}

Accessing data in another library

unsafe {
    let path_to_lib = "examplelib.dll";
    let lib = try!(Lib::new(path_to_lib));
    let my_usize_symbol: Data<usize> = try!(lib.find_data("my_usize"));
    let my_usize = my_usize_symbol.get();
    assert_eq!(*my_usize, 0);
}

Choosing your guarantees

A common problem when loading a shared library at runtime is that a symbol may be accessed after its library has been unloaded. sharedlib attempts to prevent this by allowing the lifetime of the library to be tracked. Each of the different libraries, LibUnsafe, Lib, LibTracked, LibArc, or LibRc, provides a different tracking mechanism. Below is a small overview. For more information, see the struct level documentation.

Pitfalls

While sharedlib attempts to prevent undefined behavior, loading shared libraries is inherently unsafe. Below are some tips which you may find helpful so that your code is not exposed to undefined behavior.

Avoid copying or moving data returned from get()

The get method on Symbol returns a transmuted pointer to something in a loaded library. While sharedlib tries to make sure that this pointer cannot outlive the library it is from, full protection is impossible. In particular: if a loaded struct contains pointers to things in the loaded library, and the loaded struct implements Clone, clients can clone the struct and make it to live longer than the library it is from. If this happens the pointers in the struct dangle. The example below demonstrate:

unsafe {
    let some_func = {
        let lib = try!(Lib::new("examplelib.dll"));
        let some_func_symbol: Func<extern "C" fn()> = try!(lib.find_func("some_func"));
        // All func pointers implement `Copy` so we can duplicate one.
        some_func_symbol.get()
        // lib goes out of scope here.
    };
    // Undefined behavior
    some_func();
}

Use the correct method when getting functions or data

Each library provides two different ways to get symbols from shared libraries. One way is find_func, and the other is find_data. Two functions are provded because find_data needs to return a reference to a T rather than a T itself, while find_func just needs to return a T itself. Returning the wrong thing can cause some complications. For instance: suppose we only have the find_data method, and we want to get a function pointer with the signature fn(). We are inclined to call lib.find_data::<fn()>(b"some_func"). This searches the memory of the loaded binary and finds the address of the first line of the function some_func. Next, the contents of the first line of some_func are treated as a function pointer rather than the address of the first line of some_func. When the first line of some_func is returned it is incorrectly cast into a function pointer. Calling it produces undefined behavior. The example below demonstrates:

unsafe {
    let lib = try!(Lib::new("examplelib.dll"));
    let some_func_symbol: Data<extern "C" fn()> = try!(lib.find_data("some_func"));
    // some_func actually points to a function but rust thinks it points to a function pointer.
    let some_func = some_func_symbol.get();
    // Undefined behavior
    some_func();
}

The correct way to do this with find_data is as follows:

unsafe {
    let lib = try!(Lib::new("examplelib.dll"));
    // Get a pointer to the block of memory at "some_func", this is the function itself.
    let some_func_symbol: Data<u8> = try!(lib.find_data("some_func"));
    // The type of some_func is &u8, a reference to the first byte of `some_func`. We can convert this into a function pointer.
    let some_func = some_func_symbol.get();
    let some_func_ptr: extern "C" fn() = std::mem::transmute(some_func);
    // This works now.
    some_func_ptr();
}

For convienience, the second example is provided as the find_func method, which does this error-prone conversion behind the scenes.

Comparison with other crates for loabing shared libraries

sharedlib was created out of frusteration with the existing crates for loading shared libraries. Below is a list of some of these crates with some information abuot how sharedlib improves upon them.

Frequently asked questions

What is a shared library?

A shared library is a set of functions and variables which can be loaded after a program has been compiled. By loading a library after compilation, the library can be recompiled or changed without recompiling the main program. Shared libraries can even be loaded at runtime. Common shared library filetypes are .dll for windows, .so for unix, and .dylib for osx. For more information about what a shared library is, see wikipedia.

Doesn't rust already provide linking against shared libraries?

While rust provides linking against shared libraries, it does not provide the ability to load them at runtime. If you only want to use shared libraries that you know about before runtime, you may find not find this crate very useful. On the other hand, if you wish to load something at runtime, like a plugin, you are in the right place.

Modules

error

Defines errors which may be returned by sharedlib.

Structs

Data

A pointer to shared data which uses a bound lifetime to avoid outliving its library.

DataTracked

A pointer to shared data which allows a user-provided ref-counting implementation to avoid outliving its library.

Func

A pointer to a shared function which uses a bound lifetime to avoid outliving its library.

FuncTracked

A pointer to a shared function which allows a user-provided ref-counting implementation to avoid outliving its library.

Lib

A shared library which uses bound lifetimes to track its Symbols. The inner library cannot be dropped if at least one loose symbol exists.

LibTracked

A shared library which which allows a user-provided ref-counting implementation to track its Symbols. The inner library will not be droped until all of teh ref-counts are dropped.

LibUnsafe

A shared library which does not track its Symbols. The inner library may be dropped at any time, even if it has loose symbols.

Traits

Symbol

A symbol from a shared library.

Type Definitions

DataArc

A pointer to shared data which uses atomic ref-counting to avoid outliving its library.

DataRc

A pointer to shared data which uses non-atomic ref-counting to avoid outliving its library.

DataUnsafe

A pointer to shared data which provides no protection against outliving its library.

FuncArc

A pointer to a shared function which uses atomic ref-counting to avoid outliving its library.

FuncRc

A pointer to a shared function which uses non-atomic ref-counting to avoid outliving its library.

FuncUnsafe

A pointer to a shared function which provides no protection against outliving its library.

LibArc

A shared library which implements LibTracked with atomic ref-counting to track its Symbols.

LibRc

A shared library which implements LibTracked with atomic ref-counting to track its Symbols.