加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 13.4  映像文件存儲器映射調(diào)整
  • 相關推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

嵌入式軟件開發(fā)之: 映像文件存儲器映射調(diào)整

2013/09/30
1
閱讀需 57 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

?

13.4? 映像文件存儲器映射調(diào)整

13.4.1? 關于分散加載

映像由域(Regions)和輸出段(Output Sections)組成。每個域可以有不同的加載地址和執(zhí)行地址。

分散加載可以更加方便準確的指定映像存儲器映射,為映像組件分組和布局提供了全面控制。它能夠描述由載入時和執(zhí)行時分散在存儲器映射中的多個區(qū)組成的復雜映像映射。雖然,分散加載可以用于簡單映像,但它通常僅用于具有復雜存儲器映射的映像。

要構(gòu)建映像的存儲器映射,必須向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é)的層次結(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相當于B+或C。

在其組件方面,::=表示法用于定義高級結(jié)構(gòu)。每個組件可能還有一個::=定義,對更簡單的組件進行定義。

例如,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)容和組織結(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_
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是域的結(jié)束地址。它通常與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

這是由文字文本構(gòu)成的模式。“*”通配符匹配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 屬性為棧保留一個空白存儲器塊。該存儲塊不構(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文件中預留一個空白區(qū)域。

LOADREGION ?0x700000?????????????????? ; 加載域的起始地址在0x700000

{?????????????????????????????????? ;

??? STACK 0x7000000 EMPTY –0x10000?????? ; 該域的結(jié)束地址為0x700000,因為其長度為負

??????????????????????????????????? ;

region

??????????????????????????????????? ;

??? {

??????????????????????????????????? ; 預留空白區(qū)放置棧

??? }

??? HEAP +0 EMPTY 0x10000?????????????? ; 棧的起始地址在上個預留區(qū)域介紹地址

??????????????????????????????????? ;

??????????????????????????????????? ;

??? {

??????????????????????????????????? ; 預留空白區(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關鍵字

在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)

???? }

}

相關推薦

電子產(chǎn)業(yè)圖譜

華清遠見(www.farsight.com.cn)是國內(nèi)領先嵌入師培訓機構(gòu),2004年注冊于中國北京海淀高科技園區(qū),除北京總部外,上海、深圳、成都、南京、武漢、西安、廣州均有直營分公司。華清遠見除提供嵌入式相關的長期就業(yè)培訓、短期高端培訓、師資培訓及企業(yè)員工內(nèi)訓等業(yè)務外,其下屬研發(fā)中心還負責嵌入式、Android及物聯(lián)網(wǎng)方向的教學實驗平臺的研發(fā)及培訓教材的出版,截止目前為止已公開出版70余本嵌入式/移動開發(fā)/物聯(lián)網(wǎng)相關圖書。企業(yè)理念:專業(yè)始于專注 卓識源于遠見。企業(yè)價值觀:做良心教育、做專業(yè)教育,更要做受人尊敬的職業(yè)教育。