0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

使用分散文件指定棧和堆創(chuàng)建root執(zhí)行區(qū)

技術讓夢想更偉大 ? 來源:技術讓夢想更偉大 ? 作者:李肖遙 ? 2022-11-07 15:26 ? 次閱讀

使用分散文件指定棧和堆

ARM C 庫提供了該函數(shù)的多種實現(xiàn)__user_setup_stackheap(),并且可以從分散文件中提供的信息中自動為您選擇正確的一種。

要選擇兩個區(qū)域內存模型,請在名為ARM_LIB_HEAP和的分散文件中定義兩個特殊的執(zhí)行區(qū)域ARM_LIB_STACK。兩個區(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
1234567

只能指定一個ARM_LIB_STACKARM_LIB_HEAP區(qū)域,并且必須分配一個大小,例如:

ARM_LIB_HEAP0x20100000EMPTY0x100000-0x8000;Heapstartsat1MB
;andgrowsupwards
ARM_LIB_STACK0x20200000EMPTY-0x8000;Stackspacestartsattheend
;ofthe2MBofRAM
;Andgrowsdownwardsfor32KB
12345

可以通過定義名為單一執(zhí)行區(qū)域使用組合的棧和堆區(qū)域ARM_LIB_STACKHEAP,與EMPTY屬性。這會導致__user_setup_stackheap()使用符號Image$$ARM_LIB_STACKHEAP$$BaseImage$$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_10x040000;loadregionstartsat0x40000
{;startofexecutionregiondescriptions
ER_RO0x040000;loadaddress=executionaddress
{
*(+RO);allROsections(mustincludesectionwith
;initialentrypoint)
}
...;restofscatter-loadingdescription
}
123456789
  • 使用FIXED執(zhí)行區(qū)屬性可以確保特定區(qū)域的加載地址和執(zhí)行地址相同。您可以使用該FIXED屬性將任何執(zhí)行區(qū)放置在 ROM 中的特定地址。例如,以下內存映射顯示了固定執(zhí)行區(qū):圖 8. 固定執(zhí)行區(qū)的內存映射

4546342c-5c64-11ed-a3b6-dac502259ad0.png

The following example shows the corresponding scatter-loading description:下面的例子給出了相應分散加載描述:

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用于在單個加載區(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在一起以產生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包含以下代碼的源文件:

#includeexternintsqr(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選項用于生成內存映射文件即.map文件,同樣--autoat是默認值

在此示例中,__attribute__((at(0x5000)))指定將全局變量gSquared放置在絕對地址處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包含以下代碼的源文件:

#includeexternintsqr(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_STACKARM_LIB_HEAP都需要,因為程序將與半主機庫鏈接。4、編譯并鏈接

armcc-c-gfunction.c
armcc-c-gmain.c
aarmlink--map--scatter=scatter.scatfunction.omain.o-osquared.axf
123

內存映射顯示:

LoadRegionLR1(Base:0x00000000,Size:0x00001778,Max:0x00020000,ABSOLUTE)
...
ExecutionRegionER3(Base:0x00010000,Size:0x00000004,Max:0x00002000,ABSOLUTE)

BaseAddrSizeTypeAttrIdxESectionNameObject

0x000100000x00000004DataRW15foomain.o
...
12345678

注意

如果*(foo)從分散文件中省略,則該部分將放置在相同類型的區(qū)域中。在這個例子中就是RAM區(qū)。

使用分散加載將變量放置在特定地址的示例

1、創(chuàng)建main.c包含以下代碼的源文件

#includeexternintsqr(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_STACKARM_LIB_HEAP都需要,因為程序將與半主機庫鏈接。

4、編譯并鏈接

armcc-c-gfunction.c
armcc-c-gmain.c
armlink--no_autoat--scatter=scatter.scat--mapfunction.omain.o-osquared.axf
123

內存映射顯示變量放置ER2在地址處的執(zhí)行區(qū)中0x11000:

...
ExecutionRegionER2(Base:0x00001598,Size:0x0000ea6c,Max:0xffffffff,ABSOLUTE)

BaseAddrSizeTypeAttrIdxESectionNameObject

0x000015980x0000000cCodeRO3.textfunction.o
0x000015a40x0000ea5cPAD
0x000100000x00000004DataRO15.ARM.__at_0x10000main.o...
12345678

在這個例子中,ER1的大小是未知的。因此,gValue可能放在ER1或 中ER2。要確保將gValue其放置在ER2中,您必須包含相應的選擇器ER2并與--no_autoat命令行選項鏈接。如果省略--no_autoat, gValue將被放在一個單獨的加載區(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ū)的默認ER_RW執(zhí)行區(qū)中LR_1
  • 如果源碼中使用了未定義段名(分散加載文件中無此段名),則該部分將放置在定義的 RW 執(zhí)行區(qū)中
  • --autoator--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

在這個例子中,分散加載描述放置:

  • 初始化代碼放在文件的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

lr10x80001024
{
er1+0512
{
.ANY1(+RO);evenlydistributedwither3
}
er2+0256
{
.ANY2(+RO);Highestpriority,sofilledfirst
}
er3+0256
{
.ANY1(+RO);evenlydistributedwither1
}
}
123456789101112131415

控制多個.ANY選擇器的輸入段的放置

.ANY通過使用不同的放置算法或不同的排序順序,您可以修改鏈接器在使用多個選擇器時放置未分配輸入段的方式。以下命令行選項可用:

  • --any_placement=algorithm, 其中algorithm是first_fit,worst_fit,best_fit, 或next_fit之一
  • --any_sort_order=order,其中order是cmdlinedescending_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大?。╯ize字節(jié))超過限制(limit字節(jié))。

--any_contingency選項可防止鏈接器將區(qū)域填充到最大值。它為鏈接器生成的內容保留了該區(qū)域大小的一部分,并且僅當其他區(qū)域沒有空間時才填充此應急區(qū)域。默認情況下為first_fitbest_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_REGION0x00x3000
{
ER_10x0ANY_SIZE0xF000x1000
{
.ANY
}
ER_20x0ANY_SIZE0xFB00x1000
{
.ANY
}
ER_30x0ANY_SIZE0x10000x1000
{
.ANY
}
}
123456789101112131415

在這個例子中:

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. 例如:

intfoo__attribute__((section(".ARM.__at_0x10000000"),zero_init));
1
ER_PERIPHERAL0x10000000UNINIT
{
*(.ARM.__at_0x10000000)
}
1234

使用自動放置,并假設附近沒有其他執(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_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ū)域在運行時不會初始化為零。

如果地址是相對的(+offset)形式并且長度是負的,鏈接器會產生一個錯誤。下圖顯示了該示例的圖解表示。

圖 9. 為堆棧保留一個區(qū)域

4582e688-5c64-11ed-a3b6-dac502259ad0.png

在這里插入圖片描述

在本例中,鏈接器生成符號:

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ū)域不一致。

