Linux dlopen 加载动态链接库

前言

说到加载库,第一个方法就是包含库的头文件,在编译时通过 加载链接库.第二种方法在运行时用函数来加载链接库,获取其中的函数地址并调用.本文讲述后者

正文

接触过WindowsAPI的应该知道,静态库后缀为,动态链接库后缀,而主要用到函数

1
2
3
HMODULE WINAPI LoadLibrary(
_In_ LPCTSTR lpFileName
);

获取函数地址则是

1
2
3
4
FARPROC WINAPI GetProcAddress(
_In_ HMODULE hModule,
_In_ LPCSTR lpProcName
);

MSDN上的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//声明函数指针
typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
// Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
PGNSI pGNSI;
SYSTEM_INFO si;
ZeroMemory(&si, sizeof(SYSTEM_INFO));

pGNSI = (PGNSI) GetProcAddress(
GetModuleHandle(TEXT("kernel32.dll")),
"GetNativeSystemInfo");
if(NULL != pGNSI)
{
pGNSI(&si);
}
else
{
GetSystemInfo(&si);
}

由此可看,Windows也实现了对库文件的调用,那么Linux下怎么调用呢?
关于Linux调用库的头文件可以参考 https://linux.die.net/man/3/dlopen

必须包含的头文件 dlfcn.h
常用函数:

1
2
3
4
5
6
7
8
//加载链接库
void *dlopen(const char *filename, int flag);
//返回获取的错误
char *dlerror(void);
//获取链接库函数地址
void *dlsym(void *handle, const char *symbol);
//卸载链接库
int dlclose(void *handle);

注意,在编译时,要指明 -ldl

除此之外,还有连个也比较有用的函数dladdr() dlvsym()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//获取函数的信息
//dladdr() returns 0 on error, and nonzero on success
//If no symbol matching addr could be found, then dli_sname and dli_saddr are set to NULL
int dladdr(void *addr, Dl_info *info);
The function dladdr() takes a function pointer and tries to resolve name and file where it is located. Information is stored in the Dl_info structure:
typedef struct {
const char *dli_fname; /* Pathname of shared object that
contains address */
void *dli_fbase; /* Address at which shared object
is loaded */
const char *dli_sname; /* Name of nearest symbol with address
lower than addr */
void *dli_saddr; /* Exact address of symbol named
in dli_sname */
} Dl_info;
//类似dlsym()
void *dlvsym(void *handle, char *symbol, char *version);

下面以一个简单的例子说明

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
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h> //包含的头文件
#include <string.h>
#define LIB_PATH "./libtest.so"
//声明函数指针
typedef int (*PFUN)(int ,int );
int main()
{
PFUN pf=NULL;
//加载链接库
void *handle=dlopen(LIB_PATH,RTLD_LAZY);
if(handle==NULL){
printf("load library failed!\n");
return -1;
}

//dlvsym(handle,"add","1.0.0");
//获取链接库里函数地址
//方法1,参考网站上的写法
//*(void**)(&pf)=dlsym(handle,"add");

//方法2,相当与Windows下的 GetProcAddress() 函数.
pf=(PFUN)dlsym(handle,"sub");
if(pf!=NULL){
//获取函数地址信息
Dl_info info;
int r= dladdr((void*)pf,&info);
printf("library filepath:%s\n"\
"address1:0x%08x\n"\
"funciton name:%s\n"\
"address2:0x%08x\n\n",
info.dli_fname,info.dli_fbase,
info.dli_sname,info.dli_saddr);
//0 表示失败
if(r==0){
printf("can not get the dl info...\n");
}
printf("get the function address...\n");
//调用函数
printf("result: %d \n",pf(50,15));
}else{
printf("can not get the function address...\n");
}
//卸载链接库
dlclose(handle);
return 0;
}

生成:g++ test.cpp -o out_test -ldl -rdynamic
运行./out_test
输出结果如下

1
2
3
4
5
6
library filepath:./libtest.so
address1:0xeabfa000
funciton name:sub
address2:0xeabfa7f4
get the function address...
result: 35

注意上面的库文件libtest.so在当前程序目录下的,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef _TEST_H
#define _TEST_H
#include<iostream>
using namespace std;
extern "C" int add(int a,int b){
return a+b;
}
extern "C" int sub(int a,int b){
return a-b;
}
extern "C" int maxnum(int a,int b){
return a>b?a:b;
}
#endif

然后 g++ -fPIC -shared test.cpp -o libtest.so即可生成动态链接库


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!