动态链接库(DL)demo

学习dlopen,dlsym等动态链接库函数

  • dlsym函数原型
1
2
3
4
5
6
7
8
9
#include <dlfcn.h>

void *dlsym(void *restrict handle, const char *restrict symbol);

#define _GNU_SOURCE
#include <dlfcn.h>

void *dlvsym(void *restrict handle, const char *restrict symbol,
const char *restrict version);
  • dlopen函数原型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
NAME
dlclose, dlopen, dlmopen - open and close a shared object

LIBRARY
Dynamic linking library (libdl, -ldl)

SYNOPSIS
#include <dlfcn.h>

void *dlopen(const char *filename, int flags);
int dlclose(void *handle);

#define _GNU_SOURCE
#include <dlfcn.h>

void *dlmopen(Lmid_t lmid, const char *filename, int flags);

目录结构

1
2
3
4
5
6
dl_demo/
├── mylib.c # 共享库源文件
├── mylib.h # 共享库头文件
├── main.c # 主程序源文件
├── Makefile # 编译脚本
└── README.md # 说明文档

文件内容

共享库头文件 mylib.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef MYLIB_H
#define MYLIB_H

#ifdef __cplusplus
extern "C" {
#endif

// 声明库中的函数
int add(int a, int b);
int multiply(int a, int b);
void print_message(const char* message);

// 声明库中的变量
extern const char* library_name;
extern const int version_major;
extern const int version_minor;

#ifdef __cplusplus
}
#endif

#endif // MYLIB_H

共享库源文件 mylib.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 "mylib.h"

// 定义库中的变量
const char* library_name = "SimpleMathLib";
const int version_major = 1;
const int version_minor = 0;

// 实现库中的函数
int add(int a, int b) {
printf("[Library] Calling add(%d, %d)\n", a, b);
return a + b;
}

int multiply(int a, int b) {
printf("[Library] Calling multiply(%d, %d)\n", a, b);
return a * b;
}

void print_message(const char* message) {
printf("[Library] Message: %s\n", message);
}

主程序源文件 main.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
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h> // 动态加载库函数

// 定义函数指针类型,用于从动态库中获取的函数
typedef int (*add_func_t)(int, int);
typedef int (*multiply_func_t)(int, int);
typedef void (*print_func_t)(const char*);

int main() {
void* handle = NULL;
char* error = NULL;

printf("=== 动态链接库示例程序 ===\n\n");

// 1. 使用 dlopen 打开共享库
printf("1. 正在加载共享库 'libmymath.so'...\n");
handle = dlopen("./libmymath.so", RTLD_LAZY);

if (!handle) {
fprintf(stderr, "错误: 无法加载库: %s\n", dlerror());
return EXIT_FAILURE;
}
printf(" √ 库加载成功!\n\n");

// 2. 清除之前的错误状态
dlerror();

// 3. 使用 dlsym 获取库中的函数地址
printf("2. 正在从库中获取函数地址...\n");

// 获取 add 函数
// int (*my_add)(int, int) = (int (*)(int, int))dlsym(handle,"add");
add_func_t my_add = (add_func_t)dlsym(handle, "add");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "错误: 找不到 add 函数: %s\n", error);
dlclose(handle);
return EXIT_FAILURE;
}
printf(" √ 找到 add 函数\n");

// 获取 multiply 函数
multiply_func_t my_multiply = (multiply_func_t)dlsym(handle, "multiply");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "错误: 找不到 multiply 函数: %s\n", error);
dlclose(handle);
return EXIT_FAILURE;
}
printf(" √ 找到 multiply 函数\n");

// 获取 print_message 函数
print_func_t my_print = (print_func_t)dlsym(handle, "print_message");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "错误: 找不到 print_message 函数: %s\n", error);
dlclose(handle);
return EXIT_FAILURE;
}
printf(" √ 找到 print_message 函数\n");

// 4. 获取库中的变量
printf("\n3. 正在从库中获取变量...\n");

// 获取 library_name 变量
const char** lib_name_ptr = (const char**)dlsym(handle, "library_name");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "警告: 找不到 library_name 变量: %s\n", error);
} else {
printf(" √ 库名称: %s\n", *lib_name_ptr);
}

// 获取 version_major 变量
const int* ver_major_ptr = (const int*)dlsym(handle, "version_major");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "警告: 找不到 version_major 变量: %s\n", error);
} else {
printf(" √ 主版本号: %d\n", *ver_major_ptr);
}

