Linux基礎系列-可執行程式的產生過程

來源:互聯網
上載者:User

Linux通常使用gcc,利用gcc編譯器如何產生可執行程式在這裡將有一個清楚介紹,以備參考:

 

一、GCC快速入門

Gcc指令的一般格式為:Gcc [選項] 要編譯的檔案 [選項] [目標檔案]

其中,目標檔案可預設,Gcc預設產生可執行檔檔案名稱為:編譯檔案.out

我們來看一下經典入門程式"Hello World!"

# vi hello.c

#include

#include

void main(void)

{

printf("hello world!/r/n");

}

用gcc編譯成執行程式。

#gcc hello.c

該命令將hello.c直接產生最終二進位可執行程式a.out

這條命令隱含執行了(1)預先處理、(2)彙編、(3)編譯並(4)連結形成最終的二進位可執行程式。這裡未指定輸出檔案,預設輸出為a.out。

如何要指定最終二進位可執行程式名,那麼用-o選項來指定名稱。比如需要產生執行程式hello.exe

那麼

#gcc hello.c -o hello.exe

二、GCC的命令剖析--四步走

從上面我們知道GCC編譯原始碼產生最終可執行檔二進位程式,GCC後台隱含執行了四個階段步驟。

GCC編譯C源碼有四個步驟:

預先處理-----> 編譯 ----> 彙編 ----> 連結

現在我們就用GCC的命令選項來逐個剖析GCC過程。

1)預先處理(Pre-processing)

在該階段,編譯器將C原始碼中的包含的標頭檔如stdio.h編譯進來,使用者可以使用gcc的選項”-E”進行查看。

用法:#gcc -E hello.c -o hello.i

作用:將hello.c預先處理輸出hello.i檔案。

[root]# gcc -E hello.c -o hello.i

[root]# ls

hello.c hello.i

[root]# vi hello.i

# 1 "hello.c"

# 1 ""

# 1 ""

# 1 "hello.c"

# 1 "/usr/include/stdlib.h" 1 3

# 25 "/usr/include/stdlib.h" 3

# 1 "/usr/include/features.h" 1 3

# 291 "/usr/include/features.h" 3

# 1 "/usr/include/sys/cdefs.h" 1 3

# 292 "/usr/include/features.h" 2 3

# 314 "/usr/include/features.h" 3

# 1 "/usr/include/gnu/stubs.h" 1 3

# 315 "/usr/include/features.h" 2 3

# 26 "/usr/include/stdlib.h" 2 3

# 3 "hello.c" 2

void main(void)

{

printf("hello world!/r/n");

}

2)編譯階段(Compiling)

第二步進行的是編譯階段,在這個階段中,Gcc首先要檢查代碼的規範性、是否有語法錯誤等,以確定代碼的實際要做的工作,在檢查無誤後,Gcc把代碼翻譯成組合語言。使用者可以使用”-S”選項來進行查看,該選項只進行編譯而不進行彙編,產生彙編代碼。

選項 -S

用法:[root]# gcc –S hello.i –o hello.s

作用:將預先處理輸出檔案hello.i彙編成hello.s檔案。

[root@richard hello-gcc]# ls

hello.c hello.i hello.s

如下為hello.s彙編代碼

[root@richard hello-gcc]# vi hello.s

.file   "hello.c"

.section    .rodata

.LC0:

.string "hello world!/r/n"

.text

.globl main

.type   main,@function

main:

pushl   %ebp

movl    %esp, %ebp

subl    $8, %esp

andl    $-16, %esp

movl    $0, %eax

subl    %eax, %esp

subl    $12, %esp

pushl   $.LC0

call    printf

addl    $16, %esp

movl    $0, %eax

leave

ret

.Lfe1:

.size   main,.Lfe1-main

.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

3)彙編階段(Assembling)

彙編階段是把編譯階段產生的”.s”檔案轉成二進位目標代碼.

選項 -c

用法:[root]# gcc –c hello.s –o hello.o

作用:將彙編輸出檔案test.s編譯輸出test.o檔案。

[root]# gcc -c hello.s -o hello.o

[root]# ls

hello.c hello.i hello.o hello.s

4)連結階段(Link)

在成功編譯之後,就進入了連結階段。

無選項連結

用法:[root]# gcc hello.o –o hello.exe

作用:將編譯輸出檔案hello.o連結成最終可執行檔hello.exe。

[root]# ls

hello.c hello.exe hello.i hello.o hello.s

運行該可執行檔,出現正確的結果如下。

[root@localhost Gcc]# ./hello

Hello World!

在這裡涉及到一個重要的概念:函數庫。

讀者可以重新查看這個小程式,在這個程式中並沒有定義”printf”的函數實現,且在先行編譯中包含進的”stdio.h”中也只有該函數的聲明,而沒有定義函數的實現,那麼,是在哪裡實現”printf”函數的呢?最後的答案是:系統把這些函數實現都被做到名為libc.so.6的庫檔案中去了,在沒有特別指定時,gcc會到系統預設的搜尋路徑”/usr/lib”下進行尋找,也就是連結到libc.so.6庫函數中去,這樣就能實現函數”printf” 了,而這也就是連結的作用。

你可以用ldd命令查看動態庫載入情況:

[root]# ldd hello.exe

libc.so.6 => /lib/tls/libc.so.6 (0x42000000)

/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

函數庫一般分為靜態庫和動態庫兩種。靜態庫是指編譯連結時,把庫檔案的代碼全部加入到可執行檔中,因此產生的檔案比較大,但在運行時也就不再需要庫檔案了。其尾碼名一般為”.a”。動態庫與之相反,在編譯連結時並沒有把庫檔案的代碼加入到可執行檔中,而是在程式執行時由運行時連結檔案載入庫,這樣可以節省系統的開銷。動態庫一般尾碼名為”.so”,如前面所述的libc.so.6就是動態庫。gcc在編譯時間預設使用動態庫。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.