目錄
- 重定位概念的引入
- 一、數據段重定位
- 1.作用:
- 2.目的:
- 3.自己模擬代碼
- 二、BSS段清零
- 1.作用:
- 2.目的:
- 3.自己模擬代碼
- 三,實現原理
重定位概念的引入
單片機中內存段的詳細介紹
在單片機中內存分為了很多不同的區域,在上面的文章中可以看到,flash與RAM中有兩段空間的信息在flash與RAM都保持了,分別是RWdata段與bss段,在啟動文件中對這兩塊區域分別進行了數據段重定位與BSS段清零,而對應的代碼是由編譯器根據散列文件自動生成的,我們看不到,但是我們可以不讓編譯器自動生成自己進行數據段重定位與BSS段清零
數據段重定位與BSS段清零對應的代碼是由編譯器根據散列文件自動生成的,我們看不到,我們可以通過反匯編文件以及Debug時的反匯編窗口看到編譯器給我們生成的代碼如何讓keil編譯生成bin文件與反匯編文件?,同時我們可以不讓編譯器自動生成自己進行數據段重定位與BSS段清零
操作方法就是
1.不使用main的函數名,使用其它函數名
一、數據段重定位
1.作用:
-
將.data段從非易失性存儲器(如Flash/ROM)復制到易失性存儲器(RAM)中的目標位置。
-
鏈接器在編譯鏈接階段,會將已初始化的全局變量和靜態變量放入.data段,并將它們的初始值存儲在Flash中。
-
但是程序運行時,這些變量必須位于RAM中才能被修改。
-
啟動文件負責將存儲在Flash中的初始值數據“搬運”到RAM中對應變量的地址處。
2.目的:
-
確保初始化變量擁有正確的初始值:如果.data段沒有從Flash復制到RAM,那么所有初始化為非零值的全局變量和靜態變量在程序啟動時將處于未定義狀態(RAM上電后的值是隨機的),而不是程序員設定的初始值。這會導致程序行為完全不可預測。
-
滿足C/C++語言規范要求:C/C++標準規定全局變量和靜態變量必須在程序啟動時完成初始化(如果有初始值)。重定位是實現這一語言要求的底層機制。
-
實現變量的可寫性:Flash通常是只讀的(在運行時),而變量需要被程序讀寫。將初始值復制到RAM中是變量可寫的先決條件。
3.自己模擬代碼
/*啟動文件模擬過程代碼*/IMPORT |Image$$RW_IRAM1$$Base|IMPORT |Image$$RW_IRAM1$$Length|IMPORT |Load$$RW_IRAM1$$Base|LDR R0, = |Image$$RW_IRAM1$$Base| ; DESTLDR R1, = |Load$$RW_IRAM1$$Base| ; SORUCELDR R2, = |Image$$RW_IRAM1$$Length| ; LENGTH //根據ARM架構AAPCS法則模擬函數參數BL My_Copy
/*C語言實現拷貝邏輯*/
void My_Copy(char* const Destination,char* const Soruce,int Size)
{int i = 0;for(;i < Size;i ++){Destination[i] = Soruce[i];}return;
}
二、BSS段清零
1.作用:
-
將.bss段在RAM中對應的內存區域全部填充為0。
-
鏈接器將未初始化或顯式初始化為0的全局變量和靜態變量放入.bss段。.bss段在程序映像(存儲在Flash中)中不占用實際存儲空間,它只記錄需要在RAM中預留多少空間以及起始地址。
-
啟動代碼需要找到.bss段在RAM中的起始地址和長度,然后將這塊內存區域清零。
2.目的:
-
確保未初始化的變量從0開始:C/C++標準規定未顯式初始化的全局變量和靜態變量在程序啟動時必須被初始化為0(對于基本類型是0,對于指針是NULL)。如果不清零.bss段,這些變量在啟動時將包含RAM上電后的隨機垃圾值,導致程序行為不可預測,尤其是指針變量可能指向非法地址。
-
節省Flash存儲空間:由于.bss段中的變量初始值都是0,沒有必要在Flash中存儲一大串0值。啟動時直接清零對應的RAM區域是更高效的方法(空間換時間)。
-
滿足語言規范要求:與.data段重定位一樣,BSS清零是實現C/C++語言對未初始化/零初始化全局變量和靜態變量初始化要求的底層機制。
3.自己模擬代碼
/*啟動文件模擬過程代碼*/IMPORT |Image$$RW_IRAM1$$ZI$$Base|IMPORT |Image$$RW_IRAM1$$ZI$$Length| LDR R0, = |Image$$RW_IRAM1$$ZI$$Base| ; DESTLDR R1, = 0 ; SORUCE LDR R2, = |Image$$RW_IRAM1$$ZI$$Length| ; LENGTH BL My_Zero_Init //根據ARM架構AAPCS法則模擬函數參數BL My_Zero_Init
/*C語言實現清零邏輯*/
void My_Zero_Init(char* const Destination,int Soruce,int Size)
{int i = 0;for(;i < Size;i ++){Destination[i] = Soruce;}return;
}
三,實現原理
C語言-》編譯器-》機器碼(匯編就是機器碼的注釋)
展開能力,C語言與匯編的后溝就是編譯器,C語言是編程工具,匯編語言是結果
判斷結果是否符合預期?學會看反匯編
如何控制結果符合預期?學會一定的C語言語法以及編譯器的工作特性
針對keil平臺,控制編譯器的方法就是分散加載文件
我們可以通過分散加載文件的語法來控制代碼函數變量的鏈接地址
理論上也可以控制加載地址針對使用燒錄器來說因為燒錄器使用的是hex文件包含程序燒錄的地址信息,但是一般來說程序編譯后使用bin二進制文件這個文件使用特定的燒錄工具,燒錄的是純機器碼即代碼指令不包含代碼加載地址信息。
分散加載的語法可以參考分散加載文件與attribute關鍵字使用說明
同時分散加載文件中也包含一些符號信息,我們在代碼中可以使用特定的語法來引入這些符號的值,RW-data段的加載地址大小,搬運的目的地址,同理bss段數據也是同理。
導入符號的方法有
/*匯編文件使用*/IMPORT |Image$$ER_IROM1$$Base|IMPORT |Image$$ER_IROM1$$Length|IMPORT |Load$$ER_IROM1$$Base|IMPORT |Image$$RW_IRAM1$$Base|IMPORT |Image$$RW_IRAM1$$Length|IMPORT |Load$$RW_IRAM1$$Base|IMPORT |Image$$RW_IRAM1$$ZI$$Base|IMPORT |Image$$RW_IRAM1$$ZI$$Length|
/*C語言文件使用*/extern int Image$$ER_IROM1$$Base;extern int Image$$ER_IROM1$$Length;extern int Load$$ER_IROM1$$Base;extern int Image$$RW_IRAM1$$Base;extern int Image$$RW_IRAM1$$Length;extern int Load$$RW_IRAM1$$Base;extern int Image$$RW_IRAM1$$ZI$$Base;extern int Image$$RW_IRAM1$$ZI$$Length; extern int Image$$ER_IROM1$$Base[];extern int Image$$ER_IROM1$$Length[];extern int Load$$ER_IROM1$$Base[];extern int Image$$RW_IRAM1$$Base[];extern int Image$$RW_IRAM1$$Length[];extern int Load$$RW_IRAM1$$Base[];extern int Image$$RW_IRAM1$$ZI$$Base[];extern int Image$$RW_IRAM1$$ZI$$Length[]; extern int * Image$$ER_IROM1$$Base;extern int * Image$$ER_IROM1$$Length;extern int * Load$$ER_IROM1$$Base;extern int * Image$$RW_IRAM1$$Base;extern int * Image$$RW_IRAM1$$Length;extern int * Load$$RW_IRAM1$$Base;extern int * Image$$RW_IRAM1$$ZI$$Base;extern int * Image$$RW_IRAM1$$ZI$$Length;