// 获取 version_minor 变量
const int* ver_minor_ptr = (const int*)dlsym(handle, "version_minor");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "警告: 找不到 version_minor 变量: %s\n", error);
} else {
printf(" √ 次版本号: %d\n", *ver_minor_ptr);
}

// 5. 使用动态加载的函数
printf("\n4. 调用动态加载的函数...\n");

int result;

// 调用 add 函数
result = my_add(10, 5);
printf(" add(10, 5) = %d\n", result);

// 调用 multiply 函数
result = my_multiply(10, 5);
printf(" multiply(10, 5) = %d\n", result);

// 调用 print_message 函数
my_print("Hello from main program!");

// 6. 关闭库
printf("\n5. 清理资源...\n");
if (dlclose(handle) != 0) {
fprintf(stderr, "警告: 关闭库时出错: %s\n", dlerror());
} else {
printf(" √ 库已成功关闭\n");
}

// 7. 演示 RTLD_NOW 标志的使用
printf("\n6. 演示 RTLD_NOW 标志...\n");
handle = dlopen("./libmymath.so", RTLD_NOW);
if (!handle) {
fprintf(stderr, "错误: 无法用 RTLD_NOW 加载库: %s\n", dlerror());
} else {
printf(" √ 使用 RTLD_NOW 加载成功\n");
dlclose(handle);
}

// 8. 演示加载不存在的库
printf("\n7. 演示错误处理(加载不存在的库)...\n");
handle = dlopen("./nonexistent_lib.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, " × 预期错误: %s\n", dlerror());
} else {
printf(" √ 不应该执行到这里\n");
dlclose(handle);
}

printf("\n=== 程序执行完毕 ===\n");
return EXIT_SUCCESS;
}

Makefile 编译脚本

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
CC = gcc
CFLAGS = -Wall -Wextra -g -fPIC
LDFLAGS = -ldl
TARGET = demo
LIBRARY = libmymath.so
SOURCES = main.c
LIB_SOURCES = mylib.c
LIB_HEADERS = mylib.h
OBJECTS = $(SOURCES:.c=.o)
LIB_OBJECTS = $(LIB_SOURCES:.c=.o)

# 默认目标
all: $(LIBRARY) $(TARGET)

# 编译主程序
$(TARGET): $(OBJECTS)
$(CC) $(OBJECTS) -o $@ $(LDFLAGS)

# 编译共享库
$(LIBRARY): $(LIB_OBJECTS)
$(CC) -shared $(LIB_OBJECTS) -o $@

# 编译主程序的 .o 文件
main.o: main.c
$(CC) $(CFLAGS) -c $< -o $@

# 编译共享库的 .o 文件
mylib.o: mylib.c $(LIB_HEADERS)
$(CC) $(CFLAGS) -c $< -o $@

# 清理生成的文件
clean:
rm -f $(TARGET) $(LIBRARY) $(OBJECTS) $(LIB_OBJECTS) *.so

# 运行程序
run: all
LD_LIBRARY_PATH=. ./$(TARGET)

# 显示库中的符号
nm: $(LIBRARY)
@echo "=== 库中的符号 ==="
nm -D $(LIBRARY)

# 显示库的依赖
ldd: $(LIBRARY) $(TARGET)
@echo "=== 主程序依赖 ==="
ldd ./$(TARGET) || true
@echo "\n=== 共享库依赖 ==="
ldd ./$(LIBRARY) || true

.PHONY: all clean run nm ldd

输出结果

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
make run 
LD_LIBRARY_PATH=. ./demo
=== 动态链接库示例程序 ===

1. 正在加载共享库 'libmymath.so'...
√ 库加载成功!

2. 正在从库中获取函数地址...
√ 找到 add 函数
√ 找到 multiply 函数
√ 找到 print_message 函数

3. 正在从库中获取变量...
√ 库名称: SimpleMathLib
√ 主版本号: 1
√ 次版本号: 0

4. 调用动态加载的函数...
[Library] Calling add(10, 5)
add(10, 5) = 15
[Library] Calling multiply(10, 5)
multiply(10, 5) = 50
[Library] Message: Hello from main program!

5. 清理资源...
√ 库已成功关闭

6. 演示 RTLD_NOW 标志...
√ 使用 RTLD_NOW 加载成功

7. 演示错误处理(加载不存在的库)...
× 预期错误: ./nonexistent_lib.so: cannot open shared object file: No such file or directory

=== 程序执行完毕 ===