ubuntu20.04--C源码到可执行文件编译过程
GCC的编译过程包括四个阶段:预处理(preprocess)、编译(compile)、汇编(assemble)、链接(link);如下是hello.c的源码。
#include <stdio.h> #define a 2 int main(){ int b=a; printf("hello wo%dld.\n",b); return 0; }
1.1.1预处理
预处理阶段,编译器着重处理带 # 符合的语句,如:
eg1: #define a b 将所有源码语句a替换为b
eg2: #include 把stdio.h文件模块内容插入到#include所在的位置 预处理详解
$gcc -E hello.c -o hello.i
-E:单独进行预处理 -o:指定输出文件名,如下hello.i文件:
# 1 "hello.c" # 1 "<built-in>" 略略略略略略略略略略略略略略略略略略略略略略略略 略略略略略略略略略略略略略略略略略略略略略略略略 extern int __overflow (FILE *, int); # 873 "/usr/include/stdio.h" 3 4 # 2 "hello.c" 2 # 2 "hello.c" int main(){ int b=2; printf("Hello Wo%dld.",b); return 0; }
略部分是stdio.h文件
1.1.2编译阶段
编译阶段涉及编译原理,将c语言转换为汇编语言
gcc -S hello.i -o hello.s -masm=intel -fno-asynchronous-unwind-tables
-S:进行编译 ; -masm=intel:汇编代码风格指定为intel; -fno-asynchronous-unwind-tables:消除cfi宏指令。hello.s如下:
.file "hello.c" .intel_syntax noprefix .text .section .rodata .LC0: .string "Hello Wo%dld." .text .globl main .type main, @function main: endbr64 push rbp mov rbp, rsp sub rsp, 16 mov DWORD PTR -4[rbp], 2 mov eax, DWORD PTR -4[rbp] mov esi, eax lea rdi, .LC0[rip] mov eax, 0 call printf@PLT mov eax, 0 leave ret .size main, .-main .ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0" .section .note.GNU-stack,"",@progbits .section .note.gnu.property,"a" .align 8 .long 1f - 0f .long 4f - 1f .long 5 0: .string "GNU" 1: .align 8 .long 0xc0000002 .long 3f - 2f 2: .long 0x3 3: .align 8 4:
1.1.3汇编阶段
汇编阶段将汇编语言转换为机器语言,这个阶段生成的文件为可重定位文件(relocatable file)是ELF文件的一种。此时变量的地址为虚地址,只有偏移量,所以还不能直接执行。
$gcc -c hello.s -o hello.o
-c:进行汇编。 hello.o是ELF文件,是机器能理解的语言,需要用objdump(反汇编)命令来查看文件
$ objdump -sd hello.o -M intel
-s:显示指定指令内容都会被显示,默认非空。 -d:显示特定指令机器码(main)。-M intel :指定intel格式如下:
hello.o: file format elf64-x86-64 Contents of section .text: 0000 f30f1efa 554889e5 4883ec10 c745fc02 ....UH..H....E.. 0010 0000008b 45fc89c6 488d3d00 000000b8 ....E...H.=..... 0020 00000000 e8000000 00b80000 0000c9c3 ................ Contents of section .rodata: 0000 48656c6c 6f20576f 25646c64 2e00 Hello Wo%dld.. Contents of section .comment: 0000 00474343 3a202855 62756e74 7520392e .GCC: (Ubuntu 9. 0010 332e302d 31377562 756e7475 317e3230 3.0-17ubuntu1~20 0020 2e303429 20392e33 2e3000 .04) 9.3.0. Contents of section .note.gnu.property: 0000 04000000 10000000 05000000 474e5500 ............GNU. 0010 020000c0 04000000 03000000 00000000 ................ Disassembly of section .text: 0000000000000000 <main>: 0: f3 0f 1e fa endbr64 4: 55 push rbp 5: 48 89 e5 mov rbp,rsp 8: 48 83 ec 10 sub rsp,0x10 c: c7 45 fc 02 00 00 00 mov DWORD PTR [rbp-0x4],0x2 13: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 16: 89 c6 mov esi,eax 18: 48 8d 3d 00 00 00 00 lea rdi,[rip+0x0] # 1f <main+0x1f> 1f: b8 00 00 00 00 mov eax,0x0 24: e8 00 00 00 00 call 29 <main+0x29> 29: b8 00 00 00 00 mov eax,0x0 2e: c9 leave 2f: c3 ret
1.1.4链接阶段
链接阶段是把可重定位文件转换为可执行文件。又分为静态链接和动态链接。
静态链接:将所有需要用到的库函数及主函数链接在一起,以及分配其地址空间。
动态链接:不将库函数和主函数直接链接,而是等到程序运行时,才真正的进行链接,优点:节省空间、利于维护
$ gcc hello.o -o hello -static
-static:静态链接,默认动态链接。 反汇编查看内容:
$ objdump -sd hello -M intel
由于库函数内容过多,不予展示。
参考文献
CTF竞赛权威指南Pwn篇