close
文章出處

      學C語言時,就聽老師說函數調用時是通過棧來記錄信息,又聽說什么“保留現場”,"恢復現場"一些既聽不懂,也不知道怎么弄懂的東西。最近正在學習Linux下的匯編,現在就通過一個簡單的例子來展示一下匯編級的函數調用,這樣能夠增加大家對C語言的理解。雖然并不是很完善,但是足夠闡明函數調用的思想。

在Linux下通過命令gcc -S functest.c,可以生成匯編程序functest.s

//functest.c

#include <stdio.h>

void func(int a, int b)

{

    int c;

    int d;

    c = a;

    d = b;

}

int main()

{

    func(2, 3);

    return 0;

}

在Linux下通過命令gcc -S functest.c,可以生成匯編程序functest.s

     首先可以看到一點,一個簡單的C程序的匯編卻是如此之長,可以想到良好的程序書寫習慣有多大的好處。

     言歸正傳,我們不分析每條指令都是什么意思,我們重點看函數調用那一部分。

我們注意在調用call func之前,第25,26,27行是為func準備參數。首先把%esp減去8個字節,這是因為在Linux中int型為4個字節(想想譚浩強C語言程序設計書中說int為2個字節?這東西依賴于具體的操作系統和編譯器),即手動修改棧指針,可以發現C語言中函數參數是保存在棧中的,進一步,我們發現gcc把3先壓入棧中,之后是2,可以說明,C中函數參數的壓棧順序和函數書寫順序正好相反。執行完這3條指令后,棧中的內容如下:

之后28行調用func,通過call指令。執行call指令時,不僅僅去調用func函數,而且做了一個我們程序員看不到的動作,就是把下一條指令的地址壓入到棧中。這點我們稍后會講到,而且從這我們也能略窺到,函數名只不過是地址的別名,只不過是為了方便程序員理解,我們可以完全用16進制地址取代這些別名,當然計算機并不覺得用地址比用別名更困難。

      現在我們進入func。6,7行的兩條指令可以認為是套路,具體原因可以見[注1]。先把%ebp的值壓入棧,之后把棧頂賦給%ebp,隱約我們可以猜到,因為在這里修改了%ebp,而又不想原來的%ebp被覆蓋,那么只好先把原來的%ebp存儲到棧中,必要的時候可以在棧中恢復原來的%ebp。第8行,將棧頂向下移,這個操作的目的是為函數中的臨時變量在棧中分配存儲空間。有人會想為什么不用其它的內存或者寄存器去存儲臨時變量?這是因為如果為大型項目編寫程序,要跟蹤哪些變量使用內存,而哪些使用寄存器簡直就是惡夢,所以C中用棧存儲臨時變量,因為在棧在函數調用后會釋放或者清空,那么這些變量是不可能被其它函數所調用,故這也是“局部變量”的由來。這個行為也使程序員在函數中要對操作得非常小心,例如我們在函數中開了一個很大的數組,那么在為這個數組開辟棧空間時,很可能出現段錯誤。執行完6,7,8三行后,棧中的內容如下:

9,10行是把3賦值給d11,12是把2賦值給c。那么棧中的內容確定下來如下:

      可以看到,有兩個為局部變量開辟的占空間實際并沒有被使用到。

      有人現在有疑問那個標記”XXXX”的單元式干什么用的,現在我來告訴你那個其實就是函數的返回地址,記得我前面說過,call指令會把調用函數后的下一條指令地址入棧,那么那個地址就存在XXXX那個單元中,這樣執行完函數時可以根據棧中的這個地址返回到主調用程序,繼續運行。那么完整的棧內容如下:

這樣函數基本執行完畢,leave是恢復調用func之前的棧內容和%ebp內容,完全可以用以下指令替代:

movl %ebp, %esp

popl %ebp

 

ret 指令就是返回到主程序,不贅述。

30行為主程序中從棧中刪除調用func時的參數2,3。

 

      這樣一個函數的完整調用過程就給大家展示完了,希望對大家有所幫助。本人剛學匯編,如有錯誤,還望大家指出。

 

注1:這樣做,是為了對函數參數容易進行讀取。在設計之初,考慮到用根據%esp和偏移量來對棧中的參數進行訪問,但是由于在函數中%esp極有可能改變,那么維護這個偏移量也變的復雜,所以利用%ebp記錄進入函數時棧指針的位置,而%ebp在這個函數中不會被改變,那么通過%ebp去定位函數參數就變的非常容易,所以一般有人把%ebp也稱為棧底指針,而這也成為了匯編程序員的習慣。


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 AutoPoster 的頭像
    AutoPoster

    互聯網 - 大數據

    AutoPoster 發表在 痞客邦 留言(0) 人氣()