GCC——C程式是如何編譯成的

來源:互聯網
上載者:User
摘要:
    在windows環境,我們有整合式開發環境(IDE),使得我們對編譯器瞭解的很少。當我們專向linux時需要在命令列下編譯自己的程式需要對編譯器的 命令列參數比較熟悉。而如果是做嵌入開發構建自己的作業系統時失去了系統平台,需要我們對編譯的過程以及可執行檔的內部結構有所瞭解。本文講述了如何編 譯可執行檔、靜態庫、動態庫,如何最佳化編譯器,如何編譯無作業系統環境下的程式(自己的OS)等。

1.分析普通的helloworld程式
    先書寫一下一個簡單的helloworld程式如下:
/* hello.c */
int main(int argc, char * argv[])
{
    return 0
}
    編譯器:
gcc -o hello hello.c
    等價的編譯方法:
gcc -c hello.c
gcc -o hello.my -nostartfiles hello.o /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o
    我在redflag workstation 5.0版本下用3.4.3版本GCC編譯器編譯出來的大小都是3589位元組,並且用diff命令比較為相同的檔案。由此證明gcc在編譯並連結 hello.c檔案時先將hello.c編譯成hello.o,然後將它與crt1.o、crti.o、crtn.o連結在一起。

    如果要寫一個沒有main函數的程式,就需要自己實現crt1.o。程式入口為符號“_start”處。代碼如下:
/* nomain.c */
void _start(void)
{
    _exit(0);
}
gcc -o nomain -nostartfiles hello.c

2.編譯和使用庫
    除了直接編譯代碼外還會使用到函數庫。庫有靜態庫和動態庫之分。靜態庫是以.a結尾的檔案,例如:libXXX.a;動態庫是以.so結尾的檔案,例如: libXXX.so。XXX之後或者.so和.a之後會跟版本號碼,例如:libc-2.3.4.so,或者libuuid.so.1.2。
    加入函數庫的代碼如下:
/* test.h */
int test1 (void);

/* test.c */
#include <stdio.h>
#include "libtest.h"

int test1 (void)
{
    printf("test1 function is called./n");
    return 0;
}
    當要編譯成靜態庫的時候使用如下命令產生libtest.a:
gcc -c test.c
ar -r libtest.a test.o
    當要編譯成動態連結程式庫的時候使用如下命令產生libtest.so
gcc -c -fpic test.c
gcc -shared test.o -o libtest.so

gcc -fpic -shared test.c -o libtest.so
    當需要調用該庫時使用如下代碼。
/* calllib.c */
#include "test.h"

int main (int argc, char * argv[])
{
    test1();
}
    按如下方式編譯:
gcc -o calllib.static calllib.c libtest.a

gcc -o calllib.dynamic calllib.c libtest.so

gcc -o calllib calllib.c -ltest
    前提是libtest.a或libtest.so在當前編譯的目錄。
    使用ldd顯示calllib.static依賴的庫只有libc和ld-linux,但calllib.dynamic就多了一個libtest.so。


3.庫檔案和標頭檔
    在程式中,使用#include <stdio.h>類似的標頭檔stdio.h在編譯器的標頭檔路徑中,#include "abc.h"中的stdio.h檔案則應該在目前的目錄。通過對編譯器指定參數-I<PATH>來指定標頭檔所在目錄,可以用 #include <>來引用。例如:gcc -I./include hello.c,將從目前的目錄下的include目錄下去尋找標頭檔。
    同理,程式中調用的庫函數在編譯時間也需要指定路徑,同時指定庫。使用-L<PATH>參數指定庫檔案的目錄,-l<FILE>指定包含的庫檔案。例如,要使用libXXX.so庫,參數為-lXXX。
    一般一個庫編譯完成後有庫檔案和標頭檔。如果要使用這個庫,可以將庫檔案目錄和標頭檔目錄分別用-I和-L參數指定,也可以將他們拷貝到編譯器的include和lib目錄下。


4.縮小程式體積
    代碼如下:
/* nomain.c */
void _start(void)
{
    asm("movl , eax/n"
        "movl {fckeditor}, ebx/n"
        "int {fckeditor}x80"
    );
}
使用如下方法能使可執行檔體積最小(手工方法除外)。
gcc -o nomain -s -O3 nomain.c
objcopy -R .comment -R .data
    由此得到可執行檔的體積為352位元組。


    GCC能編譯出2種格式的可執行體:a.out格式、elf格式。其中.o目標檔案、.a靜態庫檔案

Appendix.1 常用命令
ldd:顯示可執行檔或者庫檔案依賴的庫檔案。
objdump:顯示elf可執行檔的內部資訊。
    -h:顯示
    -t:顯示符號資訊
    -T:顯示動態符號資訊。(例如引用動態連結程式庫中的函數名稱)
    -r:顯示重定位入口資訊。
    -R:顯示動態重定位入口資訊。(例如:動態連結程式庫中的變數或者函數地址)
    -s:顯示所有section內容。
    -S:反組譯碼程式碼片段。
objcopy:copy elf檔案內容
    -R:刪除某個section
    -j:僅僅複製指定的section
 引用:http://blog.chinaunix.net/u/1028/showart.php?id=1668046

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.