Linux下C程序的编译和链接

这篇博文是在读了《CSAPP》中的链接部分后做的总结和摘录。

linux编译过程

命令:gcc -o prog main.c
过程:
main.c —>预处理器(cpp)—>main.i —>编译器(ccl) —>main.s —>汇编器(as)—>main.o —>链接器(ld)—>prog
main.i — ASCII码中间文件
main.s — ASCII汇编语言文件
main.o — 可重定位目标文件
prog — 可执行目标文件

在shell中输入可执行文件名就可以运行 : linux> ./prog
Shell调用操作系统中一个叫加载器的函数,它将可执行文件中的代码和数据复制到内存,然后将控制转移到这个程序的开头。

静态链接

像linux LD程序这样的静态链接器,它以一组可重定位目标文件和命令行参数作为输入,生成一个可加载和运行的可执行目标文件。输入的可重定位目标文件有各种不同的代码和数据节组成,每一节都是一个连续的字节序列。
链接器完成如下两个任务:
1.符号解析:目标文件定义和引用符号,每个符号对应一个函数或变量,符号解析的目的是将每个符号引用和其符号定义关联起来。
2.重定位:编译器和汇编器生成的代码和数据节是从地址0开始的,链接器将每个符号定义与一个内存地址关联起来,从而重定位这些节,并且修改所有对这些符号的引用,使它们指向这个内存位置。

链接器如何解析多重定义的全局符号?

由于在每个文件中都可以定义全局变量,这些变量可能会有重名的情况发生,而在将这些文件链接起来之前系统是不知道的。所以需要链接器来解析多重定义的全局符号。
把函数和已经初始化的全局变量定义为强符号,未初始化的全局变量是弱符号。
linux链接器使用如下规则来处理多重定义的符号名:
1.不允许有多个同名的强符号
2.如果有一个强符号与多个弱符号同名,那么选择强符号
3.如果有多个同名的弱符号,从中任选一个

静态库

定义:所有编译器都提供一种机制,将所有相关的目标模块打包成为一个单独的文件,称为静态库,它可以用作链接器的输入。当链接器构造一个输出的可执行文件时,他只复制被应用程序引用的目标模块。

比如我们常用的库函数atoi,printf,scanf等都在libc.a库中,我们可以这样来使用静态库:
gcc mian.c /usr/lib/libc.a
不过事实上我们不必显式地包含库文件,因为对于像libc.a这样的linux默认的库来说,编译器会自动为我们完成上面的操作。

linux系统中,静态库以一种称为存档(.a文件)的特殊文件格式存放在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合,有一个头文件用来描述每个成员目标文件的大小和位置。

加载可执行目标文件

可执行文件在运行前需要通过加载器加载到内存中:在运行程序前shell进程会生成一个子进程,子进程会调用execve系统调用来启动加载器(加载器是驻留在存储器中的一段操作系统代码)。加载器会删除子进程现有的虚拟内存段,然后将可执行文件中的代码和数据段映射到子进程的地址空间中。之后加载器会跳转到_start函数的地址,也就是程序的入口点,然后由这个函数来调用用户层的main函数。_start函数是在系统目标文件ctrl.o中定义的,每个C程序都由它来启动。

静态库的缺陷

1.静态库需要定期维护和更新:假如一个静态库有了新的更新,如果想要使用这个库的最新版本,需要显式地将程序和更新后的库重新链接。
2.占用过多的内存:在使用静态库时,我们调用的库函数的代码会被复制到进程的代码段中。对于那些几乎每个程序都会用到的库函数(如printf,scanf),假如系统中运行着上百个使用了这些函数的进程,那么内存中就会有上百个库函数代码的副本,这会是对系统内存的极大浪费。

共享库和动态链接

共享库用来解决静态库的缺陷,共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并和一个在内存中的程序链接起来。这个过程称作动态链接,由一个叫做动态链接器的程序来执行。共享库在linux中用.so文件表示,在Windows中则叫做DLL。

共享库通过两种不同的方式来共享:
1.在给定的文件系统中,每个库只有一个.so文件。所有引用该库的可执行文件共享这个.so文件中的代码和数据,而不像静态库那样复制和嵌入到可执行目标文件中。
2.其次,在内存中,一个共享库的.text节的副本可以被不同的正在运行的进程共享。
下图是一个动态链接的过程图

由上图可知动态链接发生在应用程序被加载后执行前这段时间。然而,应用程序还可以在运行时要求动态链接器加载和链接某个共享库。Linux提供了一个dlopen接口来实现该功能。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值