組合語言基礎之七 框架指標的省略(FPO)

2021-06-24 11:39:40 字數 3443 閱讀 7727



框架指標省略(frame pointer omission)(fpo)

fpo是一種優化,它壓縮或者省略了在棧上為該函式建立框架指標的過程。這個選項加速了函式呼叫,因為不需要建立和移除框架指標(esp,ebp)了。同時,它還解放出了乙個暫存器,用來儲存使用頻率較高的變數。只在intelcpu的架構上才有這種優化。

目前已經討論過的任何一種呼叫約定都儲存了前一函式中棧的資訊(壓棧ebp,然後讓ebp = esp,再移動esp來儲存區域性變數)。乙個fpo的函式可能會儲存前一函式的棧指標(esp,ebp),但是並不為當前的函式呼叫設立ebp。相反,他使用ebp來儲存一些其他的變數。debugger 會計算棧指標,但是debugger必須得到乙個使用fpo的提醒,該提醒是基於fpo型別的資訊的來完成的。

這項特性可以在ms visual c++專業版和企業版中開啟。使用的是編譯器的/oy選項。

fpo的資料結構可以在microsoft的sdk中的winnt.h中找到,其中包含了描述棧框架內容的資訊。這些資訊被使用在debugger上,或者其他的需要在棧中尋找fpo函式的程式中。kv命令可以顯示出包括fpo資訊在內的額外的執行時資訊。

0:000> kv

childebp retaddr

0012ff74 00401009 addemup!addemup (fpo: [2,0,0])

0012ff80 00401115 addemup!main+0x9 (fpo: [0,0,0])

0012ffc0 77e87903 addemup!maincrtstartup+0xb4

0012fff0 00000000 kernel32!baseprocessstart+0x3d (fpo: [non-fpo]

上面的例子中,括號括起來的fpo資料結構的意義分別是:

fpo資料表示形式

(fpo: [ebp addr][x,y,z])

x代表作為引數壓棧了的dwords個數

y代表作為區域性變數壓棧了的dwords個數

z代表在開場**中(prologue)壓棧了的暫存器個數

ebp addr代表

僅在ebp在開場**中儲存了的時候顯示

上面的例子中,由於debugger有正確的symbol,所以debugger會計算出棧底(frame pointer)的位置,而不是在ebp之中儲存它。比如說,第乙個引數的位置是棧底+0x8,返回值的位置是棧底+0x4. 開啟了fpo之後,這些值就不能通過ebp + 0x8這樣拿到了,跟ebp等值的棧底(frame pointer)需要計算才能拿到。

僅僅靠上面的資訊來理解fpo還是感覺有點雲裡霧裡的。

我總結了一些要點在下面,方便大家更好的理解fpo的一些概念。

下表列出了同樣功能,但是fpo選項不同的彙編**。

未開啟fpo的函式的彙編**

開啟了fpo的函式的彙編**

myfunction:

push    ebp

mov     ebp, esp

sub      esp,

mov     eax, [ebp+8]

: :

mov     esp, ebp

pop      ebp

retd

myfunction:

sub      sp,

mov     eax, [esp+4+]

: :

add     sp,

retd

注意,這裡訪問第乙個引數的**是 mov     eax, [ebp+8]

注意,這裡訪問第乙個引數的**是 mov     eax, [esp+4+]

下兩表分別列出了同樣功能,但是fpo選項棧內容分配,以及引數的訪問方式。

未開啟fpo的指標指向

棧中的內容

[ebp-04]
[ebp-01]

[ebp+00]

[ebp+04]
[ebp+08]
第乙個區域性變數的首位址
第乙個區域性變數的最後乙個位元組

上乙個ebp的值

返回值,即呼叫該函式之前的eip暫存器的值

第乙個引數的首位址

說明:

因為引數都是從右至左壓棧的,所以ebp+8是最後乙個壓棧的引數,所以是第乙個引數。

因為被呼叫函式中,先將esp向上移動出所有區域性變數的尺寸,然後根據ebp的位置從下到上,區域性變數從第乙個往最後乙個賦值的,所以ebp-1是第乙個區域性變數的最後乙個位元組。

開啟了fpo的指標指向

棧中的內容

[esp]
[esp+08]
[esp+11]
[esp+12]
[esp+16]
[esp+20]
最後乙個區域性變數的第乙個位元組

…  第乙個區域性變數的首位址

第乙個區域性變數的最後乙個位元組

返回值

第乙個引數的首位址 

… 前乙個函式的區域性變數

說明:

假設當前fpo的資料為(fpo: [1,2,0])

即引數有1個dwords(4位元組)區域性變數2個dwords(8位元組)壓棧的暫存器為0個(0位元組)

frame pointer omission (fpo) and consequences when debugging, part 1.

frame pointer omission (fpo) and consequences when debugging, part 2.

觀察fpo函式

只要當前函式和前乙個函式的symbol被正確載入的話,debugger就可以計算出棧底指標的位置。

因為ebp被保留下來用作通用暫存器了,並沒有用來建立棧框架,所以沒有必要將這個暫存器壓入棧中。這就是為什麼它不再指向前乙個ebp的原因。

如果fpo函式擁有區域性變數,debugger計算出來的棧底位置指向第乙個區域性變數。

如果fpo函式沒有任何的區域性變數,debugger計算出來的棧底位置指向第乙個被保留下來的暫存器。

如果fpo函式沒有任何區域性變數,也沒有保留的暫存器,debugger計算出來的棧底位置指向沒被使用的棧空間。

讓人迷惑的地方是,當乙個fpo函式,使用fastcall呼叫約定的時候。函式沒有棧底指標,引數也沒有被壓在棧上。儘管如此,這些函式通常都比較小。反彙編前面的函式,就可以看到暫存器是如何被載入的了。

學友認為,上面的話可以總結如下:fpo函式沒有儲存ebp,所以訪問引數等的時候就無法使用ebp了。所以fpo函式依靠symbol中的資訊來計算乙個類似於ebp指向的位置的指標。不同的是,ebp指向的是棧中儲存的前乙個棧底的位置。而現在是指向乙個盡可能接近原始ebp的,在棧中盡可能靠下(大位址端)的位置。

組合語言的基礎

硬體組成 暫存器,儲存器位址和輸入輸出位址。ia 32處理器中被稱之為累加器通用暫存器是eax。ia 32處理器中esp是堆疊指標暫存器。ia 32處理器支援8個32位通用暫存器。eax accumulator 累加器 ebx base address 基址暫存器 ecx counter 計數器 e...

彙編基礎 筆記5(《組合語言》第七章)

and 將相應位設為1,其它位不變 or 將相應位設為0,其它位不變 assume ds data data segment db unix db fork data ends code segment start mov al,a mov bl,b mov ax,4c00h int 21h cod...

組合語言基礎之 移位指令

原文 移位指令是一組經常使用的指令,包括 算數移位 邏輯移位 雙精度移位 迴圈移位 帶進製的迴圈移位 移位指令都有乙個指定需要移動的二進位制位數的運算元,該運算元可以是立即數,也可以是cl的值 在8086中,該運算元只能是1,但是在其後的cpu中,該立即數可以是定義域 1,31 之內的數 一 算數移...