LV04-05-文件操作-链接库的使用

本文主要是C语言——链接库基本操作的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
开发板 正点原子 i.MX6ULL Linux阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
STM32开发板 正点原子战舰V3(STM32F103ZET6)
点击查看本文参考资料
参考方向 参考原文
------
点击查看相关文件下载
--- ---

一、链接库

前面我们已经学习过链接库的概念,包扩静态库和动态库。这一部分主要是针对函数调用动态库。可以看前面《01嵌入式开发/01HQ课程体系/LV02-编译基础/LV03-01-链接库*》部分的多篇笔记学习。这部分其实和前面有重叠,主要是不想删掉了,这里说明一下。

二、 通过函数调用动态库

1. dlopen()

在 linux 下可以使用 man 3 dlopen 命令查看该函数的帮助手册。

1
2
3
4
5
6
7
/* 需包含的头文件 */
#include <dlfcn.h>

/* 函数声明 */
void *dlopen(const char *filename, int flags);

// Link with -ldl.

【函数说明】该函数用于打开指定名称的动态库,名称可以携带路径,然后返回一个句柄给调用进程。

【函数参数】

  • filename : char 类型指针变量,表示要打开的动态库的名称,可以包含路径,如果文件名包含斜杠(“/”),则它将被解释为(相对或绝对)路径名。如果filename指定的对象依赖于其他共享对象,那么动态链接器也会使用相同的规则自动加载这些共享对象。(如果这些对象有依赖关系,这个过程可能会递归发生。)
  • flags :int 类型,表示打开动态库的模式。
点击查看 flags 取值详情
  • 必须要有以下两个值之一
1
2
RTLD_LAZY : 执行惰性绑定。只在执行引用符号的代码时解析它们。如果符号从未被引用,那么它就永远不会被解析。(也就是说在 dlopen 返回前,对于动态库中存在的未定义的变量 (如外部变量 extern,也可以是函数) 不执行解析,就是不解析这个变量的地址。)
RTLD_NOW : 如果指定了该值,或者将环境变量LD_BIND_NOW设置为非空字符串,则在dlopen()返回之前解析共享对象中的所有未定义符号。如果不能这样做,则返回一个错误。换句话说就是需要在 dlopen 返回前,解析出每个未定义变量的地址,如果解析不出来,在 dlopen 会返回 NULL.

还有一些可选的值,目前还没用过,后边用到了再补充。

【返回值】void *类型,成功时,dlopen()为加载的库返回一个非NULL句柄。如果出现错误(找不到文件、不可读、格式错误或在加载过程中产生错误),将返回NULL。

【使用格式】none

【注意事项】 none

3.2 dlclose()

在 linux 下可以使用 man 3 dlclose 命令查看该函数的帮助手册。

1
2
3
4
5
6
7
/* 需包含的头文件 */
#include <dlfcn.h>

/* 函数声明 */
int dlclose(void *handle);

// Link with -ldl.

【函数说明】该函数用于关闭打开的动态库。

【函数参数】

  • handle : void 类型指针变量,表示已经打开的动态库的句柄。

【返回值】int 类型,成功返回0,失败返回非0值。

【使用格式】none

【注意事项】 none

2. dlsym()

在 linux 下可以使用 man 3 dlsym命令查看该函数的帮助手册。

1
2
3
4
5
6
7
/* 需包含的头文件 */
#include <dlfcn.h>

/* 函数声明 */
void *dlsym(void *handle, const char *symbol);

// Link with -ldl.

【函数说明】该函数用于根据动态链接库操作句柄 (handle) 与符号 (symbol),返回符号对应的地址。

【函数参数】

  • handle : void 类型指针变量,表示已经打开的动态库的句柄。
  • symbol :char 类型指针变量,表示要查找地址的符号的名称。

【返回值】void *类型,成功返回与符号关联的地址,失败返回NULL。错误的原因可以使用dlerror(3)进行诊断。

【使用格式】none

【注意事项】 none

3. 使用实例

还是使用之前创建的动态链接库 libfile.so ,这里要先清除之前设置的环境变量LD_LIBRARY_PATH中关于该动态库的位置的相关语句,这样我们才能更清楚的看到使用dlopen会为我们带来哪些好处。我们修改主程序 test.c 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <dlfcn.h>

int main(int argc, char *argv[])
{
int a = 2;
int b = 3;
int sum = 0;
void *handler = dlopen("/home/hk/6temp/test/libfile.so", RTLD_LAZY);
int (*pFunc)(int, int) = dlsym(handler, "mySum");
int *pVar = dlsym(handler, "global");

sum = pFunc(a, b);
printf("sum = %d\n",sum);
printf("This is test.c test:global=%d\n", *pVar);
*pVar = 30;

void (*mytest)(void) = dlsym(handler, "myTest");
mytest();
dlclose(handler);
return 0;
}

然后我们编译程序:

1
gcc test.c -o test -ldl

这时候我们发现,我们并没有告诉链接器动态库的位置和名称,但是依然可以编译通过,我们执行程序,会得到以下输出:

1
2
3
sum = 5
This is test.c test:global=10
This is file.c test:global=30

这说明,我们的程序正常执行了。