昨天我們討論到程式的編譯過程,今天就來將理論化為實際。透過手把手教學,我會使用 C 語言講解程式碼是如何一步步變成電腦能理解的語言。
首先,我們回顧這張圖:
不難發現,圖片中的流程:透過 gcc -E
進行預處理 -> gcc -S
進行編譯 -> gcc -c
進行組譯 -> ld
進行連結。
今天我們就是要重跑這個流程。 Start!
首先,開啟 terminal 將以下程式碼存到 helloworld.c 的程式
#include<stdio.h>
void main(){
printf("Hello, world!\n");
}
首先,介紹使用的編譯軟體:GCC
根據 Wiki
GNU編譯器套裝(英語:GNU Compiler Collection,縮寫為GCC),指一套程式語言編譯器,以GPL及LGPL授權條款所發行的自由軟體,也是GNU計劃的關鍵部分,也是GNU工具鏈的主要組成部份之一。
接著在 terminal 中,下指令:
gcc -E helloworld.c
得到的結果如下:
(節錄部分程式碼),可以看到系統把 #include<stdio.h> 展開了
...
...
...
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 858 "/usr/include/stdio.h" 3 4
extern int __uflow (FILE *);
extern int __overflow (FILE *, int);
# 873 "/usr/include/stdio.h" 3 4
# 2 "helloworld.c" 2
# 3 "helloworld.c"
void main(){
printf("Hello, world!\n");
}
在 terminal 中,下指令
gcc -S helloworld.c
得到的一個 helloworld.s
接著在 terminal 中,下指令,確認一下它的檔案形式
file helloworld.s
得到結果:
是一個組合語言檔
在 terminal 中,下指令,看看裡面長什麼樣子
cat helloworld.s
得到結果:組合語言檔的分段
:包括 .txt 、 .rodata
.file "helloworld.c"
.text
.section .rodata
.LC0:
.string "Hello, world!\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rdi
movl $0, %eax
call printf@PLT
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008"
.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:
在 terminal 中,下指令
gcc -c helloworld.s
得到的一個helloworld.o
接著在 terminal 中,下指令,確認一下它的檔案形式
file helloworld.o
是一個目的檔
在 terminal 中,下指令,看看裡面長什麼樣子
cat helloworld.o
得到結果:
首先我們先編譯,再使用 ldd 看跟該程式有關的函式庫
在 terminal 中,下指令
gcc -o helloworld helloworld.c
ldd helloworld
可以看到 Helloworld 用到以下這些函式庫
執行該程式,程式載入記憶體
./helloworld
這就是 Helloworld 的誕生,明天接著說 Helloworld 被呼叫的時候,會經過哪些奇幻旅程
!