在分散文件中使用預處理命令

您可以通過 C 預處理器傳遞分散文件。這允許訪問 C 預處理器的所有功能。

使用分散文件中的第一行指定鏈接器調用以處理文件的預處理器命令。命令的格式如下:

#!preprocessor[
pre_processor_flags
]
123

最典型的命令是#! armcc -E. 這會通過armcc預處理器傳遞分散文件。

你可以:

  • 將預處理指令添加到分散文件的頂部
  • 在分散文件中使用簡單的表達式評估。

例如,分散文件file.scat, 可能包含:

#!armcc-E#defineADDRESS0x20000000#include"include_file_1.h"

lr1ADDRESS
{
...
}
123456789

鏈接器解析預處理后的分散文件并將指令視為注釋。

您還可以將分散文件的預處理與–predefine命令行選項結合使用。對于這個例子:

  • 修改file.scat以刪除指令。#define ADDRESS 0x20000000
  • 指定命令:armlink --predefine="-DADDRESS=0x20000000" --scatter=file.scat

在分散文件中使用表達式求值以避免填充

使用ALIGN,ALIGNALL或FIXED在分散的文件屬性可導致在鏡像中的大量填充的。要刪除此填充,您可以使用表達式計算來指定加載區(qū)和執(zhí)行區(qū)的起始地址。內置函數(shù)AlignExpr可用于幫助您指定地址表達式。

