AUTOSAR从入门到精通番外篇(二)-一文读懂ld链接脚本文件
1 連接腳本
連接腳本的一個主要目的是描述輸入文件中的節如何被映射到輸出文件中,并控制輸出文件的內存排布. 幾乎所有的連接腳本只做這兩件事情. 但是,在需要的時候,連接器腳本還可以指示連接器執行很多其他的操作.這通過下面描述的命令實現.
連接器總是使用連接器腳本的.如果你自己不提供, 連接器會使用一個缺省的腳本,這個腳本是被編譯進連接器可執行文件的. 你可以使用’–verbose’命令行選項來顯示缺省的連接器腳本的內容. 某些命令行選項,比如
‘-r’或’-N’, 會影響缺省的連接腳本.
你可以過使用’-T’命令行選項來提供你自己的連接腳本. 當你這么做的時候, 你的連接腳本會替換缺省的連接腳本.
你也可以通過把連接腳本作為一個連接器的輸入文件來隱式地使用它,就象它們是一個被連接的文件一樣.
2 基本的連接腳本的概念
我們需要定義一些基本的概念與詞匯以描述連接腳本語言.
連接器把多個輸入文件合并成單個輸出文件. 輸出文件和輸入文件都以一種叫做’目標文件格式’的數據格式形式存在. 每一個文件被叫做’目標文件’. 輸出文件經常被叫做’可執行文件’,但是由于需要,我們也把它叫做目標文件. 每一個目標文件中,在其它東西之間,有一個節列表.我們有時把輸入文件的節叫做輸入節; 相似的,輸出文件中的一個節經常被叫做輸出節.
一個目標文件中的每一個節都有一個名字和一個大小尺寸. 大多數節還有一個相關的數據塊, 稱為節內容. 某一個節可能被標式詎’loadable’,含義是在輸出文件被執行時,這個節應當被載入到內存中去. 一個沒有內容的節可能是’allocatable’, 含義是內存中必須為這個節開辟一塊空間,但是沒有實際的內容載入到這里(在某些情況下,這塊內存必須被標式詎零). 一個既不是loadable也不是allocatable的節一般含有一些調試信息.
每一個loadable或allocatable的輸出節有兩個地址. 第一個是’VMA’或稱為虛擬內存地址. 這是當輸出文件運行時節所擁有的地址. 第二個是”LMA’, 或稱為載入內存地址. 這個節即將要載入的內存地址. 這大多數情況下這兩個地址是相同的. 它們兩個有可能不同的一個例子是當一個數據節在ROM中時, 當程序啟動時,被拷貝到RAM中(這個技術經常被用在基于ROM的系統中進行全局變量的初始化). 在這種情況下, ROM地址就是LMA, 而RAM地址就是VMA.
你可以通過使用帶有’-h’選項的’objdump’來察看目標文件中的節.
每一個目標文件還有一個關于符號的列表, 被稱為’符號表’. 一個符號可能是定義過了的,也可能是未定義的.
每一個符號有一個名字, 而且每一個定義的符號有一個地址. 如果你把一個C/C++程序編譯為一個目標文件,對于每一個定義的函數和全局或靜態變量,你為得到一個定義的符號. 每一個在輸入文件中只是一個引用而未定義的函數或全局變量會變成一個未定義的符號.
你可以使用’nm’程序來看一個目標文件中的符號, 或者使用’objdump’程序帶有’-t’選項.
3 連接腳本的格式
連接腳本是文本文件.
你寫了一系列的命令作為一個連接腳本. 每一個命令是一個帶有參數的關鍵字,或者是一個對符號的賦值. 你可以用分號分隔命令. 空格一般被忽略.
文件名或格式名之類的字符串一般可以被直接鍵入. 如果文件名含有特殊字符,比如一般作為分隔文件名用的逗號, 你可以把文件名放到雙引號中. 文件名中間無法使用雙引號.
你可以象在C語言中一樣,在連接腳本中使用注釋, 用’/‘和’/’隔開. 就像在C中,注釋在語法上等同于空格.
4 簡單的連接腳本示例
許多腳本是相當的簡單的.
可能的最簡單的腳本只含有一個命令: ‘SECTIONS’. 你可以使用’SECTIONS’來描述輸出文件的內存布局.
‘SECTIONS’是一個功能很強大的命令. 這里這們會描述一個很簡單的使用. 讓我們假設你的程序只有代碼節, 初始化過的數據節, 和未初始化過的數據節. 這些會存在于’.text’,’.data’和’.bss’節, 另外, 讓我們進一步假設在你的輸入文件中只有這些節.
對于這個例子, 我們說代碼應當被載入到地址’0x10000’處, 而數據應當從0x8000000處開始. 下面是一個實現這個功能的腳本:
SECTIONS {.?=?0x10000;.text?:?{?*(.text)?}.?=?0x8000000;.data?:?{?*(.data)?}.bss?:?{?*(.bss)?} }你使用關鍵字’SECTIONS’寫了這個SECTIONS命令, 后面跟有一串放在花括號中的符號賦值和輸出節描述的內容.
上例中, 在’SECTIONS’命令中的第一行是對一個特殊的符號’.’賦值, 這是一個定位計數器. 如果你沒有以其它的方式指定輸出節的地址(其他方式在后面會描述), 那地址值就會被設為定位計數器的現有值. 定位計數器然后被加上輸出節的尺寸. 在’SECTIONS’命令的開始處, 定位計數器擁有值’0’.
第二行定義一個輸出節,’.text’. 冒號是語法需要,現在可以被忽略. 節名后面的花括號中,你列出所有應當被放入到這個輸出節中的輸入節的名字. ‘‘是一個通配符,匹配任何文件名. 表達式’(.text)’意思是所有的輸入文件中的’.text’輸入節.
因為當輸出節’.text’定義的時候, 定位計數器的值是’0x10000’,連接器會把輸出文件中的’.text’節的地址設為’0x10000’.
余下的內容定義了輸出文件中的’.data’節和’.bss’節. 連接器會把’.data’輸出節放到地址’0x8000000’處. 連接器放好’.data’輸出節之后, 定位計數器的值是’0x8000000’加上’.data’輸出節的長度. 得到的結果是連接器會把’.bss’輸出節放到緊接’.data’節后面的位置.
連接器會通過在必要時增加定位計數器的值來保證每一個輸出節具有它所需的對齊. 在這個例子中, 為’.text’和’.data’節指定的地址會滿足對齊約束, 但是連接器可能會需要在’.data’和’.bss’節之間創建一個小的缺口。
就這樣,這是一個簡單但完整的連接腳本。
每個連接都被一個’連接腳本’所控制. 這個腳本是用連接命令語言書寫的。
5 MEMORY命令
連接器在缺省狀態下被配置為允許分配所有可用的內存塊。你可以使用‘MEMORY’命令重新配置這個設置。
‘MEMORY’命令描述目標平臺上內存塊的位置與長度。你可以用它來描述哪些內存區域可以被連接器使用,
 哪些內存區域是要避免使用的。然后你就可以把節分配到特定的內存區域中。連接器會基于內存區域設置節
 的地址,對于太滿的區域,會提示警告信息。連接器不會為了適應可用的區域而攪亂節。
