1?
ARM映像文件組成
ARM 映像文件其實就是源文件經編譯器生成的目標文件,一般是bin文件或者hex文件,可以直接燒錄到ROM中執行(一般是內部FLASH),這個文件稱為可執行映像文件(image file)。
映像文件一般由域組成,域最多由三個輸出段組成(RO,RW,ZI)組成,輸出段又由輸入段組成。所謂域,指的就是整個映像文件所處在的區域,它又分為加載域和運行域。我們輸入的代碼,一般有代碼部分和數據部分,這就是所謂的輸入段,經過編譯后就變成了映像文件中的RO段和RW段,還有所謂的ZI段,這就是輸出段。
加載域:映像文件被靜態存放的工作區域。
運行域:程序運行起來的存儲區域。
程序在存儲狀態時,RO段及RW段都被保存在ROM區。
當程序開始運行時,內核直接從 ROM 中讀取代碼,并且在執行主體代碼前,會先執行一段加載代碼,它把 RW 段數據從 ROM 復制到 RAM,并且在 RAM加入 ZI 段,ZI 段的數據都被初始化為 0。加載完后 RAM 區準備完畢,正式開始執行主體程序。
2?
分散加載機制
分散加載機制允許開發者為代碼或數據變量在加載和執行時指定不同的存儲空間,通知鏈接器把程序的某一部分鏈接在存儲器的某個地址空間。
2.1 何時使用分散加載
實現嵌入式系統通常需要分散加載,這些系統使用ROM、RAM和內存映射的外設。通常需要分散加載的情況如下:
1)復雜內存映射:
必須放置在許多不同內存區域的代碼和數據需要詳細的指令來說明在內存空間中放置這些部分的位置。
2)不同類型的內存:
許多系統包含各種物理存儲設備,如FLASH、ROM、SDRAM和快速SRAM。分散加載描述可以將代碼和數據與最合適的內存類型相匹配。例如,中斷代碼可以放在快速的SRAM中以改善中斷響應時間,但不常用的配置信息可以放在較慢的閃存中。
3)內存映射的外設:
分散加載描述可以將數據段放置在內存映射中的精確地址上,以便可以訪問內存映射的外設。
4)函數在固定位置:
即使周圍的應用程序已被修改和重新編譯,也可以將函數放置在內存中的相同位置。這對于跳轉表的實現很有用。
5)使用符號來標識堆和堆棧:
在鏈接應用程序時,可以為堆和堆棧位置定義符號。
2.2 怎么使用分散加載
1)對于具有簡單內存映射(僅有SRAM和ROM)的image文件,可以僅使用鏈接器命令行選項或scatter文件來指定內存映射。下圖顯示了一個簡單的內存映射:
2)對于具有復雜內存映射(具有DRAM、SRAM、ROM2、ROM1等)的image文件,不能僅使用鏈接器命令行選項指定內存映射,需要使用scatter文件。下圖顯示了一個復雜的內存映射:
3?
分散加載文件說明
分散加載文件(scatter file)是一個文本文件,它的作用是可以用于描述 ARM 鏈接器生成映像文件所需要的信息。如果不使用分散加載文件來指定,那么 ARM 鏈接器會按照默認的方式來生成映像文件。Keil的分散加載主要是通過 .sct 文件實現的,鏈接器根據 .sct 文件的配置分配各個段區地址,生成分散加載代碼。
編寫分散加載文件中可以指定下列信息:
1)各個加載域的加載起始地址、最大尺寸和屬性。
2)各個加載域中包含的輸出段。
3)各個運行域的運行起始地址、最大尺寸、存儲訪問特性和屬性。
4)各個運行域中包含的輸入段。
一個Scatter文件包含若干個加載域,一個加載域包含若干個輸出段,一個輸出段由若干個具有相同屬性的輸入段組成。
4?
分散加載文件語法
Scatter文件是一個文本文件,使用BNF語法來描述ARM鏈接器生成映像文件時所需要的信息。
BNF符號與語法:
" :由雙引號標識的符號保持其字面原意,如A”+”B表示A+B。
A ::= B :定義A為B。
[A] :用來表示可選部分,如A[B]C表示ABC或AC。
A+ :用來表示A可以重復任意次,如A+表示A,AA,AAA,......
A* :同A+。
A | B :用來表示選擇其一,不能全選。如A|B表示A或者B。
(AB) :表示一個整體,通常其他符號一起使用,如(AB)+(C|D)表示ABC,ABD,ABABC,ABABD,......
一個Scatter文件包含一個或多個加載區域,每個加載區域可以包含一個或多個執行區域。下圖顯示了典型的Scatter文件的組成結構:
4.1 加載域描述
加載域可以包含一個或多個執行域。
加載域描述的語法如下:
load_region_description::= load_region_name(base_address|("+"offset))[attribute_list][max_size] "{" execution_region_description+ "}"
1)load_region_name:命名加載區域。你可以使用帶引號的名字。只有在使用任何與區域相關的鏈接器定義的符號時,該名稱才區分大小寫。
2)base_address:指定區域內對象的鏈接地址。base_address必須滿足加載區域的對齊約束。
3)+offset:描述基址,該基址在前面的加載區域的末尾以外偏移字段。偏移量的值必須是0取4的模。如果這是第一個加載區域,那么+offset表示基址從零開始偏移字段。如果使用+offset,則加載區域可能會從前一個加載區域繼承某些屬性。
4)attribute_list:指定加載區域內容的屬性。
5)max_size:指定加載區域的最大大小。這是在進行任何解壓縮或零初始化之前加載區域的大小。如果指定了可選的max_size值,那么如果分配給該區域的字段超過max_size, armlink將生成一個錯誤。
6)execution_region_description:指定執行區域的名稱、地址和內容。
4.2 執行域描述
執行域指定在運行時將輸入段放置在目標內存中的位置。
執行域描述的語法如下:
execution_region_description::= exec_region_name(base_address|"+"offset)[attribute_list][max_size|length] "{" input_section_description* "}"
1)exec_region_name:命名執行區域。你可以使用帶引號的名字。只有在使用任何與區域相關的鏈接器定義的符號時,該名稱才區分大小寫。
2)base_address:指定區域內對象的鏈接地址。Base_address必須與字對齊。
注意:在執行區域上使用ALIGN會導致加載地址和執行地址對齊。
+offset:描述一個基址,該基址在前面執行區域的末尾以外偏移字段。偏移量的值必須是0取4的模。如果這是加載區的第一個執行區,則+offset表示基址在包含加載區的基址之后開始偏移字段。如果使用+offset,則執行區域可能會從父加載區域繼承某些屬性,或者從同一加載區域內的前一個執行區域繼承某些屬性。
4)attribute_list:指定執行區域內容的屬性。
5)max_size:對于標記為EMPTY或FILL的執行區域,max_size值被解釋為該區域的長度。否則,max_size值將被解釋為執行區域的最大大小。
6)-length:只能與EMPTY一起使用,表示在內存中增長的堆棧。如果長度為負值,則base_address被視為該區域的結束地址。
7)input_section_description:指定輸入段的內容。
4.3 輸入段描述
輸入段描述指定將哪些輸入段加載到執行區域。
輸入段描述的語法是:
input_section_description::= module_select_pattern["("input_section_selector(","input_section_selector)*")"] input_section_selector::="+"input_section_attr |input_section_pattern |input_section_type |input_symbol_pattern |section_properties
1)module_select_pattern:
一種由文本構成的模式。當module_select_pattern匹配以下選項之一時,輸入段匹配模塊選擇器模式:
包含該段的目標文件的名稱。
庫成員的名稱(不帶前導路徑名)。
提取部分的庫的全名(包括路徑名)。如果名稱包含空格,請使用通配符以簡化搜索。
例如,使用*libname來匹配C:lib dirlibname.lib。
通配符 * 匹配零個或多個字符,? 匹配任意單個字符。匹配不區分大小寫,即使在文件命名區分大小寫的主機上也是如此。使用 *.o 匹配所有對象,使用 * 來匹配所有的目標文件和庫。可以使用帶引號的文件名,例如”file one.o”。
在一個Scatter文件中不能有兩個 * 選擇器。但是可以使用兩個修改過的選擇器,例如*A和*B,并且可以將. any選擇器與*模塊選擇器一起使用。*模塊選擇器的優先級高于.any。如果文件中包含*選擇器的部分被刪除,則. any選擇器將變為活動的。
2)input_section_attr:
與輸入部分屬性匹配的屬性選擇器。每個input_section_attr后面跟著一個+。選擇器不區分大小寫,可以識別以下選擇器:
RO-CODE
RO-DATA
RO,同時選擇RO-CODE和RO-DATA
RW-DATA
RW-CODE
RW,同時選擇RW-CODE和RW-DATA
XO
ZI
ENTRY,包含入口點的部分。
可以識別以下同義詞:
CODE代表RO-CODE
CONST代表RO-DATA
TEXT代表RO
DATA代表RW
BSS代表ZI
可以識別以下偽屬性:
FIRST
LAST
如果放置順序很重要,則使用FIRST和LAST標記執行區域中的第一部分和最后一部分。例如,如果特定的輸入部分必須在區域中位于第一個,并且包含校驗和的輸入部分必須位于最后。
FIRST和LAST必須不違反基本屬性排序順序。例如,FIRST RW放在任何只讀代碼或只讀數據之后。
一個執行區域只能有一個FIRST或一個LAST屬性,并且它必須遵循一個input_section_selector。例如:
*(section, +FIRST) 這種模式是正確的。
*(+FIRST, section) 此模式不正確,并產生錯誤消息。
3)input_section_pattern:
一種模式,不區分大小寫,與輸入段名匹配。它是由文字構成的。通配符 * 匹配0個或多個字符,而 ? 匹配任何單個字符。可以使用帶引號的輸入段名。
如果使用多個input_section_pattern,請確保在不同的執行區域中沒有重復的模式,以避免歧義錯誤。
4)input_section_type:
與輸入段類型進行比較的數字。支持十進制或十六進制。
5)input_symbol_pattern:
可以通過該段定義的全局符號名選擇輸入段。全局名稱能夠使你從部分鏈接的對象中選擇具有相同名稱的單個段。
6)section_properties:
section屬性可以是+FIRST、+LAST和OVERALIGN值。OVERALIGN的值必須是2的正冪,且必須大于或等于4。
5?
如何打開.sct文件
在Options for Targets->Linker界面,Keil默認選擇了“Use Memory Layout from Target Dialog”,勾掉復選框里面的對號,就可以使用自己定義的分散加載文件。
6?
練習
根據前面介紹的語法編輯 .sct文件,將函數和數據放到指定地址:
1)在LR_ROM加載域中定義2個執行域RW_RAM1、RW_RAM2:
RW_RAM1基地址為0x20001000,域大小為0x400,將TEST_FUNCTION_ADDR段最先加載到本域的起始地址。
RW_RAM2基地址為0x20001400,域大小為0x400,將TEST_DATA_ADDR段最先加載到本域的起始地址。
LR_ROM__RO_BASE__RO_SIZE{;loadregionsize_region ER_ROM__RO_BASE__RO_SIZE{;loadaddress=executionaddress *.o(RESET,+First) *(InRoot$$Sections) ;*(Veneer$$CMSE);uncommentforsecureapplications .ANY(+RO) .ANY(+XO) } RW_RAM__RW_BASE__RW_SIZE{;RWdata .ANY(+RW+ZI) } RW_RAM10x200010000x400{;RWdata *.o(TEST_FUNCTION_ADDR,+First) } RW_RAM20x200014000x400{;RWdata *.o(TEST_DATA_ADDR,+First) } #if__HEAP_SIZE>0 ARM_LIB_HEAP__HEAP_BASEEMPTY__HEAP_SIZE{;Reserveemptyregionforheap } #endif ARM_LIB_STACK__STACK_TOPEMPTY-__STACK_SIZE{;Reserveemptyregionforstack } }
2)使用__attribute__((section("section_name"))) 定義函數和數據如下:
(表示將函數或數據放入指定名為"section_name"的段)
uint32_ttest_data__attribute__((section("TEST_DATA_ADDR"))); voidtest_function(void)__attribute__((section("TEST_FUNCTION_ADDR")));
3)調用上述函數和數據:
intmain(void) { test_function(); while(1) { test_data++; if(test_data>=1000) { test_data=0; } } }
編譯后查看map文件中內存映射部分:
TEST_FUNCTION_ADDR段對應的執行域基地址為0x20001000,TEST_DATA_ADDR段對應的執行域基地址為0x20001400,和在Scatter文件定義一致,即將函數和數據放到了指定地址。
7?
小結
分散加載文件(scatter file)是一個文本文件,用于描述 ARM 鏈接器生成映像文件所需要的信息,在一些應用場景中嵌入式系統可能會使用分散加載。本章節簡要介紹了分散加載文件的基本概念和語法,旨在對分散加載文件有初步認識,具體內容可以參考官方文檔或查閱資料進行詳細了解。
審核編輯:劉清
-
ARM
+關注
關注
134文章
8967瀏覽量
365018 -
SDRAM
+關注
關注
7文章
420瀏覽量
55047 -
存儲器
+關注
關注
38文章
7366瀏覽量
163091 -
ROM
+關注
關注
4文章
539瀏覽量
85431 -
keil
+關注
關注
68文章
1207瀏覽量
166171 -
內存映射
+關注
關注
0文章
14瀏覽量
7388 -
靈動微
+關注
關注
3文章
174瀏覽量
22617 -
MM32
+關注
關注
1文章
106瀏覽量
659
原文標題:靈動微課堂 (第278講)|Keil分散加載文件淺析
文章出處:【微信號:MindMotion-MMCU,微信公眾號:靈動MM32MCU】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論