以組合語言模擬 C 語言中的副程式呼叫與參數傳遞
#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     ;
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License