使用 gcc 進行跨平台編譯

由於 GNU 工具所支援的處理器眾多,因此在嵌入式系統的市場,gcc 的佔有率相當高。許多嵌入式開發者都會使用 gcc 作為跨平台編譯器,以開發嵌入式裝置,像是手機、MP3、機上盒、數位相框等裝置的程式。圖 1 顯示了一個使用 gcc 進行跨平台編譯的範例,該範例的開發環境是一台個人電腦。我們可以使用 gcc 編譯出在個人電腦上執行的組合語言 (或目的檔),但是也可以用跨平台編譯器,像是 arm-elf-gcc,編譯出可在ARM處理器上執行的目的檔。然後再透過燒錄或上傳的方式,將該目的檔放到目標板上執行。

GccCrossCompiler.jpg

圖 1. 使用gcc跨平台編譯的範例圖

現在,請使用者撰寫一個簡單的C語言程式,如範例 1 所示,該範例是一個可以印出 “hello!” 字樣的程式。

範例 1. C語言程式 – hello.c

char hello[]="hello !";

int main() {
  printf(hello);
  return 1;
}

接著,讓我們看看真實的編譯器是如何將函數轉換為組合語言的,我們利用 gcc加上 -S 參數的方式,將C程式 hello.c 編譯為組合語言。如果您安裝了專為 ARM CPU 所製作的跨平台 gcc 編譯器 ,那麼,您就可以使用 arm-elf-gcc 指令,將 hello.c 編譯為 ARM 的組合語言。範例 2 顯示了分別用 gcc 與 arm-elf-gcc 將 hello.c 編譯為組合語言的例子。由於我們是在安裝有MS. WindowsXP作業系統的IA32平台上,透過Cygwin環境操作的,因此,gcc 預設的輸出即是IA32的組合語言與目的檔,這稱不上是跨平台編譯器。

但是,當我們使用 arm-elf-gcc 進行編譯時,我們所輸出的 hello_ARM.s 就是 ARM 處理器的組合語言,而 hello_ARM.o 則是 ARM 處理器的目的檔,這種編譯器,就被稱為跨平台編譯器 (Cross Compiler)。因為,我們在 IA32 的平台上,編譯出的卻是 ARM 的目的檔,這在嵌入式系統的開發上相當常見。嵌入式系統程式設計師在開發的時候,通常桌上會有一塊目標板,然後在桌上型電腦上,編譯出目標板的程式,再透過傳輸線上傳至目標板進行測試。

範例 2. 在cygwin中分別以 gcc 與 arm-elf-gcc編譯 hello.c

$ gcc -S hello.c -o hello_IA32.s
$ arm-elf-gcc -S hello.c -o hello_ARM.s
$ gcc hello_IA32.s -o hello_IA32.o
$ arm-elf-gcc hello_ARM.s -o hello_ARM.o
$ ./hello_IA32.o
hello !
$ ./hello_ARM.o
./hello_ARM.o: ./hello_ARM.o: cannot execute binary file

為了讓讀者更清楚跨平台編譯與一般編譯的差別,我們在範例 3 同時列出了 ARM 與 IA32 兩個版本的組合語言檔,其中 hello_IA32.s 是本機編譯器 gcc 的輸出檔。而 hello_ARM.s 則是跨平台編譯器 arm-elf-gcc 的輸出檔,讀者可以稍為比較一下兩者的不同點,並且搭配前兩節的 IA32 與 ARM 的說明,觀察一下兩種組合語言的不同點,這對理解處理器的結構有所幫助。

範例 3. IA32 與 ARM 組合語言程式對照 (hello.c)

指令:gcc -S hello.c -o hello_IA32.s
檔案:hello_IA32.s
    .file    "hello.c"
.globl _hello
    .data
_hello:
    .ascii "hello !\0"
    .def    ___main;.scl 2; .type 32; .endef
    .text
.globl _main
    .def    _main; .scl 2; .type 32; .endef
_main:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $8, %esp
    andl    $-16, %esp
    movl    $0, %eax
    addl    $15, %eax
    addl    $15, %eax
    shrl    $4, %eax
    sall    $4, %eax
    movl    %eax, -4(%ebp)
    movl    -4(%ebp), %eax
    call    __alloca
    call    ___main
    movl    $_hello, (%esp)
    call    _printf
    movl    $1, %eax
    leave
    ret
    .def _printf; .scl 3; .type 32; .endef
指令:arm-elf-gcc -S hello.c -o hello_ARM.s
檔案:hello_ARM.s
 .file "hello.c"
.gcc2_compiled.:
    .global    hello
.data
    .align    2
    .type     hello,object
    .size     hello,8
hello:
    .ascii    "hello !\000"
.text
    .align    2
    .global    main
    .type     main,function
main:
    mov    ip, sp
    stmfd    sp!, {fp, ip, lr, pc}
    sub    fp, ip, #4
    bl    __gccmain
    ldr    r0, .L3
    bl    printf
    mov    r0, #1
    b    .L2
.L4:
    .align    2
.L3:
    .word    hello
.L2:
    ldmea    fp, {fp, sp, pc}
.Lfe1:
    .size     main,.Lfe1-main
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License