一、从源代码文件到可执行文件
从C文件到可执行文件,一般来说需要两步,先将每个C文件编译成.o文件,再把多个.o文件和链接库一起链接成可执行文件。但具体来说,其实是分为四步,下面以example.c为例进行说明。
#define MYINT int short addend1 = 1; static int addend2 = 2; const static long addend3 = 3; static MYINT g(MYINT x) { return x + addend1; } static const MYINT f(MYINT x) { return g(x + addend2); } MYINT main(void) { return f(8) + addend3; }
第一步: 预处理,进行宏替换等工作。执行gcc -E -o example.cpp example.c,得到example.cpp如下:
# 1 "example.c" # 1 "<built-in>" # 1 "<命令行>" # 1 "example.c" short addend1 = 1; static int addend2 = 2; const static long addend3 = 3; static int g(int x) { return x + addend1; } static const int f(int x) { return g(x + addend2); } int main(void) { return f(8) + addend3; }
第二步:将预处理文件编译成汇编文件。执行 gcc -x cpp-output -S -fno-asynchronous-unwind-tables -o example.s example.cpp,加入 -fno-asynchronous-unwind-tables是为了禁止生成.cfi代码。生成的汇编代码如下:
.file "example.c" ; C文件的文件名 .globl addend1 ; 全局变量 .data ; 数据段 ; short addend1 = 1;开始 .align 2 ; 地址对齐,按2的整数倍对齐 .type addend1, @object ; 类型是对象 .size addend1, 2 ; 占两个字节 addend1: ; 起始地址 .value 1 ; 初始值 ; static int addend2 = 2;开始 .align 4 .type addend2, @object .size addend2, 4 addend2: .long 2 .section .rodata ; 常量存储区开始 .align 4 .type addend3, @object .size addend3, 4 addend3: .long 3 .text ; 代码段开始 .type g, @function ; 函数g g: ; g的起始地址 pushl %ebp ; %ebp入栈 movl %esp, %ebp ; 当前函数栈从%esp开始 movzwl addend1, %eax ; 把short放入%eax cwtl addl 8(%ebp), %eax ; int + short popl %ebp ret .size g, .-g .type f, @function f: pushl %ebp movl %esp, %ebp subl $4, %esp ;