嵌入式linux開發 (二十) 內存管理(3.2) STM32F407ZGT6內存管理之從鏈接到執行的過程(MPU-Without)

鏈接

  • 鏈接做了什麼
1.3 What the linker does when constructing an executable image
armlink performs many operations, depending on the content of the input files and the command-line options you specify.

When you use the linker to construct an executable image, it:
Resolves symbolic references between the input object files.
Extracts object modules from libraries to satisfy otherwise unsatisfied symbolic references.
Removes unused sections.
Eliminates duplicate common groups and common code, data, and debug sections.
Sorts input sections according to their attributes and names, and merges sections with similar attributes and names into contiguous chunks.
Organizes object fragments into memory regions according to the grouping and placement information provided.
Assigns addresses to relocatable values.
Generates an executable image.
  • 鏈接腳本
鏈接的時候以(不同符號的相對位置信息)爲輸入,對所有.o中的符號進行重定位,可以通過三種方式設置,參考http://blog.sina.com.cn/s/blog_13f1300a80102w8de.html
1、 適用於低複雜度鏈接模式.直接通過Linker選項卡中的R/O  Base和R/W Base來設定鏈接信息
	ro-base和rw-base主要是解決加載域和執行域這些問題的,詳細的可以到網上去搜RealView Compilation Tools的Linker and Utilities Guide,其中第五章Using Scatter-loading Description Files對你的問題有非常詳細的解答。
	ro-base和rw-base都是在使用簡單鏈接模式時用到的命令行參數,而在使用分散加載文件的時候就徹底失去意義,你在程序中自己指定這些值是沒用的,只能在鏈接時修改參數來指定。ARM的鏈接器在鏈接後產生的相應變量名分別是Image$$RW$$Base和Image$$RO$$Base 。
2、 適用於中複雜度鏈接模式.採用間接生成的分散加載文件:採用target對話框中的ROM和RAM地址(地址需要開發者填寫),自動生成一個加載文件 
3、 適用於高複雜度鏈接模式.採用分散加載文件(該文件需要開發者編寫)
// 連接腳本中有 加載區域 和 執行區域的概念
-------------------- 位置在哪裏
// 加載區域 : 一般爲主Flash. hex文件的存儲位置(包括Code RO-data RW-data ZI-data)
// 執行區域 : 一般爲主Flash(包括Code RO-data) SRAM1及SRAM2(包括RW-data ZI-data) 
-------------------- 怎麼用
程序的入口點位於C庫中的_main,流程爲
1. 將 non-root (RO and RW) execution regions 從 加載地址 拷貝到 執行地址.如果data sections被壓縮,則將它們從加載地址(加載區域)解壓縮到執行地址(執行區域)//具體實現在(InRoot$$Sections) 
	// 其實 RO(RO-data) 的 加載地址 和 執行地址是一樣的.都在0x08000000(主Flash上)
	// RW(RW-data) 加載地址在 0x08000000 + sizeof(RO-data),執行地址在0x20000000(SRAM1)
	// ZI(ZI-data) 加載地址在 0x08000000 + sizeof(RO-data),執行地址在0x20000000(SRAM1) // 其實這一段並沒有加載,因爲加載了也要清0,所以乾脆留空.只記錄 ZI-data 執行地址的開始地址和結束地址
2. 將ZI regions清0
3. 跳轉到__rt_entry

  • hex 文件
分散加載文件影響了 hex文件內容佈局
hex文件格式:
	1.由N行冒號(:)開頭的行組成
	2.每行的含義
	冒號(:) + 2字節長度數據長度(表示數據有x個字節) + 四字節數據地址 + 兩字節數據類型 + x字節數據 + 2字節校驗

------------------------------------重要信息的體現

hex文件內容第一行
	:020000040800F2
	表示從第2行開始,0x0800000的數據.(:02000004/0800/F2)(加載地址在這裏體現)
	另外內容中有很多(00000020)的身影,估計是執行地址(0x20000000)的體現
	另外棧地址(0x20000738)在第二行體現(:100000003807002029020008C10300087703000810),也在其他一行體現,不知道是做什麼的
	另外堆地址(0x20000138)39行體現,:100250000105000889010008380100203807002046

