C++:动态共享库中的虚函数产生段错误

Posted

技术标签:

【中文标题】C++:动态共享库中的虚函数产生段错误【英文标题】:C++: Virtual functions in dynamic shared library produce segfault 【发布时间】:2011-11-10 15:26:36 【问题描述】:

在我正在编写的应用程序中,我正在从我编写的共享库中动态加载对象。

这很好,直到虚函数发挥作用。这意味着我可以轻松地调用 getter 和 setter,但在尝试调用覆盖虚拟基类函数的函数时,我会立即遇到分段错误。我目前的想法不多了,因为我项目中的每个类(层次结构)都会发生这种情况。

在应用程序内部创建对象时可以成功调用函数,完全不使用动态加载或共享库。

我怀疑是概念错误或某种编译/链接错误。

类层次结构如下所示:

BaseClass.h:

class BaseClass : public EvenMoreBaseClass public: virtual bool enroll(const shared_ptr<string> filepath) = 0;

派生的.h:

class Derived : public BaseClass bool enroll(const shared_ptr<string> filepath);

Derived.cpp:

bool Derived::enroll(const shared_ptr<string> filepath) cout << "enroll" << endl;

(包括此处省略的命名空间)

应用程序加载库并获得一个(共享)指向 BaseClass 对象的指针(应用程序包括 BaseClass.h)。除虚拟功能外,所有功能均可执行。

开发是在 Eclipse CDT 中完成的。当前所有内容都在一个具有不同构建配置的项目中(应用程序的 .cpp 在共享库的配置中被禁用,反之亦然)。编译器是 g++ 4.4。层次结构的所有 .o 文件都与库链接,设置了 -shared。

非常感谢任何帮助。

【问题讨论】:

你在调试器中运行过这个吗? 我有,但是当我第二次尝试访问虚拟功能时,我得到了一个段错误。我在虚函数的名称上设置了断点,但在库中从未成功调用过。 【参考方案1】:

也许这是一个很晚的答案,但我发现了相同的行为,如果您在访问虚拟方法之前使用 dlclose(handle) 会发生这种情况(尽管静态方法可以正常工作)。因此,在您处理完其中的对象之前,您不应关闭库句柄。

【讨论】:

【参考方案2】:

确保 dll 和可执行文件都使用 exact 相同的编译器设置进行编译。如果它们不同,编译器可能会在可执行文件中生成与 dll 预期不匹配的代码,反之亦然,这是 ODR 违规。

请注意,标准库类型(例如 std::string 和 std::shared_ptr 在调试配置中可能具有不同的布局,并且还可以在编译器版本之间更改(即使来自同一供应商)。

一般来说,您需要小心使用跨 dll 边界的类。通常建议只处理内置类型和无状态接口。

【讨论】:

【参考方案3】:

在构建配置中设置的项目之间的类型大小会有所不同吗?

[更新]

当一个项目将诸如short和long之类的类型定义为与另一个项目不同的大小时,我遇到过类似的问题,并且包含这些类型的类通过相同的头文件引用。一个项目将第二个 short 放在第 2 字节,另一个项目认为它在第 4 字节。

我也遇到了不同填充值的问题。一个项目将奇数字符填充到 4 个字节边界,另一个认为它被填充到 8 个字节边界。

同样的情况也适用于虚函数指针,也许一个项目适合它们为 32 位,另一个项目期望它们为 64 位。

【讨论】:

它们包含相同的文件,我通常编译这两种配置。这可能是一个问题,例如一个生成与另一个生成不同的 .o 文件,导致库和应用程序的类型不同?还是您的意思是其他一些类型的大小? 感谢您的更新,当我回到这个问题时,我会检查一下 - 因为动态加载在这个项目中是一个“不错的”功能,所以我将首先专注于应用程序逻辑。

以上是关于C++:动态共享库中的虚函数产生段错误的主要内容,如果未能解决你的问题,请参考以下文章

由于动态链接库中的虚函数而导致编译错误

从堆栈跟踪中查找共享库中的源代码行

C++ 静态库中的共享全局变量:Linux

从 C++ 中的共享库调用指向列表的静态指针

Linux动态链接之GOT与PLT

PIC和PIE