研究嵌入式軟件架構時遇到的初始化堆棧溢出問題

文章目錄

  • 2025年4月10日新增
    • 分析PC寄存器指針值排查問題
    • map文件設計到的知識點
      • 1. **.bss 段(Block Started by Symbol)**
      • 2. **.data 段**
      • 3. **.text 段**
      • 4. **.heap 段**
      • 5. **.stack 段**
      • 6. **.rodata 段(只讀數據段)**
      • 7. **.init 和 .fini 段**
      • 8. **符號信息(符號表)**
      • 總結
  • 如題
  • 如何解決?
  • 涉及到的知識點
  • 記錄遇到的坑


2025年4月10日新增

首先程序框架已經解決所有問題,可以完美運行了!今天從git上download下來昨天的出錯版本,重新分析下昨天的錯誤程序

在這里插入圖片描述
堆棧設置的是1K空間
在這里插入圖片描述
這個uart_init()函數里面的buff數組太大導致了堆棧溢出,昨天出了好幾種堆棧溢出的情況,這是其一,那個systick初始化的問題我今天沒復現,忘記昨天啥情況了,上面即使uart_inti()函數不調用,但是只要上面定義了還是會棧溢出,為什么呢?
在這里插入圖片描述
左邊error的RW多了0.25k空間,多出來的主要是ZI Data數據
在這里插入圖片描述
發現error的map文件中,出現了stdio_streams.o,這個跟printf()函數有關
在這里插入圖片描述
可以看出跟uart_init()函數沒有關系
在這里插入圖片描述
注釋了這行代碼就解決問題了
在這里插入圖片描述
分析兩個程序的內存布局差異及跑飛原因
一、內存布局差異對比
??堆棧空間分配??
??異常程序??:
STACK 段起始地址 0x20000500,大小 1024 bytes
HEAP 段起始地址 0x20000300,大小 512 bytes
??正常程序??:
STACK 段起始地址 0x200003f8,大小 1024 bytes
HEAP 段起始地址 0x200001f8,大小 512 bytes
??差異??:正常程序的堆棧和堆地址更緊湊,可能是通過調整啟動文件中的 Stack_Size 和 Heap_Size 宏定義優化了內存分布。
??全局變量區域(.bss/.data)??
??異常程序??的 .bss 段存在多模塊重疊分配(如 hal_uart1.o 和 stdio_streams.o),導致 active_config 等變量地址沖突(如 0x20000190 和 0x20000078)。
??正常程序??的 .bss 段地址分配連續且無重疊,例如 hal_uart1.o 的 rx_buffer 從 0x2000007c 開始,與前后模塊無沖突。
二、跑飛原因推測
??堆棧溢出??
??異常程序??的堆棧起始地址 0x20000500 與 .bss 段末端 0x200002a0 間隔僅 0x260 bytes(約 608 字節),若存在深層次函數調用或中斷嵌套,可能超過棧容量導致數據覆蓋。
??正常程序??的堆棧起始地址 0x200003f8 與 .bss 末端 0x20000198 間隔 0x260 bytes,但通過優化內存分布避免了關鍵變量被覆蓋。
??內存訪問越界??
??異常程序??中 hal_uart1.o 的 tx_buffer 分配了 132 bytes,但其后的 active_config 地址 0x20000190 可能被越界寫入(如環形緩沖區未正確管理),破壞相鄰數據。
??中斷嵌套未考慮??
若異常程序中存在高頻中斷(如 UART 接收中斷),且未預留足夠中斷棧空間(STM32 默認使用主堆棧 MSP),嵌套中斷可能耗盡 1024 bytes 的堆棧容量。

分析PC寄存器指針值排查問題

在這里插入圖片描述
在這里插入圖片描述
PC起始地址是0x8000240,是SystemInit
在這里插入圖片描述

在這里插入圖片描述
單步執行后發現PC指針跑飛
在這里插入圖片描述
實際上,我這里是重定向出了問題

//int fputc(int ch, FILE *f)
//{
//	while (0 == (USART1->SR & 0X40));//	USART1->DR = (uint8_t)ch;//	return ch;
//}#if 1
#pragma import(__use_no_semihosting)// 使用#pragma指令來導入__use_no_semihosting,表示不使用半主機模式。
// 半主機模式通常用于調試,允許在目標設備上運行時與主機進行交互。
struct __FILE
{int handle;
};FILE __stdout;// 定義一個全局變量__stdout,類型為FILE,用于標準輸出。
void _sys_exit(int x)
{x = x;
}int fputc(int ch, FILE *f)
{while (0 == (USART1->SR & 0X40));USART1->DR = (uint8_t)ch;return ch;
}
#endif

