从静态库中dlopen动态库,当动态库使用静态库的符号

8
这个问题与在Linux C++中从静态库dlopen动态库密切相关,但包含了进一步的复杂性(并使用C++而非C):
我有一个链接到静态库(.a)的应用程序,该库使用dlopen函数加载动态库(.so)。此外,动态库调用在静态库中定义的函数。
是否有一种方法可以将其编译而不将动态库与静态库链接或反之亦然?
以下是我迄今为止尝试过的,稍微修改了相关问题中的示例:

app.cpp:

#include "staticlib.hpp"
#include <iostream>
int main()
{
    std::cout << "and the magic number is: " << doSomethingDynamicish() << std::endl;
    return 0;
}

staticlib.hpp:

#ifndef __STATICLIB_H__
#define __STATICLIB_H__

int doSomethingDynamicish();
int doSomethingBoring();
#endif

staticlib.cpp:

#include "staticlib.hpp"
#include "dlfcn.h"
#include <iostream>
int doSomethingDynamicish()
{
    void* handle = dlopen("./libdynlib.so",RTLD_NOW);
    if(!handle)
    {
        std::cout << "could not dlopen: " << dlerror() << std::endl;
        return 0;
    }

    typedef int(*dynamicfnc)();
    dynamicfnc func = (dynamicfnc)dlsym(handle,"GetMeANumber");
    const char* err = dlerror();
    if(err)
    {
        std::cout << "could not dlsym: " <<err << std::endl;
        return 0;
    }
    return func();
}

staticlib2.cpp:

#include "staticlib.hpp"
#include "dlfcn.h"
#include <iostream>

int doSomethingBoring()

{
    std::cout << "This function is so boring." << std::endl;
    return 0;

}

dynlib.cpp:

#include "staticlib.hpp"

extern "C" int GetMeANumber()
{
    doSomethingBoring();
    return 1337;
}

和构建:

g++ -c -o staticlib.o staticlib.cpp
g++ -c -o staticlib2.o staticlib2.cpp
ar rv libstaticlib.a staticlib.o staticlib2.o
ranlib libstaticlib.a
g++ -rdynamic -o app app.cpp libstaticlib.a -ldl 
g++ -fPIC -shared -o libdynlib.so dynlib.cpp

当我使用./app运行它时,我得到了以下结果。
could not dlopen: ./libdynlib.so: undefined symbol: _Z17doSomethingBoringv
and the magic number is: 0

1
当涉及到POSIX环境中的静态库时,唯一涉及的链接是当您的应用程序与静态库链接时。静态库本身没有被链接。实际上,静态库只是一个对象文件的存档,像任何其他对象文件一样链接到您的应用程序中。此外,使用dlopen的目的是在运行时加载模块,因此您不需要链接到您dlopen的模块。 - Some programmer dude
感谢您的澄清,我对链接的世界还很陌生。然而,我的问题仍然存在:我如何使此存档(或任何目标文件)中定义的符号可用于动态加载的库? - Johanna
1
顺便提一下,记住C++使用名称修饰。这意味着符号GetMeANumber在目标文件中不会被命名为那样。要跳过名称修饰,您必须使函数extern "C",如extern "C" int GetMeANumber() { ... } - Some programmer dude
1个回答

9

来自dlopen手册页

如果可执行文件使用"-rdynamic"(或者同义词"--export-dynamic")标志进行链接,则可执行文件中的全局符号也将用于解决动态加载库中的引用。

这意味着,为了使应用程序导出其符号以供动态库使用,您需要使用-rdynamic标志链接应用程序。


除了上述问题之外,还有另一个问题与静态库有关:问题是由于在主程序中未调用doSomethingBoring函数,因此不会链接静态库中的对象文件staticlib2.o

答案可以在例如这个旧问题中找到,其中告诉您要添加--whole-archive链接器标志:

g++ -rdynamic -o app app.cpp -L. \
    -Wl,--whole-archive -lstaticlib \
    -Wl,--no-whole-archive -ldl

我已经偶然发现并尝试了这个标志。不幸的是,将应用程序与g++ -rdynamic -o app app.cpp libstaticlib.a -ldl链接会导致我的示例出现完全相同的错误。 - Johanna
@Lea 我对dlopen及其相关内容没有太多经验(只是一些简单的尝试),但你是否尝试将RTLD_LAZY标志传递给dlopen - Some programmer dude
@Joachim Pileborg:感谢您一直以来的关注。是的,我也尝试过那种方法,但它只是在不同的地方产生了错误:./app: symbol lookup error: ./libdynlib.so: undefined symbol: _Z17doSomethingBoringv - Johanna
2
请注意,这是一个纯Linux答案。在大多数Unix系统中,至少在过去,这是默认行为;链接器不需要任何特殊选项。 - James Kanze
1
顺便说一句:Posix 实际上要求传统行为;Linux 在这方面与 Posix/Unix 有所不同。 - James Kanze
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接