pub struct Library(/* private fields */);
Expand description
A loaded library handle.
Implementations§
source§impl Library
impl Library
- Constructors
Library::load
— Load a library, forever, or returnErr(io::Error)
.
- Symbols (most of these functions implicitly transmute! Use extreme caution.)
Library::has_sym
— Check if a symbol,"name\0"
, exists in the library.Library::sym
— Load a symbol from the library by"name\0"
, or returnErr(io::Error)
.Library::sym_opt
— Load a symbol from the library by"name\0"
, or returnNone
.Library::sym_by_ordinal
— Load a symbol from the library by windows ordinal, or returnErr(io::Error)
.Library::sym_opt_by_ordinal
— Load a symbol from the library by windows ordinal, or returnNone
.
- Interop
Library::from_ptr
— Wrap a forever-loaded library inLibrary
for interop purpouses.Library::from_non_null
— Wrap a forever-loaded library inLibrary
for interop purpouses.Library::as_ptr
— Return a raw handle pointer for interop purpouses.Library::as_non_null
— Return a raw handle pointer for interop purpouses.
sourcepub fn load(path: impl AsRef<Path>) -> Result<Self>
pub fn load(path: impl AsRef<Path>) -> Result<Self>
Load a library, forever.
OS | Behavior |
---|---|
Windows | LoadLibraryW(path) |
Unix | dlopen(path, ...) |
sourcepub unsafe fn from_ptr(handle: *mut c_void) -> Option<Self>
pub unsafe fn from_ptr(handle: *mut c_void) -> Option<Self>
Wrap a forever-loaded library in Library
for interop purpouses.
Wrap a winapi::shared::minwindef::HMODULE
with Library::from_ptr(handle.cast())
.
Wrap a windows::Win32::Foundation::HMODULE
with Library::from_ptr(handle.0 as _)
.
Safety
If handle
is not null, it is expected to be a valid library handle for the duration of the program.
Platform
OS | Expects |
---|---|
Windows | libloaderapi.h -compatible HMODULE |
Unix | dlfcn.h -compatible handle |
sourcepub unsafe fn from_non_null(handle: NonNull<c_void>) -> Self
pub unsafe fn from_non_null(handle: NonNull<c_void>) -> Self
Wrap a forever-loaded library in Library
for interop purpouses.
Safety
handle
is expected to be a valid library handle for the duration of the program.
Platform
OS | Expects |
---|---|
Windows | libloaderapi.h -compatible HMODULE |
Unix | dlfcn.h -compatible handle |
sourcepub fn as_ptr(&self) -> *mut c_void
pub fn as_ptr(&self) -> *mut c_void
Return a raw handle pointer for interop purpouses.
Acquire a winapi::shared::minwindef::HMODULE
with handle.as_ptr() as HMODULE
.
Acquire a windows::Win32::Foundation::HMODULE
with HMODULE(handle.as_ptr() as _)
.
Safety
Don’t use this pointer to unload the library.
sourcepub fn as_non_null(&self) -> NonNull<c_void>
pub fn as_non_null(&self) -> NonNull<c_void>
Return a raw handle pointer for interop purpouses.
Safety
Don’t use this pointer to unload the library.
sourcepub unsafe fn sym_by_ordinal<T>(self, ordinal: u16) -> Result<T>
pub unsafe fn sym_by_ordinal<T>(self, ordinal: u16) -> Result<T>
Load a symbol from the library by ordinal.
Safety
This function implicitly transmutes! Use extreme caution. Additionally, DLL ordinals are typically unstable and might change between minor versions of the same DLL, breaking your imports in nastily subtle ways. If a function name is available, use it instead!
Platform
OS | Behavior |
---|---|
Windows | GetProcAddress(..., MAKEINTRESOURCE(ordinal)) |
Err(...) |
sourcepub unsafe fn sym_opt_by_ordinal<T>(self, ordinal: u16) -> Option<T>
pub unsafe fn sym_opt_by_ordinal<T>(self, ordinal: u16) -> Option<T>
Load a symbol from the library by ordinal.
Safety
This function implicitly transmutes! Use extreme caution. Additionally, DLL ordinals are typically unstable and might change between minor versions of the same DLL, breaking your imports in nastily subtle ways. If a function name is available, use it instead!
Platform
OS | Behavior |
---|---|
Windows | GetProcAddress(..., MAKEINTRESOURCE(ordinal)) |
None |
sourcepub fn has_sym(self, name: impl AsRef<str>) -> bool
pub fn has_sym(self, name: impl AsRef<str>) -> bool
Check if a symbol existing in the library. Note that the symbol name must end with ‘\0’. Limiting yourself to basic ASCII is also likely wise.
Platform
OS | Behavior |
---|---|
Windows | !!GetProcAddress(..., name) |
Unix | !!dlsym(..., name) |
sourcepub unsafe fn close_unsafe_unsound_possible_noop_do_not_use_in_production(
self
) -> Result<()>
pub unsafe fn close_unsafe_unsound_possible_noop_do_not_use_in_production( self ) -> Result<()>
Attempt to unload the library.
Safety
❌ This is a fundamentally unsound operation that may do nothing and invalidates everything ❌
You are practically guaranteed undefined behavior from some kind of dangling pointer or reference. Several platforms don’t even bother implementing unloading. Those that do implement unloading often have ways of opting out of actually unloading. I’d argue the only valid use case for this fn is unit testing the unloading of your DLLs, when someone else was crazy enough to unload libraries in production.
Based on that reasoning, this crate has been designed around the assumption that you will never do this.
It lacks lifetimes and implements Library : Copy
, which makes use-after-free bugs easy.
Be especially careful - all of the following are invalidated when this function is called:
- All fns/pointers previously returned by
sym*
for this library, including through copies ofself
. - The
Library
itself, and anyCopy
thereof. This means evenself.has_sym(...)
is no longer sound to call, despite the fn being safe, even though it will compile without error (as self isCopy
)!
Safe Alternatives
- Restart the entire process (fine for production since process shutdown is actually tested)
- Use a sub-process and restart that (will also make your code more stable if a hot-reloading “plugin” crashes)
- Simply leak the library (fine for dev builds)
- Export a function to free memory, join threads, close file handles, etc. if you want to reduce memory use / file locks
- Load a temporary copy of the library instead of the original if you hate having a file lock on the original library
Unsafe Alternatives
A wrapper or crate with “better” support for this fundamentally flawed operation might:
- Limit support to plugin-shaped dynamic libraries that opt-in to claiming they’re safe to unload (export a special fn/symbol?)
- Actively test unloading to catch the bugs in those libraries
- Introduce lifetimes (e.g.
libloading::Symbol
), or make fn pointers private, to help combat fn pointer invalidation bugs - Not implement
Copy
forLibrary
(or equivalent) - Not default-implement
Clone
forLibrary
(or equivalent - properly implemented refcounting might be OK) - Implement
Drop
forLibrary
(or equivalent) if you’re arrogant enough to claim your unloading code is actually safe/sound.
This might do nothing useful whatsoever
- “musl’s dynamic loader loads libraries permanently for the lifetime of the process, until it exits or calls exec.
[…] only the musl behavior can satisfy the robustness conditions musl aims to provide” - “Prior to Mac OS X 10.5, only bundles could be unloaded. Starting in Mac OS X 10.5, dynamic libraries may also be unloaded.”
RTLD_NODELETE
makesdlclose
a noopGET_MODULE_HANDLE_EX_FLAG_PIN
makesFreeLibrary*
a noop
Threads are a problem
Any thread (including those started automatically by the library itself on load) containing any library code anywhere in it’s callstack will exhibit undefined behavior:
- Unwinding through library code via panic/exceptions/SEH will presumably use dangling unwinding information pointers
- Callstack snapshots (for allocation tracking, sentry.io event reporting, etc.) will contain dangling symbol pointers etc.
- Dangling instruction pointers (through execution or returning to library code) will, presumably:
- Crash if unmapped, or mapped without execution permissions
- Execute random, possibly “unreachable” code - or data misinterpreted as code - if mapped with execution permissions
These are among the many reasons windows offers such a wide variety of unloading functions (lacking in POSIX systems):
Some related reading:
- What is the point of FreeLibraryAndExitThread? (The Old New Thing)
- When is the correct time to call FreeLibraryWhenCallbackReturns? (The Old New Thing)
Additionally, there are serious limitations on what DllMain
/ destructors can do without deadlocking or worse, limiting the DLL’s ability to fix any of this:
- Dynamic-Link Library Best Practices (learn.microsoft.com)
- https://github.com/mity/old-new-win32api#dllmain (links to The Old New Thing)
- Why are DLLs unloaded in the “wrong” order? (The Old New Thing)
Callbacks are a problem
The library has many ways of creating pointers into itself which will dangle if the library is unloaded:
- Error handling
std::panic::set_hook
(100% safe - which other code can thentake_hook
, preventing you from unregistering it!)- Signal handlers
- Unhandled exception handlers
- Windows callbacks
Box<dyn Anything>
- …
String literals and other 'static
references are a problem
Allocator debugging code often likes to register references or pointers to __FILE__
(common in C++ macros) or core::file!()
(Rust).
These are generally treated - by 100% safe code - as having 'static
lifetimes, and not deep copied.
While this is sound within the context of a single module, those references and pointers will dangle - with all the undefined behavior that comes with that - if those references and pointers ever end up in another module that outlasts the library.
This is only the most ubiquitous example of the larger problem: Every reference or pointer to const
, static
, or literal variables of the library becomes a ticking timebomb and hazard in 100% “safe” code.
Testing is a problem
Nobody tests unloading dynamic libraries. Nobody. I can’t even unload debug CRTs without triggering assertions. Calling this function will simply invoke broken untested code.
Platform
OS | Behavior |
---|---|
Windows | FreeLibrary(...) |
Unix | dlclose(...) |
Trait Implementations§
source§impl Ord for Library
impl Ord for Library
source§impl PartialEq for Library
impl PartialEq for Library
source§impl PartialOrd for Library
impl PartialOrd for Library
1.0.0 · source§fn le(&self, other: &Rhs) -> bool
fn le(&self, other: &Rhs) -> bool
self
and other
) and is used by the <=
operator. Read more