程序预编译、编译、汇编和链接

        C语言的经典“Hello World”程序几乎是每个程序员闭着眼睛都能写出l来的,编译运行通过一气呵成,基本成了此程序入门和开发环境测试的默认的标准。今天,我们一起来看看这个过程中那些被隐藏了的部分。

        事实上,要运行一个程序,首先它必须得是一个可执行文件,而从一个程序的源代码(.c/.cpp等文件)到可执行文件(.exe)的过程可以分为4个阶段:预编译、编译、汇编和链接

1.预编译(.c/.cpp-->.i)

        预编译的过程主要处理那些源代码文件中的以“#”开始的预编译指令,主要的处理规则如下:

(1)删除“#define”。将所有的“#define”删除,并且展开所有的宏定义。

(2)处理“#include”。处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置。这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。

(3)处理所有条件欲编译指令。比如“#if”、“#ifdef”、“#elif”、"#else"、“#endif”

(4)删除注释。删除所有的注释"//”和"/* */"。

(5)添加行号和文件标识。比如:#2"hello.c"2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号。

(6)保留"#pragma"。保留所有的编译器指令,留给编译器处理。

2.编译(.i-->.S)

        编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件,这个过程往往是我们所说的整个程序构建的核心部分。

(1)词法分析

(2)语法分析

(3)语义分析

(4)代码优化

3.汇编(.S-->.O)

        汇编是将汇编代码转变成机器可以执行的指令,即将指令代码翻译成二进制,每一个汇编语句几乎都对应一条机器指令。所以汇编器的汇编过程相对于编译器来讲比较简单,它没有复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译就可以了。通过汇编阶段可以生成可重定位的二进制文件(.o)也叫做目标文件。

4.链接(.O-->.exe)

        人们把每个源代码模块独立的进行编译,然后按照需要将它们组装起来,这个组装的过程就是链接(Linking)。链接器的主要内容就是把各个模块之间相互引用的部分都处理好,使得各个模块之间能够正确的衔接,链接器就是把一些指令对其他符号地址的引用加以修正。

        链接分为静态链接和动态链接:静态链接是指在编译阶段直接把静态库加入到可执行文件中去,这样可执行文件会比较大。动态链接则是指链接阶段仅仅只加入一些描述信息,而程序执行时再从系统中把相应动态库加载到内存中去。动态链接主要是针对于共享对象的,动态链接器将程序所需要的所有共享库装载到进程的地址空间,并且将程序汇总所有为决议的符号绑定到相应的动态链接库(共享库)中,并进行重定位工作。

 为什么会有动态链接?第一,考虑内存和磁盘空间。静态链接极大地浪费内存空间。因为在静态链接的情况下,假设有两个程序共享一个模块,那么在静态链接后输出的两个可执行文件中各有一个共享模块的副本。如果同时运行这两个可执行文件,那么这个共享模块将在磁盘和内存中都有两个副本,对磁盘和内存造成极大地浪费;第二,程序的更新。一旦程序中的一个模块被修改,那么整个程序都要重新链接、发布给用户。如果这个程序相当的大,那么后果就会更加严重!

链接过程主要包括了以下几个部分:

(1)合并段和符号表

注意合并符号时,同名查找,未找到则用本身查找的弱符号,找到则删除弱符号改用强符号。由于数据段不可能出现两个同名的符号,所以当出现两个同名的符号时,要遵循强弱符号规则

即:两强(两个同名符号都是强符号):重定义错误

       一强一弱:选强做所有的地址,不用弱

       两弱:选字节长度较大的一个,若两个字节长度相同,看编译器如何处理

何为强弱符号?强弱符号只在.c文件中存在,.cpp文件中没有

        强符号:已初始化的全局变量

        弱符号:未初始化的全居变量

(2)符号解析:处理*UND*中的符号,在符号引用的地方找符号定义的地方。注意:链接器只关注全局符号

(3)分配地址和空间:注意此时,建立了程序与虚拟地址空间的映射,而在运行过程中创建映射结构体,建立了虚拟地址空间与物理地址空间的映射关系。

(4)符号重定位://test段  (假地址改真实,纠正虚假偏移)

        其实上述预编译、编译、汇编3个阶段归结在一起就是我们常说的"编译"。在预编译和编译阶段,生成的文件是在磁盘上存储着,直到汇编阶段完成之后,将生成的可重定位的二进制文件(目标文件)存储在4G的虚拟地址空间里,在虚拟地址空间里进行链接阶段的符号解析、分配地址和空间、符号重定位等工作,最后生成可执行文件(.exe)。当要运行此文件时,才将其以分页或分段的方式映射到内存上。

        下面是4G的虚拟地址空间具体的介绍:

扩展:

在Linux下,预编译、编译、汇编、链接的指令代码,以一个例子,现该文件夹中只存在一个mian.c文件

预编译:gcc -E main.c -o main.i(一次只能预编译一个.c文件)

ls后:main.c main.i

编译:gcc -S main.i

ls后:main.c mian.i main.s

汇编:gcc -c main.s

ls后:main.c main.i main.s mian.o

链接:法一:gcc main.o       (默认生成名为main.out的可执行文件)

ls后:main.c main.i main.s mian.o main.out(可执行文件) 

           法二:gcc main.o -o name   (可指定生成可执行文件的名称为name)

ls后:main.c main.i main.s mian.o name

以上过程可一步完成:

gcc main.c (a.c b.c) -o main (main为可执行文件名称)

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值