ps:先做草稿,以后有時間再整理並貼圖,:)
主要是利用棧底寄存器(ebp)、棧頂寄存器(esp)跟eax寄存器(存儲返回值)來實現。
假設P調用Q:
P()
{
Q(1,2);
}
(跟實際情況可能有點差異,主要還是用來了解函數調用的過程)
1.調用前准備,將Q的參數放到棧中(非push)
mov $1, (%esp)
mov $2, 4(%esp)
2.調用call 0x12345678 (Q的地址)
首先將函數的返回地址(call語句后的那條指令的地址)進棧,
然后跳到0x12345678執行Q的代碼。
3.將舊的ebp進棧(用於退出Q時還原)
push %ebp
4.設置新的棧底
mov %esp, %ebp
5.為Q分配棧空間(棧是向下生長的)
sub $24, %esp (16 * k + 8,用於對齊)
6.執行Q的相關代碼。局部變量/參數是根據esp、ebp跟偏移量來進行存取的。
7.函數離開前將返回值賦值給%eax。
8.調用leave,相當於:
mov %ebp, %esp (還原棧頂)
pop %ebp (還原棧底)
9.處理返回值並接着P接着的代碼繼續執行。
貼多一張Linux運行時存儲器映像的圖
.text 代碼段
.rodata 存儲字符串常量
.data 存儲已初始化的全局/靜態變量
.bss 存儲為初始化/初始化為0的全局/靜態變量(在可執行文件中只占一個占位符,程序加載的時候才分配空間)
ELF文件中有.rel.text/.rel.data段,用於重定位。