2008-09-24 19 views
29

有沒有辦法在C編譯時知道並輸出函數所需的堆棧大小? 這裏是我想知道:在編譯時檢查堆棧使用情況

讓我們一些功能:

void foo(int a) { 
    char c[5]; 
    char * s; 
    //do something 
    return; 
} 

當編譯這個功能,我想知道需要多少堆棧空間消耗了when它被調用。這可能對檢測隱藏大緩衝區的結構的堆棧聲明有用。

我要尋找的東西,將打印這樣的:

foo.c文件:函數foo堆棧用量是n字節

有沒有辦法不看生成的彙編知道?或者可以爲編譯器設置一個限制?我試圖避免給定進程的運行時堆棧溢出,我正在尋找一種方法來在運行時查找,如果由編譯器確定的函數堆棧使用情況可用作編譯過程。

讓我們換一種方式:是否可以知道函數中所有對象的大小?我猜編譯器優化不會是我的朋友,因爲有些變量會消失,但優越的限制是好的。

+0

如果你想知道,我輸入了祕密的'}'字符 – 2008-09-24 08:33:08

+0

這個問題似乎還不清楚。我猜如果你寫更多的關於你爲什麼想知道這個以及爲什麼檢查反彙編或可執行文件(這是檢查編譯器輸出的最簡單方法)是不可接受的,也許有人可以找到一些簡單的解決方案? – Suma 2008-09-24 11:28:11

回答

10

Linux內核代碼在x86上的4K堆棧上運行。因此他們關心。他們用來檢查的是他們編寫的perl腳本,你可以在最近的內核tarball(2.6.25已經知道它)中找到scripts/checkstack.pl。它在objdump的輸出上運行,使用文檔在最初的註釋中。

我想我已經在很久以前使用過它用於用戶空間的二進制文件,如果你知道一些perl編程,很容易修復它,如果它被破壞。

無論如何,它的基本功能是自動查看GCC的輸出。而內核黑客寫這樣一個工具的事實意味着沒有靜態的方式來做到這一點與GCC(或者,它可能是最近添加的,但我懷疑是這樣)。

順便說一句,來自mingw項目和ActivePerl的objdump,或者Cygwin,你也應該可以在Windows上以及使用其他編譯器獲得的二進制文件。

1

只有編譯器纔會真正知道,因爲它是把所有東西放在一起的人。您必須查看生成的程序集並查看序言中保留了多少空間,但這並不代表像alloca這些在運行時執行它們的事情。

+0

理論上靜態代碼分析工具,如lint可以完成這項工作,實際上我不認爲他們這樣做。 – Ilya 2008-09-24 08:49:48

-1

不一般。理論計算機科學中的暫停問題表明,你甚至無法預測一個普通程序是否停止了給定的輸入。計算用於一般程序運行的堆棧會更加複雜。所以不行。也許在特殊情況下。

假設您有一個遞歸函數,其遞歸級別取決於可以是任意長度的輸入,並且您已經失去了運氣。

+0

您談論的是進程堆棧,而不是每次調用函數將使用多少次 – shodanex 2008-09-24 10:18:33

+0

暫停問題不會停止靜態分析(即在編譯器中),並且通過查看程序文本來給出近似的答案。事實上,一個巨大的挑戰是計算兩個不同的程序是否相同,因此在靜態分析下兩個等效程序可能會給出不同的結果。 – Blaisorblade 2009-01-12 08:46:19

3

我不明白爲什麼靜態代碼分析無法爲此提供足夠好的數據。

找到任何給定函數中的所有局部變量都很簡單,每個變量的大小可以通過C標準(對於內置類型)或通過計算(對於結構和聯合之類的複雜類型)來找到, 。

當然,答案不能保證100%準確,因爲編譯器可以進行各種優化,如填充,將變量放入寄存器或完全刪除不必要的變量。但它給出的答案至少應該是一個很好的估計。

我做了一個快速谷歌搜索,發現StackAnalyzer但我的猜測是,其他靜態代碼分析工具具有類似的功能。

如果你想有一個100%準確的數字,那麼你就必須來看看從編譯器的輸出或運行時檢查(像拉爾夫在his reply建議)

+1

StackAnalyzer看起來不錯,但它沒有完成所要求的工作,因爲它分析了可執行文件,而不是源代碼。 從理論上說寫這樣的工具應該是可能的,但我並不存在這樣的工具 - 檢查堆棧使用運行時或基於組裝是非常實用的。 – Suma 2008-09-24 11:25:25

8

StackAnlyser似乎examinate可執行代碼本身加上一些調試信息。 什麼是this reply描述,我正在尋找,堆棧分析儀看起來像矯枉過正給我。

與ADA存在類似的東西會很好。看看這個手​​冊頁從蚊蚋手冊:

22.2靜態堆棧使用分析

與-fstack使用率編譯會生成一個額外的文件,指定使用的堆棧的最大數額的單位,在每個功能基礎。該文件與具有.su擴展名的目標對象文件具有相同的基本名稱。這個文件的每一行由三個字段組成:

* The name of the function. 
* A number of bytes. 
* One or more qualifiers: static, dynamic, bounded. 

第二字段對應於該函數幀的已知部分的大小。

限定符static表示功能框架大小是純靜態的。通常意味着所有局部變量都有一個靜態大小。在這種情況下,第二個字段是功能堆棧利用率的可靠度量。

限定符動態意味着功能框架大小不是靜態的。它主要發生在一些局部變量具有動態大小的情況下。當此限定符單獨出現時,第二個字段不是函數堆棧分析的可靠度量。如果限定爲有限,則意味着第二個字段是功能堆棧利用率的可靠最大值。

1

假設你在一個嵌入式平臺上,你可能會發現你的工具鏈有一個這樣的問題。良好的商用嵌入式編譯器(例如Arm/Keil編譯器)通常會生成堆棧使用報告。

當然,中斷和遞歸通常會超出它們,但是如果有人在某處堆棧中放置了一個多兆字節的緩衝區,那麼它會給你一個粗略的想法。

1

不正是「編譯時」,但我會做到這一點的生成後步驟:

  • 讓連接器爲您創建
  • 的地圖文件在地圖文件中的每個函數讀取的對應部分的可執行文件,並分析功能序言。

這與StackAnalyzer所做的相似,但要簡單得多。我認爲分析可執行文件或反彙編是最容易獲得編譯器輸出的方法。雖然編譯器在內部知道這些東西,但是恐怕你無法從中得到它(你可能會要求編譯器廠商實現這個功能,或者如果使用開源編譯器,你可以自己做或者讓別人去做爲你)。

要實現這一點,你需要:

  • 能夠解析映射文件
  • 瞭解可執行文件的格式
  • 知道函數序言可以是什麼樣子,並能夠「解碼」它

這很容易或困難取決於您的目標平臺。 (嵌入式?哪種CPU架構?什麼編譯器?)

所有這些絕對可以在x86/Win32中完成,但是如果您從未做過這樣的事情,並且必須從頭開始創建所有這些,可能需要幾天時間才能完成,並且可以開展工作。