主要介绍了在c/c++中如何使用dynamic link来动态加载.so文件以及涉及到的一些重要环境变量。
dynamic link 与 static link 这一篇 对于shared library 和 static library 说明的比较透彻,详细的内容看原文,大致上static library使用.a
的后缀,在使用static library的时候,code直接被link到binary的文件中。而使用shared library则是.so
结尾 在程序run的时候才会去寻找实际的.so
文件的位置(这个.so文件本身就是一个二进制文件)。
使用ldd <binaryfile>
可以查找到当前的这个 binary file 在动态链接的时候都链接到了哪些.so
文件以及其具体的路径 这个可以用来debug动态链接时候的问题,有的时候没有链接到一个动态库上,比如之前的安装mpi4py的问题,当时想通过python传一个mpi的communicator给c的code,只有mpi4py和本身的c code 使用了一个mpi的动态链接库的时候 这个思路才可能实现 但有的时候安装环境可能有问题 导致mpi4py所链接的mpi library和 c code 所链接的library 不是一个 具体的问题和详细信息可以参考这个 以及 这个 。
使用gcc进行dynamic link dynamic link就是在运行的时候动态地链接所需要的代码库,在Linux中,动态链接库通常是被封装成为.so的形式。下面是一个具体的例子:
比如caculate.c文件中放置了好多函数,我们希望通过动态连接的方式使用这些函数:
#include "stdio.h" int add(int a, int b) { return (a + b); } int sub(int a, int b) { return (a - b); } int mul(int a, int b) { return (a * b); } int div(int a, int b) { return (a / b); } void action() { printf("test action func\n"); }
使用如下命令编译:
gcc -fPIC -shared caculate.c -o libcaculate.so
之后可以使用nm命令查看 .so文件中的内容
$ nm libcaculate.so 0000000000000f40 T _action 0000000000000ec0 T _add 0000000000000f20 T _div 0000000000000f00 T _mul U _printf 0000000000000ee0 T _sub U dyld_stub_binder
之后可以加载这个动态链接库并且根据外部变量导入指定的函数
#include <stdio.h> #include <stdlib.h> #include <dlfcn.h> //动态链接库路径 #define LIB_CACULATE_PATH "./caculatelib/libcaculate.so" //函数指针 typedef int (*CAC_FUNC)(int, int); typedef void (*ACT_FUNC)(); int main() { void *handle; char *error; CAC_FUNC cac_func = NULL; //打开动态链接库 handle = dlopen(LIB_CACULATE_PATH, RTLD_LAZY); if (!handle) { fprintf(stderr, "%s\n", dlerror()); exit(EXIT_FAILURE); } //清除之前存在的错误 dlerror(); //获取一个函数 *(void **)(&cac_func) = dlsym(handle, "add"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(EXIT_FAILURE); } printf("add: %d\n", (*cac_func)(2, 7)); cac_func = (CAC_FUNC)dlsym(handle, "sub"); printf("sub: %d\n", cac_func(9, 2)); cac_func = (CAC_FUNC)dlsym(handle, "mul"); printf("mul: %d\n", cac_func(3, 2)); cac_func = (CAC_FUNC)dlsym(handle, "div"); printf("div: %d\n", cac_func(8, 2)); ACT_FUNC act_func = NULL; act_func = (ACT_FUNC)dlsym(handle, "action"); act_func(); //关闭动态链接库 dlclose(handle); exit(EXIT_SUCCESS); }
具体编译的方式如下:
gcc -rdynamic -o main main.c -ldl
使用g++进行dynamic link 如果使用g++对.cpp文件进行dynamic link,具体可以参考这个 ,由于c++有name mangling的作用,需要使用extern C
来对.so文件进行标记。
比如下面源文件直接进行编译g++ -fPIC -shared add.cpp -o add.so
时候再使用nm查看
#include <vector> using namespace std; int add(int c) { vector<int> v; int i; int sum = 0; for (i = 0; i < c; i++) { sum = sum + i; } return sum; }
得到如下的结果:
0000000000000ac0 T __Z3addi 0000000000000d50 t __ZNSt3__113__vector_baseIiNS_9allocatorIiEEED2Ev 0000000000000d10 t __ZNSt3__16vectorIiNS_9allocatorIiEEED1Ev 0000000000000d30 t __ZNSt3__16vectorIiNS_9allocatorIiEEED2Ev U __ZdlPv U dyld_stub_binder
由于函数的名字被加上了一些字符,所以动态链接的时候会找不到对应的函数。如果加上extern C再进行编译:
#include <vector> using namespace std; extern "C" int add(int c) { vector<int> v; int i; int sum = 0; for (i = 0; i < c; i++) { sum = sum + i; } return sum; }
使用mn查看之后结果如下:
$ nm add.so 0000000000000d50 t __ZNSt3__113__vector_baseIiNS_9allocatorIiEEED2Ev 0000000000000d10 t __ZNSt3__16vectorIiNS_9allocatorIiEEED1Ev 0000000000000d30 t __ZNSt3__16vectorIiNS_9allocatorIiEEED2Ev U __ZdlPv 0000000000000ac0 T _add U dyld_stub_binder
main函数也可以写成.cpp的形式使用g++进行编译:
#include <stdlib.h> #include <stdio.h> #include <dlfcn.h> typedef int (*add_func_ptr)(int); int main(int argc, char **argv) { void *handle; double (*cosine)(double); char *error; handle = dlopen("./add.so", RTLD_LAZY); if (!handle) { fputs(dlerror(), stderr); exit(1); } add_func_ptr addfun; addfun = (add_func_ptr)dlsym(handle, "add"); if ((error = dlerror()) != NULL) { fputs(error, stderr); exit(1); } printf("%d\n", (*addfun)(5)); dlclose(handle); }
参考资料 dynamic link的example
https://medium.com/@LeeJulija/dynamic-and-static-libraries-in-c-bcab492a8da
https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html
基于c的 dynamic link 的例子
https://www.cnblogs.com/Anker/p/3746802.html
基于c++的dynamic link 的例子 回答里有许多比较好的Link
https://stackoverflow.com/questions/49187610/dynamic-link-with-dlopen-symbol-not-found?noredirect=1#comment85380797_49187610
http://tldp.org/HOWTO/C++-dlopen/