避免在分散文件中填充的示例以下分散文件生成帶有填充的圖像:

LR10x4000
{
ER1+0ALIGN0x8000
{
...
}
}
1234567

使用ALIGN關鍵字ER10x8000加載視圖和執(zhí)行視圖中的邊界對齊。要在加載視圖中對齊,鏈接器必須插入0x4000填充字節(jié)。

以下分散文件生成沒有填充的圖像:

LR10x4000
{
ER1AlignExpr(+0,0x8000)
{
...
}
}
1234567

使用AlignExpr的結果+00x8000邊界對齊。這將創(chuàng)建一個執(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

只能指定一個ARM_LIB_STACKARM_LIB_HEAP區(qū)域,并且必須分配一個大小,例如:

ARM_LIB_HEAP0x20100000EMPTY0x100000-0x8000;Heapstartsat1MB
;andgrowsupwards
ARM_LIB_STACK0x20200000EMPTY-0x8000;Stackspacestartsattheend
;ofthe2MBofRAM
;Andgrowsdownwardsfor32KB
12345

可以通過定義名為單一執(zhí)行區(qū)域使用組合的棧和堆區(qū)域ARM_LIB_STACKHEAP,與EMPTY屬性。這會導致__user_setup_stackheap()使用符號Image$$ARM_LIB_STACKHEAP$$BaseImage$$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_10x040000;loadregionstartsat0x40000
{;startofexecutionregiondescriptions
ER_RO0x040000;loadaddress=executionaddress
{
*(+RO);allROsections(mustincludesectionwith
;initialentrypoint)
}
...;restofscatter-loadingdescription
}
123456789
  • 使用FIXED執(zhí)行區(qū)屬性可以確保特定區(qū)域的加載地址和執(zhí)行地址相同。您可以使用該FIXED屬性將任何執(zhí)行區(qū)放置在 ROM 中的特定地址。例如,以下內存映射顯示了固定執(zhí)行區(qū):圖 8. 固定執(zhí)行區(qū)的內存映射

4546342c-5c64-11ed-a3b6-dac502259ad0.png

The following example shows the corresponding scatter-loading description:下面的例子給出了相應分散加載描述:

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用于在單個加載區(qū)域內創(chuàng)建多個根區(qū)域,因此通常是單個 ROM 設備。例如,您可以使用它來將函數(shù)或數(shù)據(jù)塊(例如常量表或校驗和)放置在 ROM 中的固定地址,以便可以通過指針輕松訪問。

例如,如果您指定將一些初始化代碼放置在 ROM 的開頭并在 ROM 的末尾放置一個校驗和,則某些內存內容可能未被使用。使用*或.ANY模塊選擇器來填充初始化塊末尾和數(shù)據(jù)塊開頭之間的區(qū)域。

為了使您的代碼更易于維護和調試,建議您在分散文件中使用最少的布局規(guī)范,并將函數(shù)和數(shù)據(jù)的詳細布局留給鏈接器。

您不能指定已部分鏈接的組件對象。例如,如果您將對象obj1.oobj2.o和部分鏈接obj3.o在一起以產生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包含以下代碼的源文件:

#includeexternintsqr(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選項用于生成內存映射文件即.map文件,同樣--autoat是默認值

在此示例中,__attribute__((at(0x5000)))指定將全局變量gSquared放置在絕對地址處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包含以下代碼的源文件:

#includeexternintsqr(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_STACKARM_LIB_HEAP都需要,因為程序將與半主機庫鏈接。4、編譯并鏈接

armcc-c-gfunction.c
armcc-c-gmain.c
aarmlink--map--scatter=scatter.scatfunction.omain.o-osquared.axf
123

內存映射顯示:

LoadRegionLR1(Base:0x00000000,Size:0x00001778,Max:0x00020000,ABSOLUTE)
...
ExecutionRegionER3(Base:0x00010000,Size:0x00000004,Max:0x00002000,ABSOLUTE)

BaseAddrSizeTypeAttrIdxESectionNameObject

0x000100000x00000004DataRW15foomain.o
...
12345678

注意

如果*(foo)從分散文件中省略,則該部分將放置在相同類型的區(qū)域中。在這個例子中就是RAM區(qū)。

使用分散加載將變量放置在特定地址的示例

1、創(chuàng)建main.c包含以下代碼的源文件

#includeexternintsqr(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_STACKARM_LIB_HEAP都需要,因為程序將與半主機庫鏈接。

4、編譯并鏈接

armcc-c-gfunction.c
armcc-c-gmain.c
armlink--no_autoat--scatter=scatter.scat--mapfunction.omain.o-osquared.axf
123

內存映射顯示變量放置ER2在地址處的執(zhí)行區(qū)中0x11000:

...
ExecutionRegionER2(Base:0x00001598,Size:0x0000ea6c,Max:0xffffffff,ABSOLUTE)

BaseAddrSizeTypeAttrIdxESectionNameObject

0x000015980x0000000cCodeRO3.textfunction.o
0x000015a40x0000ea5cPAD
0x000100000x00000004DataRO15.ARM.__at_0x10000main.o...
12345678

在這個例子中,ER1的大小是未知的。因此,gValue可能放在ER1或 中ER2。要確保將gValue其放置在ER2中,您必須包含相應的選擇器ER2并與--no_autoat命令行選項鏈接。如果省略--no_autoat, gValue將被放在一個單獨的加載區(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ū)的默認ER_RW執(zhí)行區(qū)中LR_1
  • 如果源碼中使用了未定義段名(分散加載文件中無此段名),則該部分將放置在定義的 RW 執(zhí)行區(qū)中
  • --autoator--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

在這個例子中,分散加載描述放置:

  • 初始化代碼放在文件的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

lr10x80001024
{
er1+0512
{
.ANY1(+RO);evenlydistributedwither3
}
er2+0256
{
.ANY2(+RO);Highestpriority,sofilledfirst
}
er3+0256
{
.ANY1(+RO);evenlydistributedwither1
}
}
123456789101112131415

控制多個.ANY選擇器的輸入段的放置

.ANY通過使用不同的放置算法或不同的排序順序,您可以修改鏈接器在使用多個選擇器時放置未分配輸入段的方式。以下命令行選項可用:

  • --any_placement=algorithm, 其中algorithm是first_fit,worst_fit,best_fit, 或next_fit之一
  • --any_sort_order=order,其中order是cmdlinedescending_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大?。╯ize字節(jié))超過限制(limit字節(jié))。