也就是說,所有的運行相關的東西都會在hex文件中體現,(畢竟運行時的狀況來自flash,flash中的內容來自hex,)
	
  • 燒寫過程
運行jflash軟件,讀取hex文件,然後通過jlink盒子,輸出符合Jtag協議的時序
	1.將主flash燒寫算法弄到SRAM1中.
	2.讀取hex中的數據的一部分,並燒寫到sram1.
	3.運行sram1中的算法,將sram1的數據燒寫到主flash,再轉到2,直到hex中數據完全讀取完畢.
	// 因爲sram1只有112KB,而主flash有1M,所以如果燒寫的flash比較大(例如大於等於112KB)的話,肯定需要循環
	// jflash軟件 <--jlink驅動--> <--usb--> jlink盒子(可以是stm32做的) <--jtag--> 目標芯片(stm32f407zgt6)
	// 輸入文件有 1. hex文件  2. flash燒寫算法.
-------------------- hex文件內容全部燒寫到主flash中去了嗎?
少寫了hex文件中的部分內容.
hex文件中有部分內容是用來指示燒寫的.例如:
1.數據類型不是數據記錄的行 // 該行中的08000000地址作爲jtag中的時序存在,標識地址
2.數據類型爲數據記錄的行 的 冒號(:) 2字節長度數據長度(表示數據有x個字節)   四字節數據地址 2字節校驗



  • Flash
------------------------------------重要信息的體現
第一個加載域應該在Flash數據中找不到了(上電即執行0x08000000的代碼)2及N個加載域 應該在flash中找得到(畢竟代碼中要執行.跳轉指令肯定標識了第2個以及第N個加載域的起始地址)
執行域肯定是需要體現的
// 對比hex和flash中的數據區別,除了之前說的輔助信息的區別,其他有無區別(待驗證???)
// 可以反讀flash中的數據進行驗證.
  • 執行
1. RESET
2. XIP on 主Flash
3. 代碼執行序列到_main
	3.1 將 non-root (RO and RW) execution regions 從 加載地址 拷貝到 執行地址.如果data sections被壓縮,則將它們從加載地址(加載區域)解壓縮到執行地址(執行區域)//具體實現在(InRoot$$Sections) 
	------------對應 .code 段 和 .ro-data 段
	// 其實 RO(RO-data) 的 加載地址 和 執行地址是一樣的.都在0x08000000(主Flash上)
	------------對應 .data 段
	// RW(RW-data) 加載地址在 0x08000000 + sizeof(RO-data),執行地址在0x20000000(SRAM1)
	------------對應 .bss 段
	// ZI(ZI-data) 加載地址在 0x08000000 + sizeof(RO-data),執行地址在0x20000000(SRAM1) // 其實這一段並沒有加載,因爲加載了也要清0,所以乾脆留空.只記錄 ZI-data 執行地址的開始地址和結束地址
	------------對應 .bss 段
	3.2. 將ZI regions清0
	3.3. 跳轉到__rt_entry
		------------對應 .stack 段 和 .heap段
		// 棧的初始化不涉及到內存,只涉及到SP寄存器
		// 堆的初始化涉及到內存, 1.清零堆空間 2.在堆中建立數據結構來對堆進行分塊 
		3.3.1. Sets up the stack and the heap by one of a number of means that include calling __user_setup_stackheap() , calling __rt_stackheap_init() , or loading the absolute addresses of scatter-loaded regions.
		3.3.2. Calls __rt_lib_init() to initialize referenced library functions, initialize the locale and, if necessary, set up argc and argv for main() . For C++, calls the constructors for any top-level objects by way of __cpp_initialize__aeabi_ .
		3.3.3. Calls main() , the user-level root of the application. From main() , your program might call, among other things, library functions.
		3.3.4. Calls exit() with the value returned by main() 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章