參考正點原子這樣修改解決了問題

map文件設計到的知識點

在分析嵌入式系統或單片機的 map 文件時,map 文件提供了詳細的內存布局和各個段(section)在內存中的分配情況。map 文件的內容對調試、優化和了解程序結構非常有幫助。

下面是對 map 文件中常見的幾個段(例如 .bss 段、.data 段以及其他段)的詳細分析。

1. .bss 段(Block Started by Symbol)

  • 描述.bss 段是用于未初始化的全局變量和靜態變量的內存區域。在程序啟動時,這些變量會被清零或初始化為零。
  • 特點
    • .bss 段通常不占用實際的存儲空間。在編譯時,鏈接器并不為 .bss 段中的變量分配實際的存儲空間,而是指示程序運行時為這些變量分配空間,并在程序加載時清零它們。
    • 它通常包括未顯式初始化為特定值的靜態變量和全局變量。
  • 例子
    int global_var;  // 默認值為 0
    static int static_var;  // 默認值為 0
    
  • map 文件中的信息
    • map 文件中,.bss 段會列出所有未初始化的全局和靜態變量及其所占的內存空間大小。由于這些變量會在程序啟動時被清零,因此 .bss 段通常不會占用磁盤上的存儲空間。
    • 大小map 文件中 .bss 段的大小通常會較大,尤其是未初始化變量較多時。

2. .data 段

  • 描述.data 段是用于存儲已初始化的全局變量和靜態變量的內存區域。與 .bss 段不同,.data 段中的變量在編譯時就已經被賦予了初始值。
  • 特點
    • .data 段包含所有顯式初始化的全局和靜態變量。
    • 在程序啟動時,.data 段的內容會從程序的可執行文件(或固件)加載到內存中。
  • 例子
    int global_var = 10;  // 已初始化的全局變量
    static int static_var = 20;  // 已初始化的靜態變量
    
  • map 文件中的信息
    • map 文件會列出 .data 段中所有已初始化變量及其在內存中的起始地址和大小。
    • 大小.data 段的大小取決于已初始化的變量總量。

3. .text 段

  • 描述.text 段是程序的代碼段,它存放了編譯后的機器代碼。在這個段中沒有變量,只有程序的指令。
  • 特點
    • 代碼段通常是只讀的,因為程序代碼在執行時不應該被修改。
    • .text 段通常是程序中占用空間最多的部分,尤其是復雜程序和函數較多時。
  • map 文件中的信息
    • map 文件會顯示 .text 段的起始地址、大小以及每個函數的實際內存位置。
    • 大小:代碼段的大小會受到程序中函數和指令的復雜度影響。

4. .heap 段

  • 描述.heap 段用于動態分配內存(例如通過 malloc, calloc, new 等函數分配的內存),通常位于程序的棧(.stack)段之后。
  • 特點
    • 動態內存分配的內存區域。
    • 程序運行時的內存分配和釋放會發生在此區域。
  • map 文件中的信息
    • .heap 段的內存大小取決于程序運行時實際分配的內存量。
    • map 文件會顯示 .heap 段的起始地址、大小等信息。

5. .stack 段

  • 描述.stack 段用于存儲函數調用時的局部變量、返回地址等信息。棧是一個動態分配的內存區域,隨著函數的調用和返回不斷增長和縮小。
  • 特點
    • 棧的大小是由編譯器或鏈接器預設的,通常在 map 文件中可以看到棧的大小。
    • 棧是一個向下增長的內存區域,通常在內存地址空間較高的位置。
  • map 文件中的信息
    • map 文件會顯示棧的起始位置和大小等信息。棧的大小通常在 linker script 或編譯器設置中配置。

6. .rodata 段(只讀數據段)

  • 描述.rodata 段用于存儲程序中的常量數據(如字符串字面量、常量數組等),這些數據在程序運行期間不會被修改。
  • 特點
    • .rodata 是只讀的,不允許修改其中的數據。
    • 通常存儲字符串常量、全局常量等。
  • map 文件中的信息
    • map 文件會列出 .rodata 段的內存占用情況,包括字符串常量等的內存地址和大小。

