我想一般使用 libdl 动态加载 C++。问题是在运行时识别名称已被破坏的符号。
如此处所述,一种解决方案是使用 extern“C” 删除名称修饰。
http://www.tldp.org/HOWTO/C++-dlopen/theproblem.html
此解决方案的缺点是将动态加载的资源限制为 C 样式接口。例如,动态加载的函数不能是重载函数。
有什么好方法可以克服这个限制呢?
一种可能的解决方案是使用附带的函数对库源代码进行命名重整,以便在需要链接库时获取重整后的名称。 llvm 是否提供了这方面的工具?
也许一个笨拙的解决方案是一个函数,它接受函数签名,使用具有签名的函数创建虚拟代码,通过管道输入与生成程序集的标志一起使用的编译器,解析输出以检索损坏的名称,然后返回损坏的名称作为字符串。然后可以将该字符串传递给 dlsym()。
为了使问题具体化,这里有两个示例程序,它们说明了 extern“C”解决方案在不修改库代码的情况下无法动态加载的情况。第一个以传统 C++ 方式动态链接库。第二个使用 dlopen。在第一个程序中链接重载函数很简单。没有简单的方法来链接第二个程序中的重载函数。
程序1:加载时动态链接
main.cpp
// forward declarations of functions that will be linked
void say(int);
void say(float);
int main() {
int myint = 3;
say(myint);
float myfloat = 5.0f;
say(myfloat);
}
say.cpp
#include <iostream>
//extern "C" function signatures would collide
//extern "C" void say(int a) {
void say(int a) {
std::cout << "The int value is " << a << ".\n";
}
//extern "C" void say(float a) {
void say(float r) {
std::cout << "The float value is " << r << ".\n";
}
output
$ ./main
The int value is 3.
The float value is 5.
方案2:运行时动态链接
main_with_dl.cpp
#include <iostream>
#include <dlfcn.h>
int main() {
// open library
void* handle = dlopen("./say_externC.so", RTLD_LAZY);
if (!handle) {
std::cerr << "dlopen error: " << dlerror() << '\n';
return 1;
}
// load symbol
typedef void (*say_t)(int);
// clear errors, find symbol, check errors
dlerror();
say_t say = (say_t) dlsym(handle, "say");
const char *dlsym_error = dlerror();
if (dlsym_error) {
std::cerr << "dlsym error: " << dlsym_error << '\n';
dlclose(handle);
return 1;
}
// use function
int myint = 3;
say(myint);
// can't load in void say(float)
// float myfloat = 5.0f;
// say(myfloat);
// close library
dlclose(handle);
}
output
$ ./main_with_dl
The int value is 3.
编译
Makefile
CXX = g++
all: main main_with_dl say_externC.so
main: main.cpp say.so
$(CXX) -o $@ $^
main_with_dl: main_with_dl.cpp
$(CXX) -o $@ $<
%.so : %.cpp
$(CXX) -shared -o $@ $<
.PHONY: clean
clean:
rm main main_with_dl say.so say_externC.so