c/c++ dynamic link

主要介绍了在c/c++中如何使用dynamic link来动态加载.so文件以及涉及到的一些重要环境变量。

这一篇 对于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 不是一个 具体的问题和详细信息可以参考这个 以及 这个

dynamic link就是在运行的时候动态地链接所需要的代码库,在Linux中,动态链接库通常是被封装成为.so的形式。下面是一个具体的例子:

比如caculate.c文件中放置了好多函数,我们希望通过动态连接的方式使用这些函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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");
}

使用如下命令编译:

1
gcc -fPIC -shared caculate.c -o libcaculate.so

之后可以使用nm命令查看 .so文件中的内容

1
2
3
4
5
6
7
8
$ nm libcaculate.so
0000000000000f40 T _action
0000000000000ec0 T _add
0000000000000f20 T _div
0000000000000f00 T _mul
U _printf
0000000000000ee0 T _sub
U dyld_stub_binder

之后可以加载这个动态链接库并且根据外部变量导入指定的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#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);
}

具体编译的方式如下:

1
gcc -rdynamic -o main main.c -ldl

如果使用g++对.cpp文件进行dynamic link,具体可以参考这个,由于c++有name mangling的作用,需要使用extern C来对.so文件进行标记。

比如下面源文件直接进行编译g++ -fPIC -shared add.cpp -o add.so时候再使用nm查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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;
}

得到如下的结果:

1
2
3
4
5
6
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再进行编译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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查看之后结果如下:

1
2
3
4
5
6
7
$ 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++进行编译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#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/

推荐文章