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
  • 阅读 ( 233 )

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

0 条评论

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

9
提问
7
回答
4
文章