Linux 平台基于 C /C++ 的静态库和动态库的区别

我们在编写代码的时候经常用到已有的接口,他们是以库的形式提供给我们使用的,而常见形式有两种,一种常以。a 为后缀,为静态库;另一种以。so 为后缀,为动态库。那么这两种库有什么区别呢?

说明:本文主要说明 Linux 下的情况,Windows 不涉及。

1. 目标文件

在解释静态库和动态库之前,需要简单了解一下什么是目标文件。目标文件常常按照特定格式来组织,在 Linux 下,它是 ELF 格式(Executable Linkable Format,可执行可链接格式),而在 Windows 下是 PE(Portable Executable,可移植可执行)。

而通常目标文件有三种形式:

  • 可执行目标文件。即我们通常所认识的,可直接运行的二进制文件。
  • 可重定位目标文件。包含了二进制的代码和数据,可以与其他可重定位目标文件合并,并创建一个可执行目标文件。
  • 共享目标文件。它是一种在加载或者运行时进行链接的特殊可重定位目标文件。

我们来看一个简单实例:

//main.c
#include
#include
int main(int argc,char *argv[])
{
    printf("hello 编程珠玑
");
    int b = 2;
    double a = exp(b);
    printf("%lf
",a);
    return 0;
}

代码计算 e 的 2 次方并打印结果。由于代码中用到了 exp 函数,它位于数学库 libm.so 或者 libm.a 中,因此编译时需要加上 -lm。

生成可重定位目标文件 main.o:

$ gcc -c main.c   #生成可重定位目标文件 
$ readelf -h main.o  #查看 elf 文件头部信息
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
(省略其他内容)

通过上面的命令将 main.c 生成为可重定位目标文件。通过 readelf 命令也可以看出来:Type 为 REL (Relocatable file)。

观察共享目标文件 libm.so:

$ readelf -h /lib/x86_64-Linux-gnu/libm.so.6
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - GNU
  ABI Version:                       0
  Type:                              DYN (Shared object file)
(省略其他内容)

不同系统中 libm.so 的位置可能不一样,你可以通过 locate 命令来查找。locate 命令的用法可参考《Linux 中的文件查找技巧》。从结果可以看到,libm.so 是共享目标文件(Shared object file)。

查看可执行目标文件 main:

$ gcc -o main main.o -lm  #编译成最终的可执行文件 
$ readelf -h main         #查看 ELF 文件头
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
(省略其他内容)

这里必须要强调一点,如果使用到的函数没有在 libc 库中,那么你就需要指定要链接的库,本文中需要链接 libm.so 或 libm.a。可以看到,最终生成的 main 类型是 Executable file,即可执行目标文件。

2. 什么是静态库

前面所提到可重定位目标文件以一种特定的方式打包成一个单独的文件,并且在链接生成可执行文件时,从这个单独的文件中“拷贝”它自己需要的内容到最终的可执行文件中。这个单独的文件,称为静态库。Linux 中通常以。a(archive) 为后缀

还是拿前面的例子来说,我们使用静态链接构建我们的可执行文件:

$ gcc -c main.c
$ gcc -static -o main main.o -lm

在这个过程中,就会用到系统中的静态库 libm.a。这个过程做了什么呢?首先第一条命令会将 main.c 编译成可重定位目标文件 main.o,第二条命令的 static 参数,告诉链接器应该使用静态链接,-lm 参数表明链接 libm.a 这个库(类似的,如果要链接 libxxx.a, 使用 -lxxx 即可)。由于 main.c 中使用了 libm.a 中的 exp 函数,因此链接时,会将 libm.a 中需要的代码“拷贝”到最终的可执行文件 main 中。

特别注意,必须把 -lm 放在后面。放在最后时它是这样的一个解析过程:

  • 链接器从左往右扫描可重定位目标文件和静态库
  • 扫描 main.o 时,发现一个未解析的符号 exp,记住这个未解析的符号
  • 扫描 libm.a,找到了前面未解析的符号,因此提取相关代码
  • 最终没有任何未解析的符号,编译链接完成

那如果将 -lm 放在前面,又是怎样的情况呢?

  • 分享于 · 2019.07.29 15:02 · 阅读 · 2904

[版权声明] :本文系网友分享,仅以非商业性的交流和科研为目的,并不代表本网赞同其观点和对其真实性负责。如涉及作品内容、版权和其它问题,请在30日内与本网( friends@stuch.cn )联系!我们将协调给予处理。转载授权码:确权中,请原作者尽快与我们取得联系,阅读原文(请登录)..

0 条评论

请先 登录 后评论
猜猜我是谁
甜也 -研究生

8
提问
13
回答
6
文章