--any_contingency選項可防止鏈接器將區(qū)域填充到最大值。它為鏈接器生成的內容保留了該區(qū)域大小的一部分,并且僅當其他區(qū)域沒有空間時才填充此應急區(qū)域。默認情況下為first_fitbest_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_REGION0x00x3000
{
ER_10x0ANY_SIZE0xF000x1000
{
.ANY
}
ER_20x0ANY_SIZE0xFB00x1000
{
.ANY
}
ER_30x0ANY_SIZE0x10000x1000
{
.ANY
}
}
123456789101112131415

在這個例子中:

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. 例如:

intfoo__attribute__((section(".ARM.__at_0x10000000"),zero_init));
1
ER_PERIPHERAL0x10000000UNINIT
{
*(.ARM.__at_0x10000000)
}
1234

使用自動放置,并假設附近沒有其他執(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_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ū)域在運行時不會初始化為零。

如果地址是相對的(+offset)形式并且長度是負的,鏈接器會產生一個錯誤。下圖顯示了該示例的圖解表示。

圖 9. 為堆棧保留一個區(qū)域

4582e688-5c64-11ed-a3b6-dac502259ad0.png

在這里插入圖片描述

在本例中,鏈接器生成符號:

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ū)域不一致。

在分散文件中使用預處理命令

您可以通過 C 預處理器傳遞分散文件。這允許訪問 C 預處理器的所有功能。

