如何在Linux上延迟加载共享库 我一直在寻找一种方法来创建一个共享的库(让我们在Linux上命名为库 LBBAR,所以)延迟加载< /强>,希望它可以通过“强”>仅链接器< /强>来实现,而不修改C++中的源代码中的任何内容;我的意思是,我不想在父库的源代码中调用dlopen()。(简言之,即使在Linux上,我也希望采用与Visual Studio的/DELAYLOAD选项类似的方式)

如何在Linux上延迟加载共享库 我一直在寻找一种方法来创建一个共享的库(让我们在Linux上命名为库 LBBAR,所以)延迟加载< /强>,希望它可以通过“强”>仅链接器< /强>来实现,而不修改C++中的源代码中的任何内容;我的意思是,我不想在父库的源代码中调用dlopen()。(简言之,即使在Linux上,我也希望采用与Visual Studio的/DELAYLOAD选项类似的方式),c++,linux,gcc,shared-libraries,clang,C++,Linux,Gcc,Shared Libraries,Clang,无论如何,到目前为止,我在互联网上发现了一些与我的问题相关的不确定信息,所以很高兴能得到大家对以下问题的答案,以使信息更加清晰 GNU ld在Linux上是否支持延迟加载机制 如果没有,那么叮当声怎么样 在Linux上加载共享库延迟的唯一方法是dlopen()family 我测试通过一个指向库的路径将-zlazy标志传递给GCC(g++),它似乎接受了该标志,但该行为看起来不像是在创建libbar。因此延迟加载(没有libbar.so,我本以为在第一次调用libbar.so时会出现异常,但在进入

无论如何,到目前为止,我在互联网上发现了一些与我的问题相关的不确定信息,所以很高兴能得到大家对以下问题的答案,以使信息更加清晰

  • GNU ld在Linux上是否支持延迟加载机制
  • 如果没有,那么叮当声怎么样
  • 在Linux上加载共享库延迟的唯一方法是
    dlopen()
    family
  • 我测试通过一个指向库的路径将
    -zlazy
    标志传递给GCC(g++),它似乎接受了该标志,但该行为看起来不像是在创建
    libbar。因此
    延迟加载(没有
    libbar.so
    ,我本以为在第一次调用
    libbar.so
    时会出现异常,但在进入
    libfoo.so
    之前实际引发了异常)。另一方面,Clang(
    Clang++
    )留下了一条警告,表示它忽略了选项标志


    最重要的是,

    延迟加载不是一个运行时功能。MSVC++在没有Windows帮助的情况下实现了它。就像
    dlopen
    是Linux上唯一的方法一样,
    GetProcAddress
    是Windows上唯一的运行时方法

    那么,什么是延迟加载呢?很简单:对DLL的任何调用都必须经过指针(因为您不知道它将加载到哪里)。这始终由编译器和链接器为您处理。但通过延迟加载,MSVC++会将此指针最初设置为一个存根,该存根为您调用
    LoadLibrary
    GetProcAddress


    Clang可以在没有
    ld
    帮助的情况下完成同样的操作。在运行时,它只是一个普通的
    dlopen
    调用,Linux无法确定Clang是否插入了它。

    此功能可以通过使用

    在代码中,它可能如下所示:

    #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();
    }
    
    #包括
    //SharedLibraryProxy.h
    结构共享库代理
    {
    virtual~SharedLibraryProxy()=0;
    //共享库接口开始。
    虚拟void foo()=0;
    虚拟空心条()=0;
    //共享库接口结束。
    静态std::unique_ptr create();
    };
    //SharedLibraryProxy.cc
    结构SharedLibraryProxyImp:SharedLibraryProxy
    {
    void*shared_lib_uu=nullptr;
    无效(*foo_u1;)()=nullptr;
    void(*bar_u3;)()=nullptr;
    SharedLibraryProxyImp&load(){
    //用于在运行时加载共享库的平台特定位。
    如果(!shared_lib_u){
    //共享库=dlopen(…);
    //foo=dlsym(…)
    //bar=dlsym(…)
    }
    归还*这个;
    }
    void foo()重写{
    返回此->加载().foo_u2;();
    }
    无效条()覆盖{
    返回此->加载().条();
    }
    };
    SharedLibraryProxy::~SharedLibraryProxy(){}
    std::unique_ptr SharedLibraryProxy::create(){
    返回std::unique_ptr{new SharedLibraryProxyImp};
    }
    //main.cc
    int main(){
    自动共享_lib=SharedLibraryProxy::create();
    共享的_lib->foo();
    共享的_lib->bar();
    }
    
    要添加到MSalters answer,可以很容易地模仿Windows在Linux上延迟加载的方法,方法是创建一个小型静态存根库,该存根库在第一次调用任何函数时尝试
    dlopen
    所需的库(发出诊断消息,如果dlopen失败则终止),然后将所有调用转发给它

    此类存根库可以手工编写、通过特定于项目/库的脚本生成或通过通用工具生成:


    clang使用gnu ld进行链接(至少在3.4及更早版本中是这样),因此您在那里找不到任何帮助。@Mats,谢谢您的评论。好的,我不会介意clang对这个问题的提问,稍后编辑上面的原始描述。@oakad,谢谢:)我也找到了这篇文章。那么您认为GNULD真的不支持任何延迟加载机制吗?我们是否必须将dlopen()放在源代码中?(哦,即使这是真的,我也不想接受这个事实…)通过我对ld手册页的阅读-zlazy(应该是-z lazy)使您正在链接的库,在您的例子中是libfoo.so,为其调用者加载延迟。对于所需内容,在构建libbar.so时必须应用此标志。然而,手册页上还说“默认为惰性绑定”,所以libbar.so应该已经与此链接。我前面的评论是错误的。我误解了手册页
    -z lazy
    用于符号的延迟绑定,而不是库的延迟加载。@MSalters,再次感谢。你是说Clang可以自己处理延迟加载吗?实际上,我希望通过向编译器或链接器传递一个选项标志(尽管我忘记了帮助函数的名称),隐式地拥有一个像MSVC++这样的帮助函数,因为该机制隐藏了延迟加载函数调用背后的所有内容(因此用户不必关心在我们的源代码下面调用了这样的helper函数)。是的,u delayLoadHelper2是helper函数!可能一些编译器或链接器(如Sun的某物或苹果的ld64)一定提供了这样的helper函数,但我不知道Linux上发生了什么。(我是Linux的新手;)@user3591878:Linux上的GCC有混淆操作系统和编译器之间区别的不幸习惯,但这是UNIX的传统
    $ implib-gen.py libxyz.so
    $ gcc myapp.c libxyz.tramp.S libxyz.init.c ...