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 slightly 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_LAZY
norRTLD_NOW
are specified when calling the underlyingdlopen
API; - On most UNIX systems (macOS being a notable exception)
RTLD_LOCAL
is the default when neitherRTLD_LOCAL
norRTLD_GLOBAL
are specified. The explicit setting of theRTLD_LOCAL
flag 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.