使用分散文件中的第一行指定鏈接器調用以處理文件的預處理器命令。命令的格式如下:

#!preprocessor[
pre_processor_flags
]
123

最典型的命令是#! armcc -E. 這會通過armcc預處理器傳遞分散文件。

你可以:

  • 將預處理指令添加到分散文件的頂部
  • 在分散文件中使用簡單的表達式評估。

例如,分散文件file.scat, 可能包含:

#!armcc-E#defineADDRESS0x20000000#include"include_file_1.h"

lr1ADDRESS
{
...
}
123456789

鏈接器解析預處理后的分散文件并將指令視為注釋。

您還可以將分散文件的預處理與–predefine命令行選項結合使用。對于這個例子:

  • 修改file.scat以刪除指令。#define ADDRESS 0x20000000
  • 指定命令:armlink --predefine="-DADDRESS=0x20000000" --scatter=file.scat

在分散文件中使用表達式求值以避免填充

使用ALIGN,ALIGNALL或FIXED在分散的文件屬性可導致在鏡像中的大量填充的。要刪除此填充,您可以使用表達式計算來指定加載區(qū)和執(zhí)行區(qū)的起始地址。內置函數(shù)AlignExpr可用于幫助您指定地址表達式。

避免在分散文件中填充的示例以下分散文件生成帶有填充的圖像:

LR10x4000
{
ER1+0ALIGN0x8000
{
...
}
}
1234567

使用ALIGN關鍵字ER10x8000加載視圖和執(zhí)行視圖中的邊界對齊。要在加載視圖中對齊,鏈接器必須插入0x4000填充字節(jié)。

以下分散文件生成沒有填充的圖像:

LR10x4000
{
ER1AlignExpr(+0,0x8000)
{
...
}
}
1234567

使用AlignExpr的結果+00x8000邊界對齊。這將創(chuàng)建一個執(zhí)行區(qū),其加載地址為0x4000但執(zhí)行地址為0x8000。

審核編輯:郭婷


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 代碼
    +關注

    關注

    30

    文章

    4823

    瀏覽量

    68935

原文標題:在分散文件中使用表達式求值以避免填充