7. .init 和 .fini 段

  • 描述
    • .init 段包含程序初始化代碼,在程序啟動時被調用,通常用于設置初始化操作,如硬件初始化等。
    • .fini 段包含程序結束時的清理代碼,通常在程序退出前進行資源清理。
  • 特點
    • 這兩個段通常由編譯器或鏈接器自動處理。
  • map 文件中的信息
    • map 文件會顯示 .init.fini 段的地址和大小。

8. 符號信息(符號表)

  • 描述map 文件中還包含符號表,列出了程序中的所有符號(如變量、函數等)及其在內存中的地址。
  • 特點
    • 符號表包含了所有變量和函數的名稱、類型和內存地址等信息,通常用于調試。
  • map 文件中的信息
    • 每個符號(如全局變量、靜態變量、函數等)都會有一個對應的內存地址和大小。

總結

map 文件中,除了 .bss.data.text 段外,還包含了棧、堆、只讀數據段等信息。每個段都有不同的作用,且在編譯和鏈接過程中被分配到不同的內存區域。通過查看 map 文件,你可以清晰地了解程序的內存分布、變量和函數的位置,進而優化內存使用和提高程序性能。

如題

//hal_systick_config_t cfg =
//{
//    .clk_source = SYSTICK_CLK_HCLK_DIV8,
//    .reload_value = 21000,
//    .sys_clk_frequency = 168,
//    .tick_callback = timer_ticks_count
//};static void systick_init(void)
{hal_systick_config_t cfg ={.clk_source = SYSTICK_CLK_HCLK_DIV8,.reload_value = 21000,.sys_clk_frequency = 168,.tick_callback = timer_ticks_count};hal_systick_init(&cfg);
}

這個代碼中,只要cfg結構體變量放到函數外面一切正常,只要放在函數內部則進入debug模式后需要三次run之后程序才會執行,把棧空間調大依然會出現這個問題

如何解決?

在這里插入圖片描述

涉及到的知識點

一、堆棧初始值計算公式
STM32 的堆棧起始地址(即 SP 初始值)由以下公式決定:

SP_初始值=SRAM起始地址+RW_Data大小+ZI_Data大小+Stack_Size
??SRAM起始地址??:STM32F407 的 SRAM 起始地址為 0x20000000
??RW_Data(可讀寫數據)??:已初始化的全局變量和靜態變量
??ZI_Data(零初始化數據)??:未初始化的全局變量和靜態變量(默認填充為0)
??Stack_Size??:啟動文件中定義的棧大小(您設置為 0x400,即 1KB)
二、現象解釋
在您的案例中,??SP 初始值為 0x20000900?? 的具體計算邏輯如下:

??默認內存布局??:
SRAM 起始地址:0x20000000
程序中的全局變量(RW+ZI)總大小:假設為 0x900 字節
棧大小(Stack_Size):0x400 字節(1KB)
則 SP 初始值 = 0x20000000 + 0x900 + 0x400 = 0x20000D00
但實際觀察到的 SP 值為 0x20000900,這表明 ??RW+ZI 實際僅占用 0x500 字節??(0x20000900 - 0x20000000 - 0x400 = 0x500)。
??堆棧生長方向??:
STM32 的棧是 ??向下生長?? 的,SP 初始值指向棧頂(高地址),棧底為 SP - Stack_Size。因此:
棧頂地址:0x20000900
棧底地址:0x20000900 - 0x400 = 0x20000500
棧空間范圍:0x20000500 ~ 0x20000900。

根據.map文件分析,可以得出以下關于棧溢出問題的結論:

一、棧空間配置分析
??棧大小設置??
在startup_stm32f40_41xxx.o中明確顯示:
STACK 0x20000500 0x00000400 (1KB)
這與用戶設置的0x400(1024字節)完全一致。
??內存布局驗證??
棧的地址范圍:0x20000500 ~ 0x20000900
堆的地址范圍:0x20000300 ~ 0x20000500(512字節)
全局變量(ZI + RW Data):總占用2304字節(RW_IRAM1區域)。
二、棧溢出風險判斷
??棧使用量估算??
根據Image component sizes,ZI Data(零初始化數據)為2268字節,RW Data為36字節,總占用2304字節。
??關鍵點??:ZI Data包含全局變量和靜態變量,而棧和堆的地址范圍與ZI/RW區域相鄰。如果函數調用鏈中的局部變量過多,可能導致棧指針(SP)超出0x20000900,覆蓋其他內存區域。
??調試現象關聯??
用戶提到“變量放在函數內部時需要多次運行才成功”,這與棧溢出的典型現象(隨機性崩潰、HardFault)高度吻合。局部變量存儲在棧中,若超過0x400限制,會破壞中斷向量表或關鍵數據,導致程序異常。
??溢出檢測方法??
??靜態分析??:檢查函數調用層級和局部變量總大小。例如,若某函數定義了char buffer[1024],直接占滿棧空間,必然溢出。
??動態驗證??:在Keil調試器中觀察SP寄存器值是否超出0x20000900,或通過Memory窗口查看棧內存是否被意外改寫。

