gpt4 book ai didi

c++ - 如何将参数传递给从 CPP 中的静态库加载的方法

转载 作者:搜寻专家 更新时间:2023-10-31 01:46:12 27 4
gpt4 key购买 nike

我正在尝试编写一个程序,将一个 C++ 代码的静态库用于另一个 C++ 代码。第一个 C++ 代码是 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 中创建了一个静态库:

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

这是使用库的第二个 C++ 代码,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:

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

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

0x8ea4020
Hello ▒▒▒▒!

问题是什么?是否有任何技巧可以使方法参数兼容,就像我们在 ctypes 中使用的那样或者什么?

如果有帮助,我会使用 lenny。

编辑 1:

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

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

第6行代码改为:

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

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

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

我还尝试使用这一行来编译它:

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

但是出现了同样的错误。所以我删除了参数 "World" 并且编译没有错误;但是当我运行可执行文件时,我得到了与之前提到的相同的输出。

编辑 2:

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

#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 文件:

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

然后我用这个命令编译了say_hello.cpp:

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

然后使用 ./say_hello 运行它。现在一切顺利。感谢@Basile Starynkevitch 对我的问题耐心等待。

最佳答案

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

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)记录在dynamically load只有动态库(不是静态库!)。这意味着 ELF 中的共享对象 (*.so)格式。阅读 Drepper 的论文 How To Use Shared Libraries

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

您应该只dlopen position independent code shared objects

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

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

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 .

这非常有效:我的 MELT项目(一种扩展 GCC 的领域特定语言)正在大量使用它(生成 C++ 代码,像上面那样即时进行编译,然后 dlopen-ing 生成的共享对象).还有我的manydl.c示例演示了您可以在 Linux 上dlopen 大量(不同的)共享对象(通常数百万,至少数十万)。实际上限制是地址空间。

顺便说一句,你不应该 dlopenmain 函数的东西,因为 main 是在主程序调用中定义的(可能间接地)dlopen

此外,g++ 的参数顺序也很重要;你应该编译主程序

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

-rdynamic 标志是让加载的插件 (hello.so) 从您的 say_hello 程序中调用函数所必需的.

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

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

另请阅读 Program Library HowToC++ dlopen mini-howto (因为 name mangling )。


例子

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

#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;
}

我用它编译

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


然后我运行 ./mainhello 并得到预期的输出:

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


请注意 mainhello.cc 中的签名 hello_sig_t 应该与 say_hello 的函数兼容(同态,即相同) helloshared.cc 插件,否则为 undefined behavior (您可能会遇到 SIGSEGV 崩溃)。

关于c++ - 如何将参数传递给从 CPP 中的静态库加载的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20987913/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com