使用分散文件指定棧和堆
ARM C 庫(kù)提供了該函數(shù)的多種實(shí)現(xiàn)__user_setup_stackheap(),并且可以從分散文件中提供的信息中自動(dòng)為您選擇正確的一種。
要選擇兩個(gè)區(qū)域內(nèi)存模型,請(qǐng)?jiān)诿麨锳RM_LIB_HEAP和的分散文件中定義兩個(gè)特殊的執(zhí)行區(qū)域ARM_LIB_STACK。兩個(gè)區(qū)域都有該EMPTY屬性。這會(huì)導(dǎo)致庫(kù)選擇__user_setup_stackheap()使用符號(hào)值的非默認(rèn)實(shí)現(xiàn):
Image$$ARM_LIB_STACK$$Base
Image$$ARM_LIB_STACK$$ZI$$Limit
Image$$ARM_LIB_HEAP$$Base
Image$$ARM_LIB_HEAP$$ZI$$Limit
1234567
只能指定一個(gè)ARM_LIB_STACK
或ARM_LIB_HEAP
區(qū)域,并且必須分配一個(gè)大小,例如:
ARM_LIB_HEAP0x20100000EMPTY0x100000-0x8000;Heapstartsat1MB
;andgrowsupwards
ARM_LIB_STACK0x20200000EMPTY-0x8000;Stackspacestartsattheend
;ofthe2MBofRAM
;Andgrowsdownwardsfor32KB
12345
可以通過定義名為單一執(zhí)行區(qū)域使用組合的棧和堆區(qū)域ARM_LIB_STACKHEAP
,與EMPTY
屬性。這會(huì)導(dǎo)致__user_setup_stackheap()
使用符號(hào)Image$$ARM_LIB_STACKHEAP$$Base
和Image$$ARM_LIB_STACKHEAP$$ZI$$Limit
的值。
注意如果您重新實(shí)現(xiàn)__user_setup_stackheap()
,這將覆蓋所有庫(kù)里面的實(shí)現(xiàn)。
創(chuàng)建root執(zhí)行區(qū)
要將區(qū)域指定為分散文件中的根區(qū)域,您可以:
-
指定
ABSOLUTE
為執(zhí)行區(qū)的屬性(顯式或允許它默認(rèn)),并為第一個(gè)執(zhí)行區(qū)和封閉加載區(qū)使用相同的地址。要使執(zhí)行區(qū)地址與加載區(qū)地址相同,請(qǐng)執(zhí)行以下任一操作:
- 為執(zhí)行區(qū)的基地址和加載區(qū)的基地址指定相同的數(shù)值。
-
指定
+0
加載區(qū)中第一個(gè)執(zhí)行區(qū)的偏移量。如果+0
為加載區(qū)中的所有后續(xù)執(zhí)行區(qū)指定零偏移(+0)
,則所有不跟隨包含 ZI 的執(zhí)行區(qū)的執(zhí)行區(qū)也是根區(qū)。
以下示例顯示了隱式定義的根區(qū)域:
LR_10x040000;loadregionstartsat0x40000
{;startofexecutionregiondescriptions
ER_RO0x040000;loadaddress=executionaddress
{
*(+RO);allROsections(mustincludesectionwith
;initialentrypoint)
}
...;restofscatter-loadingdescription
}
123456789
-
使用
FIXED
執(zhí)行區(qū)屬性可以確保特定區(qū)域的加載地址和執(zhí)行地址相同。您可以使用該FIXED
屬性將任何執(zhí)行區(qū)放置在 ROM 中的特定地址。例如,以下內(nèi)存映射顯示了固定執(zhí)行區(qū):圖 8. 固定執(zhí)行區(qū)的內(nèi)存映射
The following example shows the corresponding scatter-loading description:下面的例子給出了相應(yīng)分散加載描述:
LR_10x040000;loadregionstartsat0x40000
{;startofexecutionregiondescriptions
ER_RO0x040000;loadaddress=executionaddress
{
*(+RO);ROsectionsotherthanthoseininit.o
}
ER_INIT0x080000FIXED;loadaddressandexecutionaddressofthis
;executionregionarefixedat0x80000
{
init.o(+RO);allROsectionsfrominit.o
}
...;restofscatter-loadingdescription
}
12345678910111213
Examples of misusing the FIXED attribute誤用 FIXED 屬性 例子
The following example shows common cases where the FIXED execution region attribute is misused:
LR10x8000
{
ER_LOW+00x1000
{
*(+RO)
}
;AtthispointthenextavailableLoadandExecutionaddressis0x8000+sizeof
;contentsofER_LOW.Themaximumsizeislimitedto0x1000sothenextavailableLoad
;andExecutionaddressisatmost0x9000
ER_HIGH0xF0000000FIXED
{
*(+RW+ZI)
}
;Therequiredexecutionaddressandloadaddressis0xF0000000.Thelinkerinserts
;0xF0000000-(0x8000+sizeof(ER_LOW))bytesofpaddingsothatloadaddressmatches
;executionaddress
}
;TheothercommonmisuseofFIXEDistogivealowerexecutionaddressthanthenext
;availableloadaddress.
LR_HIGH0x100000000
{
ER_LOW0x1000FIXED
{
*(+RO)
}
;ThenextavailableloadaddressinLR_HIGHis0x10000000.TherequiredExecution
;addressis0x1000.BecausethenextavailableloadaddressinLR_HIGHmustincrease
;monotonicallythelinkercannotgiveER_LOWaLoadAddresslowerthan0x10000000
}
12345678910111213141516171819202122232425262728293031
使用 FIXED 屬性創(chuàng)建根區(qū)域
您可以FIXED
在執(zhí)行區(qū)分散文件中使用該屬性來創(chuàng)建在固定地址加載和執(zhí)行的根區(qū)。FIXED
用于在單個(gè)加載區(qū)域內(nèi)創(chuàng)建多個(gè)根區(qū)域,因此通常是單個(gè) ROM 設(shè)備。例如,您可以使用它來將函數(shù)或數(shù)據(jù)塊(例如常量表或校驗(yàn)和)放置在 ROM 中的固定地址,以便可以通過指針輕松訪問。
例如,如果您指定將一些初始化代碼放置在 ROM 的開頭并在 ROM 的末尾放置一個(gè)校驗(yàn)和,則某些內(nèi)存內(nèi)容可能未被使用。使用*或.ANY模塊選擇器來填充初始化塊末尾和數(shù)據(jù)塊開頭之間的區(qū)域。
為了使您的代碼更易于維護(hù)和調(diào)試,建議您在分散文件中使用最少的布局規(guī)范,并將函數(shù)和數(shù)據(jù)的詳細(xì)布局留給鏈接器。
您不能指定已部分鏈接的組件對(duì)象。例如,如果您將對(duì)象obj1.o
、obj2.o
和部分鏈接obj3.o
在一起以產(chǎn)生obj_all.o
,則在生成的對(duì)象中會(huì)丟棄組件對(duì)象名稱。因此,您不能按名稱引用其中一個(gè)對(duì)象,例如,obj1.o
。您只能引用組合對(duì)象obj_all.o
。
注意在某些情況下,使用FIXED
和 單個(gè)加載區(qū)域是不合適的。指定固定位置的其他方式是:
- 如果您的加載程序可以處理多個(gè)加載區(qū)域,請(qǐng)將 RO 代碼或數(shù)據(jù)放在其自己的加載區(qū)域中。
-
如果您不要求函數(shù)或數(shù)據(jù)位于 ROM 中的固定位置,請(qǐng)使用
ABSOLUTE
代替FIXED
。然后加載器將數(shù)據(jù)從加載區(qū)復(fù)制到 RAM 中的指定地址。ABSOLUTE是默認(rèn)屬性。 -
要將數(shù)據(jù)結(jié)構(gòu)放置在內(nèi)存映射 I/O 的位置??,請(qǐng)使用兩個(gè)加載區(qū)域并指定
UNINIT. UNINIT
確保內(nèi)存位置不會(huì)被初始化為零。
在特定地址放置函數(shù)和數(shù)據(jù)
通常,編譯器從單個(gè)源文件生成 RO、RW 和 ZI 節(jié)。這些區(qū)域包含源文件中的所有代碼和數(shù)據(jù)。要將單個(gè)函數(shù)或數(shù)據(jù)項(xiàng)放置在固定地址,您必須使鏈接器能夠?qū)⒑瘮?shù)或數(shù)據(jù)與其余輸入文件分開處理。
鏈接器有兩種方法可以讓您將段放置在特定地址:
- 您可以創(chuàng)建一個(gè)分散文件,該文件在所需地址處定義一個(gè)執(zhí)行區(qū),并帶有僅選擇一個(gè)段的段描述。
-
對(duì)于特殊命名的段,鏈接器可以從段名中獲取放置地址。這些專門命名的部分稱為
__at
段。
要將函數(shù)或變量放置在特定地址,必須將其放置在其自己的段中。有幾種方法可以做到這一點(diǎn):
- 將函數(shù)或數(shù)據(jù)項(xiàng)放在其自己的源文件中。
-
使用到地方變量在一個(gè)單獨(dú)的部分,在一個(gè)特定的地址
__attribute__((at(address)))
-
用于在指定段中放置函數(shù)和變量
__attribute__((section("name")))
-
使用
AREA
匯編語言中的指令。在匯編代碼中,最小的可定位單元是AREA
. -
使用
--split_sections
編譯器選項(xiàng)為源文件中的每個(gè)函數(shù)生成一個(gè) ELF 部分。此選項(xiàng)會(huì)導(dǎo)致某些函數(shù)的代碼大小略有增加,因?yàn)樗档土嗽诤瘮?shù)之間共享地址、數(shù)據(jù)和字符串文字的可能性。但是,當(dāng)您指定armlink --remove
這可以幫助減少最終固件鏡像整體大小,使鏈接器能夠刪除未使用的函數(shù)。
在沒有分散加載的情況下將變量放置在特定地址的示例
此示例顯示如何修改源代碼以將代碼和數(shù)據(jù)放置在特定地址,并且不需要分散文件:1、創(chuàng)建main.c包含以下代碼的源文件:
#include externintsqr(intn1);
intgSquared__attribute__((at(0x5000)));//Placeat0x5000intmain()
{
gSquared=sqr(3);
printf("Valuesquaredis:%d
",gSquared);
}
12345678910
2、創(chuàng)建function.c包含以下代碼的源文件:
intsqr(intn1)
{
returnn1*n1;
}
1234
3、編譯并鏈接源:
armcc-c-gfunction.c
armcc-c-gmain.c
armlink--mapfunction.omain.o-osquared.axf
123
--map
選項(xiàng)用于生成內(nèi)存映射文件即.map文件,同樣--autoat
是默認(rèn)值
在此示例中,__attribute__((at(0x5000)))
指定將全局變量gSquared
放置在絕對(duì)地址處0x20000
。gSquared
被放置在執(zhí)行區(qū)ER$$.ARM.__AT_0x00005000
和加載區(qū)中LR$$.ARM.__AT_0x00005000
。
The memory map shows:
...
LoadRegionLR$$.ARM.__AT_0x00005000(Base:0x00005000,Size:0x00000000,Max:0x00000004,ABSOLUTE)
ExecutionRegionER$$.ARM.__AT_0x00005000(Base:0x00005000,Size:0x00000004,Max:0x00000004,ABSOLUTE,UNINIT)
BaseAddrSizeTypeAttrIdxESectionNameObject
0x000050000x00000004ZeroRW15.ARM.__AT_0x00005000main.o
123456789
使用分散加載將變量放置在指定段中的示例
此示例顯示如何使用分散文件修改源代碼以將代碼和數(shù)據(jù)放置在特定部分中:1、創(chuàng)建main.c包含以下代碼的源文件:
#include externintsqr(intn1);
intgSquared__attribute__((section("foo")));//Placeinsectionfoointmain()
{
gSquared=sqr(3);
printf("Valuesquaredis:%d
",gSquared);
}
12345678910
2、創(chuàng)建function.c包含以下代碼的源文件:
intsqr(intn1)
{
returnn1*n1;
}
1234
3、創(chuàng)建scatter.scat包含以下加載區(qū)域的分散文件:
LR10x00000x20000
{
ER10x00x2000
{
*(+RO);restofcodeandread-onlydata
}
ER20x80000x2000
{
main.o
}
ER30x100000x2000
{
function.o
*(foo);PlacegSquaredinER3
}
RAM0x200000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x200000
{
*(+RW,+ZI)
}
ARM_LIB_STACK0x800000EMPTY-0x10000
{
}
ARM_LIB_HEAP+0EMPTY0x10000
{
}
}
1234567891011121314151617181920212223242526
該ARM_LIB_STACK
和ARM_LIB_HEAP
都需要,因?yàn)槌绦驅(qū)⑴c半主機(jī)庫(kù)鏈接。4、編譯并鏈接
armcc-c-gfunction.c
armcc-c-gmain.c
aarmlink--map--scatter=scatter.scatfunction.omain.o-osquared.axf
123
內(nèi)存映射顯示:
LoadRegionLR1(Base:0x00000000,Size:0x00001778,Max:0x00020000,ABSOLUTE)
...
ExecutionRegionER3(Base:0x00010000,Size:0x00000004,Max:0x00002000,ABSOLUTE)
BaseAddrSizeTypeAttrIdxESectionNameObject
0x000100000x00000004DataRW15foomain.o
...
12345678
注意
如果*(foo)從分散文件中省略,則該部分將放置在相同類型的區(qū)域中。在這個(gè)例子中就是RAM區(qū)。
使用分散加載將變量放置在特定地址的示例
1、創(chuàng)建main.c包含以下代碼的源文件
#include externintsqr(intn1);
//Placeataddress0x10000constintgValue__attribute__((section(".ARM.__at_0x10000")))=3;
intmain()
{
intsquared;
squared=sqr(gValue);
printf("Valuesquaredis:%d
",squared);
}
12345678910111213
2、創(chuàng)建function.c包含以下代碼的源文件:
intsqr(intn1)
{
returnn1*n1;
}
1234
3、創(chuàng)建scatter.scat包含以下加載區(qū)域的分散文件:
LR10x0
{
ER10x0
{
*(+RO);restofcodeandread-onlydata
}
ER2+0
{
function.o
*(.ARM.__at_0x10000);PlacegValueat0x10000
}
RAM0x200000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x200000
{
*(+RW,+ZI)
}
ARM_LIB_STACK0x800000EMPTY-0x10000
{
}
ARM_LIB_HEAP+0EMPTY0x10000
{
}
}
12345678910111213141516171819202122
該ARM_LIB_STACK
和ARM_LIB_HEAP
都需要,因?yàn)槌绦驅(qū)⑴c半主機(jī)庫(kù)鏈接。
4、編譯并鏈接
armcc-c-gfunction.c
armcc-c-gmain.c
armlink--no_autoat--scatter=scatter.scat--mapfunction.omain.o-osquared.axf
123
內(nèi)存映射顯示變量放置ER2在地址處的執(zhí)行區(qū)中0x11000:
...
ExecutionRegionER2(Base:0x00001598,Size:0x0000ea6c,Max:0xffffffff,ABSOLUTE)
BaseAddrSizeTypeAttrIdxESectionNameObject
0x000015980x0000000cCodeRO3.textfunction.o
0x000015a40x0000ea5cPAD
0x000100000x00000004DataRO15.ARM.__at_0x10000main.o...
12345678
在這個(gè)例子中,ER1的大小是未知的。因此,gValue可能放在ER1或 中ER2。要確保將gValue其放置在ER2中,您必須包含相應(yīng)的選擇器ER2并與--no_autoat
命令行選項(xiàng)鏈接。如果省略--no_autoat
, gValue將被放在一個(gè)單獨(dú)的加載區(qū)域LR$$.ARM.__AT_0x00010000
,包含執(zhí)行區(qū)域ER$$.ARM.__AT_0x00020000
變量指定段
方式一
intvariable__attribute__((section("foo")))=10;
1
FLASH0x240000000x4000000
{
...;restofcode
ADDER0x08000000
{
file.o(foo);selectsectionfoofromfile.o
}
}
123456789
方式二
//placevariable1inasectioncalled.ARM.__at_0x00008000intvariable1__attribute__((at(0x8000)))=10;
//placevariable2inasectioncalled.ARM.__at_0x8000intvariable2__attribute__((section(".ARM.__at_0x8000")))=10;
12345
ER_FLASH0x80000x2000
{
*(+RO)
*(.ARM.__at_0x8000);
}
12345
函數(shù)地址指定
intsqr(intn1)__attribute__((section(".ARM.__at_0x20000")));
intsqr(intn1)
{
returnn1*n1;
}
123456
注意
- 如果不使用分散加載,則該部分將放置在加載區(qū)的默認(rèn)ER_RW執(zhí)行區(qū)中LR_1
- 如果源碼中使用了未定義段名(分散加載文件中無此段名),則該部分將放置在定義的 RW 執(zhí)行區(qū)中
-
--autoat
or--no_autoat
不影響放置
使用分散加載顯式放置命名部分
以下示例顯示如何使用分散加載顯式放置命名部分:
LR10x00x10000
{
ER10x00x2000;RootRegion,containinginitcode
{
init.o(INIT,+FIRST);placeinitcodeatexactly0x0
*(+RO);restofcodeandread-onlydata
}
RAM_RW0x400000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x400000
{
*(+RW)
}
RAM_ZI+0
{
*(+ZI)
}
DATABLOCK0x1FF000xFF;executionregionat0x1FF00
{;maximumspaceavailablefortableis0xFF
data.o(+RO-DATA);placeROdatabetween0x1FF00and0x1FFFF
}
}
1234567891011121314151617181920
在這個(gè)例子中,分散加載描述放置:
- 初始化代碼放在文件的INIT段中init.o。此示例顯示該INIT段中的代碼首先放置在地址 處0x0,然后是 RO 代碼的其余部分以及除對(duì)象中的 RO 數(shù)據(jù)之外的所有 RO 數(shù)據(jù)data.o。
- RAM 中的所有全局 RW 變量位于0x400000
- data.o中的所有RO-DATA數(shù)據(jù)放置在0x1FF00
使用.ANY模塊選擇器放置未分配的段
鏈接器嘗試將輸入節(jié)放入特定的執(zhí)行區(qū)。對(duì)于無法解析的任何輸入部分,并且這些部分的放置不重要,您可以使用.ANY
分散文件中的模塊選擇器。
在大多數(shù)情況下,使用單個(gè).ANY
選擇器等同于使用*
模塊選擇器。但是不同的是,您可以.ANY
在多個(gè)執(zhí)行區(qū)中指定。
放置未分配段的默認(rèn)規(guī)則
默認(rèn)情況下,鏈接器使用以下條件放置未分配的段:
-
在當(dāng)前擁有最多可用空間的執(zhí)行區(qū)中放置一個(gè)未分配的段。您可以使用執(zhí)行區(qū)域?qū)傩灾付ㄓ糜谖捶峙涠蔚淖畲罂臻g量
ANY_SIZE
。 - 按大小降序?qū)Σ糠诌M(jìn)行排序。
使用多個(gè).ANY選擇器時(shí)的放置規(guī)則
如果分散文件中存在多個(gè).ANY
選擇器,則鏈接器采用最大大小的未分配段并將該段分配給具有足夠可用空間的最具體的.ANY
執(zhí)行區(qū)。例如,.ANY(.text)
被判斷為比.ANY(+RO)
更具體。
如果多個(gè)執(zhí)行區(qū)具有相同的特性,則該段將分配給具有最多可用剩余空間的執(zhí)行區(qū)。
例如:
-
如果您有兩個(gè)同樣特定的執(zhí)行區(qū),其中一個(gè)的大小限制為0x2000,另一個(gè)沒有限制,則所有段都分配給第二個(gè)無界
.ANY
區(qū)域。 -
如果你有兩個(gè)同樣的特定執(zhí)行區(qū),其中一個(gè)大小限制為0x2000和另一個(gè)大小限制為0x3000,然后第一個(gè)段將被分配到
第二個(gè).ANY
(區(qū)域大小限制0x3000),直到第二個(gè).ANY
剩余的大小減少到0x2000。從這一點(diǎn)開始,section在兩個(gè).ANY
執(zhí)行區(qū)域之間交替分配。
.ANY優(yōu)先段
如果您有多個(gè).ANY
帶有選擇器的部分,您可以給出優(yōu)先順序,其中是從零向上的正整數(shù)。最高優(yōu)先級(jí)被賦予具有最高整數(shù)的選擇器。.ANYnum
以下示例顯示了如何使用:.ANYnum
lr10x80001024
{
er1+0512
{
.ANY1(+RO);evenlydistributedwither3
}
er2+0256
{
.ANY2(+RO);Highestpriority,sofilledfirst
}
er3+0256
{
.ANY1(+RO);evenlydistributedwither1
}
}
123456789101112131415
控制多個(gè).ANY選擇器的輸入段的放置
.ANY通過使用不同的放置算法或不同的排序順序,您可以修改鏈接器在使用多個(gè)選擇器時(shí)放置未分配輸入段的方式。以下命令行選項(xiàng)可用:
-
--any_placement=algorithm
, 其中algorithm是first_fit
,worst_fit
,best_fit
, 或next_fit
之一 -
--any_sort_order=order
,其中order是cmdline
或descending_size
之一
first_fit當(dāng)您想要按順序填充區(qū)域時(shí)使用。best_fit當(dāng)您想最大程度地填充區(qū)域時(shí)使用。worst_fit當(dāng)您想要均勻填充區(qū)域時(shí)使用。使用相同大小的區(qū)域和部分worst_fit循環(huán)填充區(qū)域。當(dāng)您需要更具確定性的填充模式時(shí),請(qǐng)使用 next_fit。
如果鏈接器嘗試將區(qū)域填充到其極限,就像使用first_fit和 一樣best_fit,它可能會(huì)過度填充該區(qū)域。這是因?yàn)樵趯⒐?jié)分配給.ANY選擇器之前,鏈接器生成的內(nèi)容(例如填充和單板)是未知的。如果發(fā)生這種情況,您可能會(huì)看到以下錯(cuò)誤:
Error: L6220E: Execution region regionname size (size bytes) exceeds limit (limit bytes).錯(cuò)誤:L6220E:執(zhí)行區(qū)regionname大小(size字節(jié))超過限制(limit字節(jié))。
該--any_contingency
選項(xiàng)可防止鏈接器將區(qū)域填充到最大值。它為鏈接器生成的內(nèi)容保留了該區(qū)域大小的一部分,并且僅當(dāng)其他區(qū)域沒有空間時(shí)才填充此應(yīng)急區(qū)域。默認(rèn)情況下為first_fit
和best_fit
算法啟用它,因?yàn)樗鼈冏钣锌赡鼙憩F(xiàn)出這種行為。
指定允許放置未分配段的最大尺寸
執(zhí)行區(qū)屬性使您能夠指定armlink可以用未分配的節(jié)填充的區(qū)域中的最大大小。ANY_SIZE max_size
使用此關(guān)鍵字時(shí)請(qǐng)注意以下限制:
-
max_size
必須小于或等于區(qū)域大小 -
您可以
ANY_SIZE
在沒有.ANY
選擇器的區(qū)域上使用,但它會(huì)被armlink忽略
當(dāng)ANY_SIZE存在時(shí),armlink:
- 不覆蓋給定的.ANY大小。也就是說,它不會(huì)降低優(yōu)先級(jí),然后嘗試在稍后放入更多段。
- 從不重新計(jì)算意外事件。
- 從不分配應(yīng)急空間中的段。
ANY_SIZE不需要--any_contingency
指定。但是,無論何時(shí)--any_contingency
指定和ANY_SIZE
未指定,armlink 都會(huì)嘗試調(diào)整意外情況。目標(biāo)是:
- 永遠(yuǎn)不會(huì)溢出一個(gè).ANY區(qū)域
- 永遠(yuǎn)不要拒絕在應(yīng)急保留空間中放置一個(gè)段。
如果您--any_contingency
在命令行上指定,則對(duì)于已ANY_SIZE
指定的區(qū)域?qū)⒑雎运KǔS糜谖?code style="padding:2px 4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(255,100,65);">ANY_SIZE指定的區(qū)域。
以下示例顯示了如何使用ANY_SIZE
:
LOAD_REGION0x00x3000
{
ER_10x0ANY_SIZE0xF000x1000
{
.ANY
}
ER_20x0ANY_SIZE0xFB00x1000
{
.ANY
}
ER_30x0ANY_SIZE0x10000x1000
{
.ANY
}
}
123456789101112131415
在這個(gè)例子中:
ER_1為鏈接器生成的內(nèi)容保留了0x100。ER_2為鏈接器生成的內(nèi)容保留了0x50。這和--any_contingency
的自動(dòng)應(yīng)急保留類似。ER_3沒有預(yù)留空間。因此,100%的區(qū)域被填滿,沒有應(yīng)急保留。省略ANY_SIZE參數(shù)會(huì)導(dǎo)致98%的區(qū)域被填滿,只有2%的應(yīng)急保留。
使用 __at 在外設(shè)寄存器上放置
要將未初始化的變量放置在外設(shè)寄存器上,您可以使用 ZI__at
部分。假設(shè)一個(gè)寄存器可用于0x10000000,定義一個(gè)__at名為.ARM.__at_0x10000000
. 例如:
intfoo__attribute__((section(".ARM.__at_0x10000000"),zero_init));
1
ER_PERIPHERAL0x10000000UNINIT
{
*(.ARM.__at_0x10000000)
}
1234
使用自動(dòng)放置,并假設(shè)附近沒有其他執(zhí)行區(qū)0x10000000,鏈接器會(huì)自動(dòng)創(chuàng)建一個(gè)UNINIT屬性為 at的區(qū)域0x10000000。該UNINIT屬性創(chuàng)建一個(gè)包含未初始化數(shù)據(jù)或內(nèi)存映射 I/O 的執(zhí)行區(qū)。
預(yù)留一個(gè)空區(qū)域
可以EMPTY在執(zhí)行區(qū)分散加載描述中使用該屬性來為堆棧保留一塊空內(nèi)存。
內(nèi)存塊不構(gòu)成加載區(qū)的一部分,而是在執(zhí)行時(shí)分配使用。因?yàn)樗亲鳛樘摂M ZI 區(qū)域創(chuàng)建的,所以鏈接器使用以下符號(hào)來訪問它:
-
Image$$region_name$$ZI$$Base
-
Image$$region_name$$ZI$$Limit
-
Image$$region_name$$ZI$$Length
如果長(zhǎng)度為負(fù)值,則該地址被視為區(qū)域的結(jié)束地址。這必須是絕對(duì)地址而不是相對(duì)地址。
在以下示例中,執(zhí)行區(qū)定義STACK 0x800000 EMPTY -0x10000定義了一個(gè)名為的區(qū)域STACK,該區(qū)域從 address 開始并在 address0x7F0000結(jié)束0x800000:
LR_10x80000;loadregionstartsat0x80000
{
STACK0x800000EMPTY-0x10000;regionendsat0x800000becauseofthe
;negativelength.Thestartoftheregion
;iscalculatedusingthelength.
{
;Emptyregionusedtoplacestack
}
HEAP+0EMPTY0x10000;regionstartsattheendofprevious
;region.Endofregioncalculatedusing
;positivelength
{
;Emptyregionusedtoplaceheap
}
...;restofscatter-loadingdescription...
}
12345678910111213141516
注意
為EMPTY執(zhí)行區(qū)域創(chuàng)建的虛擬ZI區(qū)域在運(yùn)行時(shí)不會(huì)初始化為零。
如果地址是相對(duì)的(+offset)形式并且長(zhǎng)度是負(fù)的,鏈接器會(huì)產(chǎn)生一個(gè)錯(cuò)誤。下圖顯示了該示例的圖解表示。
圖 9. 為堆棧保留一個(gè)區(qū)域
在本例中,鏈接器生成符號(hào):
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=0x10000123456
該EMPTY屬性僅適用于執(zhí)行區(qū)。鏈接器生成警告并忽略EMPTY加載區(qū)定義中使用的屬性。
鏈接器檢查用于該EMPTY區(qū)域的地址空間是否與任何其他執(zhí)行區(qū)域不一致。
在分散文件中使用預(yù)處理命令
您可以通過 C 預(yù)處理器傳遞分散文件。這允許訪問 C 預(yù)處理器的所有功能。
使用分散文件中的第一行指定鏈接器調(diào)用以處理文件的預(yù)處理器命令。命令的格式如下:
#!preprocessor[
pre_processor_flags
]
123
最典型的命令是#! armcc -E
. 這會(huì)通過armcc預(yù)處理器傳遞分散文件。
你可以:
- 將預(yù)處理指令添加到分散文件的頂部
- 在分散文件中使用簡(jiǎn)單的表達(dá)式評(píng)估。
例如,分散文件file.scat, 可能包含:
#!armcc-E#defineADDRESS0x20000000#include"include_file_1.h"
lr1ADDRESS
{
...
}
123456789
鏈接器解析預(yù)處理后的分散文件并將指令視為注釋。
您還可以將分散文件的預(yù)處理與–predefine命令行選項(xiàng)結(jié)合使用。對(duì)于這個(gè)例子:
-
修改file.scat以刪除指令。
#define ADDRESS 0x20000000
-
指定命令:
armlink --predefine="-DADDRESS=0x20000000" --scatter=file.scat
在分散文件中使用表達(dá)式求值以避免填充
使用ALIGN,ALIGNALL或FIXED在分散的文件屬性可導(dǎo)致在鏡像中的大量填充的。要?jiǎng)h除此填充,您可以使用表達(dá)式計(jì)算來指定加載區(qū)和執(zhí)行區(qū)的起始地址。內(nèi)置函數(shù)AlignExpr可用于幫助您指定地址表達(dá)式。
避免在分散文件中填充的示例以下分散文件生成帶有填充的圖像:
LR10x4000
{
ER1+0ALIGN0x8000
{
...
}
}
1234567
使用ALIGN關(guān)鍵字ER1
與0x8000
加載視圖和執(zhí)行視圖中的邊界對(duì)齊。要在加載視圖中對(duì)齊,鏈接器必須插入0x4000填充字節(jié)。
以下分散文件生成沒有填充的圖像:
LR10x4000
{
ER1AlignExpr(+0,0x8000)
{
...
}
}
1234567
使用AlignExpr
的結(jié)果+0
與0x8000
邊界對(duì)齊。這將創(chuàng)建一個(gè)執(zhí)行區(qū),其加載地址為0x4000但執(zhí)行地址為0x8000。
審核編輯:郭婷
Image$$ARM_LIB_STACK$$Base
Image$$ARM_LIB_STACK$$ZI$$Limit
Image$$ARM_LIB_HEAP$$Base
Image$$ARM_LIB_HEAP$$ZI$$Limit
1234567
只能指定一個(gè)ARM_LIB_STACK
或ARM_LIB_HEAP
區(qū)域,并且必須分配一個(gè)大小,例如:
ARM_LIB_HEAP0x20100000EMPTY0x100000-0x8000;Heapstartsat1MB
;andgrowsupwards
ARM_LIB_STACK0x20200000EMPTY-0x8000;Stackspacestartsattheend
;ofthe2MBofRAM
;Andgrowsdownwardsfor32KB
12345
可以通過定義名為單一執(zhí)行區(qū)域使用組合的棧和堆區(qū)域ARM_LIB_STACKHEAP
,與EMPTY
屬性。這會(huì)導(dǎo)致__user_setup_stackheap()
使用符號(hào)Image$$ARM_LIB_STACKHEAP$$Base
和Image$$ARM_LIB_STACKHEAP$$ZI$$Limit
的值。
注意如果您重新實(shí)現(xiàn)__user_setup_stackheap()
,這將覆蓋所有庫(kù)里面的實(shí)現(xiàn)。
創(chuàng)建root執(zhí)行區(qū)
要將區(qū)域指定為分散文件中的根區(qū)域,您可以:
-
指定
ABSOLUTE
為執(zhí)行區(qū)的屬性(顯式或允許它默認(rèn)),并為第一個(gè)執(zhí)行區(qū)和封閉加載區(qū)使用相同的地址。要使執(zhí)行區(qū)地址與加載區(qū)地址相同,請(qǐng)執(zhí)行以下任一操作:
- 為執(zhí)行區(qū)的基地址和加載區(qū)的基地址指定相同的數(shù)值。
-
指定
+0
加載區(qū)中第一個(gè)執(zhí)行區(qū)的偏移量。如果+0
為加載區(qū)中的所有后續(xù)執(zhí)行區(qū)指定零偏移(+0)
,則所有不跟隨包含 ZI 的執(zhí)行區(qū)的執(zhí)行區(qū)也是根區(qū)。
以下示例顯示了隱式定義的根區(qū)域:
LR_10x040000;loadregionstartsat0x40000
{;startofexecutionregiondescriptions
ER_RO0x040000;loadaddress=executionaddress
{
*(+RO);allROsections(mustincludesectionwith
;initialentrypoint)
}
...;restofscatter-loadingdescription
}
123456789
-
使用
FIXED
執(zhí)行區(qū)屬性可以確保特定區(qū)域的加載地址和執(zhí)行地址相同。您可以使用該FIXED
屬性將任何執(zhí)行區(qū)放置在 ROM 中的特定地址。例如,以下內(nèi)存映射顯示了固定執(zhí)行區(qū):圖 8. 固定執(zhí)行區(qū)的內(nèi)存映射
The following example shows the corresponding scatter-loading description:下面的例子給出了相應(yīng)分散加載描述:
LR_10x040000;loadregionstartsat0x40000
{;startofexecutionregiondescriptions
ER_RO0x040000;loadaddress=executionaddress
{
*(+RO);ROsectionsotherthanthoseininit.o
}
ER_INIT0x080000FIXED;loadaddressandexecutionaddressofthis
;executionregionarefixedat0x80000
{
init.o(+RO);allROsectionsfrominit.o
}
...;restofscatter-loadingdescription
}
12345678910111213
Examples of misusing the FIXED attribute誤用 FIXED 屬性 例子
The following example shows common cases where the FIXED execution region attribute is misused:
LR10x8000
{
ER_LOW+00x1000
{
*(+RO)
}
;AtthispointthenextavailableLoadandExecutionaddressis0x8000+sizeof
;contentsofER_LOW.Themaximumsizeislimitedto0x1000sothenextavailableLoad
;andExecutionaddressisatmost0x9000
ER_HIGH0xF0000000FIXED
{
*(+RW+ZI)
}
;Therequiredexecutionaddressandloadaddressis0xF0000000.Thelinkerinserts
;0xF0000000-(0x8000+sizeof(ER_LOW))bytesofpaddingsothatloadaddressmatches
;executionaddress
}
;TheothercommonmisuseofFIXEDistogivealowerexecutionaddressthanthenext
;availableloadaddress.
LR_HIGH0x100000000
{
ER_LOW0x1000FIXED
{
*(+RO)
}
;ThenextavailableloadaddressinLR_HIGHis0x10000000.TherequiredExecution
;addressis0x1000.BecausethenextavailableloadaddressinLR_HIGHmustincrease
;monotonicallythelinkercannotgiveER_LOWaLoadAddresslowerthan0x10000000
}
12345678910111213141516171819202122232425262728293031
使用 FIXED 屬性創(chuàng)建根區(qū)域
您可以FIXED
在執(zhí)行區(qū)分散文件中使用該屬性來創(chuàng)建在固定地址加載和執(zhí)行的根區(qū)。FIXED
用于在單個(gè)加載區(qū)域內(nèi)創(chuàng)建多個(gè)根區(qū)域,因此通常是單個(gè) ROM 設(shè)備。例如,您可以使用它來將函數(shù)或數(shù)據(jù)塊(例如常量表或校驗(yàn)和)放置在 ROM 中的固定地址,以便可以通過指針輕松訪問。
例如,如果您指定將一些初始化代碼放置在 ROM 的開頭并在 ROM 的末尾放置一個(gè)校驗(yàn)和,則某些內(nèi)存內(nèi)容可能未被使用。使用*或.ANY模塊選擇器來填充初始化塊末尾和數(shù)據(jù)塊開頭之間的區(qū)域。
為了使您的代碼更易于維護(hù)和調(diào)試,建議您在分散文件中使用最少的布局規(guī)范,并將函數(shù)和數(shù)據(jù)的詳細(xì)布局留給鏈接器。
您不能指定已部分鏈接的組件對(duì)象。例如,如果您將對(duì)象obj1.o
、obj2.o
和部分鏈接obj3.o
在一起以產(chǎn)生obj_all.o
,則在生成的對(duì)象中會(huì)丟棄組件對(duì)象名稱。因此,您不能按名稱引用其中一個(gè)對(duì)象,例如,obj1.o
。您只能引用組合對(duì)象obj_all.o
。
注意在某些情況下,使用FIXED
和 單個(gè)加載區(qū)域是不合適的。指定固定位置的其他方式是:
- 如果您的加載程序可以處理多個(gè)加載區(qū)域,請(qǐng)將 RO 代碼或數(shù)據(jù)放在其自己的加載區(qū)域中。
-
如果您不要求函數(shù)或數(shù)據(jù)位于 ROM 中的固定位置,請(qǐng)使用
ABSOLUTE
代替FIXED
。然后加載器將數(shù)據(jù)從加載區(qū)復(fù)制到 RAM 中的指定地址。ABSOLUTE是默認(rèn)屬性。 -
要將數(shù)據(jù)結(jié)構(gòu)放置在內(nèi)存映射 I/O 的位置??,請(qǐng)使用兩個(gè)加載區(qū)域并指定
UNINIT. UNINIT
確保內(nèi)存位置不會(huì)被初始化為零。
在特定地址放置函數(shù)和數(shù)據(jù)
通常,編譯器從單個(gè)源文件生成 RO、RW 和 ZI 節(jié)。這些區(qū)域包含源文件中的所有代碼和數(shù)據(jù)。要將單個(gè)函數(shù)或數(shù)據(jù)項(xiàng)放置在固定地址,您必須使鏈接器能夠?qū)⒑瘮?shù)或數(shù)據(jù)與其余輸入文件分開處理。
鏈接器有兩種方法可以讓您將段放置在特定地址:
- 您可以創(chuàng)建一個(gè)分散文件,該文件在所需地址處定義一個(gè)執(zhí)行區(qū),并帶有僅選擇一個(gè)段的段描述。
-
對(duì)于特殊命名的段,鏈接器可以從段名中獲取放置地址。這些專門命名的部分稱為
__at
段。
要將函數(shù)或變量放置在特定地址,必須將其放置在其自己的段中。有幾種方法可以做到這一點(diǎn):
- 將函數(shù)或數(shù)據(jù)項(xiàng)放在其自己的源文件中。
-
使用到地方變量在一個(gè)單獨(dú)的部分,在一個(gè)特定的地址
__attribute__((at(address)))
-
用于在指定段中放置函數(shù)和變量
__attribute__((section("name")))
-
使用
AREA
匯編語言中的指令。在匯編代碼中,最小的可定位單元是AREA
. -
使用
--split_sections
編譯器選項(xiàng)為源文件中的每個(gè)函數(shù)生成一個(gè) ELF 部分。此選項(xiàng)會(huì)導(dǎo)致某些函數(shù)的代碼大小略有增加,因?yàn)樗档土嗽诤瘮?shù)之間共享地址、數(shù)據(jù)和字符串文字的可能性。但是,當(dāng)您指定armlink --remove
這可以幫助減少最終固件鏡像整體大小,使鏈接器能夠刪除未使用的函數(shù)。
在沒有分散加載的情況下將變量放置在特定地址的示例
此示例顯示如何修改源代碼以將代碼和數(shù)據(jù)放置在特定地址,并且不需要分散文件:1、創(chuàng)建main.c包含以下代碼的源文件:
#include externintsqr(intn1);
intgSquared__attribute__((at(0x5000)));//Placeat0x5000intmain()
{
gSquared=sqr(3);
printf("Valuesquaredis:%d
",gSquared);
}
12345678910
2、創(chuàng)建function.c包含以下代碼的源文件:
intsqr(intn1)
{
returnn1*n1;
}
1234
3、編譯并鏈接源:
armcc-c-gfunction.c
armcc-c-gmain.c
armlink--mapfunction.omain.o-osquared.axf
123
--map
選項(xiàng)用于生成內(nèi)存映射文件即.map文件,同樣--autoat
是默認(rèn)值
在此示例中,__attribute__((at(0x5000)))
指定將全局變量gSquared
放置在絕對(duì)地址處0x20000
。gSquared
被放置在執(zhí)行區(qū)ER$$.ARM.__AT_0x00005000
和加載區(qū)中LR$$.ARM.__AT_0x00005000
。
The memory map shows:
...
LoadRegionLR$$.ARM.__AT_0x00005000(Base:0x00005000,Size:0x00000000,Max:0x00000004,ABSOLUTE)
ExecutionRegionER$$.ARM.__AT_0x00005000(Base:0x00005000,Size:0x00000004,Max:0x00000004,ABSOLUTE,UNINIT)
BaseAddrSizeTypeAttrIdxESectionNameObject
0x000050000x00000004ZeroRW15.ARM.__AT_0x00005000main.o
123456789
使用分散加載將變量放置在指定段中的示例
此示例顯示如何使用分散文件修改源代碼以將代碼和數(shù)據(jù)放置在特定部分中:1、創(chuàng)建main.c包含以下代碼的源文件:
#include externintsqr(intn1);
intgSquared__attribute__((section("foo")));//Placeinsectionfoointmain()
{
gSquared=sqr(3);
printf("Valuesquaredis:%d
",gSquared);
}
12345678910
2、創(chuàng)建function.c包含以下代碼的源文件:
intsqr(intn1)
{
returnn1*n1;
}
1234
3、創(chuàng)建scatter.scat包含以下加載區(qū)域的分散文件:
LR10x00000x20000
{
ER10x00x2000
{
*(+RO);restofcodeandread-onlydata
}
ER20x80000x2000
{
main.o
}
ER30x100000x2000
{
function.o
*(foo);PlacegSquaredinER3
}
RAM0x200000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x200000
{
*(+RW,+ZI)
}
ARM_LIB_STACK0x800000EMPTY-0x10000
{
}
ARM_LIB_HEAP+0EMPTY0x10000
{
}
}
1234567891011121314151617181920212223242526
該ARM_LIB_STACK
和ARM_LIB_HEAP
都需要,因?yàn)槌绦驅(qū)⑴c半主機(jī)庫(kù)鏈接。4、編譯并鏈接
armcc-c-gfunction.c
armcc-c-gmain.c
aarmlink--map--scatter=scatter.scatfunction.omain.o-osquared.axf
123
內(nèi)存映射顯示:
LoadRegionLR1(Base:0x00000000,Size:0x00001778,Max:0x00020000,ABSOLUTE)
...
ExecutionRegionER3(Base:0x00010000,Size:0x00000004,Max:0x00002000,ABSOLUTE)
BaseAddrSizeTypeAttrIdxESectionNameObject
0x000100000x00000004DataRW15foomain.o
...
12345678
注意
如果*(foo)從分散文件中省略,則該部分將放置在相同類型的區(qū)域中。在這個(gè)例子中就是RAM區(qū)。
使用分散加載將變量放置在特定地址的示例
1、創(chuàng)建main.c包含以下代碼的源文件
#include externintsqr(intn1);
//Placeataddress0x10000constintgValue__attribute__((section(".ARM.__at_0x10000")))=3;
intmain()
{
intsquared;
squared=sqr(gValue);
printf("Valuesquaredis:%d
",squared);
}
12345678910111213
2、創(chuàng)建function.c包含以下代碼的源文件:
intsqr(intn1)
{
returnn1*n1;
}
1234
3、創(chuàng)建scatter.scat包含以下加載區(qū)域的分散文件:
LR10x0
{
ER10x0
{
*(+RO);restofcodeandread-onlydata
}
ER2+0
{
function.o
*(.ARM.__at_0x10000);PlacegValueat0x10000
}
RAM0x200000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x200000
{
*(+RW,+ZI)
}
ARM_LIB_STACK0x800000EMPTY-0x10000
{
}
ARM_LIB_HEAP+0EMPTY0x10000
{
}
}
12345678910111213141516171819202122
該ARM_LIB_STACK
和ARM_LIB_HEAP
都需要,因?yàn)槌绦驅(qū)⑴c半主機(jī)庫(kù)鏈接。
4、編譯并鏈接
armcc-c-gfunction.c
armcc-c-gmain.c
armlink--no_autoat--scatter=scatter.scat--mapfunction.omain.o-osquared.axf
123
內(nèi)存映射顯示變量放置ER2在地址處的執(zhí)行區(qū)中0x11000:
...
ExecutionRegionER2(Base:0x00001598,Size:0x0000ea6c,Max:0xffffffff,ABSOLUTE)
BaseAddrSizeTypeAttrIdxESectionNameObject
0x000015980x0000000cCodeRO3.textfunction.o
0x000015a40x0000ea5cPAD
0x000100000x00000004DataRO15.ARM.__at_0x10000main.o...
12345678
在這個(gè)例子中,ER1的大小是未知的。因此,gValue可能放在ER1或 中ER2。要確保將gValue其放置在ER2中,您必須包含相應(yīng)的選擇器ER2并與--no_autoat
命令行選項(xiàng)鏈接。如果省略--no_autoat
, gValue將被放在一個(gè)單獨(dú)的加載區(qū)域LR$$.ARM.__AT_0x00010000
,包含執(zhí)行區(qū)域ER$$.ARM.__AT_0x00020000
變量指定段
方式一
intvariable__attribute__((section("foo")))=10;
1
FLASH0x240000000x4000000
{
...;restofcode
ADDER0x08000000
{
file.o(foo);selectsectionfoofromfile.o
}
}
123456789
方式二
//placevariable1inasectioncalled.ARM.__at_0x00008000intvariable1__attribute__((at(0x8000)))=10;
//placevariable2inasectioncalled.ARM.__at_0x8000intvariable2__attribute__((section(".ARM.__at_0x8000")))=10;
12345
ER_FLASH0x80000x2000
{
*(+RO)
*(.ARM.__at_0x8000);
}
12345
函數(shù)地址指定
intsqr(intn1)__attribute__((section(".ARM.__at_0x20000")));
intsqr(intn1)
{
returnn1*n1;
}
123456
注意
- 如果不使用分散加載,則該部分將放置在加載區(qū)的默認(rèn)ER_RW執(zhí)行區(qū)中LR_1
- 如果源碼中使用了未定義段名(分散加載文件中無此段名),則該部分將放置在定義的 RW 執(zhí)行區(qū)中
-
--autoat
or--no_autoat
不影響放置
使用分散加載顯式放置命名部分
以下示例顯示如何使用分散加載顯式放置命名部分:
LR10x00x10000
{
ER10x00x2000;RootRegion,containinginitcode
{
init.o(INIT,+FIRST);placeinitcodeatexactly0x0
*(+RO);restofcodeandread-onlydata
}
RAM_RW0x400000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x400000
{
*(+RW)
}
RAM_ZI+0
{
*(+ZI)
}
DATABLOCK0x1FF000xFF;executionregionat0x1FF00
{;maximumspaceavailablefortableis0xFF
data.o(+RO-DATA);placeROdatabetween0x1FF00and0x1FFFF
}
}
1234567891011121314151617181920
在這個(gè)例子中,分散加載描述放置:
- 初始化代碼放在文件的INIT段中init.o。此示例顯示該INIT段中的代碼首先放置在地址 處0x0,然后是 RO 代碼的其余部分以及除對(duì)象中的 RO 數(shù)據(jù)之外的所有 RO 數(shù)據(jù)data.o。
- RAM 中的所有全局 RW 變量位于0x400000
- data.o中的所有RO-DATA數(shù)據(jù)放置在0x1FF00
使用.ANY模塊選擇器放置未分配的段
鏈接器嘗試將輸入節(jié)放入特定的執(zhí)行區(qū)。對(duì)于無法解析的任何輸入部分,并且這些部分的放置不重要,您可以使用.ANY
分散文件中的模塊選擇器。
在大多數(shù)情況下,使用單個(gè).ANY
選擇器等同于使用*
模塊選擇器。但是不同的是,您可以.ANY
在多個(gè)執(zhí)行區(qū)中指定。
放置未分配段的默認(rèn)規(guī)則
默認(rèn)情況下,鏈接器使用以下條件放置未分配的段:
-
在當(dāng)前擁有最多可用空間的執(zhí)行區(qū)中放置一個(gè)未分配的段。您可以使用執(zhí)行區(qū)域?qū)傩灾付ㄓ糜谖捶峙涠蔚淖畲罂臻g量
ANY_SIZE
。 - 按大小降序?qū)Σ糠诌M(jìn)行排序。
使用多個(gè).ANY選擇器時(shí)的放置規(guī)則
如果分散文件中存在多個(gè).ANY
選擇器,則鏈接器采用最大大小的未分配段并將該段分配給具有足夠可用空間的最具體的.ANY
執(zhí)行區(qū)。例如,.ANY(.text)
被判斷為比.ANY(+RO)
更具體。
如果多個(gè)執(zhí)行區(qū)具有相同的特性,則該段將分配給具有最多可用剩余空間的執(zhí)行區(qū)。
例如:
-
如果您有兩個(gè)同樣特定的執(zhí)行區(qū),其中一個(gè)的大小限制為0x2000,另一個(gè)沒有限制,則所有段都分配給第二個(gè)無界
.ANY
區(qū)域。 -
如果你有兩個(gè)同樣的特定執(zhí)行區(qū),其中一個(gè)大小限制為0x2000和另一個(gè)大小限制為0x3000,然后第一個(gè)段將被分配到
第二個(gè).ANY
(區(qū)域大小限制0x3000),直到第二個(gè).ANY
剩余的大小減少到0x2000。從這一點(diǎn)開始,section在兩個(gè).ANY
執(zhí)行區(qū)域之間交替分配。
.ANY優(yōu)先段
如果您有多個(gè).ANY
帶有選擇器的部分,您可以給出優(yōu)先順序,其中是從零向上的正整數(shù)。最高優(yōu)先級(jí)被賦予具有最高整數(shù)的選擇器。.ANYnum
以下示例顯示了如何使用:.ANYnum
lr10x80001024
{
er1+0512
{
.ANY1(+RO);evenlydistributedwither3
}
er2+0256
{
.ANY2(+RO);Highestpriority,sofilledfirst
}
er3+0256
{
.ANY1(+RO);evenlydistributedwither1
}
}
123456789101112131415
控制多個(gè).ANY選擇器的輸入段的放置
.ANY通過使用不同的放置算法或不同的排序順序,您可以修改鏈接器在使用多個(gè)選擇器時(shí)放置未分配輸入段的方式。以下命令行選項(xiàng)可用:
-
--any_placement=algorithm
, 其中algorithm是first_fit
,worst_fit
,best_fit
, 或next_fit
之一 -
--any_sort_order=order
,其中order是cmdline
或descending_size
之一
first_fit當(dāng)您想要按順序填充區(qū)域時(shí)使用。best_fit當(dāng)您想最大程度地填充區(qū)域時(shí)使用。worst_fit當(dāng)您想要均勻填充區(qū)域時(shí)使用。使用相同大小的區(qū)域和部分worst_fit循環(huán)填充區(qū)域。當(dāng)您需要更具確定性的填充模式時(shí),請(qǐng)使用 next_fit。
如果鏈接器嘗試將區(qū)域填充到其極限,就像使用first_fit和 一樣best_fit,它可能會(huì)過度填充該區(qū)域。這是因?yàn)樵趯⒐?jié)分配給.ANY選擇器之前,鏈接器生成的內(nèi)容(例如填充和單板)是未知的。如果發(fā)生這種情況,您可能會(huì)看到以下錯(cuò)誤:
Error: L6220E: Execution region regionname size (size bytes) exceeds limit (limit bytes).錯(cuò)誤:L6220E:執(zhí)行區(qū)regionname大小(size字節(jié))超過限制(limit字節(jié))。
該--any_contingency
選項(xiàng)可防止鏈接器將區(qū)域填充到最大值。它為鏈接器生成的內(nèi)容保留了該區(qū)域大小的一部分,并且僅當(dāng)其他區(qū)域沒有空間時(shí)才填充此應(yīng)急區(qū)域。默認(rèn)情況下為first_fit
和best_fit
算法啟用它,因?yàn)樗鼈冏钣锌赡鼙憩F(xiàn)出這種行為。
指定允許放置未分配段的最大尺寸
執(zhí)行區(qū)屬性使您能夠指定armlink可以用未分配的節(jié)填充的區(qū)域中的最大大小。ANY_SIZE max_size
使用此關(guān)鍵字時(shí)請(qǐng)注意以下限制:
-
max_size
必須小于或等于區(qū)域大小 -
您可以
ANY_SIZE
在沒有.ANY
選擇器的區(qū)域上使用,但它會(huì)被armlink忽略
當(dāng)ANY_SIZE存在時(shí),armlink:
- 不覆蓋給定的.ANY大小。也就是說,它不會(huì)降低優(yōu)先級(jí),然后嘗試在稍后放入更多段。
- 從不重新計(jì)算意外事件。
- 從不分配應(yīng)急空間中的段。
ANY_SIZE不需要--any_contingency
指定。但是,無論何時(shí)--any_contingency
指定和ANY_SIZE
未指定,armlink 都會(huì)嘗試調(diào)整意外情況。目標(biāo)是:
- 永遠(yuǎn)不會(huì)溢出一個(gè).ANY區(qū)域
- 永遠(yuǎn)不要拒絕在應(yīng)急保留空間中放置一個(gè)段。
如果您--any_contingency
在命令行上指定,則對(duì)于已ANY_SIZE
指定的區(qū)域?qū)⒑雎运KǔS糜谖?code style="padding:2px 4px;margin-right:2px;margin-left:2px;background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(255,100,65);">ANY_SIZE指定的區(qū)域。
以下示例顯示了如何使用ANY_SIZE
:
LOAD_REGION0x00x3000
{
ER_10x0ANY_SIZE0xF000x1000
{
.ANY
}
ER_20x0ANY_SIZE0xFB00x1000
{
.ANY
}
ER_30x0ANY_SIZE0x10000x1000
{
.ANY
}
}
123456789101112131415
在這個(gè)例子中:
ER_1為鏈接器生成的內(nèi)容保留了0x100。ER_2為鏈接器生成的內(nèi)容保留了0x50。這和--any_contingency
的自動(dòng)應(yīng)急保留類似。ER_3沒有預(yù)留空間。因此,100%的區(qū)域被填滿,沒有應(yīng)急保留。省略ANY_SIZE參數(shù)會(huì)導(dǎo)致98%的區(qū)域被填滿,只有2%的應(yīng)急保留。
使用 __at 在外設(shè)寄存器上放置
要將未初始化的變量放置在外設(shè)寄存器上,您可以使用 ZI__at
部分。假設(shè)一個(gè)寄存器可用于0x10000000,定義一個(gè)__at名為.ARM.__at_0x10000000
. 例如:
intfoo__attribute__((section(".ARM.__at_0x10000000"),zero_init));
1
ER_PERIPHERAL0x10000000UNINIT
{
*(.ARM.__at_0x10000000)
}
1234
使用自動(dòng)放置,并假設(shè)附近沒有其他執(zhí)行區(qū)0x10000000,鏈接器會(huì)自動(dòng)創(chuàng)建一個(gè)UNINIT屬性為 at的區(qū)域0x10000000。該UNINIT屬性創(chuàng)建一個(gè)包含未初始化數(shù)據(jù)或內(nèi)存映射 I/O 的執(zhí)行區(qū)。
預(yù)留一個(gè)空區(qū)域
可以EMPTY在執(zhí)行區(qū)分散加載描述中使用該屬性來為堆棧保留一塊空內(nèi)存。
內(nèi)存塊不構(gòu)成加載區(qū)的一部分,而是在執(zhí)行時(shí)分配使用。因?yàn)樗亲鳛樘摂M ZI 區(qū)域創(chuàng)建的,所以鏈接器使用以下符號(hào)來訪問它:
-
Image$$region_name$$ZI$$Base
-
Image$$region_name$$ZI$$Limit
-
Image$$region_name$$ZI$$Length
如果長(zhǎng)度為負(fù)值,則該地址被視為區(qū)域的結(jié)束地址。這必須是絕對(duì)地址而不是相對(duì)地址。
在以下示例中,執(zhí)行區(qū)定義STACK 0x800000 EMPTY -0x10000定義了一個(gè)名為的區(qū)域STACK,該區(qū)域從 address 開始并在 address0x7F0000結(jié)束0x800000:
LR_10x80000;loadregionstartsat0x80000
{
STACK0x800000EMPTY-0x10000;regionendsat0x800000becauseofthe
;negativelength.Thestartoftheregion
;iscalculatedusingthelength.
{
;Emptyregionusedtoplacestack
}
HEAP+0EMPTY0x10000;regionstartsattheendofprevious
;region.Endofregioncalculatedusing
;positivelength
{
;Emptyregionusedtoplaceheap
}
...;restofscatter-loadingdescription...
}
12345678910111213141516
注意
為EMPTY執(zhí)行區(qū)域創(chuàng)建的虛擬ZI區(qū)域在運(yùn)行時(shí)不會(huì)初始化為零。
如果地址是相對(duì)的(+offset)形式并且長(zhǎng)度是負(fù)的,鏈接器會(huì)產(chǎn)生一個(gè)錯(cuò)誤。下圖顯示了該示例的圖解表示。
圖 9. 為堆棧保留一個(gè)區(qū)域
在本例中,鏈接器生成符號(hào):
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=0x10000123456
該EMPTY屬性僅適用于執(zhí)行區(qū)。鏈接器生成警告并忽略EMPTY加載區(qū)定義中使用的屬性。
鏈接器檢查用于該EMPTY區(qū)域的地址空間是否與任何其他執(zhí)行區(qū)域不一致。
在分散文件中使用預(yù)處理命令
您可以通過 C 預(yù)處理器傳遞分散文件。這允許訪問 C 預(yù)處理器的所有功能。
使用分散文件中的第一行指定鏈接器調(diào)用以處理文件的預(yù)處理器命令。命令的格式如下:
#!preprocessor[
pre_processor_flags
]
123
最典型的命令是#! armcc -E
. 這會(huì)通過armcc預(yù)處理器傳遞分散文件。
你可以:
- 將預(yù)處理指令添加到分散文件的頂部
- 在分散文件中使用簡(jiǎn)單的表達(dá)式評(píng)估。
例如,分散文件file.scat, 可能包含:
#!armcc-E#defineADDRESS0x20000000#include"include_file_1.h"
lr1ADDRESS
{
...
}
123456789
鏈接器解析預(yù)處理后的分散文件并將指令視為注釋。
您還可以將分散文件的預(yù)處理與–predefine命令行選項(xiàng)結(jié)合使用。對(duì)于這個(gè)例子:
-
修改file.scat以刪除指令。
#define ADDRESS 0x20000000
-
指定命令:
armlink --predefine="-DADDRESS=0x20000000" --scatter=file.scat
在分散文件中使用表達(dá)式求值以避免填充
使用ALIGN,ALIGNALL或FIXED在分散的文件屬性可導(dǎo)致在鏡像中的大量填充的。要?jiǎng)h除此填充,您可以使用表達(dá)式計(jì)算來指定加載區(qū)和執(zhí)行區(qū)的起始地址。內(nèi)置函數(shù)AlignExpr可用于幫助您指定地址表達(dá)式。
避免在分散文件中填充的示例以下分散文件生成帶有填充的圖像:
LR10x4000
{
ER1+0ALIGN0x8000
{
...
}
}
1234567
使用ALIGN關(guān)鍵字ER1
與0x8000
加載視圖和執(zhí)行視圖中的邊界對(duì)齊。要在加載視圖中對(duì)齊,鏈接器必須插入0x4000填充字節(jié)。
以下分散文件生成沒有填充的圖像:
LR10x4000
{
ER1AlignExpr(+0,0x8000)
{
...
}
}
1234567
使用AlignExpr
的結(jié)果+0
與0x8000
邊界對(duì)齊。這將創(chuàng)建一個(gè)執(zhí)行區(qū),其加載地址為0x4000但執(zhí)行地址為0x8000。
審核編輯:郭婷
-
代碼
+關(guān)注
關(guān)注
30文章
4821瀏覽量
68888
原文標(biāo)題:在分散文件中使用表達(dá)式求值以避免填充
文章出處:【微信號(hào):技術(shù)讓夢(mèng)想更偉大,微信公眾號(hào):技術(shù)讓夢(mèng)想更偉大】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論