這一篇文章,深入分析一下 MDK 的分散加載文件,力求能夠將分散文件以及程序的加載過程貫穿起來,徹底搞懂這個知識點。
使用分散文件指定棧和堆
ARM C 庫提供了函數(shù) __user_setup_stackheap() 的多個實現(xiàn),并且可以根據(jù)分散文件中提供的信息自動選擇正確的一種。
要選擇雙區(qū)域內存模型,請在分散文件中定義兩個名為?ARM_LIB_HEAP和ARM_LIB_STACK的特殊執(zhí)行區(qū)域。這兩個區(qū)域都有 EMPTY 屬性,會導致庫選擇__user_setup_stackheap()的非默認實現(xiàn),使用以下的符號值:
Image$$ARM_LIB_STACK$$Base Image$$ARM_LIB_STACK$$ZI$$Limit Image$$ARM_LIB_HEAP$$Base Image$$ARM_LIB_HEAP$$ZI$$Limit
只能指定一個ARM_LIB_STACK或ARM_LIB_HEAP區(qū)域,并且必須分配一個大小,例如:
ARM_LIB_HEAP?0x20100000?EMPTY?0x100000-0x8000??;?Heap?starts?at?1MB ???????????????????????????????????????????????;?and?grows?upwards ARM_LIB_STACK?0x20200000?EMPTY?-0x8000?????????;?Stack?space?starts?at?the?end ???????????????????????????????????????????????;?of?the?2MB?of?RAM ???????????????????????????????????????????????;?And?grows?downwards?for?32KB可以通過定義名為?ARM_LIB_STACKHEAP?單一執(zhí)行區(qū)域使用組合的棧和堆區(qū)域ARM_LIB_STACKHEAP,與EMPTY屬性。這會導致__user_setup_stackheap()使用符號Image$$ARM_LIB_STACKHEAP$$Base和Image$$ARM_LIB_STACKHEAP$$ZI$$Limit的值。 注意,如果重新實現(xiàn)__user_setup_stackheap(),這將覆蓋所有庫里面的實現(xiàn)。
創(chuàng)建 root執(zhí)行區(qū)
要將區(qū)域指定為分散文件中的根區(qū)域,您可以:
指定?ABSOLUTE為執(zhí)行區(qū)的屬性(顯式或允許它默認),并為第一個執(zhí)行區(qū)和封閉加載區(qū)使用相同的地址。要使執(zhí)行區(qū)地址與加載區(qū)地址相同,請執(zhí)行以下任一操作:
為執(zhí)行區(qū)的基地址和加載區(qū)的基地址指定相同的數(shù)值。
指定+0加載區(qū)中第一個執(zhí)行區(qū)的偏移量。如果+0為加載區(qū)中的所有后續(xù)執(zhí)行區(qū)指定零偏移(+0),則所有不跟隨包含 ZI 的執(zhí)行區(qū)的執(zhí)行區(qū)也是根區(qū)。
以下示例顯示了隱式定義的根區(qū)域:
LR_1?0x040000??????????;?load?region?starts?at?0x40000??? {??????????????????????;?start?of?execution?region?descriptions??????? ????ER_RO?0x040000?????;?load?address?=?execution?address ????{ ????????*?(+RO)????????;?all?RO?sections?(must?include?section?with? ???????????????????????;?initial?entry?point) ????} ????...????????????????;?rest?of?scatter-loading?description }
使用FIXED執(zhí)行區(qū)屬性可以確保特定區(qū)域的加載地址和執(zhí)行地址相同。您可以使用該FIXED屬性將任何執(zhí)行區(qū)放置在 ROM 中的特定地址。例如,以下內存映射顯示了固定執(zhí)行區(qū):圖 8. 固定執(zhí)行區(qū)的內存映射
下面的例子給出了相應分散加載的描述:
LR_1?0x040000??????????????;?load?region?starts?at?0x40000??? {??????????????????????????;?start?of?execution?region?descriptions??????????? ????ER_RO?0x040000?????????;?load?address?=?execution?address ????{ ????????*?(+RO)????????????;?RO?sections?other?than?those?in?init.o ????} ????ER_INIT?0x080000?FIXED?;?load?address?and?execution?address?of?this ???????????????????????????;?execution?region?are?fixed?at?0x80000 ????{ ????????init.o(+RO)????????;?all?RO?sections?from?init.o ????} ????...????????????????????;?rest?of?scatter-loading?description }
Examples of misusing the FIXED attribute誤用 FIXED 屬性 例子
The following example shows common cases where the FIXED execution region attribute is misused:
LR1?0x8000 { ????ER_LOW?+0?0x1000 ????{ ????????*(+RO) ????} ;?At?this?point?the?next?available?Load?and?Execution?address?is?0x8000?+?size?of ;?contents?of?ER_LOW.?The?maximum?size?is?limited?to?0x1000?so?the?next?available?Load ;?and?Execution?address?is?at?most?0x9000 ????ER_HIGH?0xF0000000?FIXED ????{ ????????*(+RW+ZI) ????} ;?The?required?execution?address?and?load?address?is?0xF0000000.?The?linker?inserts ;?0xF0000000?-?(0x8000?+?size?of(ER_LOW))?bytes?of?padding?so?that?load?address?matches ;?execution?address } ;?The?other?common?misuse?of?FIXED?is?to?give?a?lower?execution?address?than?the?next ;?available?load?address. LR_HIGH?0x100000000 { ????ER_LOW?0x1000?FIXED ????{ ????????*(+RO) ????} ;?The?next?available?load?address?in?LR_HIGH?is?0x10000000.?The?required?Execution ;?address?is?0x1000.?Because?the?next?available?load?address?in?LR_HIGH?must?increase ;?monotonically?the?linker?cannot?give?ER_LOW?a?Load?Address?lower?than?0x10000000 }
使用 FIXED 屬性創(chuàng)建根區(qū)域
您可以FIXED在執(zhí)行區(qū)分散文件中使用該屬性來創(chuàng)建在固定地址加載和執(zhí)行的根區(qū)。FIXED用于在單個加載區(qū)域內創(chuàng)建多個根區(qū)域,因此通常是單個 ROM 設備。例如,您可以使用它來將函數(shù)或數(shù)據(jù)塊(例如常量表或校驗和)放置在 ROM 中的固定地址,以便可以通過指針輕松訪問。
例如,如果您指定將一些初始化代碼放置在 ROM 的開頭并在 ROM 的末尾放置一個校驗和,則某些內存內容可能未被使用。使用*或.ANY模塊選擇器來填充初始化塊末尾和數(shù)據(jù)塊開頭之間的區(qū)域。
為了使您的代碼更易于維護和調試,建議您在分散文件中使用最少的布局規(guī)范,并將函數(shù)和數(shù)據(jù)的詳細布局留給鏈接器。
您不能指定已部分鏈接的組件對象。例如,如果您將對象obj1.o、obj2.o和部分鏈接obj3.o在一起以產(chǎn)生obj_all.o,則在生成的對象中會丟棄組件對象名稱。因此,您不能按名稱引用其中一個對象,例如,obj1.o。您只能引用組合對象obj_all.o。
注意在某些情況下,使用FIXED和 單個加載區(qū)域是不合適的。指定固定位置的其他方式是:
如果您的加載程序可以處理多個加載區(qū)域,請將 RO 代碼或數(shù)據(jù)放在其自己的加載區(qū)域中。
如果您不要求函數(shù)或數(shù)據(jù)位于 ROM 中的固定位置,請使用ABSOLUTE代替FIXED。然后加載器將數(shù)據(jù)從加載區(qū)復制到 RAM 中的指定地址。ABSOLUTE是默認屬性。
要將數(shù)據(jù)結構放置在內存映射 I/O 的位置??,請使用兩個加載區(qū)域并指定UNINIT. UNINIT確保內存位置不會被初始化為零。
在特定地址放置函數(shù)和數(shù)據(jù)
通常,編譯器從單個源文件生成 RO、RW 和 ZI 節(jié)。這些區(qū)域包含源文件中的所有代碼和數(shù)據(jù)。要將單個函數(shù)或數(shù)據(jù)項放置在固定地址,您必須使鏈接器能夠將函數(shù)或數(shù)據(jù)與其余輸入文件分開處理。
鏈接器有兩種方法可以讓您將段放置在特定地址:
您可以創(chuàng)建一個分散文件,該文件在所需地址處定義一個執(zhí)行區(qū),并帶有僅選擇一個段的段描述。
對于特殊命名的段,鏈接器可以從段名中獲取放置地址。這些專門命名的部分稱為__at段。
要將函數(shù)或變量放置在特定地址,必須將其放置在其自己的段中。有幾種方法可以做到這一點:
將函數(shù)或數(shù)據(jù)項放在其自己的源文件中。
使用到地方變量在一個單獨的部分,在一個特定的地址__attribute__((at(address)))
用于在指定段中放置函數(shù)和變量__attribute__((section("name")))
使用AREA匯編語言中的指令。在匯編代碼中,最小的可定位單元是AREA.
使用--split_sections編譯器選項為源文件中的每個函數(shù)生成一個 ELF 部分。此選項會導致某些函數(shù)的代碼大小略有增加,因為它降低了在函數(shù)之間共享地址、數(shù)據(jù)和字符串文字的可能性。但是,當您指定armlink --remove這可以幫助減少最終固件鏡像整體大小,使鏈接器能夠刪除未使用的函數(shù)。
在沒有分散加載的情況下將變量放置在特定地址的示例
此示例顯示如何修改源代碼以將代碼和數(shù)據(jù)放置在特定地址,并且不需要分散文件:1、創(chuàng)建main.c包含以下代碼的源文件:
#include? extern?int?sqr(int?n1); int?gSquared?__attribute__((at(0x5000)));??//?Place?at?0x5000 int?main() { ????gSquared=sqr(3); ????printf("Value?squared?is:?%d ",?gSquared); }
2、創(chuàng)建function.c包含以下代碼的源文件:
int?sqr(int?n1) { ????return?n1*n1; }
3、編譯并鏈接源:
armcc?-c?-g?function.c armcc?-c?-g?main.c armlink?--map?function.o?main.o?-o?squared.axf--map選項用于生成內存映射文件即.map文件,同樣--autoat是默認值
在此示例中,__attribute__((at(0x5000)))指定將全局變量gSquared放置在絕對地址處0x20000。gSquared被放置在執(zhí)行區(qū)ER$$.ARM.__AT_0x00005000和加載區(qū)中LR$$.ARM.__AT_0x00005000。
The memory map shows:
... ??Load?Region?LR$$.ARM.__AT_0x00005000?(Base:?0x00005000,?Size:?0x00000000,?Max:?0x00000004,?ABSOLUTE) ????Execution?Region?ER$$.ARM.__AT_0x00005000?(Base:?0x00005000,?Size:?0x00000004,?Max:?0x00000004,?ABSOLUTE,?UNINIT) ????Base?Addr????Size?????????Type???Attr??????Idx????E?Section?Name????????Object ????0x00005000???0x00000004???Zero???RW???????????15????.ARM.__AT_0x00005000??main.o使用分散加載將變量放置在指定段中的示例
此示例顯示如何使用分散文件修改源代碼以將代碼和數(shù)據(jù)放置在特定部分中:1、創(chuàng)建main.c包含以下代碼的源文件:
#include? extern?int?sqr(int?n1); int?gSquared?__attribute__((section("foo")));??//?Place?in?section?foo int?main() { ????gSquared=sqr(3); ????printf("Value?squared?is:?%d ",?gSquared); }
2、創(chuàng)建function.c包含以下代碼的源文件:
int?sqr(int?n1) { ????return?n1*n1; }
3、創(chuàng)建scatter.scat包含以下加載區(qū)域的分散文件:
LR1?0x0000?0x20000 { ????ER1?0x0?0x2000 ????{ ????????*(+RO)??????????????????????;?rest?of?code?and?read-only?data ????} ????ER2?0x8000?0x2000 ????{ ????????main.o ????} ????ER3?0x10000?0x2000 ????{ ????????function.o ????????*(foo)??????????????????????;?Place?gSquared?in?ER3 ????} ????RAM?0x200000?(0x1FF00-0x2000)???;?RW?&?ZI?data?to?be?placed?at?0x200000 ????{ ????????*(+RW,?+ZI) ????} ????ARM_LIB_STACK?0x800000?EMPTY?-0x10000 ????{ ????} ????ARM_LIB_HEAP??+0?EMPTY?0x10000 ????{ ????} }
該ARM_LIB_STACK和ARM_LIB_HEAP都需要,因為程序將與半主機庫鏈接。4、編譯并鏈接
armcc?-c?-g?function.c armcc?-c?-g?main.c aarmlink?--map?--scatter=scatter.scat?function.o?main.o?-o?squared.axf
內存映射顯示:
??Load?Region?LR1?(Base:?0x00000000,?Size:?0x00001778,?Max:?0x00020000,?ABSOLUTE) ... ????Execution?Region?ER3?(Base:?0x00010000,?Size:?0x00000004,?Max:?0x00002000,?ABSOLUTE) ????Base?Addr????Size?????????Type???Attr??????Idx????E?Section?Name????????Object ????0x00010000???0x00000004???Data???RW???????????15????foo?????????????????main.o ...
注意
如果*(foo)從分散文件中省略,則該部分將放置在相同類型的區(qū)域中。在這個例子中就是RAM區(qū)。
使用分散加載將變量放置在特定地址的示例
1、創(chuàng)建main.c包含以下代碼的源文件
#include? extern?int?sqr(int?n1); //?Place?at?address?0x10000 const?int?gValue?__attribute__((section(".ARM.__at_0x10000")))?=?3; int?main() { ????int?squared; ????squared=sqr(gValue); ????printf("Value?squared?is:?%d ",?squared); }
2、創(chuàng)建function.c包含以下代碼的源文件:
int?sqr(int?n1) { ????return?n1*n1; }
3、創(chuàng)建scatter.scat包含以下加載區(qū)域的分散文件:
LR1?0x0 { ????ER1?0x0 ????{ ????????*(+RO)??????????????????????;?rest?of?code?and?read-only?data ????} ????ER2?+0 ????{ ????????function.o ????????*(.ARM.__at_0x10000)?????????;?Place?gValue?at?0x10000 ????} ????RAM?0x200000?(0x1FF00-0x2000)???;?RW?&?ZI?data?to?be?placed?at?0x200000 ????{ ????????*(+RW,?+ZI) ????} ????ARM_LIB_STACK?0x800000?EMPTY?-0x10000 ????{ ????} ????ARM_LIB_HEAP??+0?EMPTY?0x10000 ????{ ????} }
該ARM_LIB_STACK和ARM_LIB_HEAP都需要,因為程序將與半主機庫鏈接。
4、編譯并鏈接
armcc?-c?-g?function.c armcc?-c?-g?main.c armlink?--no_autoat?--scatter=scatter.scat?--map?function.o?main.o?-o?squared.axf
內存映射顯示變量放置ER2在地址處的執(zhí)行區(qū)中0x11000:
?
?
... ????Execution?Region?ER2?(Base:?0x00001598,?Size:?0x0000ea6c,?Max:?0xffffffff,?ABSOLUTE) ????Base?Addr????Size?????????Type???Attr??????Idx????E?Section?Name????????Object ????0x00001598???0x0000000c???Code???RO????????????3????.text???????????????function.o ????0x000015a4???0x0000ea5c???PAD ????0x00010000???0x00000004???Data???RO???????????15????.ARM.__at_0x10000???main.o...
?
?
在這個例子中,ER1的大小是未知的。因此,gValue可能放在ER1或 中ER2。要確保將gValue其放置在ER2中,您必須包含相應的選擇器ER2并與--no_autoat命令行選項鏈接。如果省略--no_autoat, gValue將被放在一個單獨的加載區(qū)域LR$$.ARM.__AT_0x00010000,包含執(zhí)行區(qū)域ER$$.ARM.__AT_0x00020000
變量指定段
方式一
?
?
int?variable?__attribute__((section("foo")))?=?10; 1 FLASH?0x24000000?0x4000000 { ????...????????????????????????????????;?rest?of?code ????ADDER?0x08000000 ????{ ????????file.o?(foo)??????????????????;?select?section?foo?from?file.o ????} }
?
?
方式二
?
?
//?place?variable1?in?a?section?called?.ARM.__at_0x00008000 int?variable1?__attribute__((at(0x8000)))?=?10; //?place?variable2?in?a?section?called?.ARM.__at_0x8000 int?variable2?__attribute__((section(".ARM.__at_0x8000")))?=?10; 12345 ER_FLASH?0x8000?0x2000 { ????*(+RO) ????*(.ARM.__at_0x8000)?; }
?
?
函數(shù)地址指定
?
?
int?sqr(int?n1)?__attribute__((section(".ARM.__at_0x20000"))); int?sqr(int?n1) { ????return?n1*n1; }
?
?
注意
如果不使用分散加載,則該部分將放置在加載區(qū)的默認ER_RW執(zhí)行區(qū)中LR_1
如果源碼中使用了未定義段名(分散加載文件中無此段名),則該部分將放置在定義的 RW 執(zhí)行區(qū)中
--autoat or --no_autoat 不影響放置
使用分散加載顯式放置命名部分
以下示例顯示如何使用分散加載顯式放置命名部分:
?
?
LR1?0x0?0x10000 { ????ER1?0x0?0x2000???????????????????;?Root?Region,?containing?init?code ????{ ????????init.o?(INIT,?+FIRST)????????;?place?init?code?at?exactly?0x0 ????????*(+RO)???????????????????????;?rest?of?code?and?read-only?data?? ????} ????RAM_RW?0x400000?(0x1FF00-0x2000)?;?RW?&?ZI?data?to?be?placed?at?0x400000 ????{ ????????*(+RW) ????} ????RAM_ZI?+0 ????{ ????????*(+ZI) ????} ????DATABLOCK?0x1FF00?0xFF???????????;?execution?region?at?0x1FF00 ????{????????????????????????????????;?maximum?space?available?for?table?is?0xFF ????????data.o(+RO-DATA)?????????????;?place?RO?data?between?0x1FF00?and?0x1FFFF ????} }
?
?
在這個例子中,分散加載描述放置:
初始化代碼放在文件的INIT段中init.o。此示例顯示該INIT段中的代碼首先放置在地址 處0x0,然后是 RO 代碼的其余部分以及除對象中的 RO 數(shù)據(jù)之外的所有 RO 數(shù)據(jù)data.o。
RAM 中的所有全局 RW 變量位于0x400000
data.o中的所有RO-DATA數(shù)據(jù)放置在0x1FF00
使用.ANY模塊選擇器放置未分配的段
鏈接器嘗試將輸入節(jié)放入特定的執(zhí)行區(qū)。對于無法解析的任何輸入部分,并且這些部分的放置不重要,您可以使用.ANY分散文件中的模塊選擇器。
在大多數(shù)情況下,使用單個.ANY選擇器等同于使用*模塊選擇器。但是不同的是,您可以.ANY在多個執(zhí)行區(qū)中指定。
放置未分配段的默認規(guī)則
默認情況下,鏈接器使用以下條件放置未分配的段:
在當前擁有最多可用空間的執(zhí)行區(qū)中放置一個未分配的段。您可以使用執(zhí)行區(qū)域屬性指定用于未分配段的最大空間量ANY_SIZE。
按大小降序對部分進行排序。
使用多個.ANY選擇器時的放置規(guī)則
如果分散文件中存在多個.ANY 選擇器,則鏈接器采用最大大小的未分配段并將該段分配給具有足夠可用空間的最具體的.ANY執(zhí)行區(qū)。例如,.ANY(.text)被判斷為比 .ANY(+RO)更具體。
如果多個執(zhí)行區(qū)具有相同的特性,則該段將分配給具有最多可用剩余空間的執(zhí)行區(qū)。
例如:
如果您有兩個同樣特定的執(zhí)行區(qū),其中一個的大小限制為0x2000,另一個沒有限制,則所有段都分配給第二個無界.ANY區(qū)域。
如果你有兩個同樣的特定執(zhí)行區(qū),其中一個大小限制為0x2000和另一個大小限制為0x3000,然后第一個段將被分配到第二個.ANY(區(qū)域大小限制0x3000),直到第二個.ANY剩余的大小減少到0x2000。從這一點開始,section在兩個.ANY執(zhí)行區(qū)域之間交替分配。
.ANY優(yōu)先段
如果您有多個.ANY帶有選擇器的部分,您可以給出優(yōu)先順序,其中是從零向上的正整數(shù)。最高優(yōu)先級被賦予具有最高整數(shù)的選擇器。.ANYnum以下示例顯示了如何使用:.ANYnum
?
?
lr1?0x8000?1024 { ????er1?+0?512 ????{ ????????.ANY1(+RO)?;?evenly?distributed?with?er3 ????} ????er2?+0?256 ????{ ????????.ANY2(+RO)?;?Highest?priority,?so?filled?first ????} ????er3?+0?256 ????{ ????????.ANY1(+RO)?;?evenly?distributed?with?er1 ????} }
?
?
控制多個.ANY選擇器的輸入段的放置
.ANY通過使用不同的放置算法或不同的排序順序,您可以修改鏈接器在使用多個選擇器時放置未分配輸入段的方式。以下命令行選項可用:
--any_placement=algorithm, 其中algorithm是first_fit, worst_fit, best_fit, 或next_fit之一
--any_sort_order=order,其中order是cmdline或descending_size之一
first_fit當您想要按順序填充區(qū)域時使用。best_fit當您想最大程度地填充區(qū)域時使用。worst_fit當您想要均勻填充區(qū)域時使用。使用相同大小的區(qū)域和部分worst_fit循環(huán)填充區(qū)域。當您需要更具確定性的填充模式時,請使用 next_fit。
如果鏈接器嘗試將區(qū)域填充到其極限,就像使用first_fit和 一樣best_fit,它可能會過度填充該區(qū)域。這是因為在將節(jié)分配給.ANY選擇器之前,鏈接器生成的內容(例如填充和單板)是未知的。如果發(fā)生這種情況,您可能會看到以下錯誤:
Error: L6220E: Execution region regionname size (size bytes) exceeds limit (limit bytes).錯誤:L6220E:執(zhí)行區(qū)regionname大小(size字節(jié))超過限制(limit字節(jié))。
該--any_contingency選項可防止鏈接器將區(qū)域填充到最大值。它為鏈接器生成的內容保留了該區(qū)域大小的一部分,并且僅當其他區(qū)域沒有空間時才填充此應急區(qū)域。默認情況下為first_fit和best_fit算法啟用它,因為它們最有可能表現(xiàn)出這種行為。
指定允許放置未分配段的最大尺寸
執(zhí)行區(qū)屬性使您能夠指定armlink可以用未分配的節(jié)填充的區(qū)域中的最大大小。ANY_SIZE max_size
使用此關鍵字時請注意以下限制:
max_size必須小于或等于區(qū)域大小
您可以ANY_SIZE在沒有.ANY選擇器的區(qū)域上使用,但它會被armlink忽略
當ANY_SIZE存在時,armlink:
不覆蓋給定的.ANY大小。也就是說,它不會降低優(yōu)先級,然后嘗試在稍后放入更多段。
從不重新計算意外事件。
從不分配應急空間中的段。
ANY_SIZE不需要--any_contingency指定。但是,無論何時--any_contingency指定和ANY_SIZE未指定,armlink 都會嘗試調整意外情況。目標是:
永遠不會溢出一個.ANY區(qū)域
永遠不要拒絕在應急保留空間中放置一個段。
如果您--any_contingency在命令行上指定,則對于已ANY_SIZE指定的區(qū)域將忽略它。它通常用于未ANY_SIZE指定的區(qū)域。
以下示例顯示了如何使用ANY_SIZE:
?
?
LOAD_REGION?0x0?0x3000 { ???ER_1?0x0?ANY_SIZE?0xF00?0x1000 ???{ ??????.ANY ???} ???ER_2?0x0?ANY_SIZE?0xFB0?0x1000 ???{ ??????.ANY ???} ???ER_3?0x0?ANY_SIZE?0x1000?0x1000 ???{ ??????.ANY ???} }
?
?
在這個例子中:
ER_1為鏈接器生成的內容保留了0x100。ER_2為鏈接器生成的內容保留了0x50。這和--any_contingency的自動應急保留類似。ER_3沒有預留空間。因此,100%的區(qū)域被填滿,沒有應急保留。省略ANY_SIZE參數(shù)會導致98%的區(qū)域被填滿,只有2%的應急保留。
使用 __at 在外設寄存器上放置
要將未初始化的變量放置在外設寄存器上,您可以使用 ZI__at部分。假設一個寄存器可用于0x10000000,定義一個__at名為.ARM.__at_0x10000000. 例如:
?
?
int?foo?__attribute__((section(".ARM.__at_0x10000000"),?zero_init)); 1 ER_PERIPHERAL?0x10000000?UNINIT { ????*(.ARM.__at_0x10000000) }
?
?
使用自動放置,并假設附近沒有其他執(zhí)行區(qū)0x10000000,鏈接器會自動創(chuàng)建一個UNINIT屬性為 at的區(qū)域0x10000000。該UNINIT屬性創(chuàng)建一個包含未初始化數(shù)據(jù)或內存映射 I/O 的執(zhí)行區(qū)。
預留一個空區(qū)域
可以EMPTY在執(zhí)行區(qū)分散加載描述中使用該屬性來為堆棧保留一塊空內存。
內存塊不構成加載區(qū)的一部分,而是在執(zhí)行時分配使用。因為它是作為虛擬 ZI 區(qū)域創(chuàng)建的,所以鏈接器使用以下符號來訪問它:
Image$$region_name$$ZI$$Base
Image$$region_name$$ZI$$Limit
Image$$region_name$$ZI$$Length
如果長度為負值,則該地址被視為區(qū)域的結束地址。這必須是絕對地址而不是相對地址。
在以下示例中,執(zhí)行區(qū)定義STACK 0x800000 EMPTY -0x10000定義了一個名為的區(qū)域STACK,該區(qū)域從 address 開始并在 address0x7F0000結束0x800000:
?
?
LR_1?0x80000??????????????????????????;?load?region?starts?at?0x80000??? { ????STACK?0x800000?EMPTY?-0x10000?????;?region?ends?at?0x800000?because?of?the ??????????????????????????????????????;?negative?length.?The?start?of?the?region ??????????????????????????????????????;?is?calculated?using?the?length. ????{ ??????????????????????????????????????;?Empty?region?used?to?place?stack ????} ????HEAP?+0?EMPTY?0x10000?????????????;?region?starts?at?the?end?of?previous ??????????????????????????????????????;?region.?End?of?region?calculated?using ??????????????????????????????????????;?positive?length ????{ ??????????????????????????????????????;?Empty?region?used?to?place?heap ????} ????.???..???????????????????????????????;?rest?of?scatter-loading?description... }
?
?
注意
為EMPTY執(zhí)行區(qū)域創(chuàng)建的虛擬ZI區(qū)域在運行時不會初始化為零。
如果地址是相對的(+offset)形式并且長度是負的,鏈接器會產(chǎn)生一個錯誤。下圖顯示了該示例的圖解表示。
圖 9. 為堆棧保留一個區(qū)域
在本例中,鏈接器生成符號:
Image$$STACK$$ZI$$Base??????=?0x7f0000 Image$$STACK$$ZI$$Limit?????=?0x800000 Image$$STACK$$ZI$$Length????=?0x10000 Image$$HEAP$$ZI$$Base???????=?0x800000 Image$$HEAP$$ZI$$Limit??????=?0x810000 Image$$HEAP$$ZI$$Length?????=?0x10000
該EMPTY屬性僅適用于執(zhí)行區(qū)。鏈接器生成警告并忽略EMPTY加載區(qū)定義中使用的屬性。
鏈接器檢查用于該EMPTY區(qū)域的地址空間是否與任何其他執(zhí)行區(qū)域不一致。
在分散文件中使用預處理命令
您可以通過 C 預處理器傳遞分散文件。這允許訪問 C 預處理器的所有功能。
使用分散文件中的第一行指定鏈接器調用以處理文件的預處理器命令。命令的格式如下:
#!?preprocessor?[ pre_processor_flags ]
最典型的命令是#! armcc -E. 這會通過armcc預處理器傳遞分散文件。
你可以:
將預處理指令添加到分散文件的頂部
在分散文件中使用簡單的表達式評估。
例如,分散文件file.scat, 可能包含:
#!?armcc?-E #define?ADDRESS?0x20000000 #include?"include_file_1.h" lr1?ADDRESS { ????... }
鏈接器解析預處理后的分散文件并將指令視為注釋。
您還可以將分散文件的預處理與–predefine命令行選項結合使用。對于這個例子:
修改file.scat以刪除指令。#define ADDRESS 0x20000000
指定命令:armlink --predefine="-DADDRESS=0x20000000" --scatter=file.scat
在分散文件中使用表達式求值以避免填充
使用ALIGN,ALIGNALL或FIXED在分散的文件屬性可導致在鏡像中的大量填充的。要刪除此填充,您可以使用表達式計算來指定加載區(qū)和執(zhí)行區(qū)的起始地址。內置函數(shù)AlignExpr可用于幫助您指定地址表達式。
避免在分散文件中填充的示例以下分散文件生成帶有填充的圖像:
LR1?0x4000 { ????ER1?+0?ALIGN?0x8000 ????{ ????????... ????} }
使用ALIGN關鍵字ER1與0x8000加載視圖和執(zhí)行視圖中的邊界對齊。要在加載視圖中對齊,鏈接器必須插入0x4000填充字節(jié)。
以下分散文件生成沒有填充的圖像:
LR1?0x4000 { ????ER1?AlignExpr(+0,?0x8000) ????{ ????????... ????} }
使用AlignExpr的結果+0與0x8000邊界對齊。這將創(chuàng)建一個執(zhí)行區(qū),其加載地址為0x4000但執(zhí)行地址為0x8000。
審核編輯:黃飛
評論
查看更多