一段代碼的前世今生

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內核開發人員之外的人,大概都不怎么會用到。
算是自己記錄一下冷知識吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容