文章出處:【微信號:技術讓夢想更偉大,微信公眾號:技術讓夢想更偉大】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    禁止使用root用戶通過ssh遠程登錄Linux

    1、背景描述 出于安全考慮,需要禁止使用root用戶通過ssh遠程登錄Linux 禁用root用戶遠程登錄后,需要提供一個權限用戶用于ssh遠程登錄 2、創(chuàng)建擁有sudo權限的用戶 2.1、創(chuàng)
    的頭像 發(fā)表于 12-21 16:25 ?489次閱讀
    禁止使用<b class='flag-5'>root</b>用戶通過ssh遠程登錄Linux

    linux網(wǎng)卡配置文件

    網(wǎng)卡配置文件 ? ? 網(wǎng)卡目錄[root@localhost opt]# ls /etc/sysconfig/network-scripts/網(wǎng)卡文件名字,和我們ifconfig看到的一樣
    的頭像 發(fā)表于 12-10 10:09 ?286次閱讀

    使用lsof實現(xiàn)對linux文件的誤刪除恢復練習

    lsof命令的幫助,恢復該日志數(shù)據(jù) 確保當前nginx進程運行中 [root@master10 ~]# systemctl status nginx 查看nginx日志文件 [root
    的頭像 發(fā)表于 11-24 11:14 ?247次閱讀
    使用lsof實現(xiàn)對linux<b class='flag-5'>文件</b>的誤刪除恢復練習

    AG32 下的分散加載

    AG32 下的分散加載 分散加載: 在實際應用中,有時候需要強制把一段數(shù)據(jù)或一段代碼固定到指定的一個地址。這個時候,就不能使用默認的編譯配置,需要自己來配置連接屬性。 AG32 中可以通過修改ld
    發(fā)表于 09-20 15:26

    labview怎么生成可執(zhí)行文件

    生成可執(zhí)行文件(EXE)是LabVIEW程序開發(fā)中的一個重要步驟,它允許用戶將LabVIEW項目打包成一個獨立的應用程序,便于在沒有安裝LabVIEW的計算機上運行。 1. 準備工作 在開始生成
    的頭像 發(fā)表于 09-04 17:07 ?1171次閱讀

    執(zhí)行shell腳本的方式包括什么

    執(zhí)行權限,但是需要指定腳本的解釋器為bash。 使用source命令 可以使用 source 命令來運行腳本文件,例如: source scrip
    的頭像 發(fā)表于 08-30 15:17 ?384次閱讀

    MDBT50Q-512K基于Nordic nRF52833 SoC解決方案設計的BT 5.2堆棧模塊

    電子發(fā)燒友網(wǎng)站提供《MDBT50Q-512K基于Nordic nRF52833 SoC解決方案設計的BT 5.2堆棧模塊.pdf》資料免費下載
    發(fā)表于 06-22 09:17 ?1次下載

    創(chuàng)建DMA通道時,能否將DMA緩沖區(qū)的大小指定為8字節(jié),并將DMA緩沖區(qū)的編號指定為1?

    創(chuàng)建 DMA 通道時,能否將 DMA 緩沖區(qū)的大小指定為 8 字節(jié),并將 DMA 緩沖區(qū)的編號指定為 1?
    發(fā)表于 05-31 07:46

    如何用加載分散法將軟件中部分變量從內部RAM轉移到外部RAM?

    如何用加載分散法將軟件中部分變量從內部RAM轉移到外部RAM, 加載分散文件怎么設置?需要設置嗎?
    發(fā)表于 05-10 07:52

    深入分析一下MDK的分散加載文件

    ARM C 庫提供了函數(shù) __user_setup_stackheap() 的多個實現(xiàn),并且可以根據(jù)分散文件中提供的信息自動選擇正確的一種。
    發(fā)表于 04-28 14:21 ?897次閱讀
    深入分析一下MDK的<b class='flag-5'>分散</b>加載<b class='flag-5'>文件</b>

    【操作指引】鐵威馬NAS加密文件創(chuàng)建和使用教程

    共享文件夾是一個網(wǎng)絡存儲空間。您可以建立數(shù)個共享文件夾來存放不同類別的數(shù)據(jù),或提供給不同的用戶或用戶群組來存取文件。 ? 一旦加密共享文件夾被創(chuàng)建
    的頭像 發(fā)表于 04-02 14:17 ?977次閱讀
    【操作指引】鐵威馬NAS加密<b class='flag-5'>文件</b>夾<b class='flag-5'>創(chuàng)建</b>和使用教程

    rt-thread studio執(zhí)行清空項目的時候發(fā)現(xiàn)系統(tǒng)找不到指定文件怎么解決?

    在基于rt-thread studio開發(fā)時,出于嘗試添加了一些組件,后來又刪掉了一些不需要的組件,重新編譯的時候拋出錯誤,錯誤指出刪掉的組件的相關.o文件還是存在,執(zhí)行清空項目的時候發(fā)現(xiàn)系統(tǒng)找不到
    發(fā)表于 03-05 06:18

    PSoC? 5LP時增加TX緩沖區(qū)大小會損壞怎么解決?

    我遇到了一個關于內存的奇怪問題——如果我將 UART 傳輸緩沖區(qū)的大小從 256 字節(jié)增加到 1024 字節(jié),似乎會損壞。 第一次調用 malloc () 返回 NULL。 但是,據(jù)我所知
    發(fā)表于 02-26 06:58

    怎么在rtthread studio中使用分散加載功能,工程里也找不到sct文件

    怎么在rtthread studio中使用分散加載功能,工程里也找不到sct文件
    發(fā)表于 02-23 08:28

    STM32H750IBK6如何將指定代碼存放在外部QSPI FLASH里?

    如題,使用的STM32H750IBK6,內部只有128KB的FLASH,太小了。MDK使用的是分散加載文件指定代碼存放內存。同理我嘗試修改了studio中的鏈接腳本,可無論如何都無法將指定
    發(fā)表于 02-23 06:02