C程式執行過程
阿新 • • 發佈:2019-02-09
在C程式執行時,記憶體被劃分為了三個區:1、程式碼區;2、靜態資料區;3、動態資料區。
C程式經過編譯連結之後,在執行執行程式時,程式的一系列指令就被讀取到所連結的記憶體地址上,然後通過eip暫存器來指向要執行的下一條指令;而靜態資料區則是用於存放全域性變數和靜態變數的地方,在程式開始執行前就已經存在初始化的資料了;最後的動態資料區則是在程式執行後才會產生資料,而每個函式的壓棧和清棧就是在這一區域進行的,ebp暫存器指向棧底,esp暫存器指向棧頂。
接下來通過一段程式碼來進行說明,如下所示:
#include <stdio.h>
void fun(int a, int b);
int main()
{
fun(1, 2);
printf("scv!\n");
}
void fun(int a, int b)
{
int i = a + b;
}
在執行此程式時,會首先進入main函式中進行。在進入main函式時,會將ebp暫存器的值進行壓棧處理,並建立main函式的棧區,也就是將esp暫存器(棧頂)值傳遞給ebp暫存器。而eip暫存器的值將會隨著程式的執行而遞增。
當main函式中對fun函式進行呼叫時,在進入fun函式時,同樣會將ebp暫存器的值進行壓棧處理,並建立fun函式的棧區,也就是將esp暫存器(棧頂)值傳遞給ebp暫存器。如下圖所示:
而從上述C程式的彙編程式碼中,我們可以印證此說法,如下是此C程式的彙編程式碼:
.file "a.i"
.section .rodata
.LC0:
.string "scv!"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
movl $2, 4(%esp)
movl $1, (%esp)
call fun
movl $.LC0, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
.globl fun
.type fun, @function
fun:
.LFB1:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $16, %esp
movl 12(%ebp), %eax
movl 8(%ebp), %edx
addl %edx, %eax
movl %eax, -4(%ebp)
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1:
.size fun, .-fun
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits
在上述程式碼中,我們可以看到main和fun函式中都存在 pushl %ebp
和 movl %esp, %ebp
語句。這就證明了在呼叫這兩個函式時,都進行了壓棧和新建棧區。
且經過實驗,在fun函式中不使用return語句,在fun函式執行完成後,也同樣會跳轉到呼叫函式繼續往下執行。