动态加载无外部“C” [英] Dynamic Loading Without extern "C"

查看:182
本文介绍了动态加载无外部“C”的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用libdl来动态加载C ++。



如上所述,一个解决方案是使用externC删除名称调整。



http://www.tldp。 org / HOWTO / C ++ - dlopen / theproblem.html



此解决方案具有将动态加载的资源限制为C样式接口的缺点。动态加载的函数不能,例如,是重载的函数。



什么是克服这个限制的好方法?



一个可能的解决方案是使用附带的函数来命名mangle库的源代码的工具,当库需要链接时获取被破坏的名称。 llvm是否为此提供了工具?



也许一个笨拙的解决方案是一个函数,它接受函数签名,创建具有签名的函数的虚拟代码,编译器,它与用于生成程序集的标志一起使用,解析输出以检索该标志名称,并将该标志名称作为字符串返回。然后可以将字符串传递给dlsym()。



为了保持问题的具体,下面是两个示例程序,说明外部C语言解决方案不能动态加载而不修改库代码。第一个以传统的C ++方式动态链接库。第二个使用dlopen。在第一个程序中链接重载的函数很简单。在第二个程序中没有简单的链接重载函数的方法。



程序1:加载时动态链接



main.cpp

  //转发要链接的函数的声明
void say (int);
void say(float);

int main(){
int myint = 3;
say(myint);
float myfloat = 5.0f;
say(myfloat);
}

say.cpp

  #include< iostream> 

// externC函数签名会冲突

// externCvoid say(int a){
void say(int a){
std :: cout<< int值是< a<< .\\\
;
}

// externCvoid say(float a){
void say(float r){
std :: cout< 浮点值是< r<< .\\\
;
}

输出

  $ ./main 
int值为3.
float值为5.

计划2:运行时动态链接



main_with_dl.cpp

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

int main(){
//打开库
void * handle = dlopen(./ say_externC.so,RTLD_LAZY);
if(!handle){
std :: cerr<< dlopen error:< dlerror()<< '\\\
';
return 1;
}

//加载符号
typedef void(* say_t)(int);

//清除错误,查找符号,检查错误
dlerror();
say_t say =(say_t)dlsym(handle,say);
const char * dlsym_error = dlerror();
if(dlsym_error){
std :: cerr<< dlsym error:< dlsym_error<< '\\\
';
dlclose(handle);
return 1;
}

//使用函数
int myint = 3;
say(myint);
//无法加载在void say(float)
// float myfloat = 5.0f;
// say(myfloat);

//关闭库
dlclose(handle);
}

输出

  $ ./main_with_dl 
int值为3.

编译





< 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)。



EDIT Mooing Duck删除了更多的知识。这里有一个main_with_dl.cpp版本,可以在问题中使用say.cpp。



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



代码必须使用clang ++与__FUNCDNAME__的-fms-extensions标志一起工作。


I'd like to use libdl to dynamically load C++ in general. The problem is identifying symbols at runtime that have been name mangled.

As described here, one solution is to remove name mangling by using extern "C".

http://www.tldp.org/HOWTO/C++-dlopen/theproblem.html

This solution has the drawback of limiting dynamically loaded resources to C style interfaces. Dynamically loaded functions cannot, for instance, be overloaded functions.

What is a good way to overcome this limitation?

One possible solution would be tools to name mangle the library source code with an accompanying function to get the mangled names when the library needs to be linked. Does llvm provide tools for this?

Maybe a clumsy solution would be a function that takes a function signature, creates dummy code with a function that has the signature, pipes into the compiler that was used with a flag for generating assembly, parses the output to retrieve the mangled name, and returns the mangled name as a string. The string could then be passed to dlsym().

To keep the problem concrete, here are two example programs that illustrate something the extern "C" solution can't dynamically load without modifying library code. The first dynamically links a library in traditional C++ fashion. The second uses dlopen. Linking an overloaded function in the first program is simple. There's no simple way to link the overloaded function in the second program.

Program 1: Loadtime Dynamic Linking

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.

Program 2: Runtime Dynamic Linking

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.

Compiling

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

解决方案

Thanks to Mooing Duck I was able to come up with a solution using clang and inspired by Visual Studio.

The key is a macro provided by Visual Studio and clang. The __FUNCDNAME__ macro resolves to the mangled name of the enclosing function. By defining functions with the same signature as the ones we want to dynamically link, we can get __FUNCDNAME__ to resolve to the needed name mangle.

Here's the new version of program 2 that can call both void say(int) and void say(float).

EDIT Mooing Duck dropped more knowledge on me. Here's a version of main_with_dl.cpp that works with say.cpp in the question.

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

The code must be compiled using clang++ with the -fms-extensions flag for __FUNCDNAME__ to work.

这篇关于动态加载无外部“C”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