天天看點

c函數調用過程原理及函數棧幀分析

        今天突然想分析一下函數在互相調用過程中棧幀的變化,還是想盡量以比較清晰的思路把這一過程描述出來,關于c函數調用原理的了解是很重要的。

1.關于棧

        首先必須明确一點也是非常重要的一點,棧是向下生長的,所謂向下生長是指從記憶體高位址->低位址的路徑延伸,那麼就很明顯了,棧有棧底和棧頂,那麼棧頂的位址要比棧底低。對x86體系的cpu而言,其中

---> 寄存器ebp(base pointer )可稱為“幀指針”或“基址指針”,其實語意是相同的。

---> 寄存器esp(stack pointer)可稱為“ 棧指針”。

       要知道的是:

---> ebp 在未受改變之前始終指向棧幀的開始,也就是棧底,是以ebp的用途是在堆棧中尋址用的。

---> esp是會随着資料的入棧和出棧移動的,也就是說,esp始終指向棧頂。

       見下圖,假設函數a調用函數b,我們稱a函數為"調用者",b函數為“被調用者”則函數調用過程可以這麼描述:

(1)先将調用者(a)的堆棧的基址(ebp)入棧,以儲存之前任務的資訊。

(2)然後将調用者(a)的棧頂指針(esp)的值賦給ebp,作為新的基址(即被調用者b的棧底)。

(3)然後在這個基址(被調用者b的棧底)上開辟(一般用sub指令)相應的空間用作被調用者b的棧空間。

(4)函數b傳回後,從目前棧幀的ebp即恢複為調用者a的棧頂(esp),使棧頂恢複函數b被調用前的位置;然後調用者a再從恢複後的棧頂可彈出之前的ebp值(可以這麼做是因為這個值在函數調用前一步被壓入堆棧)。這樣,ebp和esp就都恢複了調用函數b前的位置,也就是棧恢複函數b調用前的狀态。

這個過程在at&t彙編中通過兩條指令完成,即:

       leave

       ret

      這兩條指令更直白點就相當于:

      mov   %ebp , %esp

      pop    %ebp

c函數調用過程原理及函數棧幀分析

2.舉個簡單的執行個體,從彙編的視角看函數調用

2.1建立一個簡單的程式,程式檔案名為  main.c

    開發測試環境:

    ubuntu 12.04

    gcc版本:4.6.3 (ubuntu/linaro 4.6.3-1ubuntu5)  (是ubuntu自帶的)

2.2編譯

#gcc    -g   -o   main   main.c

#objdump   -d  main   >   main.dump

#gcc   -wall   -s  -o   main.s   main.c

c函數調用過程原理及函數棧幀分析

        這樣大家可以看main.s也可以看main.dump,這裡我們選擇使用main.dump。

        截取關鍵的部分,即_start,   swap  ,  main,為什麼會有_start呢,因為elf格式的入口其實是_start而不是main()。下面的圖展示了main()函數調用swap()前後的棧空間的結構。右邊的數字代表相對幀指針的偏移位元組數。後面我們使用gdb調試就會發現棧的變化跟下圖是一緻的。

(!!!請注意,由于棧對齊的緣故,編譯器配置設定棧空間時可能會有沒用到的記憶體位址,而這些沒使用到的記憶體位址就沒在下圖表示出來,是以下圖隻能當作示意圖來了解函數棧幀結構!!具體的棧記憶體内容以下文的gdb調試的資訊為準!!!)

c函數調用過程原理及函數棧幀分析

      下面是main.dump中_start的代碼注釋,比較重要的是對esp的棧對齊操作,esp是16位元組對齊的,注意左邊行号的右邊的0x8048300一類的數字是指令位址。

c函數調用過程原理及函數棧幀分析

      下面是main.dump中swap()函數和main()函數的彙編代碼,代碼旁有詳細的注釋。

c函數調用過程原理及函數棧幀分析

    下面我們使用gdb調試main.c的代碼,使用剛才編譯好的main鏡像。

# gdb    start      (啟動gdb)

# (gdb) file     main      (加載鏡像檔案)

# (gdb) break  main     (把main()設定為斷點,注意gdb并沒有把斷點設定在main的第一條指令,而是設定在了調整棧指針為局部變量保留白間之後)

# (gdb) run                     (運作程式)

# (gdb) stepi                  (單步執行,不熟悉gdb的童鞋要注意了,stepi指令執行之後顯示出來的源代碼行或者指令位址,都是即将執行的指令,而不是剛剛執行完的指令!)

c函數調用過程原理及函數棧幀分析
c函數調用過程原理及函數棧幀分析
c函數調用過程原理及函數棧幀分析
c函數調用過程原理及函數棧幀分析
c函數調用過程原理及函數棧幀分析
c函數調用過程原理及函數棧幀分析
c函數調用過程原理及函數棧幀分析