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

[技術文章] 以組合語言模擬 C 語言中的副程式呼叫與參數傳遞 [複製連結]

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

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

狀態︰ 離線
跳轉到指定樓層
1
發表於 2012-7-9 09:34:39 |只看該作者 |倒序瀏覽


#include <stdio.h>

int main() {
    int x = 1;
    int y;
    y = f1(x);
    printf("y=%d\n", y);
    return 1;
}
int f1(int t) {
    int b = f2(&t);
    return b+b;
}
int f2(int *p) {
    int r= *p+5;
    return r;
}

如果我們用gcc 編譯器將C 語言轉換為組合語言,就會發現其中有些難以理解的地方,這是因為 gcc 會將程式中的區域變數存放在堆疊當中,並且在進入函數前先將參數壓入堆疊。在進入

也就是要保存框架指標 ebp 與設定堆疊指標 esp,因此這兩個程式並不能再度呼叫下一層的函數,否則將會導致框架指標遺失,而產生堆疊錯誤的情況。

在 IA32 的處理器架構下,要進行多層次的副程式呼叫,必須在程式一進入時就保存 ebp 框架指標於堆疊中,然後設定 esp 堆疊指標,接著分配區域變數的空間。當函數呼叫結束,要返回上一層函數時,則必須先將傳回值存入 eax 當中,然後利用 leave 與 ret 等指令,完成返回動作。

為了觀察上述的過程,我們利用gcc將範例 3.13的 C 語言程式 f2.c,轉換成組合語言 f2.s,範例 3.19顯示了這兩個程式的對照情況,只要仔細觀察,您將會瞭解 C 語言函數呼叫機制的實作方式。

File:ch03/f2.s                                               ; File:ch03/f2.c                    
        .file    "f2.c"                                      ; #include <stdio.h>               
    .def    ___main;    .scl    2;    .type    32;    .endef ;                                 
    .section .rdata,"dr"                                     ;                                 
LC0:                                                         ;                                 
    .ascii "y=%d\12\0"                                       ;                                 
    .text                                                    ;                                 
.globl _main                                                 ;                                 
    .def    _main;    .scl    2;    .type    32;    .endef   ;                                 
_main:                                                       ; int 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, -12(%ebp)                                  ;                                 
    movl    -12(%ebp), %eax                                  ;                                 
    call    __alloca                                         ; // 分配堆積(heap)空間            
    call    ___main                                          ;                                 
    movl    $1, -4(%ebp)                                     ;     int x = 1;                    
    movl    -4(%ebp), %eax                                   ;     int y;                        
    movl    %eax, (%esp)                                     ;                                 
    call    _f1                                              ;     y = f1(x);                    
    movl    %eax, -8(%ebp)                                   ; // y = eax                        
    movl    -8(%ebp), %eax                                   ; // eax = y                        
    movl    %eax, 4(%esp)                                    ; // 參數 y (推入堆疊)            
    movl    $LC0, (%esp)                                     ; // 參數 "y=%d\n"               
    call    _printf                                          ;     printf("y=%d\n", y);        
    movl    $1, %eax                                         ; // 設定傳回值 eax=1               
    leave                                                    ;                                 
    ret                                                      ;     return 1;                    
.globl _f1                                                   ; }                                
    .def    _f1;    .scl    2;    .type    32;    .endef     ;                                 
_f1:                                                         ; int f1(int t) {                    
    pushl    %ebp                                            ; // 保存框架指標                    
    movl    %esp, %ebp                                       ; // 設定堆疊指標                    
    subl    $8, %esp                                         ; // 分配區域變數空間               
    leal    8(%ebp), %eax                                    ; // eax = 8(%ebp) 的位址            
    movl    %eax, (%esp)                                     ; // esp 上移 8 byte               
    call    _f2                                              ;     int b = f2(&t);               
    movl    %eax, -4(%ebp)                                   ; // -4(%ebp) 就是 b               
    movl    -4(%ebp), %eax                                   ; // eax = b                        
    addl    -4(%ebp), %eax                                   ; // eax = eax + b               
    leave                                                    ;                                 
    ret                                                      ;     return b+b;                    
.globl _f2                                                   ; }                                
    .def    _f2;    .scl    2;    .type    32;    .endef     ;                                 
_f2:                                                         ; int f2(int *p) {               
    pushl    %ebp                                            ; // 保存框架指標                    
    movl    %esp, %ebp                                       ; // 設定堆疊指標                    
    subl    $4, %esp                                         ; // 分配區域變數空間               
    movl    8(%ebp), %eax                                    ;                                 
    movl    (%eax), %eax                                     ;                                 
    addl    $5, %eax                                         ;     int r= *p+5;               
    movl    %eax, -4(%ebp)                                   ; // 設定傳回值 eax=r               
    movl    -4(%ebp), %eax                                   ;                                 
    leave                                                    ;                                 
    ret                                                      ;     return r;                    
    .def    _f2;    .scl    3;    .type    32;    .endef     ; }                                
    .def    _printf;    .scl    3;    .type    32;    .endef ;                                 
    .def    _f1;    .scl    3;    .type    32;    .endef     ;

喜歡嗎?分享這篇文章給親朋好友︰
               感謝作者     

Rank: 2

狀態︰ 離線
2
發表於 2012-7-9 10:40:07 |只看該作者
請問一下板大

有沒有自學寫程式的建議

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

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

狀態︰ 離線
3
發表於 2012-7-9 13:55:06 |只看該作者
Waned 發表於 2012-7-9 10:40  
請問一下板大

有沒有自學寫程式的建議

我覺得C語言是眾多高階程式語言中最難一步登天的
所以還是按步就班來是不二法門
先從基礎開始學,一開始可能會覺得很難入門
但是一但過了基礎學習之後,應該就會學得越來越快了
加油!!
建議你使用 DEV C++ 來開發
http://www.math.ncu.edu.tw/~jovice/c++/boards/devcpp.htm

建議你可以參考良葛格的學習筆記
http://caterpillar.onlyfun.net/Gossip/

建議你可以用「高中生程式解題系統」來練習
http://zerojudge.tw/
根據我的經驗 進步的必要條件一定要多找一些題目作 以下有許多題目與解答
不過最好是不得以才看解答

http://www.math.ncu.edu.tw/~ziyou/c++/ex/index.htm

Rank: 2

狀態︰ 離線
4
發表於 2012-7-9 16:51:20 |只看該作者
mm117777 發表於 2012-7-9 13:55  
我覺得C語言是眾多高階程式語言中最難一步登天的
所以還是按步就班來是不二法門
先從基礎開始學,一開始 ...

謝謝板大的建議

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


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

GMT+8, 2024-9-27 02:09

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