fedem::dso::DSOLoader is the loader. Every function is static, and the class cannot be instantiated — it is a namespace with a mutex.
#include <dso/DSOLoader.hh>
using fedem::dso::DSOLoader;
Loading one file
DSOLoader::load( "./libCircle.so" );
DSOLoader::load( "libCircle.so", { "/opt/app/plugins", "/usr/lib/app" } );
The single-argument form passes the string straight to dlopen, so the platform search rules apply (RPATH, LD_LIBRARY_PATH, absolute/relative path). The two-argument form tries each directory in the list until one loads.
dlopen is called with RTLD_GLOBAL | RTLD_LAZY:
RTLD_GLOBAL— the DSO's symbols join the global scope, so a second plugin can resolve against the first, and the host'sCREATECATALOGsymbol is visible to the plugin. This is what makes the shared-catalog design work.RTLD_LAZY— relocations are resolved on first use.
A successful load runs the DSO's static initialisers — that is when REGISTER fires and the plugin's keys appear in the catalog.
Loading many at once
DSOLoader::loadAll( ".so", "/opt/app/plugins" ); // every *.so in that dir
DSOLoader::loadAll( ".so" ); // every *.so in the cwd
DSOLoader::loadAll( ".so", { "/opt/app/plugins", "/usr/lib/app/plugins" } );
loadAll scans the directory (non-recursively), matches regular files and symlinks whose name ends in the extension — and, if a prefix is set, begins with it — and loads each. Files that fail to load are collected; if any failed, loadAll throws FileNotFound naming them, after loading the rest.
DSOLoader::loadAllByEnvironment( ".so", "APP_PLUGIN_PATH" ); // each dir on $APP_PLUGIN_PATH
DSOLoader::loadAllByEnvironment( ".so" ); // $LD_LIBRARY_PATH (or the
// platform equivalent)
loadAllByEnvironment splits the named environment variable on : (; on Cygwin) and runs loadAll for each directory. The no-argument overload uses LD_LIBRARY_PATH on Linux, LIBPATH on AIX, SHLIB on HP-UX, PATH on Cygwin. An unset variable is a no-op.
The filename prefix
DSOLoader::prefix( "lib" ); // set
std::string p = DSOLoader::prefix(); // get -> "lib"
When set, loadAll and loadAllByEnvironment only consider files that start with the prefix. It does not affect the single-file load(). Set it to "lib" to skip stray non-plugin .sos in a shared directory, or to a project-specific tag ("app-plugin-").
Handle lifetime
Every loaded handle is stored in a process-wide set and dlclosed only at program exit (the set's destructor). There is deliberately no unload() in the public API: unloading a DSO whose types are still live — a unique_ptr<Shape> holding a Circle from libCircle.so — is a use-after-unload waiting to happen. If you need it, you own the dlclose and the ordering.
Loading the same file twice is cheap and harmless — dlopen reference-counts, and the second call returns the same handle.
Error handling
#include <dso/DSOExceptions.hh>
try {
DSOLoader::load( "libCircle.so", searchDirs );
}
catch( fedem::exception::FileNotFound const& e ) {
std::clog << "plugin missing: " << e.missingFilename()
<< " (" << e.reason() << ")\n";
}
FileNotFound carries the name it tried and the dlerror() string. It is thrown when no directory in the list yields a loadable file, and (as above) by loadAll when some files failed. DSOLoader never calls std::abort or exit on a load failure — a missing or broken plugin is a recoverable condition.
Next: Signing & Trust.