記錄遇到的坑

1、函數指針一定不能跑飛,加上保護,防止程序跑飛
2、局部變量或者局部數據,尤其局部大數組一定要static化,或者直接全局化,防止堆棧溢出
3、利用三元運算符替代if邏輯,程序看起來簡潔優雅
4、巧用#ifndef HAL_STATUS_T_DEFINED
#define HAL_STATUS_T_DEFINED
命令來解決重復定義

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/901179.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/901179.shtml
英文地址,請注明出處:http://en.pswp.cn/news/901179.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

軟件架構評估兩大法:ATAM 和 SAAM 的對比與實踐

架構權衡分析方法(ATAM)和軟件架構分析方法(SAAM)是軟件架構評估領域中非常重要的兩種方法,以下為你詳細介紹: 一、架構權衡分析方法(ATAM) 1.背景與起源:ATAM 是由卡耐…

Python爬蟲-爬取全球股市漲跌幅和漲跌額數據

前言 本文是該專欄的第52篇,后面會持續分享python爬蟲干貨知識,記得關注。 本文中,筆者將基于Python爬蟲,實現批量采集全球股市行情(亞洲,美洲,歐非,其他等)的各股市“漲跌幅”以及“漲跌額”數據。 具體實現思路和詳細邏輯,筆者將在正文結合完整代碼進行詳細介紹。…

電流互感器的兩相星形接線的建模與仿真

微?“電擊小子程高興的MATLAB小屋”獲取巨額優惠 1.模型簡介 本仿真模型基于MATLAB/Simulink(版本MATLAB 2016Rb)軟件。建議采用matlab2016 Rb及以上版本打開。(若需要其他版本可聯系代為轉換) 2.仿真模型 3.仿真結果 3.1一次…

詳解 kotlin 相對 Java 特有的關鍵字及使用

文章目錄 1. val 和 var2. fun3. when4. is 和 !is5. lateinit6. by7. reified8. companion 本文首發地址:https://h89.cn/archives/366.html 最新更新地址:https://gitee.com/chenjim/chenjimblog Kotlin 在兼容Java的基礎上,引入了許多特有…

國標GB28181視頻平臺EasyCVR如何搭建汽車修理廠遠程視頻網絡監控方案

一、背景分析 近年我國汽車保有量持續攀升,與之相伴的汽車保養維修需求也逐漸提高。隨著社會經濟的發展,消費者對汽車維修服務質量的要求越來越高,這使得汽車維修店的安全防范與人員管理問題面臨著巨大挑戰。 多數汽車維修店分布分散&#…

linux RCU技術

RCU(Read-Copy-Update)是Linux內核中的一種同步機制,用于在多核處理器環境中實現無鎖讀取和延遲更新。Linux RCU(Read-Copy-Update)技術通過一種高效的同步機制來處理并發沖突,確保在多核環境中讀者和寫者對…

【筆記ing】AI大模型-02開發環境搭建

按實驗需求合理選用實例規格,一般:模型開發階段:使用最低算力2U8GB CPU。訓練或推理階段:切換至GPU規格,用完及時關閉算力環境,且切回最低算力規格。 每次實驗結束手動關閉實例。使用ModelArts公有云資源。…

Python——numpy測試題目

