Il caricamento ritardato NON è una funzionalità di runtime. MSVC++ lo ha implementato senza l'aiuto di Windows. E come dlopen
è l'unico modo su Linux, GetProcAddress
è l'unico metodo di runtime su Windows.
Allora, cos'è il caricamento ritardato? È molto semplice:qualsiasi chiamata a una DLL deve passare attraverso un puntatore (poiché non sai dove verrà caricata). Questo è sempre stato gestito dal compilatore e dal linker per te. Ma con il caricamento ritardato, MSVC++ inizialmente imposta questo puntatore su uno stub che chiama LoadLibrary
e GetProcAddress
per te.
Clang può fare lo stesso senza l'aiuto di ld
. In fase di esecuzione, è solo un normale dlopen
chiama e Linux non può determinare che Clang l'ha inserita.
Per aggiungere alla risposta di MSalters, si può facilmente imitare l'approccio di Windows al caricamento pigro su Linux creando una piccola libreria stub statica che proverebbe a dlopen
libreria necessaria alla prima chiamata a una qualsiasi delle sue funzioni (emissione di messaggi diagnostici e terminazione se dlopen fallisce) e quindi inoltro di tutte le chiamate ad essa.
Tali librerie di stub possono essere scritte a mano, generate da script specifici del progetto/libreria o generate dallo strumento universale Implib.so:
$ implib-gen.py libxyz.so
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...
Questa funzionalità può essere ottenuta in modo portatile utilizzando il modello di progettazione proxy.
Nel codice potrebbe assomigliare a questo:
#include <memory>
// SharedLibraryProxy.h
struct SharedLibraryProxy
{
virtual ~SharedLibraryProxy() = 0;
// Shared library interface begin.
virtual void foo() = 0;
virtual void bar() = 0;
// Shared library interface end.
static std::unique_ptr<SharedLibraryProxy> create();
};
// SharedLibraryProxy.cc
struct SharedLibraryProxyImp : SharedLibraryProxy
{
void* shared_lib_ = nullptr;
void (*foo_)() = nullptr;
void (*bar_)() = nullptr;
SharedLibraryProxyImp& load() {
// Platform-specific bit to load the shared library at run-time.
if(!shared_lib_) {
// shared_lib_ = dlopen(...);
// foo_ = dlsym(...)
// bar_ = dlsym(...)
}
return *this;
}
void foo() override {
return this->load().foo_();
}
void bar() override {
return this->load().bar_();
}
};
SharedLibraryProxy::~SharedLibraryProxy() {}
std::unique_ptr<SharedLibraryProxy> SharedLibraryProxy::create() {
return std::unique_ptr<SharedLibraryProxy>{new SharedLibraryProxyImp};
}
// main.cc
int main() {
auto shared_lib = SharedLibraryProxy::create();
shared_lib->foo();
shared_lib->bar();
}