在嵌入式系統(tǒng)中,RAM 的大小是非常有限的。尤其是做器件選型時(shí),更小 RAM 的芯片意味著更低的采購(gòu)價(jià)格,產(chǎn)品才會(huì)更具競(jìng)爭(zhēng)力,有更高的毛利。
在這樣極致的壓榨下,留給堆棧的空間更加少了。開(kāi)發(fā)者不得不面對(duì)爆棧的巨大風(fēng)險(xiǎn)。每個(gè)軟件工程師都想有一個(gè)工具能夠幫助他們檢驗(yàn)棧的使用情況,從而很好的評(píng)估風(fēng)險(xiǎn)。
人們尋常采用的方法是把棧里都先寫(xiě)滿(mǎn)一個(gè)特定的值,比如 0xAA。隨后在程序運(yùn)行一段時(shí)間之后看看還剩多少 0xAA 沒(méi)有被改掉。這種方法確實(shí)有一定的效果,但是顯然還不夠直觀,又比較麻煩。尤其是當(dāng)工程有不止一個(gè)棧的時(shí)候。
為此,新版本的 gcc 編譯器提供了一個(gè)有用的編譯選項(xiàng)-fstack-usage。使用這個(gè)選項(xiàng)后,編譯器會(huì)額外產(chǎn)生有關(guān)棧使用情況的信息,而 MCUXpresso 可以整理這些信息,并將它們非常清晰地顯示出來(lái)。
fstack-usage與Call Graph
先讓我們看看 GNU 關(guān)于-fstack-usage 的說(shuō)明:(https://gcc.gnu.org/onlinedocs/gcc/Developer-Options.html)
它以每個(gè)函數(shù)為基礎(chǔ),使編譯器生成程序的堆棧使用信息。信息存放在后綴名為.su 的文件中。
下圖是編譯文件夾的內(nèi)容,可以看到有一個(gè)同名的 su 文件。
這個(gè)文件的內(nèi)容也很簡(jiǎn)單,它列 出 了 文 件名(system_MIMXRT1052.c), 函數(shù)的 行號(hào)和列號(hào), 函數(shù)的名稱(chēng), 如 SystemInit,堆棧的使用情況(8),以及如何分配(static)。
但是這樣單個(gè)顯示是沒(méi)有什么參考價(jià)值的,我們需要的是整個(gè)工程的全景,要把所有的 su 文件整理出來(lái)。 MCUXpresso IDE v11 版本提供了這一功能。在 Image Info 窗口中有一個(gè) Call Graph 標(biāo)簽。單擊右上角的導(dǎo)入按鈕就可以導(dǎo)入當(dāng)前整個(gè)工程的 stack 使用情況。
前面帶“>”的函數(shù)名稱(chēng)顯示“根”函數(shù):它們不能被從其他任何地方調(diào)用的。其中ResetISR就是 reset 入 口 函 數(shù) , 而 exception handlers 里 面 都 是 中 斷 服 務(wù) 程 序 。
HAL_UartReceiveBlocking()函數(shù)因?yàn)闆](méi)有其它函數(shù)顯式的調(diào)用它,所以也被認(rèn)為是根函數(shù)。
這里我們可以看到這種分析的一個(gè)弱點(diǎn),如果函數(shù)是通過(guò)函數(shù)指針的方式來(lái)調(diào)用那么該功能就無(wú)能為例了。但是使用者可以自己分析程序給續(xù)上。
如果函數(shù)是遞歸的,則用一個(gè)特殊的雙箭頭標(biāo)記。成本估算將針對(duì)單級(jí)遞歸。
Full Cost 表示累積堆棧使用量(此函數(shù)加上所有被調(diào)用的)。
Local Cost 表示本層的堆棧使用量。
Depth 表示由該函數(shù)引起的調(diào)用級(jí)別數(shù)。
請(qǐng)注意Exception Handlers 這里,它集中了所有的中斷服務(wù)程序。由于沒(méi)有顯式的調(diào)用,它們都是根函數(shù),并且這里只統(tǒng)計(jì)非中斷嵌套情況下的最大用量。所以如果允許中斷嵌套, 那么對(duì)于棧的分配應(yīng)該更加保守。
此外,如果函數(shù)是用匯編語(yǔ)言寫(xiě)的,那么工具是無(wú)法統(tǒng)計(jì)它們的棧使用情況, 一律會(huì)統(tǒng)計(jì)成‘4’。但如果調(diào)用到了其它函數(shù),深度和 Full cost 還是會(huì)被統(tǒng)計(jì)的。
需要注意這個(gè)堆棧使用報(bào)告僅涵蓋每個(gè)函數(shù)或調(diào)用樹(shù)的堆棧使用情況。它們不包括異常處理程序所需的額外堆棧空間。所以最后的堆棧計(jì)算是 ResetISR 棧+中斷棧,如果允許中斷嵌套,那么整個(gè)中斷嵌套最長(zhǎng)的情況必須要被考慮。 該工具在基于 RTOS(例如 FreeRTOS)的系統(tǒng)中同樣運(yùn)行良好且開(kāi)箱即用。因此,使用該工具,我們可以很好地估計(jì)每個(gè)任務(wù)堆棧的使用情況。棧計(jì)算可以使用下圖,它來(lái)自 Joseph Yiu,一位來(lái)自 ARM 的大牛。
其它同棧保護(hù)有關(guān)的編譯選項(xiàng)
GCC 除了提供 stack-usage 這個(gè)編譯選項(xiàng)外,還有其它一些相關(guān)的選項(xiàng)可供選擇。
- Wstack-usage
這是一個(gè)有用處的編譯選項(xiàng):-Wstack-usage。它能夠在堆棧使用超過(guò)限制時(shí)產(chǎn)生 warning
信息。用法是:
-Wstack-usage=256
它表示如果棧使用量超過(guò) 256 時(shí)產(chǎn)生警告。這樣就能更快速地知道哪個(gè)函數(shù)超了。只說(shuō)它有些用處而不是非常有用是因?yàn)椋会槍?duì)單個(gè)函數(shù)的堆棧用量, 不會(huì)按調(diào)用樹(shù)累計(jì)被調(diào)用函數(shù)的堆棧總數(shù)。
- fstack-protector 這個(gè)選項(xiàng)的解釋是: 產(chǎn)生額外的代碼來(lái)檢查緩沖區(qū)溢出,例如堆棧粉碎攻擊。這是通過(guò)向具有易受攻擊對(duì)象的函數(shù)添加保護(hù)變量來(lái)實(shí)現(xiàn)的。這包括調(diào)用 alloca 的函數(shù),以及緩沖區(qū)大于 8 字節(jié)的函數(shù)。在輸入函數(shù)時(shí)初始化保護(hù),然后在函數(shù)退出時(shí)檢查保護(hù)。如果保護(hù)檢查失敗,將打印錯(cuò)誤消息,程序退出。
- fstack-protector-all
同-fstack-protector 基本相同, 區(qū)別是它為所有的函數(shù)都提供保護(hù)。
-fstack-protector和-fstack-protector-all在ebp和ip等信息的地址下面放一個(gè)保護(hù)數(shù), 如果棧溢出 ,那么這個(gè) 32 位數(shù)會(huì)被修改,就會(huì)導(dǎo)致函數(shù)進(jìn)入棧溢出錯(cuò)誤處理函數(shù)。一旦檢測(cè)到溢出就會(huì)調(diào)用__stack_chk_fail()函數(shù)。這個(gè)函數(shù)需要用戶(hù)自己來(lái)寫(xiě),比如可以打印一個(gè)報(bào)錯(cuò)信息,或者執(zhí)行其它一些保護(hù)措施。 下圖是在編譯選項(xiàng)里加入-fstack-protector-all 后一個(gè)普通C函數(shù)的匯編內(nèi)容。
當(dāng)然,可以想見(jiàn),如果每個(gè)函數(shù)都加這么一段,編譯出來(lái)的二進(jìn)制文件會(huì)大上許多,執(zhí)行速度也會(huì)變慢一些。而如果僅僅使用-fstack-protector,則很少有函數(shù)會(huì)被保護(hù)。因?yàn)?alloca() 是在棧(stack)里面分配空間,而我們一般都是用 malloc()在堆(heap)里面分配。
小結(jié)
棧空間防溢出是軟件設(shè)計(jì)中非常關(guān)鍵的問(wèn)題。MCUXpresso IDE 的 Call Graph 窗口為開(kāi)發(fā)者提供了很好的可視化統(tǒng)計(jì)表格, 非常便于對(duì)堆棧使用情況的評(píng)估。論程序有沒(méi)有操作系統(tǒng),它都非常有效。 而GCC編譯器提供的其它一些選項(xiàng),雖然也有用處,但是在嵌入式軟件設(shè)計(jì)中還是用在 debug 階段會(huì)更好一些。開(kāi)發(fā)者還是應(yīng)該盡量使用 call Graph 功能做到事先防范。
審核編輯:湯梓紅
-
mcu
+關(guān)注
關(guān)注
146文章
17123瀏覽量
350983 -
恩智浦
+關(guān)注
關(guān)注
14文章
5857瀏覽量
107315 -
IDE
+關(guān)注
關(guān)注
0文章
338瀏覽量
46737 -
編譯器
+關(guān)注
關(guān)注
1文章
1623瀏覽量
49108 -
mcuxpresso
+關(guān)注
關(guān)注
1文章
40瀏覽量
4174
原文標(biāo)題:MCUXpresso IDE 的棧分析功能
文章出處:【微信號(hào):NXP_SMART_HARDWARE,微信公眾號(hào):恩智浦MCU加油站】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論