本文介绍Unix/Linux环境下怎么使用命令行生成动态链接库,怎么链接动态链接库。
本文的代码在macOS环境下一一测试过。
生成动态链接库
如果用C
calculate.c
extern int g_Ver = 2;
int add(int a, int b) // extern can be ignore
{
return a + b;
}
如果用C++,需要extern "C"
,因为目前动态链接库目前只支持C的接口。如果想导入类,也只能通过Factory函数来实现。
calculate.cpp
extern "C"
{
extern int g_Ver = 2;// also can be declared as `extern "C" int g_Ver = 2;`
}
extern "C" int add(int a, int b)
{
return a + b;
}
在linux环境下调用如下指令生成动态链接库:
$ gcc -shared calculate.c -o libcalculate.so
在macOS环境下也可以用如下指令生成动态链接库:
$ gcc -dynamiclib calculate.c -o libcalculate.so
C++也是一样的指令。
使用nm
可以查看动态库的详情:
$ nm libcalculate.so
动态链接库和静态库的区别
静态链接库的本质是目标文件的集合,一般使用如下方式生成静态库
$ gcc -c calculate.c # generates calculate.o
$ ar rc libcalculate.a calculate.o # use `ar` to pack the static library
需要注意,如果当前已经有libcalculate.a,那么ar rc
可能生成失败,可以先删除lib。
静态库生成的过程不涉及链接,只是一堆目标文件的集合,而动态链接库需要把代码中涉及到的外部函数和变量链接到位,才能生成成功。这是静态库和动态链接库的区别。
链接动态链接库
首先编写如下编码,以下为C语言版本
main.c
#include "stdio.h"
extern int add(int a, int b);
extern int g_Ver;
int main()
{
printf("%d\n", add(5, 5));
printf("%d\n", g_Ver);
return 0;
}
这个代码可以用来跟静态库链接,也可以跟动态链接库进行运行时隐式链接(dynamic linking)。
使用如下指令进行动态库链接:
$ gcc -rdynamic main.c -o main -lcalculate -L.
去掉-rdynamic
即为静态链接的方式。
虽然动态链接时不会把动态链接库加载进执行文件,但依旧会检查隐式链接的接口是否在动态库中实现。
C++的版本同理,需要在声明中添加extern "C"
如果想要使用显式链接的模式(dynamic loading),需要编写如下的代码:
#include "stdio.h"
#include "dlfcn.h"
typedef int (*Func)(int a, int b);
int main()
{
void* pdlhandle = dlopen("./libcalculate.so", RTLD_LAZY);
char* pszerror;
Func mytest;
int * p_Ver;
if (0 != pszerror) {
printf("%s\n", pszerror);
return 1;
}
mytest = (Func)dlsym(pdlhandle, "add");
pszerror = dlerror();
if (0 != pszerror) {
printf("%s\n", pszerror);
return 1;
}
printf("%d\n", mytest(5, 5));
p_Ver = (int *)dlsym(pdlhandle, "g_Ver");
pszerror = dlerror();
if (0 != pszerror) {
printf("%s\n", pszerror);
return 1;
}
printf("%d\n", *p_Ver);
dlclose(pdlhandle);
return 0;
}
依旧是使用一样的指令就可以生成执行文件main,但是这次不会检查是否存在add
和g_Ver
。
动态加载(dynamic loading)的优势是,可以卸载重新加载,特别适用于动态库模块的升级。