【问题标题】:dlopen() gives unresolved symbol error when .so tries to use a class from the main executable. Why?当 .so 尝试使用主可执行文件中的类时,dlopen() 会给出未解决的符号错误。为什么?
【发布时间】:2011-12-16 16:10:04
【问题描述】:

我在 Linux 上,问题是关于 C++ 类的共享对象。

当我的共享对象尝试使用链接到主可执行文件的资源时,问题就出现了。我有以下代码:

loader.cpp:

#include <dlfcn.h>
#include <iostream>
#include "CommonInfo.h"

using namespace std;

int main(int argc, char** argv) {
    for(int i=1; i<argc; ++i) {
        string pth = "./";
        pth.append(argv[i]);
        void* dh = dlopen(pth.c_str(), RTLD_NOW);
        if(dh==NULL) {
            cerr << dlerror() << endl;
            return 1;
        }

        CommonInfo::GetInfoFunc getInfo = (CommonInfo::GetInfoFunc)(dlsym(dh,"getInfo"));
        if(getInfo==NULL) {
            cerr << dlerror() << endl;
            return 1;
        }

        CommonInfo* info = getInfo();
        cout << "INFO: " << info->getX() << endl;
        delete info;
    }
    return 0;
}

CommonInfo.h:

#include <string>

class CommonInfo {
    public:
        typedef CommonInfo* (*GetInfoFunc)();
    private:
        std::string x;
    public:
        CommonInfo(const std::string& nx);
        std::string getX() const;
};

编辑: 我不小心忘记了 ctrl-c + ctrl-v 这里的 CommonInfo.cpp 的来源。当然是编译的时候就有了,所以CommonInfo.cpp:

#include "CommonInfo.h"

CommonInfo::CommonInfo(const std::string& nx) : x(nx) {
}

std::string CommonInfo::getX() const {
    return x;
}

一个 Plugin.h 头文件:

#include "CommonInfo.h"
extern "C" CommonInfo* getInfo();

一个非常简单的Plugin.cpp:

#include <iostream>
#include "Plugin.h"
#include "CommonInfo.h"

using namespace std;

CommonInfo* getInfo() {
    return new CommonInfo("I'm a cat!");
}

编译完成:

g++ -rdynamic -ldl -Werror CommonInfo.cpp loader.cpp -o loader
g++ -shared -fPIC -Werror Plugin.cpp -o Plugin.so

跑步:

./loader Plugin.so

然后出现错误:

./loader: symbol lookup error: ./Plugin.so: undefined symbol: _ZN10CommonInfoC1ERKSs

确实,使用nm Plugin.so | grep -i CommonInfo 查看 Plugin.so 会为这个符号提供一个“U”(未解决),这完全没问题。 此外,使用nm loader.so | grep -i CommonInfo 查看加载程序的二进制文件,我可以找到带有“T”的符号,这也可以。 问题是,不应该 dlfcn.h 从主二进制文件中解开有问题的符号吗?没有这个功能,使用这些东西就变得非常困难......我是否必须为 CommonInfo 编写一个类工厂函数,使用插件中的 dlfcn 加载它并调用它?

提前致谢, 丹尼斯

【问题讨论】:

  • (更正:不是 C++ 类的共享对象,虽然在我开始写这篇文章的时候,Plugin.so 中有一个类,但为了让错误更清楚,我把它去掉了。)
  • 看来是CommonInfo的构造函数的问题。这是在哪里定义的?
  • 哎呀,它就在那里,我只是忘了写在这里。 :)
  • 仅供参考:$ echo _ZN10CommonInfoC1ERKSs | c++filt 产生 CommonInfo::CommonInfo(std::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt; &gt; const&amp;)
  • 我很确定你在可执行编译行也需要-fPIC

标签: c++ plugins shared-libraries


【解决方案1】:

我没有仔细查看您的代码,但我过去发现当我没有将可执行文件与-E 链接时,我发现了与您在标题中描述的行为类似的行为。 (或-Wl,-Egcc 而不是ld 链接时。)

请注意,并非所有平台都允许共享库从调用二进制文件中获取符号。 Linux 和 *BSD 允许您这样做。但是如果你想移植到,比如说,Windows,你将无法使用这种模式。我相信也有一些 Unix 类型的操作系统不会让你这样做。 (已经有一段时间了,所以我不记得了……也许是 Solaris?)

【讨论】:

  • 嗯,谢谢,但它似乎没有解决它:现在我链接到g++ -ldl -W,-E -o loader CommonInfo.o loader.o。我假设如果一切正确,nm -D loader 会向我显示有问题的符号......但不幸的是,即使使用 -E 选项它也不会这样做。 :/ 也许我在链接时做错了什么?
  • 它工作!您的解决方案是正确的,我只是不小心写了-W,-E 而不是-l。大声笑,写一封简单的信真是浪费时间……:)
  • @asveikau:您可以从 Windows 上的 EXE 导出符号。但是,Windows 有一个两级符号命名空间,因此插件需要显式请求主 EXE 的符号(通过链接到主 EXE 的导入库或调用 GetProcAddress())。
猜你喜欢
  • 2022-10-12
  • 1970-01-01
  • 2013-01-15
  • 1970-01-01
  • 2010-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-19
相关资源
最近更新 更多