如何将参数传递给从CPP中的静态库加载的方法 [英] How to pass arguments to a method loaded from a static library in CPP

查看:129
本文介绍了如何将参数传递给从CPP中的静态库加载的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个程序,以将C ++代码的静态库使用到另一个C ++代码中.第一个C ++代码是hello.cpp:

I'm trying to write a program to use a static library of a C++ code into another C++ code. The first C++ code is hello.cpp:

#include <iostream>
#include <string.h>
using namespace std;
extern "C" void say_hello(const char* name) {
    cout << "Hello " <<  name << "!\n";
}
int main(){
    return 0;
}

我使用以下命令从此代码hello.a中创建了一个静态库:

The I made a static library from this code, hello.a, using this command:

g++ -o hello.a -static -fPIC hello.cpp -ldl 

这是使用库say_hello.cpp的第二个C ++代码:

Here's the second C++ code to use the library, say_hello.cpp:

#include <iostream>
#include <string>
#include <dlfcn.h>
using namespace std;
int main(){
    void* handle = dlopen("./hello.a", RTLD_LAZY);
    cout<<handle<<"\n";
    if (!handle) {
        cerr<<"Cannot open library: "<<dlerror()<<'\n';
        return 1;
    }
    typedef void (*hello_t)();
    dlerror(); // reset errors
    hello_t say_hello = (hello_t) dlsym(handle, "say_hello");
    const char *dlsym_error = dlerror();
    if (dlsym_error) {
        cerr<<"Cannot load symbol 'say_hello': "<<dlsym_error<<'\n';
        dlclose(handle);
        return 1;
    }
    say_hello("World");
    dlclose(handle);
    return 0;
}

然后我使用以下命令编译了say_hello.cpp:

Then I compiled say_hello.cpp using:

g++ -W -ldl say_hello.cpp -o say_hello 

并在命令行中运行./say_hello.我期望得到Hello World!作为输出,但是我得到了这个:

and ran ./say_hello in the command line. I expected to get Hello World! as output, but I got this instead:

0x8ea4020
Hello ▒▒▒▒!

出什么问题了?是否有任何技巧可以使方法的参数兼容,例如我们在中使用的方法ctypes 还是什么?

What is the problem? Is there any trick to make compatibility for method's argument like what we use in ctypes or what?

如果有帮助的话,我可以花点钱.

If it helps I use a lenny.

我更改了代码,并使用了动态库'hello.so',该库是使用以下命令创建的:

I have changed the code and used a dynamic library, 'hello.so', which I've created using this command:

g++ -o hello.so -shared -fPIC hello.cpp -ldl

代码的第六行更改为:

void* handle = dlopen("./hello.so", RTLD_LAZY);

当我尝试编译say_hello.cpp时,出现此错误:

When I tried to compile say_hello.cpp, I got this error:

say_hello.cpp: In function ‘int main()’:
say_hello.cpp:21: error: too many arguments to function

我还尝试使用此行对其进行编译:

I also tried to compile it using this line:

g++ -Wall -rdynamic say_hello.cpp -ldl -o say_hello

但是出现同样的错误.因此,我删除了参数"World",并且该参数已被正确编译.但是当我运行可执行文件时,得到的输出与前面提到的相同.

But same error raised. So I removed the argument "World" and the it has been compiled with no error; but when I run the executable, I get the same output like I have mentioned before.

根据@Basile Starynkevitch的建议,我将say_hello.cpp代码更改为此:

Based on @Basile Starynkevitch 's suggestions, I changed my say_hello.cpp code to this:

#include <iostream>
#include <string>
#include <dlfcn.h>
using namespace std;
int main(){
    void* handle = dlopen("./hello.so", RTLD_LAZY);
    cout<<handle<<"\n";
    if (!handle) {
        cerr<<"Cannot open library: "<<dlerror()<<'\n';
        return 1;
    }
    typedef void hello_sig(const char *);
    void* hello_ad = dlsym(handle, "say_hello");
    if (!hello_ad){
        cerr<<"dlsym failed:"<<dlerror()<<endl;
        return 1;
    }
    hello_sig* fun = reinterpret_cast<hello_sig*>(hello_ad);
    fun("from main");
    fun = NULL;
    hello_ad = NULL;
    dlclose(handle);
    return 0;
}

在此之前,我使用下面的代码制作一个.so文件:

Before that, I used below line to make a .so file:

g++ -Wall -fPIC -g -shared hello.cpp -o hello.so

然后我通过以下命令编译了say_hello.cpp:

Then I compiled say_hello.cpp wth this command:

g++ -Wall -rdynamic -g say_hello.cc -ldl -o say_hello

,然后使用./say_hello运行它.现在一切正常.感谢@Basile Starynkevitch对我的问题保持耐心.

And then ran it using ./say_hello. Now everything is going right. Thanks to @Basile Starynkevitch for being patient about my problem.

推荐答案

函数永远不会有空地址,因此函数名称(或实际上在C ++或C中定义的任何名称)上的dlsym不能没有失败而成为NULL :

Functions never have null addresses, so dlsym on a function name (or actually on any name defined in C++ or C) cannot be NULL without failing:

hello_t say_hello = (hello_t) dlsym(handle, "say_hello");
if (!say_hello) {
    cerr<<"Cannot load symbol 'say_hello': "<<dlerror()<<endl;
    exit(EXIT_FAILURE);
};

