简体   繁体   English

如何在 Linux 上延迟加载共享库

[英]How to make a shared library delay loaded on Linux

I've been searching a way to make a shared library (let's name the library libbar.so ) delay loaded on Linux and it should hopefully be realized with a help from only a linker , not modifying anything on the source code written in C++;我一直在寻找一种在 Linux 上延迟加载共享库(让我们将库命名为libbar.so )的方法,希望它应该仅在链接器的帮助下实现,而不是修改用 C++ 编写的源代码中的任何内容; I mean I don't want to invoke dlopen() nor dlsym() in the source code of the parent library (let's name it libfoo.so ) to invoke a function of libbar.so because they make the source code messy and the maintenance process difficult.我的意思是我不想在父库的源代码中调用dlopen()dlsym() (让我们将其命名为libfoo.so )来调用libbar.so的函数,因为它们使源代码变得混乱和维护过程困难。 (In short, I'm expecting to go on the similar way to Visual Studio's /DELAYLOAD option even on Linux) (简而言之,即使在 Linux 上,我也希望采用与 Visual Studio 的/DELAYLOAD选项类似的方式)

Anyway, I've found some uncertain information pieces related to my question on the internet so far, so it would be very nice to have the answers from you all for the following questions to make the information clear.无论如何,到目前为止,我在互联网上发现了一些与我的问题相关的不确定信息,因此如果您能从以下问题中得到大家的答案以使信息清晰,那就太好了。

  1. Does GNU ld support any delay loading mechanism on Linux? GNU ld是否支持 Linux 上的任何延迟加载机制?
  2. If it doesn't, how about Clang ?如果没有,那么Clang怎么样?
  3. Is the dlopen() family the only way to make a shared library delay loaded on Linux? dlopen()系列是在 Linux 上加载共享库延迟的唯一方法吗?

I tested to pass -zlazy flag to GCC (g++) with a path to the library, it seemed to accept the flag but the behavior did not look making libbar.so delay loaded (Not having libbar.so , I was expecting to have an exception at the first call of libbar.so , but the exception actually raised before entering to libfoo.so ).我测试将-zlazy标志传递给带有库路径的 GCC (g++),它似乎接受了该标志,但行为看起来并没有使libbar.so延迟加载(没有libbar.so ,我期待有一个第一次调用libbar.so时出现异常,但实际上在进入libfoo.so之前引发了异常)。 On the other hand, Clang ( clang++ ) left a warning that it ignored the option flag.另一方面,Clang ( clang++ ) 留下了一个警告,指出它忽略了选项标志。

Best regards,此致,

Delay loading is NOT a runtime feature.延迟加载不是运行时功能。 MSVC++ implemented it without help from Windows. MSVC++ 在没有 Windows 帮助的情况下实现了它。 And like dlopen is the only way on Linux, GetProcAddress is the only runtime method on Windows.就像dlopen是 Linux 上的唯一方法一样, GetProcAddress是 Windows 上唯一的运行时方法。

So, what is delay loading then?那么,什么是延迟加载呢? It's very simple: Any call to a DLL has to go through a pointer (since you don't know where it will load).这很简单:对 DLL 的任何调用都必须通过一个指针(因为您不知道它将加载到哪里)。 This has always been handled by the compiler and linker for you.这一直由编译器和链接器为您处理。 But with delay loading, MSVC++ sets this pointer initially to a stub that calls LoadLibrary and GetProcAddress for you.但是对于延迟加载,MSVC++ 最初将此指针设置为为您调用LoadLibraryGetProcAddress的存根。

Clang can do the same without help from ld . Clang 可以在没有ld帮助的情况下做同样的事情。 At runtime, it's just an ordinary dlopen call, and Linux cannot determine that Clang inserted it.在运行时,它只是一个普通的dlopen调用,Linux 无法确定是 Clang 插入的。

This functionality can be achieved in a portable way using Proxy design pattern .可以使用代理设计模式以可移植的方式实现此功能。

In code it may look something like this:在代码中它可能看起来像这样:

#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();
}

To add to MSalters answer, one can easily mimic Windows approach to lazy loading on Linux by creating a small static stub library that would try to dlopen needed library on first call to any of it's functions (emitting diagnostic message and terminating if dlopen failed) and then forwarding all calls to it.要添加到 MSalters 答案中,可以通过创建一个小型静态存根库来轻松模仿 Windows 方法在 Linux 上进行延迟加载,该存根库将尝试在第一次调用其任何函数时dlopen所需的库(发出诊断消息并在 dlopen 失败时终止)和然后将所有呼叫转发给它。

Such stub libraries can be written by hand, generated by project/library-specific script or generated by universal tool Implib.so :这样的存根库可以手工编写,由项目/库特定脚本生成或由通用工具Implib.so生成:

$ implib-gen.py libxyz.so
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM