0x01
首先,Linux下,一個C語言的hello world程序如下:
#include <stdio.h>
int main(void)
{
printf("Hello world!\n");
return 0;
}
然后使用gcc命令將這段代碼變成一個可執行程序。
$ gcc -g -Wall 0_1_hello_world.c -o hello_world
0x02
先講gcc的過程。
其中過程涉及到:預處理、編譯、匯編和鏈接四個步驟。
只是gcc自動完成了這一系列的步驟。
預處理
預處理是用來處理預處理命令的。
例如,c語言中的#include就是預處理命令,它的作用是把頭文件的內容包含到本文件中。(大一就學了好嗎?)
$ gcc -E 0_1_hello_wolrd.c
$ gcc -E 0_1_hello_world.c > 0_1_hello_world.i
執行上面第一條命令,可以在預處理后自動停止后面的操作,并將預處理的結果輸出到標準輸出。
執行上面第二條命令,可以得到預處理后的文件,很明顯能看出,預處理后的文件是一個后綴名為i的文件。
這個時候,我才明白,當時學習c語言的時候為什么說千萬不能在頭文件中定義全局變量。
答案是:
因為定義全局變量的代碼會存在于所有以#include包含該頭文件的文件中,也就是說,所有的這些文件,都會定義一個同樣的全局變量,這樣不可避免的就會引起沖突。
編譯
編譯,就是對源代碼進行語法分析,并優化產生對應的匯編代碼的過程。
簡單的說,編譯做的事情就是:
**源碼---->匯編代碼 **
同樣,gcc也可以讓你看到編譯后的匯編代碼。
$ gcc -S 0_1_hello_world.c -o hello_world.s
-S這個參數是啥意思呢?
就是讓gcc在編譯完成后停止后面的動作。
可以看到,源碼編譯完成后的匯編代碼,是一個后綴名為s的文件。
匯編
匯編,就是把源碼變成可執行的指令,并生成目標文件。
粗俗一點講,就是把一堆你認識的代碼變成計算機認識的指令。
$ gcc -c 0_1_hello_world.c -o 0_1_hello_wolrd.o
此指令作用在此就不贅述了。
鏈接
這一步驟,大概大學是教過的吧,但的確是完全還給老師,不記得了。
鏈接,就是把各個目標文件(包括庫文件)鏈接成為一個可執行的程序。這里面涉及的東西就太多了,什么地址和空間的分配啥的....
在Linux里面,這個是由GNU的鏈接器ld完成的。
**整個gcc的過程,可以用gcc的-v參數查看完成和詳細的編譯過程
$ gcc -g -Wall -v 0_1_hello_world.c -o hello_world
0x03
一個可執行的文件,里面是什么樣的呢?
Linux下面的二進制可執行文件的格式一般都是ELF。
可以使用readelf命令來查看ELF格式。
然后你會看到一大堆不是很懂的東西....
包括ELF Header、Section Headers、Key to Flags等...
0x04
一個程序 到底是怎么run起來的?
在Linux下,可以用strace這個命令來跟蹤系統的調用,從而明白這個程序是怎么運行的,調用了一些什么。
$ strace ./hello_world
strace命令顯示的是hello_world這個程序開始執行后的所有輸出。
0x05
此文是之前瞄了一眼同事的Linux內核相關書籍后總結整理的,僅為記錄,知識相對較深。對于除Linux內核開發人員之外的人,大概都不怎么會用到。
算是自己記錄一下冷知識吧。