我一直在寻找一种方法来创建一个共享库(让我们命名库libbar.so
)延迟加载到Linux上,它应该只在一个链接器的帮助下实现,而不是修改用C ++编写的源代码的任何东西;我的意思是我不想在父库的源代码中调用dlopen()
或dlsym()
(让我们将其命名为libfoo.so
)来调用libbar.so
的函数,因为它们使源代码变得混乱并且维护过程很困难。 (简而言之,即使在Linux上,我也希望以类似的方式继续使用Visual Studio的/DELAYLOAD
选项)
无论如何,到目前为止,我已经在互联网上找到了与我的问题相关的一些不确定的信息片段,所以很高兴能得到你们所有人的答案,以便让信息清晰。
dlopen()
系列是否是在Linux上加载共享库延迟的唯一方法?我测试将-zlazy
标志传递给GCC(g ++)并带有一个通向库的路径,它似乎接受了标志,但行为并没有使libbar.so
延迟加载(没有libbar.so
,我期待在第一次调用时有异常libbar.so
,但在进入libfoo.so
之前实际上提出了例外)。另一方面,Clang(clang++
)发出警告称它忽略了选项标志。
最好的祝福,
延迟加载不是运行时功能。 MSVC ++在没有Windows帮助的情况下实现了它。就像dlopen
是Linux上唯一的方法一样,GetProcAddress
是Windows上唯一的运行时方法。
那么,什么是延迟加载呢?它非常简单:对DLL的任何调用都必须通过一个指针(因为你不知道它将加载到哪里)。这总是由编译器和链接器为您处理。但是通过延迟加载,MSVC ++最初将此指针设置为一个存根,为您调用LoadLibrary
和GetProcAddress
。
没有ld
的帮助,Clang可以做同样的事情。在运行时,它只是一个普通的dlopen
调用,Linux无法确定Clang是否插入了它。
可以使用Proxy design pattern以便携方式实现此功能。
在代码中它可能看起来像这样:
#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();
}
要添加到MSalters答案,可以通过创建一个小的静态存根库来轻松模仿Windows在懒惰加载上的方法,该库将首先调用任何一个函数来尝试dlopen
所需的库(如果dlopen失败,则发出诊断消息并终止)然后转发所有呼叫。
这样的存根库可以手工编写,由项目/库特定的脚本生成或由通用工具Implib.so生成:
$ implib-gen.py libxyz.so
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...