Prerequisites
First we need to get the library we want to load, here we will make a small example library
Loading dynamic libraries at runtime is an operation where at runtime you use the OS’ API to load symbols from a shared library. This library loads named functions, that being functions whose names are not under the influence of name mangling. Because of this your functions have to be exported with C semantics
To create a library simply write some C or C++ code like this:
Header:
#pragma once
extern "C"
{void begin();
}
Source file:
#include "libtest.hpp"
#include <iostream>
void begin()
{std::cout << "begin" << std::endl;
}
As you can see the library we made is a C++ one, to compile with C semantics you need to wrap your functions under extern "C"
otherwise name mangling will be enabled
Make sure that if compiling for a Unix system, you need to enable -fPIC
in your compiler flags. This way you can have PIC(Position Independent Code)
On Windows make sure all the functions you want to export are prefixed with __declspec(dllexport)
otherwise they will not be exported from the DLL
Using the library
C++ API
The C++ API provides some good syntactic sugar like templates that generally make your life better. The library looks like this:
namespace URLL
{void* dlopen(const char* location) noexcept;
template<typename T, typename... T2>
void* dlsym(void* handle, const char* name, std::function<T(T2...)>& function) noexcept;
template<typename T>
void* dlsym_val(void* handle, const char* name, T* var) noexcept;
template<typename T>
void* dlsym_func(void* handle, const char* name, T& var) noexcept;
void* dlsym(void* handle, const char* name) noexcept;
// returns 0 on success, everything else must be an error
int dlclose(void* handle) noexcept;
// returns a string with the corresponding error, if there is no error it returns null
char* dlerror() noexcept;
}
First you need to include the urll.h
header file, after that you need to load a library from a file. This is how the library is intended to be used:
- Call
dlopen
with a string argument being the path to the library you want to load, save the handle pointer - Call one of the
dlsym
functions to load a symbol and check if it has been successfully loaded by checking if the return value is notnullptr
- Use your functions
- When you’re done with using the functions, simply call
dlclose
- If there is any error along the way call
dlerror
to get an error message
All functions are under the URLL
namespace. Here is an example of the C++ API
void load()
{auto* handle = URLL::dlopen("https://madladsquad.com/libLIBRARY.so");
if (handle == nullptr)
{std::cout << dlerror() << std::endl;
return;
}
std::function<void(void)> func;
if (URLL::dlsym(handle, "begin", func) == handle)
func();
URLL::dlclose(handle); }
The code is completely compatible with the library example we listed above.
On the line where we use dlsym
we use the std::function
support and templates. This as said in the last page is enabled using the URLL_USE_FUNCTIONAL
macro
We also have a generic dlsym
that just returns a void*
that you can use to manually cast to the type of your variable/function. Additionally we also provide support for C function pointers using the dlsym_func
function and support for variables using the dlsym_var
function
C API
The C API is much more barebones, it looks like this:
void* urll_dlopen(const char* location);
void* urll_dlsym(void* handle, const char* name);
void* urll_dlsym_func(void* handle, const char* name, void** function);
void* urll_dlsym_var(void* handle, const char* name, void* var);
int urll_dlclose(void* handle);
char* urll_dlerror();
notice how if we compare with the C++ API, the only differences in most cases are that the names are prefixed with urll_
to signify the missing of the namespaces feature in C
Here is an example that uses the C API. Remember that for the C API you need to include the curll.h
header
void load()
{void* handle = urll_dlopen("https://madladsquad.com/libLIBRARY.so");
if (handle == NULL)
{"%s\n", urll_dlerror());
printf(return;
}void (*test)(void) = NULL;
if (urll_dlsym_func(handle, "begin", &test))
{
test();
}
urll_dlclose(handle); }
Here to load a function we call dlsym_func
to load a C function pointer. For loading variables we also have dlsym_var
and a generic dlsym
that just returns a void*
where you can manually cast your variables/functions
Compiling your application
This library depends on libdl
when compiling on Unix based systems so make sure you are linking against it