在小型复制器中,带有 vtable 的异常类的类型信息/vtable 的符号查找失败。为什么会出错?是否有可能使 RTTI 对加载了 dlopen 的 vtable 的类正常工作?间接加载的目的是runtime binding based on cpu .
库.h:
#include <exception>
class myexception : public std::exception {
virtual void info();
};
void f();
lib.cc:
#include "lib.h"
void myexception::info() {};
void f() { throw myexception(); }
主.cc:
#include "lib.h"
int main() {
try { f(); }
catch(myexception) {}
}
stub .cc:
#include <dlfcn.h>
#include <stdlib.h>
__attribute__((constructor)) void init() {
dlopen("libreal.so", RTLD_NOW | RTLD_GLOBAL);
}
build.sh:
g++ lib.cc -Wall -Wextra -shared -o libf.so -fPIC -g
g++ main.cc -Wall -Wextra libf.so -fPIE -g
mv libf.so libreal.so
g++ stub.cc -Wall -Wextra -shared -o libf.so -fPIC -ldl -g
使用 GCC 或 clang+libstdc++:
./a.out |& c++filt
./a.out: symbol lookup error: ./a.out: undefined symbol: typeinfo for myexception
,以及使用 clang+libc(或使用 -fPIC 而不是 -fPIE 的 GCC):
./a.out |& c++filt
./a.out: symbol lookup error: ./a.out: undefined symbol: vtable for myexception
编辑: 最初的问题表明二进制文件是使用 GCC 段错误编译的。只有在没有 fPIC/fpic/fPIE/fpie 的情况下编译二进制文件时才会出现这种情况。 (Clang 不需要标志,并且关于 clang 行为的问题没有更新)。为了简化问题,我将问题编辑为仅询问运行时链接器问题而不是段错误。
最佳答案
来自 dlopen
的手册页:
RTLD_LAZY
Perform lazy binding. Only resolve symbols as the code that references them is executed. If the symbol is never referenced, then it is never resolved. (Lazy binding is only performed for function references; references to variables are always immediately bound when the library is loaded.)
这很重要。 GNU ld 默认实现惰性绑定(bind)。这意味着您的后期绑定(bind)方法可能适用于函数,但不适用于数据。 (Vtables 和 RTTI 信息是数据)。如果您使用 -z now
链接可执行文件(类似于 dlopen
的 RTLD_NOW
标志),该方法也将停止对函数起作用。
有两种基本方法可以解决这种情况。
- 不要在库外使用
myexception
的运行时数据(vtable 和类型信息)。这意味着您可以直接使用myexception
做什么非常受限。您可以将引用运行时数据的所有操作包装在从库导出的非虚函数中。 - 将
myexception
的运行时数据移动到 stub 库中。对于大多数 C++ 编译器,这意味着在那里定义第一个(按声明顺序)非内联虚函数。您可以在类的顶部声明一个虚拟虚函数,并在stub.cc
中实现它。其余的可能在 lib.cc 中实现。
关于c++ - 虚拟异常类导致动态链接器错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48246196/