SOGO論壇
  登入   註冊   找回密碼
查看: 551|回覆: 0
列印 上一主題 下一主題

[技術文章] GNU 的連結工具 [複製連結]

Rank: 11Rank: 11Rank: 11Rank: 11

熱心參予論壇活動及用心回覆主題勳章 數位硬體勳章

狀態︰ 離線
跳轉到指定樓層
1
發表於 2012-7-10 10:05:59 |只看該作者 |倒序瀏覽
本帖最後由 mm117777 於 2012-7-10 10:08 編輯

我們將示範如何使用 GNU 的連結工具,這包含目的檔格式的觀察、函式庫的建立、程式的編譯與連結等。

首先,請讀者先撰寫下列四個程式,這四個檔案實作了堆疊的定義、資料結構、函數與測試程式。

範例 1 :實作堆疊的四個 C 語言程式 (分別是 Stack.h, StackType.c, StackFunc.c, StackMain.c)
// 檔案 Stack.h
#ifndef _StackType_H_
#define _StackType_H_

#define STACK_SIZE 100

extern int stack[];
extern int top;

extern void push(int x);
extern int pop();

#endif
        // 檔案:StackType.c
#include <Stack.h>

int stack[STACK_SIZE];
int top = 0;
        // 檔案 StackFunc.c
#include <assert.h>
#include <Stack.h>

void push(int x) {
assert(top < STACK_SIZE);
stack[top++] = x;
}

int pop() {
assert(top > 0);
return stack[—top];
}
        // 檔案 StackMain.c
#include <stdio.h>
#include <Stack.h>

int main() {
int x;
push(3);
x= pop();
printf("x=%d\n",x);
return 0;
}

接著,讓我們來練習編譯與連結動作,請讀者按照範例 1 的步驟進行操作。

範例 1 的編譯、連結與執行結果

$ gcc -I . -c StackType.c -o StackType.o       // 編譯 StackType.c 為目的檔
$ gcc -I . -c StackFunc.c -o StackFunc.o       // 編譯 StackFunc.c 為目的檔
$ gcc -I . -c StackMain.c -o StackMain.o       // 編譯 StackMain.c為執行檔
$ gcc StackMain.o StackFunc.o StackType.o -o stack // 連結上述三個程式
$ ./stack                                                    //  執行 stack 檔
x=3                                                              //  結果輸出 3。

那麼,到底這些具有外部引用的 C 語言程式,會被編譯器編譯程甚麼樣子呢?讓我們用 gcc 中的 –S 參數,將這些檔案編譯程組合語言看看。您可以使用

將範例 1 的 C語言程式編譯為 IA32 組合語言

$ gcc -S StackType.c -o StackType.s -I . // 編譯 StackType.c,輸出組合語言檔StackType.s
$ gcc -S StackFunc.c -o StackFunc.s -I . // 編譯 StackFunc.c,輸出組合語言檔StackFunc.s
$ gcc -S StackMain.c -o StackMain.s -I . // 編譯 StackMain.c,輸出組合語言檔StackFunc.s

範例 2 顯示了 StackType.s, StackFunc.s, StackMain.c 等三個組合語言檔的內容。

範例 2. 範例 1 所對應的 IA32 組合語言檔

// 檔案:StackType.s
    .file    "StackType.c"
.globl _top
    .bss
    .align 4
_top:
    .space 4
    .comm    _stack, 400     # 400

// 檔案:StackFunc.s
        .file    "StackFunc.c"
    .section .rdata,"dr"
LC0:
    .ascii "top < STACK_SIZE\0"
LC1:
    .ascii "StackFunc.c\0"
    .text
.globl _push
    .def _push;.scl 2;.type 32;.endef
_push:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    cmpl    $99, _top
    jle    L3
    movl    $LC0, 8(%esp)
    movl    $5, 4(%esp)
    movl    $LC1, (%esp)
    call    ___assert
L3:
    movl    _top, %eax
    movl    %eax, %edx
    movl    8(%ebp), %eax
    movl    %eax, _stack(,%edx,4)
    incl    _top
    leave
    ret
    .section .rdata,"dr"
LC2:
    .ascii "top > 0\0"
    .text
.globl _pop
    .def _pop;.scl 2;.type 32;.endef
_pop:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    cmpl    $0, _top
    jg    L6
    movl    $LC2, 8(%esp)
    movl    $10, 4(%esp)
    movl    $LC1, (%esp)
    call    ___assert
