為了識別運行的嵌入式系統(tǒng)中的堆棧溢出問題,SEGGER編譯器通過為每個函數(shù)生成檢測代碼的方式來檢查堆棧溢出。該功能可以使用命令行開關(guān)-mstack-overflow-check來使能。對于安全系統(tǒng),必須在溢出的堆棧破壞內(nèi)存之前檢測到堆棧溢出,因此需要在更改堆棧指針和需大量堆棧空間之前進行檢查。
一,Embedded Studio的堆棧溢出預(yù)防
在Embedded Studio中啟用堆棧溢出預(yù)防功能,僅需將工程選項Code->Code Generation->Enable Stack Overflow Prevention設(shè)置為“Yes”。
如果工程中沒有實現(xiàn)錯誤回調(diào)函數(shù)__SEGGER_STOP_X_OnError,它默認保持在一個無限循環(huán)中。Embedded Studio安裝目錄$(StudioDir)/samples下的SEGGER_STOP.c中包含一個錯誤處理的示例實現(xiàn)。
二,編譯器生成的代碼
如果在編譯器中使能堆棧溢出檢查,生成的代碼將被改變,如下所示:
1、不使用堆棧的函數(shù)不會被更改。
2、使用本地堆棧幀但不使用R3傳遞參數(shù)的函數(shù)中:堆棧幀的設(shè)置(通常使用sub sp, #size指令)被替換為將所需棧大小加載到寄存器R3中,然后調(diào)用函數(shù)__SEGGER_STOP_GROW_R3()。
3、使用本地堆棧幀并使用R3傳遞參數(shù)的函數(shù)中,操作與2相同,但是使用寄存器R4傳值,并調(diào)用函數(shù)__SEGGER_STOP_GROW_R4()。這意味著,R4必須在進入函數(shù)時必須入棧。
4、不使用本地堆棧但需在堆棧中保存寄存器的函數(shù)中,在寄存器壓棧后將調(diào)用不帶參數(shù)的函數(shù)__SEGGER_STOP_GROW_0()。
5、需要動態(tài)分配堆棧的函數(shù)(如使用alloc()函數(shù)或可變大小的數(shù)組),編譯后代碼也將調(diào)用__SEGGER_STOP_GROW_R3()。因為分配可能發(fā)生在函數(shù)執(zhí)行中,需指示寄存器分配器,確保R3可以使用。
然后,被調(diào)用的函數(shù)可以使用存儲在全局變量中的堆棧上限值檢查堆棧是否溢出。檢查函數(shù)將在需要保存的寄存器被壓棧后調(diào)用。因此,必須計算堆棧上限,以便始終有棧空間用于:
在函數(shù)入口處,入棧所有的通用寄存器(R0 - R11, LR),有一些優(yōu)化可能導(dǎo)致R0 - R3入棧。
所有被調(diào)用者需保存的浮點/矢量寄存器(D8 - D15)
中斷入口需保存的寄存器 (8個字)
3個(備用)字用于對齊和緊急溢出緩存
可以使用__attribute__((no_stack_overflow_check))禁止生成單個函數(shù)的堆棧檢查代碼。
三、堆棧檢查和錯誤處理
啟用“防止堆棧溢出”功能時,必須實現(xiàn)下列堆棧檢查函數(shù)。在Embedded Studio中,這些函數(shù)已添加到標準庫中。
__SEGGER_STOP_GROW_R3
__SEGGER_STOP_GROW_R4
__SEGGER_STOP_GROW_0
堆棧溢出時,堆棧檢查函數(shù)應(yīng)該跳轉(zhuǎn)到用戶提供的錯誤處理回調(diào)函數(shù)__SEGGER_STOP_X_OnError()。
堆棧檢查函數(shù)
編譯器生成的代碼調(diào)用檢查函數(shù)檢查剩余的堆棧大小,在堆棧溢出的情況下函數(shù)不能返回。為了提高效率,這些函數(shù)沒有遵循標準調(diào)用約定。因此,函數(shù)不能修改除了R12和包含堆棧大小參數(shù)的寄存器之外的任何寄存器,函數(shù)在返回之前還必須調(diào)整堆棧指針。
錯誤處理回調(diào)
為了確保錯誤處理回調(diào)不使用溢出堆棧,它應(yīng)該在純匯編中實現(xiàn),并且在禁用堆棧溢出檢查功能狀態(tài)下進行編譯。
在默認實現(xiàn)中,__SEGGER_STOP_X_OnError定義為:
__attribute__((naked, no_stack_overflow_check)) void __SEGGER_STOP_X_OnError(void);
它在堆棧檢查函數(shù)尾部調(diào)用,不遵循常規(guī)調(diào)用約定。堆棧上限值、新的堆棧指針值和調(diào)用者通過R3、R12和LR傳遞。
錯誤處理回調(diào)可能會將溢出堆棧重置為安全值。同時,它可能會調(diào)用其他函數(shù),比如記錄錯誤和重置系統(tǒng)。
示例:
void __SEGGER_STOP_X_OnError(void) { asm( "cpsid i " // Disable interrupts "mov r0, r12 " // Save overflowed SP "mov r1, r3 " // Save SP limit "sub r2, lr, #5 " // Save caller "mrs r3, CONTROL " // Get currently used stack "lsls r3, #30 " "ittee pl " // Reset this stack "ldrpl r12, =__stack_end__ " "msrpl msp, r12 " "ldrmi r12, =__stack_process_end__ " "msrmi psp, r12 " "bl _HandleStackError " // Call error handler "b . " // Stay here ); }
四、堆棧上限
啟動代碼必須初始化堆棧上限,至少初始化主堆棧上限變量__SEGGER_STOP_Limit_MSP。在默認實現(xiàn)中,該符號由運行時初始化代碼自動初始化為默認值。
為了調(diào)整上限值,例如改變?yōu)楸4婕拇嫫鞅A舻目臻g,以及初始化__SEGGER_STOP_Limit_PSP,應(yīng)該實現(xiàn)并調(diào)用__SEGGER_STOP_X_InitLimits。
使用SEGGER鏈接器,運行時初始化代碼將自動調(diào)用__SEGGER_STOP_X_InitLimits:
initialize bycalling __SEGGER_STOP_X_InitLimits { section .data.stop.* };
使用GNU鏈接器時,應(yīng)該在main中的開始位置調(diào)用__SEGGER_STOP_X_InitLimits
int main(void) { int NumItems; #if !defined (__SEGGER_LINKER) // // Optionally initialize stack limits if not done by runtime init. // __SEGGER_STOP_X_InitLimits(); #endif ... }
在運行初始化之前調(diào)用函數(shù)
當系統(tǒng)在運行初始化之前調(diào)用函數(shù)時,Cortex-M上默認在Reset_Handler中調(diào)用SystemInit, __SEGGER_STOP_Limit_MSP應(yīng)設(shè)置為0,以禁用堆棧檢查。
Reset_Handler: .extern __SEGGER_STOP_Limit_MSP // // Initialize main stack limit to 0 to disable stack checks before runtime init // movs R0, #0 ldr R1, =__SEGGER_STOP_Limit_MSP str R0, [R1] // // Call SystemInit // bl SystemInit ...
使用RTOS
當使用RTOS或其他多任務(wù)機制時,任務(wù)切換程序必須在切換堆棧時更新堆棧上限變量(通常為__SEGGER_STOP_Limit_PSP)。
ChangeTask: ... ldr r0, [r1, #0] // OS.pCurTask ldr r3, [r0, #8] // OS.pCurTask->pStackBottom add r3, #100 // Buffer before stack overflow ldr r2, =__SEGGER_STOP_Limit_PSP str r3, [r2] // Update stack limit ...
建議對所有任務(wù)啟用堆棧檢查。RTOS可以通過將limit變量設(shè)置為0來禁用某些任務(wù)的堆棧檢查。
堆棧溢出幾乎在每個系統(tǒng)中都可能遇到。Embedded Studio提供了使用示例,展示簡單系統(tǒng)和多任務(wù)系統(tǒng)的堆棧溢出行為及處理。
審核編輯:劉清
-
寄存器
+關(guān)注
關(guān)注
31文章
5321瀏覽量
120017 -
RTOS
+關(guān)注
關(guān)注
21文章
809瀏覽量
119437 -
編譯器
+關(guān)注
關(guān)注
1文章
1618瀏覽量
49052 -
緩存器
+關(guān)注
關(guān)注
0文章
63瀏覽量
11652
發(fā)布評論請先 登錄
相關(guān)推薦
評論