先來簡單說一說棧指標和幀指標。
棧指標就是用來儲存棧頂的地址,幀指標是用來儲存函數剛被調用時候的地址。
舉個例子:
int add(int a,int b){
int c=a+b;
return c;
}
這是被調函數。
int x=add( 5,7);
這是主調函數的一個語句。
接下來是主調函數的反組譯碼代碼:
8048459: movl $0x7,0x4(%esp)
8048461: movl $0x5,(%esp)
8048468: call 8048434
804846d: mov %eax,-0x8(%ebp)
再看被調函數的反組譯碼代碼:
8048434: push %ebp
8048435: mov %esp,%ebp
8048437: sub $0x4,%esp
804843a: mov 0xc(%ebp),%eax
804843d: add 0x8(%ebp),%eax
8048440: mov %eax,-0x4(%ebp)
8048443: mov -0x4(%ebp),%eax
8048446: leave
8048447: ret
前邊的十六進位就是代碼所在的記憶體位址,首先執行第一條指令,將7裝入esp+4地址中,再將5裝入esp地址中,接下來執行call指令,call指令後的地址是被調函數的入口地址,call的作用是將下一條指令的地址壓入運行棧,這個地址就是函數返回時的地址,接下來調用被調函數的代碼,第一條指令是將ebp的原值壓入運行棧,然後將esp的值賦給ebp,同時將esp減4,這是為了給被調函數的局部變數c留空間,然後將ebp+12的值裝入eax寄存器,再將ebp+8的值與eax中的值相加儲存在eax寄存器中,然後將eax寄存器中的值放入ebp-4地址中,至此,函數中的int c=a+b;指令就執行完了,接下來執行將ebp-4中的值裝入eax寄存器中,接下來的指令是將ebp的值賦給esp,同時將棧頂元素彈出並賦給ebp,這樣ebp和esp就回到了函數調用之初的狀態,接下來的指令是將返回地址彈出並跳轉至該地址,這樣就回到了主調函數中,然後將eax的值裝入ebp-8地址內,就是將c的值賦給x,這樣整個函數調用就完成了。
以上就是我對函數調用的一些理解。