題目: 生成一個2行3列隨機整數二維數組a使用Numpy方法對(1)中數組a進行整體求積使用Numpy方法對(1)中數組a進行求每列最大值索引定義一個NumPy一維數組 b,元素為 1 到 10 的整數獲取(4&#x…

系分論文《論面向服務開發方法在設備租賃行業的應用》

系統分析師論文系列 【摘要】 2022年5月,我司承接某工程機械租賃企業"智能租賃運營管理平臺"建設項目,我作為系統分析師主導系統架構設計。該項目需整合8大類2000余臺設備資產,覆蓋全國15個區域運營中心與300家代理商,實…

Unity UI中的Pixels Per Unit

Pixels Per Unit在圖片導入到Unity的時候,將圖片格式設置為Sprite的情況下會出現,其意思是精靈中的多少像素對應世界中的一個單位,默認是100 1. 對于在世界坐標中 在世界坐標中,一般對于Sprite的應用是Sprite Renderer組件 使…

Boost Graph Library (BGL) 介紹與使用示例

Boost Graph Library (BGL) 介紹與使用示例 Boost Graph Library (BGL) 是 Boost 庫中用于圖論計算的模塊,提供了處理圖數據結構的通用接口和多種圖算法實現。 BGL 主要特性 提供多種圖表示方式:鄰接表、鄰接矩陣等包含常用圖算法:DFS、BF…

opencv(C++)操作圖像像素

文章目錄 添加噪點的案例圖像像素值1、訪問圖像屬性2、像素訪問方法 at灰度圖像彩色圖像 3、OpenCV 的向量類型4、 圖像傳遞方式 The cv::Mat_ 類1、作用及優點2、使用 cv::Mat_ 簡化像素訪問 用指針掃描圖像背景算法案例原理1. 圖像數據存儲的基本結構2、行填充(Pa…

Python實現貪吃蛇一

貪吃蛇是一款經典的小游戲,最近嘗試用Python實現它。先做一個基礎版本實現以下目標: 1、做一個按鈕,控制游戲開始 2、按Q鍵退出游戲 3、右上角顯示一個記分牌 4、隨機生成一個食物,蛇吃到食物后長度加一,得10分 5、蛇碰…

《AI大模型應知應會100篇》第13篇:大模型評測標準:如何判斷一個模型的優劣

第13篇:大模型評測標準:如何判斷一個模型的優劣 摘要 近年來,大語言模型(LLMs)在自然語言處理、代碼生成、多模態任務等領域取得了顯著進展。然而,隨著模型數量和規模的增長,如何科學評估這些模…

工會考試重點內容有哪些:核心考點與備考指南

工會考試重點內容總結:核心考點與備考指南 工會考試主要考察考生對工會法律法規、職能職責、實務操作等內容的掌握程度,適用于企事業單位工會干部、社會化工會工作者等崗位的選拔。本文梳理工會考試的核心考點,幫助考生高效備考。 一、工會…

Verilog學習-1.模塊的結構

module aoi(a,b,c,d,f);/*模塊名為aoi,端口列表a、b、c、d、f*/ input a,b,c,d;/*模塊的輸入端口為a,b,c,d*/ output f;;/*模塊的輸出端口為f*/ wire a,b,c,d,f;/*定義信號的數據類型*/ assign f~((a&b)|(~(c&d)));/*邏輯功能描述*/ endmoduleveirlog hdl 程…

MySQL數據庫備份與恢復詳解

在數據庫管理中,數據的備份與恢復是至關重要的一環。對于MySQL數據庫,定期備份不僅能防止數據丟失,還能在發生故障時快速恢復數據庫。本文將詳細介紹MySQL數據庫的備份與恢復方法,覆蓋所有常用備份和恢復方式,幫助大家…

FFMPEG和opencv的編譯

首先 sudo apt-get update -qq && sudo apt-get -y install autoconf automake build-essential cmake git-core libass-dev libfreetype6-dev libgnutls28-dev libmp3lame-dev libsdl2-dev libtool libva-dev libvdpau-dev libvorbis-de…

華為機試—最大最小路

題目 對于給定的無向無根樹&#xff0c;第 i 個節點上有一個權值 wi? 。我們定義一條簡單路徑是好的&#xff0c;當且僅當&#xff1a;路徑上的點的點權最小值小于等于 a &#xff0c;路徑上的點的點權最大值大于等于 b 。 保證給定的 a<b&#xff0c;你需要計算有多少條簡…

spring cloud微服務開發中聲明式服務調用詳解及主流框架/解決方案對比

聲明式服務調用詳解 1. 核心概念 定義&#xff1a;通過配置或注解聲明服務調用邏輯&#xff0c;而非手動編寫客戶端代碼&#xff0c;提升開發效率與可維護性。核心特性&#xff1a; 解耦&#xff1a;調用邏輯與業務代碼分離內置容錯&#xff1a;熔斷、超時、重試等動態發現&am…