一個連接腳本最多可以包含一次MEMORY命令。但是,你可以在命令中隨心所欲定義任意多的內存塊,語法
 如下:
NAME是用在連接腳本中引用內存區域的名字。出了連接腳本,區域名就沒有任何實際意義。區域名存儲在一個
 單獨的名字空間中,它不會和符號名,文件名,節名產生沖突,每一塊內存區域必須有一個唯一的名字。
ATTR字符串是一個可選的屬性列表,它指出是否為一個沒有在連接腳本中進行顯式映射地輸入段使用一個特定
 的內存區域。如果你沒有為某些輸入段指定一個輸出段,連接器會創建一個跟輸入段同名的輸出段。如果你定
 義了區域屬性,連接器會使用它們來為它創建的輸出段選擇內存區域。
ATTR字符串必須包含下面字符中的一個,且必須只包含一個:
R
 只讀節。
W
 可讀寫節。
X
 可執行節。
A
 可分配節。
I
 已初始化節。
L
 同‘I’
!
 對前一個屬性值取反。
如果一個未映射節匹配了上面除’!’之外的一個屬性,它就會被放入該內存區域。’!’屬性對該測試取反,所以
 只有當它不匹配上面列出的行何屬性時,一個未映射節才會被放入到內存區域。
ORIGIN是一個關于內存區域地始地址的表達式。在內存分配執行之前,這個表達式必須被求值產生一個常數,
 這意味著你不可以使用任何節相關的符號。關鍵字’ORIGIN’可以被縮寫為’org’或’o’(但是,不可以寫為,比
 如‘ORG’)
LEN是一個關于內存區域長充(以字節為單位)的表達式。就像ORIGIN表達式,這個表達式在分配執行前也
 必須被求得為一個常數值。關鍵字’LENGTH’可以被簡寫為‘len’或’l’。
在下面的例子中,我們指定兩個可用于分配的內存區域:一個從0開始,有256kb長度,另一個從0x4000000
 開始,有4mb長度。連接器會把那些沒有進行顯式映射且是只讀或可執行的節放到’rom’內存區域。并會把另外的沒有被顯式映射地節放入到’ram’內存區域。
一旦你定義了一個內存區域,你也可以指示連接器把指定的輸出段放入到這個內存區域中,這可以通過使用
 ‘>REGION’輸出段屬性。比如,如果你有一個名為’mem’的內存區域,你可以在輸出段定義中使用’>mem’。如
 果沒有為輸出段指定地址,連接器就會把地址設置為內存區域中的下一個可用的地址。如果總共的映射到一
 個內存區域的輸出段對于區域來說太大了,連接器會提示一條錯誤信息。
6 輸入section和垃圾回收
在連接命令行內使用了選項–gc-sections后,連接器可能將某些它認為沒用的section過濾掉,此時就有必要強制連接器保留一些特定的 section,可用KEEP()關鍵字達此目的。如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)?} }總結
以上是生活随笔為你收集整理的AUTOSAR从入门到精通番外篇(二)-一文读懂ld链接脚本文件的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: AUTOSAR从入门到精通100讲(八十
- 下一篇: 知识图谱应用实战案例100篇(二)-以知