L6:
    decl    _top
    movl    _top, %eax
    movl    _stack(,%eax,4), %eax
    leave
    ret
    .def ___assert;.scl 3;.type 32;.endef

// 檔案:StackMain.s   
    .file    "StackMain.c"
    .def ___main;.scl 2;.type 32;.endef
    .section .rdata,"dr"
LC0:
    .ascii "x=%d\12\0"
    .text
.globl _main
    .def    _main;    .scl    2;    .type    32;    .endef
_main:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    andl    $-16, %esp
    movl    $0, %eax
    addl    $15, %eax
    addl    $15, %eax
    shrl    $4, %eax
    sall    $4, %eax
    movl    %eax, -8(%ebp)
    movl    -8(%ebp), %eax
    call    __alloca
    call    ___main
    movl    $3, (%esp)
    call    _push
    call    _pop
    movl    %eax, -4(%ebp)
    movl    -4(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    $LC0, (%esp)
    call    _printf
    movl    $0, %eax
    leave
    ret
    .def _printf;.scl 3;.type 32;.endef
    .def _pop;.scl 3;.type 32;.endef
    .def _push;.scl 3;.type 32;.endef

對於程式 StackType.c 而言,其中的指令 int top=0; 被編譯為組合語言 .bss 段中的 _top .space 4,而 int stack[STACK_SIZE] 被編譯為 .comm _stack, 400,而 _top 被宣告為 .globl,代表全域變數 (global)。

對於程式 StackFunc.c 而言,其中的 push, pop 函數分別被編譯為標記 _push: 與 _pop,並且用.globl _push與 .globl _pop 標記為全域變數。同樣的 StackMain.c 當中的主程式 main 也被編譯為標記 _main,並標上全域變數記號 .globl,放在程式段 .text 當中。

如果我們用 nm 指令,檢視目的檔 StackType.o 與 StackFunc.o 中的連結地圖 (符號表),那麼,我們可以看到其中的分段與全域變數,下列範例顯示了使用 nm StackType.o 與 nm StackFunc.o 兩個指令,所查看到的連結地圖。

$ nm StackType.o            顯示StackType.o的連結地圖(map)
00000000 b .bss               bss 段 (b:未初始化變數)
00000000 d .data              data 段 (d:已初始化資料)
00000000 t .text              text 段 (t:程式段)
00000190 C _stack             int stack[] 的定義 (C:Common)
00000000 B _top               int top=0 的定義 (B:bss)

$ nm StackFunc.o            顯示StackType.o的連結地圖(map)
00000000 b .bss               bss 段 (b:未初始化變數)
00000000 d .data              data 段 (d:已初始化資料)
00000000 r .rdata             rdata 段 (r:唯讀資料段)
00000000 t .text              text 段 (t:程式段)
         U ___assert          未定義 (U:___assert)
00000044 T _pop               int stack[] 的定義 (C:Common)
00000000 T _push              int top=0 的定義 (B:bss)
         U _stack             未定義 (U:_stack)
         U _top               未定義 (U:_top)

如果要檢視目的檔中的字串表,可使用 strings 指令,下列範例就顯示了以 strings 檢視 StackType.o 檔案中字串表的結果,其中的檔案名稱、分段名稱、變數名稱都被放入的字串表當中。

$ strings StackType.o
.text
0`.data
.bss
.file
StackType.c
.text
.data
.bss
_top
_stack

如果想知道每一個區段 (Section) 的大小,可以使用 size 指令,就顯示了以 size 檢視 stack.exe 檔案中的區段大小,其中 text 段為 1140 bytes, data 段為 416 bytes, bss 段為 468 bytes, 總長為 2024 bytes。下列範例顯示了 size 指令的使用結果

範例:用 size 指令檢視目的檔中分段大小

$ size stack.exe
   text    data     bss     dec     hex filename
   1140     416     468    2024     7e8 stack.exe
喜歡嗎?分享這篇文章給親朋好友︰
               感謝作者     

請注意︰利用多帳號發表自問自答的業配文置入性行銷廣告者,將直接禁訪或刪除帳號及全部文章!
您需要登錄後才可以回覆 登入 | 註冊


本論壇為非營利自由討論平台,所有個人言論不代表本站立場。文章內容如有涉及侵權,請通知管理人員,將立即刪除相關文章資料。侵權申訴或移除要求:abuse@oursogo.com

GMT+8, 2025-2-6 21:50

© 2004-2025 SOGO論壇 OURSOGO.COM
回頂部