平常我们很少关注编译和链结的过程,因为开发环境都集成开发的环境,比如Visual Studio、Eclipse,这样的IDE一般都将编译和链结的过程一步完成,因此我们必须深入了解这些被隐藏的过程。
#include <stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}
给定一个普通的输出 Hello World 的程序
通常我们可以使用GCC 进行编译
gcc -o hello hello.c 会输出ELF 格式的执行档
我们可以使用file hello 命令用於辨识文件类型
$ ./hello 即可执行
事实上,上述的过程可分解为四个步骤,分别是预处理、编译、组译和链结
C预处理器参照标头档stdio.h的内容,展开macro和验证prototype,并输出成一个.i文件。输出的结果就不会再见到 "#"开头字样,预编译的过程的命令用 -E 表示:
$ gcc -E hello.c -o hello.i
预编译完成後,替换完标头档和macro之後的样子如下
extern int printf (const char *__restrict __format, ...);
........
int main(void)
{
printf("Hello, world!\n");
return 0;
}
上面程序码撷取了标头档文件中相关的部分,省略了stdio.h的其他部分,在这一步骤注释也会被移除。
※不同的原码文件,可能会引用一个标头档(比如stdio.h),编译的时候,标头档也必须一起编译,而编译器会先编译标头档,这是为了确保标头档只需编译一次,不必每次用到的时候都重新编译。
编译过程就是把预处理完的文件进行一系列字汇分析、语法分析、语意分析、最佳化後生成对映的组合语言(hello.s),编译过程的命令如下:
$gcc -S hello.i -o hello.s
组译就是一个将组合语言转换成机器可以执行的指令,组译过程我们可以使用组译器 as 来完成:
as hello.s -o hello.o 或 gcc -c hello.s -o hello.o
会发现其实出来的 hello.o 并无法执行,因为缺少链结的过程。
在编译阶段并不知道printf的位址,所以暂时会以printf符号名称代替
main
LDR R1, [R2 + l2]
BAL printf
而在组译器输出的对应的组合语言,仍然不知道printf的位址,因此暂时不填入
main:
EC 00 00 12
F0 ?? ?? ??
printf 实作於libc.a(C语言标准含市库的静态版本),
其地址为0x1000 Linker重新配置(relocate)
0x2000 <main>:
EC 00 00 12
F0 00 10 00
链结通常是一个比较费解的过程,有静态链结、和动态链结,下次会更详细的分析此过程
From Source to Binary: How A Compiler Works: GNU Toolchain
程序设计师的自我修养
我们能够做完一个网页後,接下来我们可以让这个网页有更多的动态、趣味性,今天要介绍的动画效果trans...
前言 如果你去过天珑书局,应该会被比一般书店多了不知道几倍的电脑书吓到,幸好这个产业有很多被称为圣经...
递回 在函式里面可以呼叫函式本身 下面例子中,fun() 会先将参数 x 显示到视窗上,之後判断 x...
1.header的用法 (1)画画时 指定画面Content-type网页输出,用image/jpe...
ShiojiLogin.py: 加了contracts_timeout=10000的参数,在登入时...