Module libloading::changelog::r0_7_0
source · Expand description
Release 0.7.0 (2021-02-06)
Breaking changes
Loading functions are now unsafe
A number of associated methods involved in loading a library were changed to
be unsafe. The affected functions are: Library::new, os::unix::Library::new,
os::unix::Library::open, os::windows::Library::new,
os::windows::Library::load_with_flags. This is the most prominent breaking change in this
release and affects majority of the users of libloading.
In order to see why it was necessary, consider the following snippet of C++ code:
#include <vector>
#include <iostream>
static std::vector<unsigned int> UNSHUU = { 1, 2, 3 };
int main() {
std::cout << UNSHUU[0] << UNSHUU[1] << UNSHUU[2] << std::endl; // Prints 123
return 0;
}
The std::vector type, much like in Rust’s Vec, stores its contents in a buffer allocated on
the heap. In this example the vector object itself is stored and initialized as a static
variable – a compile time construct. The heap, on the other hand, is a runtime construct. And
yet the code works exactly as you’d expect – the vector contains numbers 1, 2 and 3 stored in
a buffer on heap. So, what makes it work out, exactly?
Various executable and shared library formats define conventions and machinery to execute
arbitrary code when a program or a shared library is loaded. On systems using the PE format
(e.g. Windows) this is available via the optional DllMain initializer. Various systems
utilizing the ELF format take a sightly different approach of maintaining an array of function
pointers in the .init_array section. A very similar mechanism exists on systems that utilize
the Mach-O format.
For the C++ program above, the object stored in the UNSHUU global variable is constructed
by code run as part of such an initializer routine. This initializer is run before the entry
point (the main function) is executed, allowing for this magical behaviour to be possible.
Were the C++ code built as a shared library instead, the initialization routines would run as
the resulting shared library is loaded. In case of libloading – during the call to
Library::new and other methods affected by this change.
These initialization (and very closely related termination) routines can be utilized outside of C++ too. Anybody can build a shared library in variety of different programming languages and set up the initializers to execute arbitrary code. Potentially code that does all sorts of wildly unsound stuff.
The routines are executed by components that are an integral part of the operating system. Changing or controlling the operation of these components is infeasible. With that in mind, the initializer and termination routines are something anybody loading a library must carefully evaluate the libraries loaded for soundness.
In practice, a vast majority of the libraries can be considered a good citizen and their initialization and termination routines, if they have any at all, can be trusted to be sound.
Also see: issue #86.
Better & more consistent default behaviour on UNIX systems
On UNIX systems the Library::new, os::unix::Library::new and
os::unix::Library::this methods have been changed to use
RTLD_LAZY | RTLD_LOCAL as the default set of loader options (previously:
RTLD_NOW). This has a couple benefits. Namely:
- Lazy binding is generally quicker to execute when only a subset of symbols from a library are
used and is typically the default when neither
RTLD_LAZYnorRTLD_NOWare specified when calling the underlyingdlopenAPI; - On most UNIX systems (macOS being a notable exception)
RTLD_LOCALis the default when neitherRTLD_LOCALnorRTLD_GLOBALare specified. The explicit setting of theRTLD_LOCALflag makes this behaviour consistent across platforms.
Dropped support for Windows XP/Vista
The (broken) support for Windows XP and Windows Vista environments was removed. This was
prompted primarily by a similar policy change in the Rust
project but also as an acknowledgement
to the fact that libloading never worked in these environments anyway.
More accurate error variant names
Finally, the Error::LoadLibraryW renamed to Error::LoadLibraryExW to more accurately
represent the underlying API that’s failing. No functional changes as part of this rename
intended.