- 註冊時間
- 2006-10-8
- 最後登錄
- 2019-6-20
- 主題
- 查看
- 積分
- 1306
- 閱讀權限
- 110
- 文章
- 1393
- 相冊
- 0
- 日誌
- 1
![Rank: 11](static/image/common/star_level3.gif) ![Rank: 11](static/image/common/star_level3.gif) ![Rank: 11](static/image/common/star_level2.gif) ![Rank: 11](static/image/common/star_level1.gif)
狀態︰
離線
|
本帖最後由 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
|
|