?
13.4? 映像文件存儲器映射調(diào)整
13.4.1? 關(guān)于分散加載
映像由域(Regions)和輸出段(Output Sections)組成。每個域可以有不同的加載地址和執(zhí)行地址。
分散加載可以更加方便準確的指定映像存儲器映射,為映像組件分組和布局提供了全面控制。它能夠描述由載入時和執(zhí)行時分散在存儲器映射中的多個區(qū)組成的復(fù)雜映像映射。雖然,分散加載可以用于簡單映像,但它通常僅用于具有復(fù)雜存儲器映射的映像。
要構(gòu)建映像的存儲器映射,必須向armlink提供以下信息:
·? 分組信息:決定如何將各輸入段組織成相應(yīng)的輸出段和域;
·? 定位信息:決定各域在存儲空間的起始地址。
有兩種方法可以配置指定映像文件的分組和定位信息:如果映像文件中地址映射關(guān)系比較簡單,可以使用命令行選項;如果映像文件中地址映射關(guān)系比較復(fù)雜的情況,可以使用一個配置文件。使用該配置文件可以告訴鏈接器相關(guān)的地址映射關(guān)系。配置文件又叫Scatter文件,是一個文本文件,通過下面的鏈接選項來實現(xiàn)。
-scatter? filename
1.為分散加載定義的符號
當(dāng)armlink使用Scatter文件創(chuàng)建映像時,它創(chuàng)建一些區(qū)相關(guān)符號。表13.2概括了這些符號的意義。
表13.2???????? 域相關(guān)符號
符??? 號 |
意??? 義 |
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)復(fù)雜存儲器映射
系統(tǒng)中的代碼和數(shù)據(jù)必須放在多個不同存儲器區(qū)域中,這樣連接器必須知道哪個段放在哪個儲存器空間的詳細信息。這種情況下,最好用Scatter文件實現(xiàn)代碼映像的分散加載。
② 系統(tǒng)中存在多種不同類型存儲器
許多系統(tǒng)包含多種不同類型存儲器,如flash存儲器、ROM、SDRAM和快速SRAM。分散載入描述可以將代碼和數(shù)據(jù)放置在最適合的存儲器類型中。例如,中斷代碼可能放在快速SRAM中,以加快中斷響應(yīng)時間,而不頻繁使用的配置信息可能放在較慢的flash存儲器中。
③ 存儲器映射I/O
分散載入描述可以將數(shù)據(jù)精確定位在內(nèi)存地址中,而避免數(shù)據(jù)和內(nèi)存映射外圍地址相沖突。
④ 位于固定位置函數(shù)
可以將特定函數(shù)放在存儲器中的同一個位置,這樣即使周圍的應(yīng)用程序已經(jīng)被修改并重新編譯,也可以使具有特定功能的函數(shù)地址保持不變。
⑤ 使用符號識別堆和棧
可以為堆和棧的位置定義符號,鏈接應(yīng)用程序時可以指定該封閉模塊的位置。
隨著目前嵌入式系統(tǒng)越來越復(fù)雜,系統(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.復(fù)雜存儲器映像實現(xiàn)舉例
一個復(fù)雜存儲器映射如圖13.9所示。
圖13.8? 實現(xiàn)簡單內(nèi)存映射的Scatter文件
圖13.9? 復(fù)雜存儲器映射實例
?
為了實現(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),但第二個復(fù)雜存儲器映射的例子卻只能使用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é)的層次結(jié)構(gòu)。
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相當(dāng)于B+或C。 在其組件方面,::=表示法用于定義高級結(jié)構(gòu)。每個組件可能還有一個::=定義,對更簡單的組件進行定義。 例如,A::=B以及B::= C | D表示定義A相當(dāng)于模式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組合在一起。 這在使用 | 操作符時,或重復(fù)復(fù)雜模式時尤其適用。 例如,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)容和組織結(jié)構(gòu)。
圖13.10? 典型的分散載入描述文件的內(nèi)容和組織結(jié)構(gòu)
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_ |
② 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項屬性為互斥關(guān)系)。默認的加載域?qū)傩允茿BSOLUTE。具有PI、RELOC或OVERLAY屬性之一的加載域可以有重疊的地址范圍。對于ABSOLUTE加載域,armlink不允許重疊的地址范圍。OVERLAY關(guān)鍵字允許在同一個地址有多個執(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是域的結(jié)束地址。它通常與EMPTY一起使用,以表示在存儲器中變小的棧。
當(dāng)確定執(zhí)行域?qū)傩詴r,注意以下幾點。
① PI、OVERPLAY、FIXED和ABSOLUTE為并列關(guān)系屬性,某一個執(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
這是由文字文本構(gòu)成的模式?!?”通配符匹配0個或多個字符,而“?”匹配任何單個字符。匹配不區(qū)分大小寫。
使用*.o可以匹配所有對象。使用*可以匹配所有目標文件和庫。
當(dāng)滿足下列條件之一時,鏈接器認為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
輸入段屬性符定義了一個用逗號隔開的模式類別。該類表中的每個模式定義了輸入段名稱或輸入段屬性匹配方式。當(dāng)匹配模式使用輸入段名稱時,它前面必須使用符號“+”,而符號“+”前面緊接的逗號可以省略。
輸入段屬性不區(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”相當(dāng)于使用“*”模塊選擇屬性。
在分散載入描述文件中不能使用兩個“*”選擇屬性。但是,可以使用兩個變形的選擇程序,例如,*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設(shè)置執(zhí)行區(qū)屬性,并且對第一個執(zhí)行區(qū)及其所在的加載區(qū)使用相同的地址。為確保執(zhí)行域地址和加載域地址相同,可以將加載域的起始地址和執(zhí)行域的起始地址設(shè)為相同的值或者將第一個執(zhí)行域的地址偏移量設(shè)為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)
?? }
}
③ 如果使用分散加載,負責(zé)創(chuàng)建執(zhí)行域的代碼和數(shù)據(jù)不能將其自身復(fù)制到另一位置,因此啟動域必須包含以下內(nèi)容。
·? _main.o和_scatter*.o:包含復(fù)制代碼和數(shù)據(jù)的代碼。
·? Region$$Table和ZISection$$Table段:包含要復(fù)制代碼和數(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設(shè)備)創(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 屬性為棧保留一個空白存儲器塊。該存儲塊不構(gòu)成載入?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被視為域的結(jié)束地址。它是絕對地址,不是相對地址。下面例子顯示了如何在Scatter文件中預(yù)留一個空白區(qū)域。
LOADREGION ?0x700000?????????????????? ; 加載域的起始地址在0x700000
{?????????????????????????????????? ;
??? STACK 0x7000000 EMPTY –0x10000?????? ; 該域的結(jié)束地址為0x700000,因為其長度為負
??????????????????????????????????? ;
region
??????????????????????????????????? ;
??? {
??????????????????????????????????? ; 預(yù)留空白區(qū)放置棧
??? }
??? HEAP +0 EMPTY 0x10000?????????????? ; 棧的起始地址在上個預(yù)留區(qū)域介紹地址
??????????????????????????????????? ;
??????????????????????????????????? ;
??? {
??????????????????????????????????? ; 預(yù)留空白區(qū)域放置堆
??? }
??? ; rest of scatter description...
}
在上面的例子中定義了一個執(zhí)行域STACK 0x7000000 EMPTY -0x10000,它從地址 (0x7000000-0x1000)開始,在地址0x7000000結(jié)束。
在此示例中,鏈接程序生成符號:
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關(guān)鍵字
在ARM以前的編譯器中,沒有提供地址空間的重疊管理。如果有運行時域地址空間重疊,需要用戶自己提供地址空間重疊的管理機制。但在RVDS的編譯器中,提供了運行時域?qū)傩躁P(guān)鍵字OVERLAY,用戶可以使用該關(guān)鍵字生成自己的重疊空間。
下面例子顯示了如何使用OVERLAY關(guān)鍵字,生成運行時域的重疊空間。
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文件中使用預(yù)處理偽操作
可用在Scatter文件的第一行加上需要編譯器進行預(yù)處理的操作。語法格式如下所示。
#! <preprocessor> [pre_processor_flags]
LOAD_FLASH ( 0x8000 + ( 0x2 * 0x400 ))? ;
例如:
#! armcc ?-E
聯(lián)接器可以對預(yù)處理的表達式進行簡單的計算,可以識別簡單的運算符如+、-、×、/、AND和OR,如:
#define AN_ADDRESS (BASE_ADDRESS+(ALIAS_NUMBER*ALIAS_SIZE))
同時,也可以在Scatter文件頭加一些預(yù)處理的偽操作,如:
#define ADDRESS 0x20000000
#include "include_file_1.h"
#define BASE_ADDRESS 0x8000
#define ALIAS_NUMBER 0x2
#define ALIAS_SIZE 0x400
在Scatter文件中,使用預(yù)處理的更詳細的信息,請參見ARM相關(guān)文件。
?
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混合使用,則可以生成位置無關(guān)代碼。
下面的例子顯示了與-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和位置無關(guān)選項-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???????? ;設(shè)置第二執(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)
???? }
}