嵌入式軟件開發(fā)之: 映像文件存儲器映射調(diào)整
映像由域(Regions)和輸出段(Output Sections)組成。每個域可以有不同的加載地址和執(zhí)行地址。
分散加載可以更加方便準確的指定映像存儲器映射,為映像組件分組和布局提供了全面控制。它能夠描述由載入時和執(zhí)行時分散在存儲器映射中的多個區(qū)組成的復雜映像映射。雖然,分散加載可以用于簡單映像,但它通常僅用于具有復雜存儲器映射的映像。
要構建映像的存儲器映射,必須向armlink提供以下信息:
· 分組信息:決定如何將各輸入段組織成相應的輸出段和域;
· 定位信息:決定各域在存儲空間的起始地址。
有兩種方法可以配置指定映像文件的分組和定位信息:如果映像文件中地址映射關系比較簡單,可以使用命令行選項;如果映像文件中地址映射關系比較復雜的情況,可以使用一個配置文件。使用該配置文件可以告訴鏈接器相關的地址映射關系。配置文件又叫Scatter文件,是一個文本文件,通過下面的鏈接選項來實現(xiàn)。
-scatter filename
1.為分散加載定義的符號當armlink使用Scatter文件創(chuàng)建映像時,它創(chuàng)建一些區(qū)相關符號。表13.2概括了這些符號的意義。
表13.2 域相關符號
符 號
意 義
Load$$region_name$$Base
域的載入地址
Image$$region_name$$Base
域的執(zhí)行地址
Image$$region_name$$Length
執(zhí)行域字節(jié)長度(4的倍數(shù))
Image$$region_name$$Limit
執(zhí)行區(qū)末尾地址
Image$$region_name$$ZI$$Base
執(zhí)行域中ZI段的執(zhí)行地址
Image$$region_name$$ZI$$Length
ZI輸出段的長度(4的倍數(shù))
Image$$region_name$$ZI$$Limit
執(zhí)行域中ZI段的末尾地址
2.使用Scatter文件的優(yōu)勢鏈接程序的命令行選項提供了一些對數(shù)據(jù)和代碼布局的控制,但要實現(xiàn)對布局的全面控制命令行輸入的指令是遠遠不夠的。在下面一些情況下,就需要使用Scatter文件對映像布局進行控制。
① 需要實現(xiàn)復雜存儲器映射
系統(tǒng)中的代碼和數(shù)據(jù)必須放在多個不同存儲器區(qū)域中,這樣連接器必須知道哪個段放在哪個儲存器空間的詳細信息。這種情況下,最好用Scatter文件實現(xiàn)代碼映像的分散加載。
② 系統(tǒng)中存在多種不同類型存儲器
許多系統(tǒng)包含多種不同類型存儲器,如flash存儲器、ROM、SDRAM和快速SRAM。分散載入描述可以將代碼和數(shù)據(jù)放置在最適合的存儲器類型中。例如,中斷代碼可能放在快速SRAM中,以加快中斷響應時間,而不頻繁使用的配置信息可能放在較慢的flash存儲器中。
③ 存儲器映射I/O
分散載入描述可以將數(shù)據(jù)精確定位在內(nèi)存地址中,而避免數(shù)據(jù)和內(nèi)存映射外圍地址相沖突。
④ 位于固定位置函數(shù)
可以將特定函數(shù)放在存儲器中的同一個位置,這樣即使周圍的應用程序已經(jīng)被修改并重新編譯,也可以使具有特定功能的函數(shù)地址保持不變。
⑤ 使用符號識別堆和棧
可以為堆和棧的位置定義符號,鏈接應用程序時可以指定該封閉模塊的位置。
隨著目前嵌入式系統(tǒng)越來越復雜,系統(tǒng)中可能同時使用flash、ROM和RAM,所以建議在生產(chǎn)系統(tǒng)映像時使用Scatter文件。
3.分散加載命令行選項可以使用下面的命令行選項使用分散加載文件。
-scatter description_file_name
使用該命令可以使鏈接器使用命令中給出的description_file_name文件生成最終的映像文件。
4.簡單存儲器映像舉例例如,一個實際系統(tǒng)的存儲器映射如圖13.7所示。
圖13.7 簡單存儲器映射
為了實現(xiàn)圖13.7的存儲器映射,使用圖13.8所現(xiàn)實的Scatter文件。
5.復雜存儲器映像實現(xiàn)舉例一個復雜存儲器映射如圖13.9所示。
圖13.8 實現(xiàn)簡單內(nèi)存映射的Scatter文件
圖13.9 復雜存儲器映射實例
為了實現(xiàn)圖13.9的存儲器映射,使用以下程序所現(xiàn)實的Scatter文件。
LOAD_ROM_1 0x0000 ;第一個加載時域的起始地址
{
EXEC_ROM_1 0x0000 ;第一個運行時域的起始地址
{
programl.o(+RO) ;放置program.o中所以的RO段
{
SRAM 0x9000 ;運行時域的起始地址
{
programl.o(+RW,+ZI) ;放置program.o中所有的RW和ZI段
}
}
LOAD_ROM_2 0x4000 ;第二個加載時域的起始地址
{
EXEC_ROM_2 0x4000 ;運行時域的起始地址
{
program2.o(+RO)
}
DRAM 0x18000 ;運行時域的起始地址
{
program2.o(+RW,+ZI)
}
}
上面兩個例子中,簡單存儲器映射可以使用命令行選項實現(xiàn),但第二個復雜存儲器映射的例子卻只能使用Scatter文件實現(xiàn)。
13.4.2 Scatter文件語法分散載入描述文件是一個文本文件,它向 armlink 描述目標系統(tǒng)的存儲器映射。如果從命令行加載Scatter文件,可以使用任意類型的文件擴展名。
在Scatter文件中,用戶可以指定以下存儲器映像內(nèi)容:
· 每個載入?yún)^(qū)的載入地址和最大尺寸;
· 每個載入?yún)^(qū)的屬性;
· 從每個載入?yún)^(qū)派生的執(zhí)行區(qū);
· 每個執(zhí)行區(qū)的執(zhí)行地址和最大尺寸;
· 每個執(zhí)行區(qū)的輸入節(jié)。
描述文件的格式反映出載入?yún)^(qū)、執(zhí)行區(qū)和輸入節(jié)的層次結構。
1.BNF的表示法和語法所謂BNF(Backus Naur Format)即Scatter文件所用的形式語言。表13.3概括了其所用的符號和語法規(guī)則。
表13.3 BNF語法
符 號
說 明
”
引號用于表示BNF語法中的字符被用作普通字符。
例如,定義B"+"C,它只能替換為模式B+C。而定義B+C可以替換為模式BC、BBC或BBBC
A ::= B
將A定義為B。例如,A::= B"+" | C 表示A相當于B+或C。
在其組件方面,::=表示法用于定義高級結構。每個組件可能還有一個::=定義,對更簡單的組件進行定義。
例如,A::=B以及B::= C | D表示定義A相當于模式C或D
續(xù)表
符 號
說 明
[A]
可選元素A。例如,A::= B[C]D 表示定義A可以擴展為BD或BCD
A+
元素A可以出現(xiàn)一次或多次。例如,A::= B+表示定義A可以擴展為B、BB或BBB等
A*
元素A可以不出現(xiàn)或多次出現(xiàn)
A|B
出現(xiàn)元素A或B,但不能同時出現(xiàn)
(A|B)
元素A和B組合在一起。
這在使用 | 操作符時,或重復復雜模式時尤其適用。
例如,A::=(B C)+ (D | E) 表示定義A可以擴展為BCD、BCE、BCBCD、BCBCE、BCBCBCD或BCBCBCE
2.Scatter文件語法概述分散加載描述scatter_description被定義為一個或多個load_region_description模式:
Scatter_description ::=
load_region_description+
加載域描述load_region_description 被定義為載入?yún)^(qū)名稱,可以選擇性地在其后跟隨屬性、尺寸說明符以及一個或多個執(zhí)行區(qū)描述:
load_region_description ::=
load_region_name (base_address | ("+" offset)) [attributes] [max_size]
"{"
execution_region_description+
"}"
執(zhí)行域描述execution_region_description 被定義為執(zhí)行區(qū)名稱,是一種基址規(guī)范,可以選擇性地在其后跟隨屬性、尺寸說明符以及一個或多個輸入段描述:
execution_region_description ::=
exec_region_name (base_address | "+" offset) [attribute_list] [max_size | "–"
length]
"{"
input_section_description*
"}"
輸入段描述input_section_description被定義為源模塊選擇程序模式,可以在其后選擇性地跟隨輸入節(jié)選擇程序:
input_section_description ::=
module_select_pattern
[ "("
("+" input_section_attr | input_section_pattern)
([","] "+" input_section_attr | "," input_section_pattern))*
")" ]
圖13.10顯示一個典型的分散載入描述文件的內(nèi)容和組織結構。
圖13.10 典型的分散載入描述文件的內(nèi)容和組織結構
3.加載域描述一個加載域具有以下屬性:
· 名稱:鏈接程序使用它識別不同的加載域;
· 基址:載入視圖中的代碼和數(shù)據(jù)的起始地址;
· 屬性:可選;
· 最大尺寸:可選;
· 執(zhí)行區(qū)列表:這些執(zhí)行區(qū)標識執(zhí)行視圖中模塊的類型和位置。
圖13.11顯示了加載域的描述。
BNF語法為:
load_region_description ::=
load_region_name (base_address | ("+" offset)) [attribute_list] [ max_size
]
"{"
execution_region_description+
"}"
語法說明如下。
① load_region_name為加載域的名稱。只有前31個字符有效。該名稱僅用于識別每個域。
注意
load_region_name與執(zhí)行域exec_region_name不同,load_region_name不用于生成Load$$region_
name符號。
② base_address是區(qū)中對象的鏈接地址。base_address必須是一個字對齊數(shù)值。
③ +offset描述基址,它從前一個加載域的末尾偏移offset個字節(jié)。offset的值必須能被4整除。如果是第一個加載域,則+offset表示該域的基地址是從0之后的offset字節(jié)開始。
④ attribute_list指定加載域內(nèi)容的屬性:
· PI:位置獨立;
· RELOC:可重定位;
· OVERLAY:重疊;
· ABSOLUTE:絕對地址;
· NOCOMPRESS:代碼不被壓縮。
可以指定這些屬性中的一項(除NOCOMPRESS外,其他4項屬性為互斥關系)。默認的加載域?qū)傩允茿BSOLUTE。具有PI、RELOC或OVERLAY屬性之一的加載域可以有重疊的地址范圍。對于ABSOLUTE加載域,armlink不允許重疊的地址范圍。OVERLAY關鍵字允許在同一個地址有多個執(zhí)行區(qū)。
注意
ARM在RVCT中不提供重疊機制。要在同一個地址使用多個執(zhí)行區(qū),必須提供自己的重疊管理程序。
⑤ max_size:它指定加載域的最大尺寸。(如果指定了可選的max_size值,但分配給該區(qū)的字節(jié)超過max_size字節(jié),armlink將生成錯誤。)
⑥ execution_region_description:它指定執(zhí)行區(qū)名稱、地址和內(nèi)容。
4.執(zhí)行域描述符執(zhí)行域具有以下一些屬性:
·
域名稱;
· 執(zhí)行域基地址(支持絕對地址的或相對地址的);
· 執(zhí)行域的最大尺寸(可選);
· 指定執(zhí)行域?qū)傩裕?/p>
· 一個或多個輸入段描述(放在本執(zhí)行區(qū)中的模塊)。
圖13.12顯示了一個典型的執(zhí)行域描述。
執(zhí)行域描述符中的BNF語法為:
execution_region_description ::=
exec_region_name (base_address | "+" offset) [attribute_list] [max_size | "–"
length]
"{"
input_section_description+
"}"
其語法說明如下。
① exec_region_name 為執(zhí)行域命名。(只有前31個字符有效。)
② base_address是域中對象的鏈接地址。base_address必須是字對齊的。
③ +offset是描述基址,它從前一個執(zhí)行區(qū)的末尾偏移offset個字節(jié)。offset的值必須能被4整除。如果前面沒有執(zhí)行區(qū)(即,這是載入?yún)^(qū)中的第一個執(zhí)行區(qū)),則+offset表示基址從它所在的載入?yún)^(qū)的基址之后offset個字節(jié)開始。如果使用+offset格式并且所在的加載域具有RELOC屬性,則執(zhí)行區(qū)繼承該RELOC屬性。但是,如果使用固定的base_address,則隨后出現(xiàn)的offset不繼承RELOC屬性。
④ attribute_list指定執(zhí)行區(qū)內(nèi)容的屬性:
· PI:位置獨立。
· OVERLAY:重疊。
· ABSOLUTE:絕對地址。域的執(zhí)行地址由base_designator指定。
· FIXED:固定地址。執(zhí)行域的加載地址和執(zhí)行地址都由base_designator指定。base_designator必須是絕對基址,或者偏移量為+0。
· EMPTY:它在執(zhí)行區(qū)中保留一個已知長度的空白存儲器塊,通常用作堆或棧。
· PADVALUE:指定填充字的默認值,如果在域定義中指定了該屬性,則必須為該屬性賦值。使用該屬性的例子如下。
EXEC 0x10000 PADVALUE 0xffffffff EMPTY ZEROPAD 0x2000
通過該Scatter文件描述符,創(chuàng)建了一個長度為0x2000的域,該域中的所有內(nèi)容用0xffffffff填充。
注意
所指定的域值必須以字為單位。
· ZEROPAD 0:初始化一塊內(nèi)容全為0的內(nèi)存區(qū)域,并將其作為一個輸入段填充到ELF映像文件中。這樣減少了在運行時將某段內(nèi)存初始化為0的操作。
注意
只有根執(zhí)行區(qū)可以使用ZEROPAD屬性進行0初始化。對非根執(zhí)行區(qū)使用ZEROPAD屬性將出現(xiàn)警告信息,并且忽略該屬性。
· UNINIT:指示該段為不能被初始化為0。
⑤ max_size為可選的參數(shù),如果分配給域的存儲器超過max_size字節(jié),則它指示armlink生成錯誤。
⑥ -length如果指定的長度為負值,則base_address是域的結束地址。它通常與EMPTY一起使用,以表示在存儲器中變小的棧。
當確定執(zhí)行域?qū)傩詴r,注意以下幾點。
① PI、OVERPLAY、FIXED和ABSOLUTE為并列關系屬性,某一個執(zhí)行域只能為這4種屬性之一。如果沒有指定,ABSOLUTE為其默認屬性。
② 使用+offset格式的base_designator的執(zhí)行區(qū)繼承前一個執(zhí)行區(qū)的屬性(如果它是加載域中的第一個執(zhí)行區(qū),則繼承所在加載域的屬性,),或者具有ABSOLUTE屬性。
③ 不能為執(zhí)行域顯式指定RELOC屬性。該屬性只能從前面的執(zhí)行域或父區(qū)繼承才能具有RELOC屬性。
④ 被指定了PI或OVERLAP屬性的執(zhí)行域,不能有重疊的地址范圍。但對于ABLOUTE和FIXED屬性的執(zhí)行域,ARM編譯器不允許有重疊的地址范圍。
⑤ RW段默認使用壓縮屬性。如果不想鏈接器對該段進行壓縮,必須在Scatter文件中使用NOCOMPRESS顯示聲明。
⑥ UNINIT指定執(zhí)行區(qū)中的ZI輸出節(jié)(如果有)不被初始化為0。使用它可以創(chuàng)建包含未初始化數(shù)據(jù)或存儲器映射I/O的執(zhí)行區(qū)。
5.輸入段描述符輸入段由以下部分組成。
· 模塊名稱,如目標文件名稱、庫成員名稱或庫文件名稱。模塊名稱可以使用通配符。
· 輸入段名稱,或輸入節(jié)屬性,如READ-ONLY或CODE。
圖13.13顯示了輸入段描述符的基本組成。
BNF語法為:
input_section_description ::=
module_select_pattern
["("
("+" input_section_attr | input_section_pattern)
([","] "+" input_section_attr | "," input_section_pattern))*
")"]
其語法說明如下。
① module_select_pattern
這是由文字文本構成的模式。“*”通配符匹配0個或多個字符,而“?”匹配任何單個字符。匹配不區(qū)分大小寫。
使用*.o可以匹配所有對象。使用*可以匹配所有目標文件和庫。
當滿足下列條件之一時,鏈接器認為module_selector_pattern與輸入段匹配。
· 包含輸入段的目標文件與module_selector_pattern匹配。
· 包含輸入段的庫成員名稱(不帶路徑名)與module_selector_pattern匹配。
· 從其中提取段的庫全名(包含路徑名)。如果名稱包含空格,使用通配符可以簡化搜索。例如,使用*libname.lib匹配C:lib dirlibname.lib。
另外,ARM鏈接器支持特殊的模塊選擇程序模式“.ANY”,允許將輸入節(jié)分配給執(zhí)行區(qū),而無需考慮其父模塊。使用.ANY以任意分配方式填充執(zhí)行區(qū)。
注意
最好不要依賴編譯程序生成的或ARM庫代碼使用的輸入段名。因為,這些名稱在每次編譯之間可以變化,例如編譯選項的改變或編譯器版本發(fā)生變化,都可能引起輸入段名稱的變化。
② input_section_attr
輸入段屬性符定義了一個用逗號隔開的模式類別。該類表中的每個模式定義了輸入段名稱或輸入段屬性匹配方式。當匹配模式使用輸入段名稱時,它前面必須使用符號“+”,而符號“+”前面緊接的逗號可以省略。
輸入段屬性不區(qū)分大小寫??梢允窍铝袑傩灾唬?/p>
· RO-CODE;
· RO-DATA;
· RO,同時選擇RO-CODE和RO-DATA;
· RW-DATA;
· RW-CODE;
· RW,同時選擇RW-CODE和RW-DATA;
· ZI;
· ENTRY,包含ENTRY點的節(jié)。
可以識別以下同義詞:
· CODE代表RO-CODE;
· CONST代表RO-DATA;
· TEXT代表RO;
· DATA代表RW;
· BSS代表ZI。
可以識別以下偽屬性:
· FIRST;
· LAST。
如果對輸入段的排列順序有特殊的要求,如特定的輸入段必須是域中的第一個輸入節(jié),而包含校驗和的輸入段必須是最后一個輸入段,可以使用FIRST和LAST標記執(zhí)行區(qū)中的第一個和最后一個段。
FIRST或LAST偽屬性必須放在屬性列表的最后。
特殊的模塊選擇程序模式“.ANY”允許在不考慮其父模塊的情況下,將輸入段分配給執(zhí)行域。使用一個或多個“.ANY”模式以任意分配方式填充執(zhí)行域。在大多數(shù)情況下,使用單個“.ANY”相當于使用“*”模塊選擇屬性。
在分散載入描述文件中不能使用兩個“*”選擇屬性。但是,可以使用兩個變形的選擇程序,例如,*A和*B,也可以將.ANY選擇屬性與模塊選擇屬性一起使用。*模塊選擇屬性的優(yōu)先級比.ANY高。如果刪除了文件中包含*選擇屬性的部分,.ANY選擇屬性才能在鏈接時起作用。
在解析所有其他(非.ANY)輸入段描述并且將輸入段分配給最匹配的執(zhí)行區(qū)之后,才解析使用.ANY模塊選擇程序模式的input_section_descriptions。如果有一個以上.ANY模式,則鏈接程序盡可能多地填充第一個.ANY,然后開始填充下一個.ANY。
每個未被分配的剩余輸入段將被分配給具有以下特性的執(zhí)行區(qū):
· 最大的剩余空間(由max_size的值和已分配給該區(qū)的輸入段的尺寸確定);
· 匹配.ANY的input_section_description;
· 與輸入段的存儲器屬性相匹配的存儲器訪問屬性(如果有);
· input_section_pattern。
13.4.3 Scatter文件典型用法1.創(chuàng)建啟動域所謂啟動域就是加載地址和執(zhí)行地址相同的域。系統(tǒng)執(zhí)行的初始入口點必須要在啟動域中,否則鏈接器將報告以下錯誤。
Entry point (0x00000000) lies within non-root region ER_ROM
在Scatter文件中確定啟動域可以使用下面兩種方法。
① 使用ABSOLUTE設置執(zhí)行區(qū)屬性,并且對第一個執(zhí)行區(qū)及其所在的加載區(qū)使用相同的地址。為確保執(zhí)行域地址和加載域地址相同,可以將加載域的起始地址和執(zhí)行域的起始地址設為相同的值或者將第一個執(zhí)行域的地址偏移量設為0。
下面的例子,指定了一個啟動域。
BOOT 0x0000 ;加載域的起始地址在0x0
{
EXER 0x0000 ;指定加載域和執(zhí)行域的地址相同
{
* (+RO) ;必須將啟動域包含在內(nèi)
}
;其他執(zhí)行域
}
② 使用FIXED執(zhí)行域?qū)傩?,確保指定域的載入地址和執(zhí)行地址相同。
下面的例子顯示了使用FIXED屬性,將執(zhí)行域的起始地址固定在ROM中。
BOOT 0x0000 ;加載域的起始地址在0x0
{
EXER 0x0000 ;指定加載域和執(zhí)行域的地址相同
{
* (+RO) ;必須將啟動域包含在內(nèi)
}
EXER_INIT 0x8000 FIXED
{
init.o(+RO)
}
}
③ 如果使用分散加載,負責創(chuàng)建執(zhí)行域的代碼和數(shù)據(jù)不能將其自身復制到另一位置,因此啟動域必須包含以下內(nèi)容。
· _main.o和_scatter*.o:包含復制代碼和數(shù)據(jù)的代碼。
· Region$$Table和ZISection$$Table段:包含要復制代碼和數(shù)據(jù)的地址。
· _dc*.o:執(zhí)行代碼壓縮。
可以使用armlinker產(chǎn)生的InRoot$$Sections符號放置啟動代碼。因為這些代碼被定義為只讀屬性,所有如果Scatter文件中包含了“* (+RO)”,則表示啟動域中包含了這些代碼?;蛘唢@式的使用InRoot$$Sections符號在Scatter文件中對以上代碼進行配置。
下面的例子顯示了如何在Scatter文件中使用InRoot$$Sections鏈接符號,放置啟動域。
LOADREG 0x8000 ;
{
ROOT 0x8000
{
* (InRoot$$Sections) ;放置啟動域
}
OTHER 0x100000
{
* (RO,+RW,+ZI)
}
;其他Scatter文件描述
}
2.為執(zhí)行域確定固定地址可以在執(zhí)行區(qū)分散加載描述中使用FIXED屬性來創(chuàng)建根區(qū),該根區(qū)在固定地址載入和執(zhí)行。
FIXED可以用于在單一加載域內(nèi)(因此通常用于單個ROM設備)創(chuàng)建多個根區(qū)。
例如,使用FIXED屬性將函數(shù)或數(shù)據(jù)塊(如常數(shù)表或校驗和)放在ROM中的固定地址,這樣就可以使用指針很方便的對其進行訪問。
下面的例子顯示了如何放置單個目標內(nèi)容。
LOADREG1 0x0 0x10000
{
EXECREG1 0x0 0x1000 ;啟動域,包含初始化代碼
{ ;將初始化代碼放在0x0地址
init.o (Init, +FIRST)
* (+RO) ;隨后排放余下的只讀數(shù)據(jù)
}
RAM 0x400000 0x2000 ;將可讀可寫數(shù)據(jù)放在0x400000地址
{
* (+RW +ZI)
}
DATABLOCK 0x4FF00 FIXED 0xFF ;執(zhí)行域放在 0x4FF00地址
{ ;限制該域的最大長度為 0xFF
data.o(+RO-DATA) ;將只讀數(shù)據(jù)放在0x1FF00 和 0x1FFFF之間
}
}
通過上面的Scatter文件,可以將初始化代碼放在0x0處,其后是其他RO代碼和除了data.o對象中的RO數(shù)據(jù)之外的所有RO數(shù)據(jù);所有全局的RW變量放在RAM中0x400000處;最好將data.o的RO-DATA只讀數(shù)據(jù)表放在地址0x4FF00處,并指定其最大長度為0xFF。
上例將代碼或數(shù)據(jù)對象放在其各自的源文件中,然后放置目標文件域,這些操作方式是ARM公司建議的標準編碼方式。為方便起見,可以使用編譯指示#pragma和分散載入描述文件放置已命名的域。下面的例子創(chuàng)建模塊dump.c并顯式命名域。
// file dump.c
int a = 10; // 放入數(shù)據(jù)域
short b[100]; // 放入bss段
int const c[3] = {1,2,3}; // 放入.constdata段
int func1(int a) {return a*1;} // 放入.text段
#pragma arm section rwdata = "foo", code ="foo"
int x = 5; // 在foo的數(shù)據(jù)域
char *s = "abc"; // s3在code段, "abc" 在 .constdata
int func2(int x) {return x+1;} // 放入foo的.text段
#pragma arm section code, rwdata // 返回
使用下面的Scatter文件指定上面的代碼在內(nèi)存中的放置位置。如果代碼和數(shù)據(jù)段的名稱相同,則首先放置代碼段。
FLASH 0x10000000 0x2000000
{
FLASH 0x10000000 0x2000000
{
init.o (Init, +First) ; 放置初始化代碼
* (+RO) ;
}
RAM 0x0000
{
vectors.o (Vect, +First) ; 放置向量表
* (+RW,+ZI) ;
}
DUMP 0x08000000
{
dump.o (foo) ;
}
}
通過上面的Scatter文件,將init中的初始化段放在0x10000000地址,并將除foo外的只讀數(shù)據(jù)func1和c[]放在該初始段的后面;接下來的執(zhí)行域RAM放置向量表;最后的DUMP域放置由#pragma指定的段dump。
3.在代碼映像中保留空白域可以在Scatter中使用 EMPTY 屬性為棧保留一個空白存儲器塊。該存儲塊不構成載入?yún)^(qū)的一部分,但指定在執(zhí)行時使用。由于它創(chuàng)建為虛 ZI區(qū),所以 armlink 使用以下符號訪問它:
· Image$$region_name$$ZI$$Base;
· Image$$region_name$$ZI$$Limit;
· Image$$region_name$$ZI$$Length。
如果指定的長度為負值,則Image$$region_name$$ZI$$Limit被視為域的結束地址。它是絕對地址,不是相對地址。下面例子顯示了如何在Scatter文件中預留一個空白區(qū)域。
LOADREGION 0x700000 ; 加載域的起始地址在0x700000
{ ;
STACK 0x7000000 EMPTY –0x10000 ; 該域的結束地址為0x700000,因為其長度為負
;
region
;
{
; 預留空白區(qū)放置棧
}
HEAP +0 EMPTY 0x10000 ; 棧的起始地址在上個預留區(qū)域介紹地址
;
;
{
; 預留空白區(qū)域放置堆
}
; rest of scatter description...
}
在上面的例子中定義了一個執(zhí)行域STACK 0x7000000 EMPTY -0x10000,它從地址 (0x7000000-0x1000)開始,在地址0x7000000結束。
在此示例中,鏈接程序生成符號:
Image$$STACK$$ZI$$Base = 0x6ff0000
Image$$STACK$$ZI$$Limit = 0x7000000
Image$$STACK$$ZI$$Length = 0x1000
Image$$HEAP$$ZI$$Base = 0x7000000
Image$$HEAP$$ZI$$Limit = 0x7010000
Image$$HEAP$$ZI$$Length = 0x1000
EMPTY屬性僅適用于執(zhí)行區(qū)。如果在載入?yún)^(qū)定義中使用EMPTY屬性,則鏈接程序生成警告信息并忽略該屬性。鏈接程序檢查用于EMPTY區(qū)的地址空間不與任何其他執(zhí)行區(qū)重疊。
4.使用OVERLAY關鍵字在ARM以前的編譯器中,沒有提供地址空間的重疊管理。如果有運行時域地址空間重疊,需要用戶自己提供地址空間重疊的管理機制。但在RVDS的編譯器中,提供了運行時域?qū)傩躁P鍵字OVERLAY,用戶可以使用該關鍵字生成自己的重疊空間。
下面例子顯示了如何使用OVERLAY關鍵字,生成運行時域的重疊空間。
LOADREG 0x8000
{
;
STATIC_RAM 0x0 ; 靜態(tài)RAM區(qū),包含大部分的RW和ZI
{
* (+RW,+ZI)
}
OVERLAY_A_RAM 0x1000 OVERLAY ; 重疊區(qū)...
{
module1.o (+RW,+ZI)
}
OVERLAY_B_RAM 0x1000 OVERLAY
{
module2.o (+RW,+ZI)
}
;
}
5.在Scatter文件中使用預處理偽操作可用在Scatter文件的第一行加上需要編譯器進行預處理的操作。語法格式如下所示。
#! <preprocessor> [pre_processor_flags]
LOAD_FLASH ( 0x8000 + ( 0x2 * 0x400 )) ;
例如:
#! armcc -E
聯(lián)接器可以對預處理的表達式進行簡單的計算,可以識別簡單的運算符如+、-、×、/、AND和OR,如:
#define AN_ADDRESS (BASE_ADDRESS+(ALIAS_NUMBER*ALIAS_SIZE))
同時,也可以在Scatter文件頭加一些預處理的偽操作,如:
#define ADDRESS 0x20000000
#include "include_file_1.h"
#define BASE_ADDRESS 0x8000
#define ALIAS_NUMBER 0x2
#define ALIAS_SIZE 0x400
在Scatter文件中,使用預處理的更詳細的信息,請參見ARM相關文件。
13.4.4 等效的簡單映像分散載入描述前面介紹了分散加載的命令行選項,如-ro-base、-rw-base、-reloc、-split、-ropi和-rwpi。但在實際編程時,因為使用Scatter文件可以產(chǎn)生更清晰的內(nèi)存映像視圖,所以最好使用Scatter文件對映像進行加載。
本節(jié)詳細介紹如何將各分散加載的命令行選項,替換為Scatter文件。
1.-ro-base address選項的替換使用-ro-base address命令行鏈接產(chǎn)生的內(nèi)存映像由一個加載域和三個執(zhí)行域組成。執(zhí)行域放在存儲器映像中的相鄰位置。
選項中的address指定了加載域和第一個執(zhí)行域的起始地址(加載域和第一個執(zhí)行域的起始地址相同)。
下面的例子顯示了與“-ro-base 0x8000”命令行選項等價的Scatter文件。
LOADREG 0x8000 ;定義加載域的起始地址0x8000
{ ;
ROM +0 ;定義第一個執(zhí)行域的起始地址,該地址與加載域的起始地址相同,為0x8000
;
{
*(+RO) ;該域放置所有的RO段
}
RAM_RW +0 ;定義第二個執(zhí)行域,起始地址為0x8000+ROM段大小
;
{
*(+RW) ;將所有的RW代碼放置在該段
}
RAM_ZI +0 ;定義ZI段
;ZI段的起始地址為0x8000+ROM段的大小+RAM_RW段的大小
;
{
*(+ZI) ;放置所有的ZI段
}
}
上例中的Scatter文件創(chuàng)建的映像由一個加載域和三個執(zhí)行域組成。加載域的起始地址為0x8000。三個執(zhí)行域分別為ROM、RAM_RW和RAM_ZI,它們分別包含RO、RW和ZI輸出段。RO和RAM_RW為啟動域,RAM_ZI在執(zhí)行時動態(tài)創(chuàng)建。ROM的執(zhí)行地址是0x8000,通過對執(zhí)行區(qū)描述使用+offset格式的基址指定程序,所有三個執(zhí)行域在存儲器映射中相鄰放置,即前一個執(zhí)行域的末尾放置后一個執(zhí)行域。
如果鏈接程序時,將-ro-base選項和-ropi混合使用,則可以生成位置無關代碼。
下面的例子顯示了與-ro-base 0x8000 -ropi等效的Scatter文件。
LOADREG 0x8000 PI ;加載域的地址為0x8000,并指定該加載域的屬性為PI
{
ROM +0 ;第一執(zhí)行域的地址為0x8000,而且該執(zhí)行域繼承了加載域的PI屬性
;所有該域的執(zhí)行地址是可變的
{
*(+RO) ;放置所有的RO段
}
RAM_RW +0 ABSOLUTE ;使用ABSOLUTE屬性代替PI屬性
{
*(+RW) ;放置RW段
}
RAM_ZI +0
{
*(+ZI)
}
}
執(zhí)行域ROM從LOADREG加載域繼承 PI 屬性。下一個執(zhí)行域 RAM_RW 被標記為 ABSOLUTE 所以其不再具有PI屬性。另外,因為RAM_ZI 域使用了+0的偏移量,所以它從 RAM_RW域繼承 ABSOLUTE 屬性。
2.-ro-base和-rw-base選項的替換使用-ro-base和-rw-base選項鏈接的映像也由一個加載域和三個執(zhí)行域組成,它與類型1生成的映像十分相似,只是此類映像的RW執(zhí)行區(qū)與RO執(zhí)行區(qū)不相鄰。
在-ro-base選項中指定加載域的起始地址,在-rw-base選項中指定執(zhí)行域的地址。
下面的例子顯示與使用-ro-base 0x8000 -rw-base 0x040000等效的分散載入描述。
LOADREG 0x8000 ;定義加載域的起始地址為0x8000
{
ROM_RO +0 ;定義第一個執(zhí)行域的起始地址為0x8000
{
* (+RO) ;在該域中放置所有的RO段
}
RAM_RW 0x040000 ;第二個執(zhí)行域名為RAM_RW,起始地址為0x40000
{
* (+RW) ;放置所有的RW段
}
RAM_ZI +0
{
* (+ZI) ;放置所有的ZI段
}
}
該Scatter文件創(chuàng)建的映像有一個名為LOADREG的加載域,載入地址是0x8000。該映像有3個執(zhí)行區(qū),分別為ROM、RAM_RW和RAM_ZI,它們分別包含RO、RW和ZI輸出段。其中,RO域是啟動域,執(zhí)行地址是0x8000,RAM_RW執(zhí)行域與第一個執(zhí)行域RAM_RW不相鄰。其執(zhí)行地址是0x040000。緊隨其后的執(zhí)行區(qū)RAM_ZI放置所有的ZI數(shù)據(jù)。
另外,也可以將-rw-base和位置無關選項-rwpi配合使用,將RW輸出節(jié)的執(zhí)行區(qū)標記為位置獨立。
下面的例子顯示了使用-ro-base 0x8000 -rw-base 0x40000 -rwpi等效的Scatter文件。
LOADREG 0x0x8000 ;定義加載域的起始地址為0x8000
{
ROM +0 ;定義第一執(zhí)行域,其起始地址為0x8000
{
*(+RO) ;放置所有RO段
}
RAM_RW 0x40000 PI ;設置第二執(zhí)行域的屬性為PI屬性
{
*(+RW)
}
ER_ZI +0 ;繼承了PI屬性
{
*(+ZI)
}
}
第一個執(zhí)行域ROM從加載域LOADREG繼承ABSOLUTE屬性。第二個執(zhí)行區(qū)RAM_RW標記為PI屬性。另外,因為ER_ZI區(qū)的偏移為+0,所以它從RAM_RW區(qū)繼承PI屬性。
3.-reloc -split選項的替換使用-split選項生成的映像由兩個加載域和三個執(zhí)行域組成。
使用以下的鏈接選項重新分割并定位加載域。
· -reloc
組合使用-reloc -split生成具有兩個加載域的映像,并且使加載域具有RELOC屬性。
· -ro-base address1
指定包含RO輸出段的域的載入地址和執(zhí)行地址。
· -ro-base address2
指定包含RW輸出段的域的載入地址和執(zhí)行地址。
· -split
將默認的單一加載域(包含RO和RW輸出段的加載域)分成兩個加載域。一個載入域包含RO輸出段,另一個包含RW輸出段。
下面的例子顯示了與使用-ro-base 0x8000 -rw-base 0x040000 -split等效的Scatter文件。
LOADREG1 0x8000 ;指定第一個加載域的起始地址為0x8000
{
ROM +0
{
*(+RO)
}
}
LOADREG2 0x040000 ;第二個加載域的起始地址為0x40000
{
RAM_RW +0
{
*(+RW) ;放置所有的RW段
}
RAM_ZI +0
{
*(+ZI)
}
}
使用上例中的Scatter文件創(chuàng)建的內(nèi)存映像有兩個加載域,分別為LOADREG1和LOADREG2,它們的起始地址分別為0x8000和0x040000。
該映像文件有三個執(zhí)行域,分別為ROM、RAM_RW和RAM_ZI,它們分別包含RO、RW和ZI輸出段。ROM的執(zhí)行地址是0x8000。
RAM_RW執(zhí)行域與ROM不相鄰。其執(zhí)行地址是0x040000。
執(zhí)行域RAM_ZI緊隨RAM_RW域放置。
可以使用-reloc選項和-split選項配合使用,指定兩個加載域具有RELOC屬性。
下面的例子顯示與使用-ro-base 0x8000 -rw-base 0x040000 -reloc -split等效的Scatter文件。
LOADREG 0x010000 RELOC
{
ROM + 0
{
* (+RO)
}
}
LOADREG 0x040000 RELOC
{
RAM_RW + 0
{
* (+RW)
}
RAM_ZI +0
{
* (+ZI)
}
}