為了識(shí)別運(yùn)行的嵌入式系統(tǒng)中的堆棧溢出問(wèn)題,SEGGER編譯器通過(guò)為每個(gè)函數(shù)生成檢測(cè)代碼的方式來(lái)檢查堆棧溢出。該功能可以使用命令行開(kāi)關(guān)-mstack-overflow-check來(lái)使能。對(duì)于安全系統(tǒng),必須在溢出的堆棧破壞內(nèi)存之前檢測(cè)到堆棧溢出,因此需要在更改堆棧指針和需大量堆棧空間之前進(jìn)行檢查。
一,Embedded Studio的堆棧溢出預(yù)防
在Embedded Studio中啟用堆棧溢出預(yù)防功能,僅需將工程選項(xiàng)Code->Code Generation->Enable Stack Overflow Prevention設(shè)置為“Yes”。
如果工程中沒(méi)有實(shí)現(xiàn)錯(cuò)誤回調(diào)函數(shù)__SEGGER_STOP_X_OnError,它默認(rèn)保持在一個(gè)無(wú)限循環(huán)中。Embedded Studio安裝目錄$(StudioDir)/samples下的SEGGER_STOP.c中包含一個(gè)錯(cuò)誤處理的示例實(shí)現(xiàn)。
二,編譯器生成的代碼
如果在編譯器中使能堆棧溢出檢查,生成的代碼將被改變,如下所示:
1、不使用堆棧的函數(shù)不會(huì)被更改。
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必須在進(jìn)入函數(shù)時(shí)必須入棧。
4、不使用本地堆棧但需在堆棧中保存寄存器的函數(shù)中,在寄存器壓棧后將調(diào)用不帶參數(shù)的函數(shù)__SEGGER_STOP_GROW_0()。
5、需要?jiǎng)討B(tài)分配堆棧的函數(shù)(如使用alloc()函數(shù)或可變大小的數(shù)組),編譯后代碼也將調(diào)用__SEGGER_STOP_GROW_R3()。因?yàn)榉峙淇赡馨l(fā)生在函數(shù)執(zhí)行中,需指示寄存器分配器,確保R3可以使用。
然后,被調(diào)用的函數(shù)可以使用存儲(chǔ)在全局變量中的堆棧上限值檢查堆棧是否溢出。檢查函數(shù)將在需要保存的寄存器被壓棧后調(diào)用。因此,必須計(jì)算堆棧上限,以便始終有棧空間用于:
在函數(shù)入口處,入棧所有的通用寄存器(R0 - R11, LR),有一些優(yōu)化可能導(dǎo)致R0 - R3入棧。
所有被調(diào)用者需保存的浮點(diǎn)/矢量寄存器(D8 - D15)
中斷入口需保存的寄存器 (8個(gè)字)
3個(gè)(備用)字用于對(duì)齊和緊急溢出緩存
可以使用__attribute__((no_stack_overflow_check))禁止生成單個(gè)函數(shù)的堆棧檢查代碼。
三、堆棧檢查和錯(cuò)誤處理
啟用“防止堆棧溢出”功能時(shí),必須實(shí)現(xiàn)下列堆棧檢查函數(shù)。在Embedded Studio中,這些函數(shù)已添加到標(biāo)準(zhǔn)庫(kù)中。
__SEGGER_STOP_GROW_R3
__SEGGER_STOP_GROW_R4
__SEGGER_STOP_GROW_0
堆棧溢出時(shí),堆棧檢查函數(shù)應(yīng)該跳轉(zhuǎn)到用戶(hù)提供的錯(cuò)誤處理回調(diào)函數(shù)__SEGGER_STOP_X_OnError()。
堆棧檢查函數(shù)
編譯器生成的代碼調(diào)用檢查函數(shù)檢查剩余的堆棧大小,在堆棧溢出的情況下函數(shù)不能返回。為了提高效率,這些函數(shù)沒(méi)有遵循標(biāo)準(zhǔn)調(diào)用約定。因此,函數(shù)不能修改除了R12和包含堆棧大小參數(shù)的寄存器之外的任何寄存器,函數(shù)在返回之前還必須調(diào)整堆棧指針。
錯(cuò)誤處理回調(diào)
為了確保錯(cuò)誤處理回調(diào)不使用溢出堆棧,它應(yīng)該在純匯編中實(shí)現(xiàn),并且在禁用堆棧溢出檢查功能狀態(tài)下進(jìn)行編譯。
在默認(rèn)實(shí)現(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)用者通過(guò)R3、R12和LR傳遞。
錯(cuò)誤處理回調(diào)可能會(huì)將溢出堆棧重置為安全值。同時(shí),它可能會(huì)調(diào)用其他函數(shù),比如記錄錯(cuò)誤和重置系統(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 ); }
四、堆棧上限
啟動(dòng)代碼必須初始化堆棧上限,至少初始化主堆棧上限變量__SEGGER_STOP_Limit_MSP。在默認(rèn)實(shí)現(xiàn)中,該符號(hào)由運(yùn)行時(shí)初始化代碼自動(dòng)初始化為默認(rèn)值。
為了調(diào)整上限值,例如改變?yōu)楸4婕拇嫫鞅A舻目臻g,以及初始化__SEGGER_STOP_Limit_PSP,應(yīng)該實(shí)現(xiàn)并調(diào)用__SEGGER_STOP_X_InitLimits。
使用SEGGER鏈接器,運(yùn)行時(shí)初始化代碼將自動(dòng)調(diào)用__SEGGER_STOP_X_InitLimits:
initialize bycalling __SEGGER_STOP_X_InitLimits { section .data.stop.* };
使用GNU鏈接器時(shí),應(yīng)該在main中的開(kāi)始位置調(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 ... }
在運(yùn)行初始化之前調(diào)用函數(shù)
當(dāng)系統(tǒng)在運(yùn)行初始化之前調(diào)用函數(shù)時(shí),Cortex-M上默認(rèn)在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
當(dāng)使用RTOS或其他多任務(wù)機(jī)制時(shí),任務(wù)切換程序必須在切換堆棧時(shí)更新堆棧上限變量(通常為_(kāi)_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 ...
建議對(duì)所有任務(wù)啟用堆棧檢查。RTOS可以通過(guò)將limit變量設(shè)置為0來(lái)禁用某些任務(wù)的堆棧檢查。
堆棧溢出幾乎在每個(gè)系統(tǒng)中都可能遇到。Embedded Studio提供了使用示例,展示簡(jiǎn)單系統(tǒng)和多任務(wù)系統(tǒng)的堆棧溢出行為及處理。
審核編輯:劉清
-
寄存器
+關(guān)注
關(guān)注
31文章
5357瀏覽量
120685 -
RTOS
+關(guān)注
關(guān)注
22文章
817瀏覽量
119724 -
編譯器
+關(guān)注
關(guān)注
1文章
1636瀏覽量
49173 -
緩存器
+關(guān)注
關(guān)注
0文章
63瀏覽量
11673
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論