dlopen(3)被记录为仅动态加载动态库(不是静态库!).这意味着共享对象(*.so)的格式为 ELF 格式.阅读Drepper的论文如何使用共享库

And dlopen(3) is documented to dynamically load only dynamic libraries (not static ones!). This implies shared objects (*.so) in ELF format. Read Drepper's paper How To Use Shared Libraries

我相信您可能在dlopen中发现了一个错误(另请参见其 POSIX dlopen 规范);对于静态库hello.a,它应该失败; 始终用于位置无关的共享库(如hello.so).

I believe you might have found a bug in dlopen (see also its POSIX dlopen specification); it should fail for a static library hello.a; it is always used on position independent shared libraries (like hello.so).

您应仅dlopen 位置独立代码 共享对象 编译为

g++ -Wall -O -shared -fPIC hello.cpp -o hello.so 

或者如果您有几个C ++源文件:

or if you have several C++ source files:

g++ -Wall -O -fPIC src1.cc -c -o src1.pic.o
g++ -Wall -O -fPIC src2.cc -c -o src2.pic.o
g++ -shared src1.pic.o src2.pic.o -o yourdynlib.so

,您可以删除-O优化标记,也可以添加-g进行调试,或者根据需要将其替换为-O2.

you could remove the -O optimization flag or add -g for debugging or replace it with -O2 if you want.

,并且效果非常好:我的 MELT 项目(用于扩展GCC的领域特定语言)正在使用此工具很多(生成C ++代码,即时进行如上的编译,然后dlopen-生成生成的共享对象).我的 manydl.c 示例演示了您可以dlopen大量(不同)共享Linux上的对象(通常为数百万个,至少数十万个).实际上限制是地址空间.

and this works extremely well: my MELT project (a domain specific language to extend GCC) is using this a lot (generating C++ code, forking a compilation like above on the fly, then dlopen-ing the resulting shared object). And my manydl.c example demonstrates that you can dlopen a big lot of (different) shared objects on Linux (typically millions, and hundred of thousands at least). Actually the limitation is the address space.

顺便说一句,您不应该dlopen具有main功能的东西,因为main是根据定义在主程序调用中定义的(可能是间接地)dlopen.

BTW, you should not dlopen something having a main function, since main is by definition defined in the main program calling (perhaps indirectly) dlopen.

此外,g++的参数顺序也很重要.您应该使用

Also, order of arguments to g++ matters a lot; you should compile the main program with

g++ -Wall -rdynamic say_hello.cpp -ldl -o say_hello 

-rdynamic标志是必需的,以便让已加载的插件(hello.so)从您的say_hello程序内部调用函数.

The -rdynamic flag is required to let the loaded plugin (hello.so) call functions from inside your say_hello program.

出于调试目的,请始终将-Wall -g传递给上面的g++.

For debugging purposes always pass -Wall -g to g++ above.

顺便说一句,原则上您可以dlopen一个没有PIC的共享对象(即未使用-fPIC编译);但是dlopen一些PIC共享对象要好得多.

BTW, you could in principle dlopen a shared object which don't have PIC (i.e. was not compiled with -fPIC); but it is much better to dlopen some PIC shared object.

另请阅读程序库操作指南名称修改).

文件helloshared.cc(我在C ++中的小插件源代码)是

File helloshared.cc (my tiny plugin source code in C++) is

#include <iostream>
#include <string.h>
using namespace std;
extern "C" void say_hello(const char* name) {
    cout << __FILE__ << ":" << __LINE__ << " hello " 
         <<  name << "!" << endl;
}

我正在用它编译:

g++ -Wall -fPIC -g -shared helloshared.cc -o hello.so


主程序在文件mainhello.cc中:

#include <iostream>
#include <string>
#include <dlfcn.h>
#include <stdlib.h>
using namespace std;
int main() {
    cout << __FILE__ << ":" << __LINE__ << " starting." << endl;
    void* handle = dlopen("./hello.so", RTLD_LAZY);
    if (!handle) {
        cerr << "dlopen failed:" << dlerror() << endl;
        exit(EXIT_FAILURE);
    };
    // signature of loaded function
    typedef void hello_sig_t(const char*);
    void* hello_ad = dlsym(handle,"say_hello");
    if (!hello_ad) {
        cerr << "dlsym failed:" << dlerror() << endl;
        exit(EXIT_FAILURE);
    }
    hello_sig_t* fun = reinterpret_cast<hello_sig_t*>(hello_ad);
    fun("from main");
    fun = NULL; hello_ad = NULL;
    dlclose(handle);
    cout << __FILE__ << ":" << __LINE__ << " ended." << endl;
    return 0;
}

我编译时使用的

which I compile with

g++ -Wall -rdynamic -g mainhello.cc -ldl -o mainhello


然后我以预期的输出运行./mainhello:

Then I am running ./mainhello with the expected output:

mainhello.cc:7 starting.
helloshared.cc:5 hello from main!
mainhello.cc:24 ended.


请注意,mainhello.cc中的签名hello_sig_t应该与helloshared.cc插件的功能say_hello兼容(同形,即相同),否则为

Please notice that the signature hello_sig_t in mainhello.cc should be compatible (homomorphic, i.e. the same as) with the function say_hello of the helloshared.cc plugin, otherwise it is undefined behavior (and you probably would have a SIGSEGV crash).

这篇关于如何将参数传递给从CPP中的静态库加载的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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