AUTOSAR从入门到精通番外篇(二)-一文读懂ld链接脚本文件
1 連接腳本
連接腳本的一個主要目的是描述輸入文件中的節(jié)如何被映射到輸出文件中,并控制輸出文件的內(nèi)存排布. 幾乎所有的連接腳本只做這兩件事情. 但是,在需要的時候,連接器腳本還可以指示連接器執(zhí)行很多其他的操作.這通過下面描述的命令實現(xiàn).
連接器總是使用連接器腳本的.如果你自己不提供, 連接器會使用一個缺省的腳本,這個腳本是被編譯進連接器可執(zhí)行文件的. 你可以使用’–verbose’命令行選項來顯示缺省的連接器腳本的內(nèi)容. 某些命令行選項,比如
‘-r’或’-N’, 會影響缺省的連接腳本.
你可以過使用’-T’命令行選項來提供你自己的連接腳本. 當(dāng)你這么做的時候, 你的連接腳本會替換缺省的連接腳本.
你也可以通過把連接腳本作為一個連接器的輸入文件來隱式地使用它,就象它們是一個被連接的文件一樣.
2 基本的連接腳本的概念
我們需要定義一些基本的概念與詞匯以描述連接腳本語言.
連接器把多個輸入文件合并成單個輸出文件. 輸出文件和輸入文件都以一種叫做’目標(biāo)文件格式’的數(shù)據(jù)格式形式存在. 每一個文件被叫做’目標(biāo)文件’. 輸出文件經(jīng)常被叫做’可執(zhí)行文件’,但是由于需要,我們也把它叫做目標(biāo)文件. 每一個目標(biāo)文件中,在其它東西之間,有一個節(jié)列表.我們有時把輸入文件的節(jié)叫做輸入節(jié); 相似的,輸出文件中的一個節(jié)經(jīng)常被叫做輸出節(jié).
一個目標(biāo)文件中的每一個節(jié)都有一個名字和一個大小尺寸. 大多數(shù)節(jié)還有一個相關(guān)的數(shù)據(jù)塊, 稱為節(jié)內(nèi)容. 某一個節(jié)可能被標(biāo)式詎’loadable’,含義是在輸出文件被執(zhí)行時,這個節(jié)應(yīng)當(dāng)被載入到內(nèi)存中去. 一個沒有內(nèi)容的節(jié)可能是’allocatable’, 含義是內(nèi)存中必須為這個節(jié)開辟一塊空間,但是沒有實際的內(nèi)容載入到這里(在某些情況下,這塊內(nèi)存必須被標(biāo)式詎零). 一個既不是loadable也不是allocatable的節(jié)一般含有一些調(diào)試信息.
每一個loadable或allocatable的輸出節(jié)有兩個地址. 第一個是’VMA’或稱為虛擬內(nèi)存地址. 這是當(dāng)輸出文件運行時節(jié)所擁有的地址. 第二個是”LMA’, 或稱為載入內(nèi)存地址. 這個節(jié)即將要載入的內(nèi)存地址. 這大多數(shù)情況下這兩個地址是相同的. 它們兩個有可能不同的一個例子是當(dāng)一個數(shù)據(jù)節(jié)在ROM中時, 當(dāng)程序啟動時,被拷貝到RAM中(這個技術(shù)經(jīng)常被用在基于ROM的系統(tǒng)中進行全局變量的初始化). 在這種情況下, ROM地址就是LMA, 而RAM地址就是VMA.
你可以通過使用帶有’-h’選項的’objdump’來察看目標(biāo)文件中的節(jié).
每一個目標(biāo)文件還有一個關(guān)于符號的列表, 被稱為’符號表’. 一個符號可能是定義過了的,也可能是未定義的.
每一個符號有一個名字, 而且每一個定義的符號有一個地址. 如果你把一個C/C++程序編譯為一個目標(biāo)文件,對于每一個定義的函數(shù)和全局或靜態(tài)變量,你為得到一個定義的符號. 每一個在輸入文件中只是一個引用而未定義的函數(shù)或全局變量會變成一個未定義的符號.
你可以使用’nm’程序來看一個目標(biāo)文件中的符號, 或者使用’objdump’程序帶有’-t’選項.
3 連接腳本的格式
連接腳本是文本文件.
你寫了一系列的命令作為一個連接腳本. 每一個命令是一個帶有參數(shù)的關(guān)鍵字,或者是一個對符號的賦值. 你可以用分號分隔命令. 空格一般被忽略.
文件名或格式名之類的字符串一般可以被直接鍵入. 如果文件名含有特殊字符,比如一般作為分隔文件名用的逗號, 你可以把文件名放到雙引號中. 文件名中間無法使用雙引號.
你可以象在C語言中一樣,在連接腳本中使用注釋, 用’/‘和’/’隔開. 就像在C中,注釋在語法上等同于空格.
4 簡單的連接腳本示例
許多腳本是相當(dāng)?shù)暮唵蔚?
可能的最簡單的腳本只含有一個命令: ‘SECTIONS’. 你可以使用’SECTIONS’來描述輸出文件的內(nèi)存布局.
‘SECTIONS’是一個功能很強大的命令. 這里這們會描述一個很簡單的使用. 讓我們假設(shè)你的程序只有代碼節(jié), 初始化過的數(shù)據(jù)節(jié), 和未初始化過的數(shù)據(jù)節(jié). 這些會存在于’.text’,’.data’和’.bss’節(jié), 另外, 讓我們進一步假設(shè)在你的輸入文件中只有這些節(jié).
對于這個例子, 我們說代碼應(yīng)當(dāng)被載入到地址’0x10000’處, 而數(shù)據(jù)應(yīng)當(dāng)從0x8000000處開始. 下面是一個實現(xiàn)這個功能的腳本:
SECTIONS {.?=?0x10000;.text?:?{?*(.text)?}.?=?0x8000000;.data?:?{?*(.data)?}.bss?:?{?*(.bss)?} }你使用關(guān)鍵字’SECTIONS’寫了這個SECTIONS命令, 后面跟有一串放在花括號中的符號賦值和輸出節(jié)描述的內(nèi)容.
上例中, 在’SECTIONS’命令中的第一行是對一個特殊的符號’.’賦值, 這是一個定位計數(shù)器. 如果你沒有以其它的方式指定輸出節(jié)的地址(其他方式在后面會描述), 那地址值就會被設(shè)為定位計數(shù)器的現(xiàn)有值. 定位計數(shù)器然后被加上輸出節(jié)的尺寸. 在’SECTIONS’命令的開始處, 定位計數(shù)器擁有值’0’.
第二行定義一個輸出節(jié),’.text’. 冒號是語法需要,現(xiàn)在可以被忽略. 節(jié)名后面的花括號中,你列出所有應(yīng)當(dāng)被放入到這個輸出節(jié)中的輸入節(jié)的名字. ‘‘是一個通配符,匹配任何文件名. 表達(dá)式’(.text)’意思是所有的輸入文件中的’.text’輸入節(jié).
因為當(dāng)輸出節(jié)’.text’定義的時候, 定位計數(shù)器的值是’0x10000’,連接器會把輸出文件中的’.text’節(jié)的地址設(shè)為’0x10000’.
余下的內(nèi)容定義了輸出文件中的’.data’節(jié)和’.bss’節(jié). 連接器會把’.data’輸出節(jié)放到地址’0x8000000’處. 連接器放好’.data’輸出節(jié)之后, 定位計數(shù)器的值是’0x8000000’加上’.data’輸出節(jié)的長度. 得到的結(jié)果是連接器會把’.bss’輸出節(jié)放到緊接’.data’節(jié)后面的位置.
連接器會通過在必要時增加定位計數(shù)器的值來保證每一個輸出節(jié)具有它所需的對齊. 在這個例子中, 為’.text’和’.data’節(jié)指定的地址會滿足對齊約束, 但是連接器可能會需要在’.data’和’.bss’節(jié)之間創(chuàng)建一個小的缺口。
就這樣,這是一個簡單但完整的連接腳本。
每個連接都被一個’連接腳本’所控制. 這個腳本是用連接命令語言書寫的。
5 MEMORY命令
連接器在缺省狀態(tài)下被配置為允許分配所有可用的內(nèi)存塊。你可以使用‘MEMORY’命令重新配置這個設(shè)置。
‘MEMORY’命令描述目標(biāo)平臺上內(nèi)存塊的位置與長度。你可以用它來描述哪些內(nèi)存區(qū)域可以被連接器使用,
哪些內(nèi)存區(qū)域是要避免使用的。然后你就可以把節(jié)分配到特定的內(nèi)存區(qū)域中。連接器會基于內(nèi)存區(qū)域設(shè)置節(jié)
的地址,對于太滿的區(qū)域,會提示警告信息。連接器不會為了適應(yīng)可用的區(qū)域而攪亂節(jié)。
一個連接腳本最多可以包含一次MEMORY命令。但是,你可以在命令中隨心所欲定義任意多的內(nèi)存塊,語法
如下:
NAME是用在連接腳本中引用內(nèi)存區(qū)域的名字。出了連接腳本,區(qū)域名就沒有任何實際意義。區(qū)域名存儲在一個
單獨的名字空間中,它不會和符號名,文件名,節(jié)名產(chǎn)生沖突,每一塊內(nèi)存區(qū)域必須有一個唯一的名字。
ATTR字符串是一個可選的屬性列表,它指出是否為一個沒有在連接腳本中進行顯式映射地輸入段使用一個特定
的內(nèi)存區(qū)域。如果你沒有為某些輸入段指定一個輸出段,連接器會創(chuàng)建一個跟輸入段同名的輸出段。如果你定
義了區(qū)域?qū)傩?#xff0c;連接器會使用它們來為它創(chuàng)建的輸出段選擇內(nèi)存區(qū)域。
ATTR字符串必須包含下面字符中的一個,且必須只包含一個:
R
只讀節(jié)。
W
可讀寫節(jié)。
X
可執(zhí)行節(jié)。
A
可分配節(jié)。
I
已初始化節(jié)。
L
同‘I’
!
對前一個屬性值取反。
如果一個未映射節(jié)匹配了上面除’!’之外的一個屬性,它就會被放入該內(nèi)存區(qū)域。’!’屬性對該測試取反,所以
只有當(dāng)它不匹配上面列出的行何屬性時,一個未映射節(jié)才會被放入到內(nèi)存區(qū)域。
ORIGIN是一個關(guān)于內(nèi)存區(qū)域地始地址的表達(dá)式。在內(nèi)存分配執(zhí)行之前,這個表達(dá)式必須被求值產(chǎn)生一個常數(shù),
這意味著你不可以使用任何節(jié)相關(guān)的符號。關(guān)鍵字’ORIGIN’可以被縮寫為’org’或’o’(但是,不可以寫為,比
如‘ORG’)
LEN是一個關(guān)于內(nèi)存區(qū)域長充(以字節(jié)為單位)的表達(dá)式。就像ORIGIN表達(dá)式,這個表達(dá)式在分配執(zhí)行前也
必須被求得為一個常數(shù)值。關(guān)鍵字’LENGTH’可以被簡寫為‘len’或’l’。
在下面的例子中,我們指定兩個可用于分配的內(nèi)存區(qū)域:一個從0開始,有256kb長度,另一個從0x4000000
開始,有4mb長度。連接器會把那些沒有進行顯式映射且是只讀或可執(zhí)行的節(jié)放到’rom’內(nèi)存區(qū)域。并會把另外的沒有被顯式映射地節(jié)放入到’ram’內(nèi)存區(qū)域。
一旦你定義了一個內(nèi)存區(qū)域,你也可以指示連接器把指定的輸出段放入到這個內(nèi)存區(qū)域中,這可以通過使用
‘>REGION’輸出段屬性。比如,如果你有一個名為’mem’的內(nèi)存區(qū)域,你可以在輸出段定義中使用’>mem’。如
果沒有為輸出段指定地址,連接器就會把地址設(shè)置為內(nèi)存區(qū)域中的下一個可用的地址。如果總共的映射到一
個內(nèi)存區(qū)域的輸出段對于區(qū)域來說太大了,連接器會提示一條錯誤信息。
6 輸入section和垃圾回收
在連接命令行內(nèi)使用了選項–gc-sections后,連接器可能將某些它認(rèn)為沒用的section過濾掉,此時就有必要強制連接器保留一些特定的 section,可用KEEP()關(guān)鍵字達(dá)此目的。如KEEP((.text))或KEEP(SORT()(.text))
7 一個完整的lds文件示例
/*GNU?linker?script?for?STM32F405 *//*?Specify?the?memory?areas?*/ MEMORY {FLASH?(rx)??????:?ORIGIN?=?0x08000000,?LENGTH?=?0x100000?/*?entire?flash,?1?MiB?*/FLASH_ISR?(rx)??:?ORIGIN?=?0x08000000,?LENGTH?=?0x004000?/*?sector?0,?16?KiB?*/FLASH_TEXT?(rx)?:?ORIGIN?=?0x08020000,?LENGTH?=?0x080000?/*?sectors?5,6,7,8,?4*128KiB?=?512?KiB?(could?increase?it?more)?*/CCMRAM?(xrw)????:?ORIGIN?=?0x10000000,?LENGTH?=?0x010000?/*?64?KiB?*/RAM?(xrw)???????:?ORIGIN?=?0x20000000,?LENGTH?=?0x020000?/*?128?KiB?*/ }/*?top?end?of?the?stack?*/ _estack?=?ORIGIN(RAM)?+?LENGTH(RAM);/*?RAM?extents?for?the?garbage?collector?*/ _ram_end?=?ORIGIN(RAM)?+?LENGTH(RAM); _heap_end?=?0x2001c000;?/*?tunable?*//*?define?output?sections?*/ SECTIONS {/*?The?startup?code?goes?first?into?FLASH?*/.isr_vector?:{.?=?ALIGN(4);KEEP(*(.isr_vector))?/*?Startup?code?*/.?=?ALIGN(4);}?>FLASH_ISR/*?The?program?code?and?other?data?goes?into?FLASH?*/.text?:{.?=?ALIGN(4);*(.text)???????????/*?.text?sections?(code)?*/*(.text*)??????????/*?.text*?sections?(code)?*/*(.rodata)?????????/*?.rodata?sections?(constants,?strings,?etc.)?*/*(.rodata*)????????/*?.rodata*?sections?(constants,?strings,?etc.)?*//*??*(.glue_7)???*/????/*?glue?arm?to?thumb?code?*//*??*(.glue_7t)??*/????/*?glue?thumb?to?arm?code?*/.?=?ALIGN(4);_etext?=?.;????????/*?define?a?global?symbol?at?end?of?code?*/_sidata?=?_etext;??/*?This?is?used?by?the?startup?in?order?to?initialize?the?.data?secion?*/}?>FLASH_TEXT/*.ARM.extab?:{*(.ARM.extab*?.gnu.linkonce.armextab.*)}?>FLASH.ARM?:{__exidx_start?=?.;*(.ARM.exidx*)__exidx_end?=?.;}?>FLASH*//*?This?is?the?initialized?data?sectionThe?program?executes?knowing?that?the?data?is?in?the?RAMbut?the?loader?puts?the?initial?values?in?the?FLASH?(inidata).It?is?one?task?of?the?startup?to?copy?the?initial?values?from?FLASH?to?RAM.?*/.data?:?AT?(?_sidata?){.?=?ALIGN(4);_sdata?=?.;????????/*?create?a?global?symbol?at?data?start;?used?by?startup?code?in?order?to?initialise?the?.data?section?in?RAM?*/_ram_start?=?.;????/*?create?a?global?symbol?at?ram?start?for?garbage?collector?*/*(.data)???????????/*?.data?sections?*/*(.data*)??????????/*?.data*?sections?*/.?=?ALIGN(4);_edata?=?.;????????/*?define?a?global?symbol?at?data?end;?used?by?startup?code?in?order?to?initialise?the?.data?section?in?RAM?*/}?>RAM/*?Uninitialized?data?section?*/.bss?:{.?=?ALIGN(4);_sbss?=?.;?????????/*?define?a?global?symbol?at?bss?start;?used?by?startup?code?*/*(.bss)*(.bss*)*(COMMON).?=?ALIGN(4);_ebss?=?.;?????????/*?define?a?global?symbol?at?bss?end;?used?by?startup?code?*/}?>RAM/*?this?is?to?define?the?start?of?the?heap,?and?make?sure?we?have?a?minimum?size?*/.heap?:{.?=?ALIGN(4);_heap_start?=?.;????/*?define?a?global?symbol?at?heap?start?*/}?>RAM/*?this?just?checks?there?is?enough?RAM?for?the?stack?*/.stack?:{.?=?ALIGN(4);}?>RAM/*?Remove?information?from?the?standard?libraries?*//*/DISCARD/?:{libc.a?(?*?)libm.a?(?*?)libgcc.a?(?*?)}*/.ARM.attributes?0?:?{?*(.ARM.attributes)?} }總結(jié)
以上是生活随笔為你收集整理的AUTOSAR从入门到精通番外篇(二)-一文读懂ld链接脚本文件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AUTOSAR从入门到精通100讲(八十
- 下一篇: 知识图谱应用实战案例100篇(二)-以知