动态链接库的创建和使用

前面章节中,给读者详细阐述了什么是库文件、什么是静态链接库和动态链接库,同时还介绍了手动创建静态链接库的过程。在此基础上,本节继续讲解动态链接库的创建和使用。

有关动态链接库,以及它的特性、和静态链接库的区别,读者可阅读《GCC使用静态链接库和动态链接库》一节做详细了解,这里不再做赘述。

为了方便读者更好地理解创建和使用动态链接库的过程,本节仍以在《静态链接库的创建和使用》一节中创建好的 demo 项目(一个 C 语言多文件项目)为例,如下是该目录的具体构成和相关源码:

[root@bogon demo]# ls                       <- demo 目录结构
add.c  div.c  main.c  sub.c  test.h
[root@bogon demo]# cat test.h           <- test.h 文件内容
#ifndef __TEST_H_
#define __TEST_H_

int add(int a,int b);
int sub(int a,int b);
int div(int a,int b);

#endif
[root@bogon demo]# cat add.c           <- add.c 文件内容
#include “test.h”
int add(int a,int b)
{
    return a + b;
}
[root@bogon demo]# cat sub.c           <- sub.c 文件内容
#include “test.h”
int sub(int a,int b)
{
    return a - b;
}
[root@bogon demo]# cat div.c           <- div.c 文件内容       
#include “test.h”
int div(int a,int b)
{
    return a / b;
}
[root@bogon demo]# cat main.c        <- main.c 文件内容
#include <stdio.h>
#include "test.h"  //必须引入头文件
int main(void)
{
    int m, n;
    printf("Input two numbers: ");
    scanf("%d %d", &m, &n);
    printf("%d+%d=%d\n", m, n, add(m, n));
    printf("%d-%d=%d\n", m, n, sub(m, n));
    printf("%d÷%d=%d\n", m, n, div(m, n));
    return 0;
}
[root@bogon demo]#

动态链接库的创建

总的来说,动态链接库的创建方式有 2 种。

1) 直接使用源文件创建动态链接库,采用 gcc 命令实现的基本格式如下:

gcc -fpic -shared 源文件名... -o 动态链接库名

其中,-shared 选项用于生成动态链接库;-fpic(还可写成 -fPIC)选项的功能是,令 GCC 编译器生成动态链接库(多个目标文件的压缩包)时,表示各目标文件中函数、类等功能模块的地址使用相对地址,而非绝对地址。这样,无论将来链接库被加载到内存的什么位置,都可以正常使用。

例如,由 demo 项目中的 add.c、sub.c 和 div.c 这 3 个源文件生成一个动态链接库,执行命令为:

[root@bogon demo]# ls
add.c  div.c  main.c  sub.c  test.h
[root@bogon demo]# gcc -fpic -shared add.c sub.c div.c -o libmymath.so
[root@bogon demo]# ls
add.c  div.c  libmymath.so  main.c  sub.c  test.h

注意,动态链接库的命令规则和静态链接库完全相同,只不过在 Linux 发行版系统中,其后缀名用 .so 表示;Windows 系统中,后缀名为 .dll。

2) 先使用 gcc -c 指令将指定源文件编译为目标文件。仍以 demo 项目中的 add.c、sub.c 和 div.c 为例,先执行如下命令:

[root@bogon demo]# ls
add.c  div.c  main.c  sub.c  test.h
[root@bogon demo]# gcc -c -fpic add.c sub.c div.c
[root@bogon demo]# ls
add.c  add.o  div.c  div.o  main.c  sub.c  sub.o  test.h

注意,为了后续生成动态链接库并能正常使用,将源文件编译为目标文件时,也需要使用 -fpic 选项。

在此基础上,接下来利用上一步生成的目标文件,生成动态链接库:

[root@bogon demo]# gcc -shared add.o sub.o div.o -o libmymath.so
[root@bogon demo]# ls
add.c  add.o  div.c  div.o  libmymath.so  main.c  sub.c  sub.o  test.h


以上 2 种操作,生成的动态链接库是完全一样的,读者任选一种即可。

动态链接库的使用

通过前面章节的学习我们知道,动态链接库的使用场景就是和项目中其它源文件或目标文件一起参与链接。以 demo 项目为例,前面我们将 add.c、sub.c 和 div.c 打包到了 libmymath.so 动态链接库中,此时该项目中仅剩 main.c 源程序文件,因此执行 demo 项目也就演变成了将 main.c 和 libmymath.so 进行链接,进而生成可执行文件。

注意,test.h 头文件并不直接参与编译,因为在程序的预处理阶段,已经对项目中需要用到的头文件做了处理。

执行如下指令,即可借助动态链接库成功生成可执行文件:

[root@bogon demo]# gcc main.c  libmymath.so -o main.exe
[root@bogon demo]# ls
add.c  div.c  libmymath.so  main.c  main.exe  sub.c  test.h


注意,生成的 main.exe 通常无法直接执行,例如:

[root@bogon demo]# ./main.exe
./a.out: error while loading shared libraries: libd.so: cannot open shared object file: No such file or directory

可以看到,执行过程中无法找到 libmymath.so 动态链接库。通过执行ldd main.exe指令,可以查看当前文件在执行时需要用到的所有动态链接库,以及各个库文件的存储位置:

[root@bogon demo]# ldd main.exe
linux-vdso.so.1 =>  (0x00007fff423ff000)
libmymath.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00000037e2c00000)
/lib64/ld-linux-x86-64.so.2 (0x00000037e2800000)

可以看到,main.exe 文件的执行需要 4 个动态链接库的支持,其中就包括 libmymath.so,但该文件无法找到,因此 main.exe 执行会失败。

运行由动态链接库生成的可执行文件时,必须确保程序在运行时可以找到这个动态链接库。常用的解决方案有如下几种:
  • 将链接库文件移动到标准库目录下(例如 /usr/lib、/usr/lib64、/lib、/lib64);
  • 在终端输入export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx,其中 xxx 为动态链接库文件的绝对存储路径(此方式仅在当前终端有效,关闭终端后无效);
  • 修改~/.bashrc 或~/.bash_profile 文件,即在文件最后一行添加export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx(xxx 为动态库文件的绝对存储路径)。保存之后,执行source .bashrc指令(此方式仅对当前登陆用户有效)。

本操作系统(CentOS 6.5 64 位)中,只需要将 libmymath.so 库文件移动 /usr/lib64 或者 /lib64 目录下,即可使 main.exe 成功执行:

[root@bogon demo]# ldd main.exe
linux-vdso.so.1 =>  (0x00007fff06fb3000)
libmymath.so => /lib64/libmymath.so (0x00007f65b2a62000)
libc.so.6 => /lib64/libc.so.6 (0x00000037e2c00000)
/lib64/ld-linux-x86-64.so.2 (0x00000037e2800000)
[root@bogon demo]# ./main.exe
Input two numbers: 10 2
10+2=12
10-2=8
10÷2=5