POSIX 动态链接库 API
动态库常用 API
加载动态库
#include <dlfcn.h>
void *dlopen(const char *pcFile, int iMode);
函数 dlopen 原型分析:
- 此函数成功时返回模块句柄,失败时返回 NULL 并设置错误号。
- 参数 pcFile 是动态库文件名。
- 参数 iMode 以掩码形式表示库的加载属性。
调用 dlopen 函数将按指定的 iMode 打开一个动态库。SylixOS 加载器会检测 pcFile 是否为路径,如果是则加载该路径对应的文件,否则在动态库文件搜索路径中查找名称为 pcFile 的文件,动态库文件搜索路径见 下载动态库 章节。
SylixOS 打开动态库模式包括:RTLD_GLOBAL 和 RTLD_LOCAL,其中 RTLD_GLOBAL 表示模块为全局模块,需要注意的是,只有全局模块能导出符号到内核符号表;RTLD_LOCAL 表示局部模块。
查找符号
#include <dlfcn.h>
void *dlsym(void *pvHandle, const char *pcName);
函数 dlsym 原型分析:
- 此函数成功时返回符号地址或者 NULL,失败时返回 NULL 并设置错误号。
- 参数 pvHandle 是模块句柄,由 dlopen 函数返回。
- 参数 pcName 是被查找的符号名称。
调用 dlsym 函数将从 pvHandle 动态库文件中返回 pcName 代表的函数地址,如果 pcName 不存在,则返回 NULL,因此不应该从返回值来判断 dlsym 函数是否成功,可以通过调用 dlerror 函数获得错误信息。
卸载动态库
#include <dlfcn.h>
int dlclose(void *pvHandle);
函数 dlclose 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pvHandle 为模块句柄,由 dlopen 函数返回。
调用 dlclose 函数将减少 pvHandle 动态库的引用计数,如果引用计数减到零并且没有符号被引用,则卸载该动态库。
获取错误信息
#include <dlfcn.h>
char *dlerror(void);
函数 dlerror 原型分析:
- 返回带有错误信息的字符串。
调用 dlerror 函数将返回调用 dlopen 函数、dlsym 函数和 dlclose 函数的错误信息,如果没有错误,则返回 NULL。
下面程序展示加载动态库的方法。
#include <stdio.h>
#include <dlfcn.h>
int main (int argc, char *argv[])
{
void *so_handler;
void (*sub_fun)();
fprintf(stdout, "Hello World!\n");
so_handler = dlopen("libsubfun.so", RTLD_GLOBAL);
if (!so_handler) {
fprintf(stderr, "%s \n",dlerror());
return (-1);
}
sub_fun = dlsym(so_handler, "lib_func_test");
if (!sub_fun) {
fprintf(stderr, "%s \n",dlerror());
return (-2);
}
sub_fun();
dlclose(so_handler);
return (0);
}
#include <stdio.h>
void lib_func_test (void)
{
fprintf(stdout, "hello library lib_func_test() run!\n");
}
在 SylixOS Shell 下运行程序,运行结果如下:
# ./Load_Dynamic_Library
Hello World!
hello library lib_func_test() run!
其他 API
下面函数可获取比指定地址小的且离指定地址最近的符号信息。
#include <dlfcn.h>
int dladdr(void *pvAddr, Dl_info *pdlinfo);
函数 dladdr 原型分析:
- 此函数成功返回大于 0 的值,失败返回 0 并设置错误号。
- 参数 pvAddr 为符号地址。
- 输出参数 pdlinfo 返回符号信息,以下是它的成员信息。
调用 dladdr 函数将返回 pvAddr 地址的信息,该信息由 Dl_info 结构体类型返回,该结构体,如下所示:
typedef struct {
const char *dli_fname;
void *dli_fbase;
const char *dli_sname;
void *dli_saddr;
} Dl_info;
下面是 Dl_info 结构成员含义:
- dli_fname:表示模块文件路径。
- dli_fbase:表示模块的加载地址。
- dli_sname:表示符号名称。
- dli_saddr:表示符号地址。
dladdr 函数一般用于错误定位时的程序栈信息打印。
下面程序展示了如何利用 dladdr 函数进行栈回溯。
#include <dlfcn.h>
#include <execinfo.h>
#include <stdio.h>
#include <string.h>
#define BT_SIZE 100
void print_backtrace()
{
Dl_info info;
int nptrs;
int i;
int ret;
void *ptr_buffer[BT_SIZE];
nptrs = backtrace(ptr_buffer, BT_SIZE); /* 获取回溯栈信息 */
for (i = 1; i < nptrs; i++) { /* 去掉本函数帧打印 */
ret = dladdr(ptr_buffer[i], &info); /* 获取该帧符号信息 */
if (ret == 0) {
break;
}
fprintf(stdout, "module:%s, function:%s, address:%p\n",
info.dli_fname, info.dli_sname, info.dli_saddr);
if (strcmp(info.dli_sname, "main") == 0) {
break;
}
}
}
void func_test2 (void)
{
print_backtrace(); /* 打印栈信息 */
}
void func_test1 (void)
{
func_test2();
}
int main (int argc, char *argv[])
{
func_test1();
return (0);
}
在 SylixOS Shell 下运行程序,运行结果如下:
# ./Stack_Backtracking
module:/apps/pSuite/Chapter19_DynamicLoading/Stack_Backtracking, function:main, address:0xc00084c4