以組合語言模擬 C 語言中的副程式呼叫與參數傳遞

GNU 程式設計

GNU 程式簡介

GNU 與 C 語言

gcc 編譯器

glibc 函式庫

make 建置

ld 連結器

as 組譯器

ar 函式庫

objdump

objcopy

訊息

相關網站

參考文獻

最新修改

簡體版

English

#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將範例 f2.c 轉換成組合語言 f2.s,以下顯示了這兩個程式的對照情況,只要仔細觀察,您將會瞭解 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     ;

Facebook

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License