簡體   English   中英

查找堆棧框架大小

[英]Finding stack frame size

調用函數的堆棧幀可以通過__builtin_frame_address(1)輕松獲得,但堆棧幀大小怎么樣?

是否有一個函數可以讓我知道調用函數的堆棧幀有多大?

我的第一反應是,為什么有人想要這個? 對於C函數來說,動態確定堆棧幀的大小應該被認為是不好的做法。 CDECL(經典 C調用約定)的整點是函數本身(“被叫”)沒有堆棧幀的大小的知識。 任何轉向該哲學都可能導致您的代碼在切換到不同的平台,不同的地址大小(例如從32位到64位),不同的編譯器甚至不同的編譯器設置(特別是優化)時中斷。

另一方面,由於gcc已經提供了這個函數__builtin_frame_address ,因此可以看到從那里可以獲得多少信息。

文檔

幀地址通常是函數推入堆棧的第一個字的地址。

在x86上,函數通常以以下內容開頭:

push ebp       ; bp for 16-bit, ebp for 32-bit, rbp for 64-bit

換句話說, __builtin_frame_address返回調用者堆棧幀的基指針 不幸的是,基指針很少或根本沒有說明任何堆棧幀的開始或結束位置; 基指針指向位於堆棧幀中間某處的位置(在參數局部變量之間 )。

如果您只對包含局部變量的堆棧幀的部分感興趣,那么函數本身就具有所有知識。 該部分的大小是堆棧指針和基指針之間的差異。

register char * const basepointer  asm("ebp");
register char * const stackpointer asm("esp");

size_localvars = basepointer - stackpointer;

請記住,gcc似乎從一開始就在堆棧上分配空間,用於保存從被調用者內部調用的其他函數的參數。 嚴格來說,該空間屬於其他函數的堆棧幀,但邊界不清楚。 這是否是一個問題,取決於你的目的; 您將如何處理計算的堆棧幀大小?

至於其他部分(參數),這取決於。 如果您的函數具有固定數量的參數,那么您可以簡單地測量(形式)參數的大小。 它並不能保證調用者實際上在堆棧上推送了相同數量的參數,但是如果調用者編譯時沒有針對被調用者原型的警告,則應該沒問題。

void callee(int a, int b, int c, int d)
{
    size_params = sizeof d + (char *)&d - (char *)&a;
}

您可以結合使用這兩種技術來獲得完整的堆棧幀(包括返回地址和保存的基本指針):

register char * const stackpointer asm("esp");

void callee(int a, int b, int c, int d)
{
    total_size = sizeof d + (char *)&d - stackpointer;
}

但是,如果你的函數有一個可變數量的參數('省略號',就像printf那樣),那么參數的大小只有調用者知道。 除非被調用者有辦法獲得參數的大小和數量(如果是printf style函數,通過分析格式字符串),你必須讓調用者將該信息傳遞給被調用者。

編輯:請注意,這只能讓函數測量自己的堆棧幀。 被叫者無法計算其呼叫者的堆棧幀大小; 被叫者必須向呼叫者詢問該信息。

但是,被調用者可以對調用者的局部變量的大小做出有根據的猜測。 該塊從被調用者的參數結束( sizeof d + (char *)&d )開始,並以調用者的基指針( __builtin_frame_address(1) )結束。 由於編譯器強加的地址對齊,起始地址可能稍微不准確; 計算出的大小可以包括一塊未使用的堆棧空間。

void callee(int a, int b, int c, int d)
{
   size_localvars_of_caller = __builtin_frame_address(1) - sizeof d - (char *)&d;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM