c – 没有extern“C”的动态加载

我想使用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";
}

产量

$./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);
}

产量

$./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

最佳答案 感谢Mooing Duck,我能够使用clang提出解决方案并受Visual Studio的启发.

关键是Visual Studio和clang提供的宏. __FUNCDNAME__宏解析为封闭函数的错位名称.通过定义与我们想要动态链接的签名具有相同签名的函数,我们可以获得__FUNCDNAME__来解析所需的名称mangle.

这是程序2的新版本,可以同时调用void say(int)和void say(float).

编辑Mooing Duck对我的了解更多.这是main_with_dl.cpp的一个版本,它与问题中的say.cpp一起使用.

http://coliru.stacked-crooked.com/a/7249cc6c82ceab00

必须使用clang编译代码,并使用-fms-extensions标志__FUNCDNAME__才能生效.

点赞