Not all systems support dynamic loading.
Unix-like operating systems such as
macOS,
Linux, and
Solaris provide dynamic loading with the
C programming language "dl" library. The
Windows operating system provides dynamic loading through the
Windows API.
Summary Loading the library Loading the library is accomplished with LoadLibrary or LoadLibraryEx on
Windows and with dlopen on
Unix-like operating systems. Examples follow:
Most Unix-like operating systems (Solaris, Linux, *BSD, etc.) void* sdl_library = dlopen("libSDL.so", RTLD_LAZY); if (!sdl_library) { // report error ... } else { // use the result in a call to dlsym }
macOS As a
Unix library: void* sdl_library = dlopen("libSDL.dylib", RTLD_LAZY); if (!sdl_library) { // report error ... } else { // use the result in a call to dlsym } As a
macOS Framework: void* sdl_library = dlopen("/Library/Frameworks/SDL.framework/SDL", RTLD_LAZY); if (!sdl_library) { // report error ... } else { // use the result in a call to dlsym } Or if the framework or bundle contains Objective-C code: NSBundle *bundle = [NSBundle bundleWithPath:@"/Library/Plugins/Plugin.bundle"]; NSError *err = nil; if ([bundle loadAndReturnError:&err]) { // Use the classes and functions in the bundle. } else { // Handle error. }
Windows HMODULE sdl_library = LoadLibrary(TEXT("SDL.dll")); if (!sdl_library) { // report error ... } else { // use the result in a call to GetProcAddress }
Extracting library contents Extracting the contents of a dynamically loaded library is achieved with GetProcAddress on
Windows and with dlsym on
Unix-like
operating systems.
Unix-like operating systems (Solaris, Linux, *BSD, macOS, etc.) void* initializer = dlsym(sdl_library, "SDL_Init"); if (!initializer) { // report error ... } else { // cast initializer to its proper type and use } On macOS, when using Objective-C bundles, one can also: Class rootClass = [bundle principalClass]; // Alternatively, NSClassFromString() can be used to obtain a class by name. if (rootClass) { id object = rootClass alloc] init]; // Use the object. } else { // Report error. }
Windows FARPROC initializer = GetProcAddress(sdl_library, "SDL_Init"); if (!initializer) { // report error ... } else { // cast initializer to its proper type and use }
Converting a library function pointer The result of dlsym() or GetProcAddress() has to be converted to a pointer of the appropriate type before it can be used.
Windows In Windows, the conversion is straightforward, since FARPROC is essentially already a
function pointer: typedef INT_PTR (*FARPROC)(void); This can be problematic when the address of an object is to be retrieved rather than a function. However, usually one wants to extract functions anyway, so this is normally not a problem. typedef void (*SDLInitFunctionType)(void); SDLInitFunctionType init_func = (SDLInitFunctionType)initializer;
Unix (POSIX) According to the POSIX specification, the result of dlsym() is a void pointer. However, a function pointer is not required to even have the same size as a data object pointer, and therefore a valid conversion between type void* and a pointer to a function may not be easy to implement on all platforms. On most systems in use today, function and object pointers are
de facto convertible. The following code snippet demonstrates one workaround which allows to perform the conversion anyway on many systems: typedef void (*SDLInitFunctionType)(void); SDLInitFunctionType init_func = (SDLInitFunctionType)initializer; The above snippet will give a warning on some compilers: warning: dereferencing type-punned pointer will break strict-aliasing rules. Another workaround is: typedef void (*SDLInitFunctionType)(void); union { SDLInitFunctionType func; void* obj; } alias; alias.obj = initializer; SDLInitFunctionType init_func = alias.func; which disables the warning even if strict aliasing is in effect. This makes use of the fact that reading from a different union member than the one most recently written to (called "
type punning") is common, and explicitly allowed even if strict aliasing is in force, provided the memory is accessed through the union type directly. However, this is not strictly the case here, since the function pointer is copied to be used outside the union. Note that this trick may not work on platforms where the size of data pointers and the size of function pointers is not the same.
Solving the function pointer problem on POSIX systems The fact remains that any conversion between function and data object pointers has to be regarded as an (inherently non-portable) implementation extension, and that no "correct" way for a direct conversion exists, since in this regard the POSIX and ISO standards contradict each other. Because of this problem, the POSIX documentation on dlsym() for the outdated issue 6 stated that "a future version may either add a new function to return function pointers, or the current interface may be deprecated in favor of two new functions: one that returns data pointers and the other that returns function pointers". For the subsequent version of the standard (issue 7, 2008), the problem has been discussed and the conclusion was that function pointers have to be convertible to void* for POSIX compliance. This requires compiler makers to implement a working cast for this case. If the contents of the library can be changed (i.e. in the case of a custom library), in addition to the function itself a pointer to it can be exported. Since a pointer to a function pointer is itself an object pointer, this pointer can always be legally retrieved by call to dlsym() and subsequent conversion. However, this approach requires maintaining separate pointers to all functions that are to be used externally, and the benefits are usually small.
Unloading the library Loading a library causes memory to be allocated; the library must be deallocated in order to avoid a
memory leak. Additionally, failure to unload a library can prevent
filesystem operations on the
file which contains the library. Unloading the library is accomplished with FreeLibrary on
Windows and with dlclose on Unix-like
operating systems. However, unloading a DLL can lead to program crashes if objects in the main application refer to memory allocated within the DLL. For example, if a DLL introduces a new class and the DLL is closed, further operations on instances of that class from the main application will likely cause a memory access violation. Likewise, if the DLL introduces a factory function for instantiating dynamically loaded classes, calling or dereferencing that function after the DLL is closed leads to undefined behaviour.
Unix-like operating systems (Solaris, Linux, *BSD, macOS, etc.) dlclose(sdl_library);
Windows FreeLibrary(sdl_library);
Special library The implementations of dynamic loading on
Unix-like operating systems and
Windows allow programmers to extract symbols from the currently executing process. Unix-like operating systems allow programmers to access the global symbol table, which includes both the main executable and subsequently loaded dynamic libraries.
Windows allows programmers to access symbols exported by the main executable. Windows does not use a global symbol table and has no API to search across multiple modules to find a symbol by name.
Unix-like operating systems (Solaris, Linux, *BSD, macOS, etc.) void* this_process = dlopen(NULL, 0);
Windows HMODULE this_process = GetModuleHandle(NULL); HMODULE this_process_again; GetModuleHandleEx(0, 0, &this_process_again); ==In Java==