【链接、装载与库】程序的编译过程

前言

程序的编译过程包含4步:预编译、编译、汇编、链接。本文通过一个实例对这一过程进行详细讲解。
在这里插入图片描述

预编译

预编译用于对源代码文件进行初步的处理,主要处理规则如下:

  • 将所有的”#define“删除,并且展开所有的宏定义。
  • 处理所有条件编译指令,如“#if”、“#ifdef”、“#ifndef”、“#else”、“#elif”、“#endif”。
  • 处理“#include”预编译指令,将被包含的文件插入到预编译指令的位置。
  • 删除所有的注释“//”、“/* */”。
  • 添加行号和文件名标识,如# 4 “hello.c”,这样编译器产生编译错误或警告时可以显示行号。

实例:
创建一个hello.c,文件内容如下:

#include <stdio.h>
#define MARK 1
//这是一段示例代码
void output()
{
#if MARK
	printf("MARK is 1\n");
#else
	printf("MARK is 0\n");
#endif
}
/*
作者:inputA
*/
int main()
{
	output();
	return 0;
}

输入如下指令对hello.c进行预编译:
gcc -E hello.c -o hello.i
得到hello.i文件
打开hello.i发现文件内容非常多,在文件最后有如下一段代码:
在这里插入图片描述
开头的# 4 "hello.c"表示接下来的代码段源自文件hello.c,并且是从该文件的第4行开始的。
输出的.i文件中删除了注释、删除了MARK宏定义,并且根据MARK的值为1删除掉了printf("MARK is 0\n");这段代码。
删除内容后的空白行依然被保留,这是为了在编译时出现编译错误或警告方便显示行号。
搜索printf可以在hello.i文件中找到如下这段代码:
在这里插入图片描述
在它上面最相邻的行号和文件名标识为:
在这里插入图片描述
表示printf是在/usr/include/stdio.h中声明的一个外部函数。

编译

编译用于将预编译之后代码转换成汇编代码。
在刚在的实例中输入如下指令:
gcc -S hello.i -o hello.s
即可生成hello.i对应的汇编文件hello.s。
打开hello.s之后可以发现是一堆汇编代码。

汇编

汇编用于将汇编代码转换成机器码。
在刚在的实例中输入如下指令:
gcc -c hello.s -o hello.o
即可生成hello.s对应的机器码文件hello.o
输入如下指令:
objdump -S hello.o
即可查看hello.o的反汇编代码,内容如下:
在这里插入图片描述

链接

链接用于生成最后的可执行文件。
在刚才的实例中输入如下指令:
gcc -o hello hello.o
即可生成最后的可执行文件hello
输入./hello即可执行hello文件,执行结果如下:
在这里插入图片描述
输入如下指令对hello可执行文件进行反汇编:
objdump -S hello
可以看到output函数对应的反汇编代码如下:
在这里插入图片描述
其中对应的调用printf函数从原来的
b: e8 00 00 00 00 callq 10 <output+0x10>
变成了
645: e8 c6 fe ff ff callq 510 <puts@plt>
可见在链接过程中调用函数的跳转地址发生了变化,查找0x510处的代码如下:
在这里插入图片描述
这段代码与动态链接有关,整段代码可以分成两部分。
510: ff 25 ba 0a 20 00 jmpq *0x200aba(%rip) # 200fd0 <puts@GLIBC_2.2.5>
表示从0x200fd0这个地址取出printf的地址,并将其赋值给PC指针,然后跳转过去。
如果发现0x200fd0这个地址里的内容为空,就会执行如下代码:
516: 68 00 00 00 00 pushq $0x0
51b: e9 e0 ff ff ff jmpq 500 <.plt>
这段代码用于跳转到动态链接器将printf函数的地址填写到0x200fd0这个位置处。
所以第二段代码只在程序第一次运行时会执行,在0x200fd0地址内容不为空后将不会再执行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值