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篇

posted @ 2021-01-13 22:29  cynault  阅读(540)  评论(0编辑  收藏  举报