|
Posted on 2011-06-03 23:15 S.l.e!ep.¢% 閱讀(1457) 評論(0) 編輯 收藏 引用 所屬分類: Unix
使用ld
********
本文檔介紹GNU連接器ld的2.14版本.
本文檔在GNU自由文檔許可證下發行.在"GNU自由文檔許可證"一章中有關于本許可證的一份拷貝.
概述
********
'ld'把一定量的目標文件跟檔案文件連接起來,并重定位它們的數據,連接符號引用.一般,在編譯一個程序
時,最后一步就是運行'ld'.
'ld'能接受連接命令語言文件,這是一種用AT&T的連接編輯命令語言的超集寫成的文件,用來在連接的整個
過程中提供顯式的,全局的控制.
本版本的'ld'使用通用BFD庫來操作目標文件.這就允許'ld'讀取,合并,寫入目標文件時,可以使用各種不同
的格式,比如,COFF或'a.out'. 不同的格式可以被連接到一起產生一個有效的目標文件.
除了它的靈活性,GNU連接器比其它連接器更有用的地方在于它提供了診斷信息. 許多連接器在碰到一個錯誤
的時候立即放棄執行;但'ld'卻能夠繼續執行,以讓你發現其他的錯誤(或者,在某些情況下,得到一個帶有錯誤
的輸出文件)
引用
**********
GNU連接器'ld'能夠處理大量的不同情況,并且跟其他的連接器保持盡可能的兼容.這樣,你就擁有更多的選擇來
控制它的行為.
命令行選項
====================
連接器提供大量的命令行選項,但是,在實際使用中,只有少數被經常使用.比如,'ld'的一個經常的使用場合是在
一個標準的Unix系統上連接標準的Unix目標文件.在這樣的一個系統上,連接文件'hello.o'如下:
ld -o OUTPUT /lib/crt0.o hello.o -lc
這告訴'ld'產生一個叫OUTPUT的文件,作為連接文件'/lib/crt0.o'和'hello.o'和庫'libc.a'的結果.'libc.a'
來自標準的搜索路徑.(參閱下文的關于'-l'選項的討論).
有些命令行選項可以在命令行的任何位置出現.但是,那些帶有文件名的選項,比如'-l'或者'-T',會讓文件在選
項出現的位置上被讀取. 對于非文件選項,以帶不同的參數重復它,不會有進一步的效果,或者覆蓋掉前面的相同
項.那些多次出現時具有特殊含義的選項會在下文的描述中指出.
無參數選項是那些被連接的目標文件和檔案文件.它們可能緊隨命令行選項,或在它們前面,或者跟它們夾雜在一
起,但是一個目標文件參數是不會出現在一個選項跟它的參數之間的.
通常,連接器至少引用一個目標文件,但是你可指定其它形式的二進制輸入文件,這可以通過'-l','-R'或者腳本
命令語言來實現.如果沒有任何二進制文件被指定,連接器不會產生任何輸出,并給出信息:"缺少輸入文件."
如果連接器不能識別目標文件的格式,它會假設這些只是連接腳本.以這種方式指定的腳本增加了連接用的主連
接腳本的內容(主連接腳本即缺省連接腳本或使用'-T'指定的腳本). 這個特性可以允許連接器連接一些文件,
它們看上去既像目標文件,又像檔案文件,但實際上只是定義了一些符號值,或者使用'INPUT'或'GROUP'來載入其
它的目標文件.需要注意的是,用這種方式指定一個腳本只是增加了主連接腳本的內容;要完全替換掉主連接腳本
,需要使用'-T'.
對于名稱是單個字符的選項,選項參數必須緊跟在選項字母后面,中間不留空,或者也可留有一個空格.
對于名稱是多個字符的選項,選項前可以有一個或兩個破折號;比如,'-trace-symbol'和`--trace-symbol'是等價
的. 注意,對于這條規則有一個例外.那些以小寫字母'o'開頭的多字符選項前面只能是兩個破折號,這是為了避免
跟選項'-o'混淆. 比如'-omagic'把輸出文件的名字定為'magic',而'--omagic'在輸出文件中設置NMAGIC標志.
多字符選項的參數必須跟選項名間以一個等于號分開,或者以一個空格分開.比如:`--trace-symbol foo'和
`--trace-symbol=foo'是等價的. 多字符選項的名字唯一縮寫符也是可以被接受的.
注意,如果連接器通過被編譯器驅動來間接引用(比如gcc), 那所有的連接器命令行選項前必須加上前綴'-Wl'
(或者能被特定編譯器驅動接受的其他前綴),就像下面這樣:
gcc -Wl,--startgroup foo.o bar.o -Wl,--endgroup
這很重要,因為否則的話,編譯器驅動程序會默認丟掉這些連接選項,產生一個錯誤的連接.
下面是關于被GNU連接器接受的常用命令行開關的一個列表:
`-aKEYWORD'
這個選項在HP/UX兼容系統上被支持. 參數KEYWORD必須是下面字符串中的一個:`archive',
`shared', or `default'. `-aarchive'在功能上跟`-Bstatic'相同,而另外兩個關鍵字功能上跟
`-Bdynamic'相同. 這個選項可被多次使用.
`-AARCHITECTURE'
`--architecture=ARCHITECTURE'
在最近發行版本的'ld'中,這個選項只在Intel 960系列架構上有用. 在那種'ld'配置中,參數
ARCHITECTURE確定960系列的某一特定架構,啟用某些安全措施,并修改檔案庫的搜索路徑.
將來的'ld'發行版可能為其它架構系列支持相似的功能.
`-b INPUT-formAT'
`--format=INPUT-formAT'
'ld'可以被配置為支持多于一種的目標文件.如果你的'ld'以這種方式被配置,你可以使用'-b'選
項為輸入目標文件指定二進制格式. 就算'ld'被配置為支持可選目標格式,你不必經常指定這一項,
因為'ld'被配置為在每一臺機子上把最常用的格式作為默認輸入格式. INPUT-formAT是一個字符串,
你可能在連接一個不常用的二進制格式文件時需要這個參數.你也可使用'-b'來顯式切換格式(在連接
不同格式的目標文件時),方法是在每一組特定格式的目標前使用'-b INPUT-formAT'.
缺省的格式是從環境變量'GNUTARGET'中得到的.你也可以從一個腳本中定義輸入格式,使用的命令是
'TARGET'.
`-c MRI-COMMANDFILE'
`--mri-script=MRI-COMMANDFILE'
為了跟MRI生產的連接器兼容,'ld'接受另一種用受限命令語言寫成的腳本文件,通過選項'-c'引入MRI
腳本文件;使用'-T'選項是運行用普通'ld'腳本語言寫的連接腳本.如果MRI-CMDFILE不存在,'ld'在'-L'
指定的目錄中尋找.
`-d'
`-dc'
`-dp'
這三個選項是等價的; 多字符形式是為了跟其他連接器兼容才被支持的.它們給普通符號分配空間,即
使一個重定位輸出文件已經被指定(通過'-r'). 腳本命令`FORCE_COMMON_ALLOCATION'具有同樣的效果.
`-e ENTRY'
`--entry=ENTRY'
使用符號ENTRY作為你的程序的開始執行點,而不是使用缺省的進入點.如果沒有叫做ENTRY的符號,連接器
會企圖把ENTRY作為一個數字進行分析,并使用它作為入口地址(數字會被解釋為10進制的;你可以使用前
導的'0x'強制為16進制,或'0'作為8進制.)
`-E'
`--export-dynamic'
當創建一個動態連接的可執行程序時, 把所有的符號加到動態符號表中.動態符號表是一個符號集,這
些符號對于運行時的動態對象是可見的.
如果你不使用這個選項,動態符號表中就會只含有那些連接進來的動態對象中用到的符號
如果你使用'dlopen'來載入動態對象,它需要引用程序中的符號,那你可能需要在連接程序時用到這個
選項.
你也可以使用版本腳本來控制哪些符號應當被加到動態符號表中.
`-EB'
連接big-endian對象. 這會影響缺省輸出格式.
`-EL'
連接little-endian對象. 這會影響缺省輸出格式.
`-g'
忽略. 為了跟其它工具兼容而提供.
`-i'
執行一個增量連接(跟'-r'等同)
`-init NAME'
當創建一個ELF可執行文件或共享對象時,當可執行文件或共享對象被加載時,調用NAME, 這是通過把
DT_INIT設置成函數的地址實現的. 缺省情況下,連接器使用'_init'作為調用的函數.
`-lARCHIVE'
`--library=ARCHIVE'
增加一個檔案文件ARCHIVE到連接的文件列表中.這個選項可以被多次使用. 'ld'會為每一個指定的
ARCHIVE搜索它的路徑列表,尋找`libARCHIVE.a'
對于支持共享庫的系統, 'ld'可能還會搜索擴展名不是'.a'庫.特別的,在ELF和SunOS系統上,'ld'會
在搜索帶有'.a'擴展名的庫前搜索帶'.so'擴展名的庫.
`-M'
`--print-map'
打印一個連接位圖到標準輸出.一個連接位圖提供的關于連接的信息有如下一些:
* 目標文件和符號被映射到內存的哪些地方.
* 普通符號如何被分配空間.
* 所有被連接進來的檔案文件,還有導致檔案文件被包含進來的那個符號.
`-n'
`--nmagic'
關閉所有節的頁對齊,如果可能,把輸出格式標識為'NMAGIC'.
`-N'
`--omagic'
把text和data節設置為可讀寫.同時,取消數據節的頁對齊,同時,取消對共享庫的連接.如果輸出格式
支持Unix風格的magic number, 把輸出標志為'OMAGIC'.
`--no-omagic'
這個選項執行的操作大部分正好跟'-N'相反.它設置text節只讀,強制data節頁對齊. 但是,這個選項
并不開啟連接共享庫的功能. 使用'-Bdynamic'開啟這個功能.
`-o OUTPUT'
`--output=OUTPUT'
使用OUTPUT作為'ld'產生的程序的名字;如果這個選項沒有指定,缺省的輸出文件名是'a.out'.腳本命
令'OUTPUT'也可以被用來指定輸出文件的文件名.
`-O LEVEL'
如果LEVEL是一個比0大的數值, 'ld'優化輸出.這可能會明顯多占用時間,所以只有在生成最后的文件
時使用.
`-q'
`--emit-relocs'
把重定位節和內容留在完全連接后的可執行文件中. 連接分析和優化工具可能需要這些信息用來進行
正確的修改與執行. 這在大的可執行文件中有用.
這個選項目前只支持ELF平臺.
`-r'
`--relocateable'
產生可重定位的輸出, 比如,產生一個輸出文件它可再次作為'ld'的輸入.這經常被叫做"部分連接".
作為一個副作用,在支持標準Unix魔數的環境中,這個選項會把輸出文件的魔數設置為'OMAGIC'. 如
果這個選項沒有被指定,一個絕對文件就會被產生.當連接C++程序時,這個選項就不會解析構造函數的
引用;要解析,必須使用'-Ur'
如果輸入文件跟輸出文件的格式不同,只有在輸入文件不含有重定位信息的時候部分連接才被支持.輸
出格式不同的時候會有更多的限制.比如,有些'a.out'的格式在輸入文件是其他格式的時候完全不支
持部分連接.
這個選項跟'-i'等效.
`-R FILENAME'
`--just-symbols=FILENAME'
從FILENAME中讀取符號名跟它們的值,但不重位這個文件,或者根本不把它包含在輸出文件中.這就允
許你的輸出文件引用其它程序中定義的絕對內存地址.你可以多次使用這個選項.
為了跟其他ELF連接器兼容,如果'-R'選項后面跟有一個目錄名,而不是一個文件名,它會被處理成
'-rpath'選項.
`-s'
`--strip-all'
忽略輸出文件中所有的符號信息.
`-S'
`--strip-debug'
忽略輸出文件中所有的調試符號信息(但不是所有符號).
`-t'
`--trace'
打印'ld'處理的所有輸入文件的名字.
`-T SCRIPTFILE'
`--script=SCRIPTFILE'
把SCRIPTFILE作為連接腳本使用. 這個腳本會替代'ld'的缺省連接腳本(而不是增加它的內容),所以
命令文件必須指定所有需要的東西以精確描述輸出文件. 如果SCRIPTFILE在當前目錄下不存在,'ld'
會在'-L'選項指定的所有目錄下去尋找.多個'-T'選項會使內容累積.
`-u SYMBOL'
`--undefined=SYMBOL'
強制SYMBOL在輸出文件中作為一個無定義的符號被輸入.這樣做會有一些效果,比如,會引發從標準庫
中連接更多的模塊. '-u'可以以不同的參數反復使用,以輸入多個無定義的符號.這個選項跟連接腳
本命令中的'EXTERN'是等效的.
`-Ur'
對于不是C++的程序,這個選項跟'-r'是等效的: 它產生可重定位的輸出,比如,一個輸出文件它可以再
次作為'ld'的輸入. 當連接C++程序時,'-Ur'解析構造函數的引用,跟'-r'不同. 但如果在一些用'-Ur'
連接過的文件上再次使用'-Ur',它不會工作,因為一旦構造函數表被建立,它不能被添加內容.請只在
最后一遍連接的時候使用'-Ur', 對其它的,只使用'-r'.
`--unique[=SECTION]'
對于所有匹配SECTION的輸入節,在輸出文件中都各自創建單獨的節,或者,如果可選的通配符SECTION
參數丟失了,為每一個孤兒輸入節創建一個輸出節. 一個孤兒節是一個連接腳本中沒有指定的節.你
可以在命令行上多次使用這個選項; 它阻止對同名輸入節的合并,在連接腳本中重載輸出節分配.
`-v'
`--version'
`-V'
顯示'ld'的版本. '-V'選項同時會列出支持的模擬器.
`-x'
`--discard-all'
刪除所有的本地符號.
`-X'
`--discard-locals'
刪除所有的臨時本地符號.對于大多數目標平臺,就是所有的名字以'L'開頭的本地符號.
`-y SYMBOL'
`--trace-symbol=SYMBOL'
打印出所有SYMBOL出現的被連接文件的名字. 這個選項可以被多次使用. 在很多系統中,這在預先確定底
線時很有必要.
當你擁有一個未定義的符號,但不知道這個引用出自哪里的時候,這個選項很有用.
`-Y PATH'
為缺省的庫搜索路徑增加一條路徑.這個選項是為了跟Solaris兼容.
`-z KEYWORD'
能被識別的關鍵字包括'initfirst', 'interpose', 'loadfltr',`nodefaultlib', `nodelete',
`nodlopen', `nodump', `now', `origin',`combreloc', `nocombreloc' and `nocopyreloc'. 為了跟
Solaris兼容,所有其它的關鍵字都被忽略. 'initfirst'標志一個對象,使它在運行時,在所有其他對象之
前被初始化. 'interpose'標志一個對象,使它的符號表放在所有其他符號之前,作為主要的執行者.
'loadfltr'標志一個對象, 使它的過濾器在運行時立即被處理.'nodefaultlib'標志一個對象,使在搜索
本對象所依賴的庫時,忽略所有缺省庫搜索路徑. 'nodelete'標志一個對象,使它在運行時不會被從內存
中刪除.'nodlopen'標志一個對象,使這個對象不可以通過'dlopen'載入.'nodump'標志一個對象,使它不能
被'dldump'轉儲. 'now'標志一個對象,使它成為非懶惰運行時綁定對象. 'origin'標志一些可能含有
$ORIGIN的對象,'defs'不允許無定義符號. 'muldefs'允許重定義. 'comberloc'組合多個重定位節,重新
排布它們,讓動態符號可見. 'nocomberloc'使多個重定位節組合無效. 'nocopyreloc'使重定位拷貝后的
結果無效.
`-( ARCHIVES -)'
`--start-group ARCHIVES --end-group'
ARCHIVES應當是一個關于檔案文件的列表. 它們可以是顯式的文件名,或者'-l'選項.
這些指定的檔案文件會被多遍搜索,直到沒有新的無定義引用被創建. 通常,一個檔案文件只會被搜索一
次. 但如果這個檔案文件中的一個符號需要被用來解析一個檔案中的目標引用到的無定義的符號,而這個
符號在命令行上的后面某個檔案文件中出現, 連接器不能解析這個引用. 把這些檔案文件分組后,它們都
可被反復搜索直到所有可能的引用都被解析了為止.
使用這個選項有一個很大的運行開銷. 只有在無法避免在多個檔案文件中使用循環引用時才用它.
`--accept-unknown-input-arch'
`--no-accept-unknown-input-arch'
告訴連接器接受那些架構不能被識別的輸入文件. 但前提假設是用戶知道他們在做什么,并且是故意要連
接這些未知的輸入文件. 在版本2.14之前,這個是連接器的缺省行為. 從版本2.14以后的,缺省行為是拒
絕這類輸入文件, 所以`--accept-unknown-input-arch'選項被用來恢復舊的行為.
`-assert KEYWORD'
這個選項被忽略,只是用來跟SunOS保持兼容.
`-Bdynamic'
`-dy'
`-call_shared'
連接動態鏈接庫. 這個僅僅在支持共享庫的平臺上有用.在這些平臺上,這個選項通常是默認行為. 這個選
項的不同形式是為了跟不同的系統保持兼容. 你可以在命令行上多次使用這個選項:它影響緊隨其后的'-l'
選項的庫搜索.
`-Bgroup'
在動態節的'DT_FLAGS_1'入口上設置'DF_1_GROUP'標志.這會讓運行時連接器在處理在這個對象和它的相
關部分搜索時只在組中. '--no-undefined'是隱式的. 這個選項只在支持共享庫的ELF平臺上有用.
`-Bstatic'
`-dn'
`-non_shared'
`-static'
不連接共享庫. 這個僅僅在支持共享庫的平臺上有用. 這個選項的不同形式是為了跟不同的系統保持兼
容. 你可以在命令行上多次使用這個選項:它影響緊隨其后的'-l'選項的庫搜索.
`-Bsymbolic'
當創建一個共享庫時, 把對全局符號的引用綁定到共享庫中的定義(如果有), 通常, 一個連接共享庫的程
序重載共享庫中的定義是可能的. 這個選項只在支持共享庫的ELF平臺上有用.
`--check-sections'
`--no-check-sections'
讓連接器在節地址被分配后不要去檢查節地址是否重疊.通常,連接器會執行這種檢查,如果它發現了任何
重疊,它會產生相應的錯誤信息. 連接器知道也允許節的重疊. 缺省的行為可以使用命令行開關
`--check-sections'來恢復.
`--cref'
輸出一個交叉引用表. 如果一個連接器位圖文件被產生, 交叉引用表被打印到位圖文件. 否則, 它被打印
到標準輸出.
表的格式相當的簡單, 所以,如果需要,可以通過一個腳本很輕易地處理它. 符號是以名字被打印輸出,存
儲. 對于每一個符號,給出一個文件名列表. 如果符號被定義了, 列出的第一個文件是符號定義的所在.
接下來的文件包含符號的引用.
`--no-define-common'
這個選項限制對普通符號的地址分配. 腳本命令`INHIBIT_COMMON_ALLOCATION'具有同等的效果.
`--no-define-common'選項允許從輸出文件的類型選擇中確定對普通符號的地址分配; 否則, 一個非重定
位輸出類型強制為普通符號分配地址. 使用'--no-define-common'允許那些從共享庫中引用的普通符號只
在主程序中被分配地址. 這會消除在共享庫中的無用的副本的空間, 同時,也防止了在有多個指定了搜索
路徑的動態模塊在進行運行時符號解析時引起的混亂.
`--defsym SYMBOL=EXdivSSION'
在輸出文件中建立一個全局符號,這個符號擁有一個EXdivSSION指定的絕對地址. 你可以多次使用這個選
項定義多個符號. EXdivSSION支持一個受限形式的算術運算:你可以給出一個十六進制常數或者一個已存
在符號的名字,或者使用'+'和'-'來加或減十六進制常數或符號. 如果你需要更多的表達式,可以考慮在腳
本中使用連接器命令語言, 注意在SYMBOL,=和EXdivSSION之間不允許有空格.
`--demangle[=style]'
`--no-demangle'
這些選項控制是否在錯誤信息和其它的輸出中重組符號名. 當連接器被告知要重組, 它會試圖把符號名以
一種可讀的形式的展現: 如果符號被以目標文件格式使用,它剝去前導的下劃線,并且把C++形式的符號名
轉換成用戶可讀的名字. 不同的編譯器有不同的重組形式. 可選的重組形式參數可以被用來為你的編譯器
選擇一個相應的重組形式. 連接器會以缺省形式重組直至環境變量`COLLECT_NO_DEMANGLE'被設置. 這些
選項可以被用來重載缺省的設置.
`--dynamic-linker FILE'
設置動態連接器的名字. 這個只在產生動態連接的ELF可執行文件時有效. 缺省的動態連接器通常是正確
的; 除非你知道你在干什么,不要使用這個選項.
`--embedded-relocs'
這個選項只在連接MIPS嵌入式PIC代碼時有效, 這些代碼必須是由GNU的編譯器跟匯編器通過-membedded-pic
選項生成的. 它導致連接器產生一個表,這個表被用來在運行時重定位所有的被靜態初始化為指針值的數
據.
`--fatal-warnings'
把所有的警告視為錯誤.
`--force-exe-suffix'
確保輸出文件有一個.exe后綴.
如果一個被成功完整連接的輸出文件不帶有一個'.exe'或'.dll'后綴, 這個選項確保連接器把輸出文件
拷貝成帶有'.exe'后綴的同名文件. 這個選項在使用微軟系統來編譯未經修改的Unix的makefile時很有
用, 因為有些版本的
`--no-gc-sections'
`--gc-sections'
允許對未使用的輸入節的碎片收集. 在不支持這個選項的平臺上,被忽略. 這個選項不能跟 '-r'選項共存
也不能被用來進行動態連接. 缺省行為可以用`--no-gc-sections'進行恢復.
`--help'
在標準輸出上打印一個命令行選項概要,然后退出.
`--target-help'
打印一個所有目標平臺相關的選項的概要,然后退出.
`-Map MAPFILE'
打印一個連接位圖到文件MAPFILE中. 參閱上面關于'-M'選項的描述.
`--no-keep-memory'
'ld'通常會以速度優先于內存使用的方式優化程序,這是通過把輸入文件的符號表放在內存緩沖中實現的,
這個選項告訴'ld'以內存使用優先來優化, 盡可能的減小符號表的重讀. 這在'ld'在連接一個大文件時
超出內存限制時有用.
`--no-undefined'
`-z defs'
通常,當創建一個非符號共享庫時, 無定義的符號允許出現,并留待運行時連接器去解決. 這個選項關閉這
樣的無定義符號的使用. 開關`--no-allow-shlib-undefined'控制共享對象被連接進共享庫時的行為.
`--allow-multiple-definition'
`-z muldefs'
通常,當一個符號被定義多次時, 連接器會報告一個致命錯誤. 這些選項允許重定義并且第一個定義被使
用
`--allow-shlib-undefined'
`--no-allow-shlib-undefined'
允許(缺省)或不允許無定義符號存在于共享對象中. 這個開關的設置會重載'--no-undefined',這里只關
注共享對象. 這樣,如果'--no-undefined'被設置,但'--no-allow-shlib-undefined'未被設置, 連鎖反應
是存在于規則對象文件中的無定義的符號會引起一個錯誤,但是在共享對象中的未定義的符號會被忽略.
把`--allow-shlib-undefined'設置為缺省的原因是在連接時指定的共享對象并不一定是載入時可載入的
那個,所以,符號可能要到載入時間才被解析.
`--no-undefined-version'
通常當一個符號有一個未定義的版本時,連接器會忽略它. 這個選項不允許符號有未定義的版本,并且碰
到這種情況,會報告一個嚴重錯誤.
`--no-warn-mismatch'
通常, 如果你因為一些原因,企圖把一些不匹配的輸入文件連接起來的時候,'ld'會給出一個錯誤,可能這
些文件是因為由不同的處理器編譯. 這個選項告訴'ld'應當對這樣的錯誤默認允許. 這個選項必須小心
使用.
`--no-whole-archive'
為后面的檔案文件關閉'--whole-archive'選項的影響.
`--noinhibit-exec'
當一個可執行文件還可以使用時,就保留它. 通常,連接器如果在連接過程中遇到了錯誤,就不會產生輸出
文件;當它遇上錯誤時,它會退出而不寫輸出文件.
`-nostdlib'
僅搜索那些在命令行上顯式指定的庫路徑. 在連接腳本中(包含在命令行上指定的連接腳本)指定的庫路
徑都被忽略.
`--oformat OUTPUT-formAT'
'ld'可以被配置為支持多于一種的目標文件. 如果你的'ld'以這種方式被配置,你可以使用'--oformat'
選項來指定輸出目標文件的二進制格式.就算'ld'被配置為支持多種目標格式,你也不必指定這個項,因
為'ld'應當被配置為把最常用的輸出格式作為默認格式. OUTPUT-formAT是一個文本串,是被BFD庫支持
的一個特定格式的名字.腳本命令'OUTPUT_formAT'也可以指定輸出格式,但這個選項可以覆蓋它.
`-qmagic'
這個選項被忽略,只是為了跟Linux保持兼容.
`-Qy'
這個選項被忽略,只是為了跟SVR4保持兼容.
`--relax'
一個機器相關的選項. 只有在少數平臺上,這個選項被支持.
在某些平臺上,'--relax'選項在連接器解析程序中的地址時執行可能的全局優化, 比如松散地址模式和在輸出文件
中合成新的指令.
在某些平臺上,連接時全局優化會進行符號調試導致程序不能運行.
在不支持這個選項的平臺上,'--relax'被接受,但被忽略.
`--retain-symbols-file FILENAME'
只保留在FILENAME中列出的那些符號,丟棄所有其他的. FILENAME是一個簡單地平坦模式文件, 一個符號占一行.
這個選項在那些會逐步積累起一個大的全局符號表的系統中(比如 VxWorks)會很有用,它能有效地節約內存空間.
'--retain-symbols-file'不丟棄未定義的符號,和需要重定位的符號.
你可能在命令行上只指定'--retain-symbol-file'一次, 它覆蓋'-s'和'-S'的功能.
`-rpath DIR'
為運行時庫的搜索路徑增加一個目錄. 這個在連接帶有共享庫的ELF可執行文件時有用. '-rpath'的所有參數會被
連接起來傳遞給運行時連接器, 運行時連接器在運行時用它們定位共享對象. '-rpath'選項在定位那些在連接參數
指定的共享對象需要的共享對象時也很有用; 參閱關于'-rpath-link'選項的描述, 如果在連接一個ELF可執行文件
時不使用'-rpath'選項,那些環境變量'LD_RUN_PATH'選項就會被使用.
'-rptah'選項也可以使用在SunOS上. 缺省地,在SunOS上,連接器會從所有的'-L'選項中形成一個運行時搜索路徑.
如果使用了'-rpath'選項, 那運行時搜索路徑就只從'-rpath'選項中得到, 忽略'-L'選項. 這在使用GCC時非常有
用, 它會用上很多的'-L'選項,而這些路徑很可能就是NFS掛上去的文件系統中.
為了同ELF的連接器兼容, 如果'-R'選面后面跟有一個目錄名, 而不是一個文件名,那它也會被處理成'-rpath'選
項.
`-rpath-link DIR'
當在SunOS上使用ELF時,一個共享庫可能會用到另一個共享庫. 當'ld -share'把一個共享庫作為一個輸入文件連接
時就有可能發生這種情況.
當一個連接器在作非共享,不可重定位連接時,如果遇上這種依賴情況,它會自動定位需要的共享庫,然后把它包含在
連接中, 如果在這種情況中,它沒有被顯式包含, 那'-rpath-link'選項指定優先搜索的一組路徑名.
這個選項必須小心使用,因為它會覆蓋那些可能已經被編譯進共享庫中的搜索路徑. 在這種情況下,它就有可能使用
一個非內部的不同的搜索路徑.
連接器使用下面的搜索路徑來定位需要的共享庫:
1. 所有由'-rpath-link'選項指定的搜索路徑.
2. 所有由'-rpath'指定的搜索路徑. '-rpath'跟'-rpath_link'的不同之處在于,由'-rpath'指定的路徑被包含在可
執行文件中,并在運行時使用, 而'-rpath-link'選項僅僅在連接時起作用. 它只用于本地連接器.
3. 在一個ELF系統中, 如果'-rpath'和'rpath-link'選項沒有被使用, 會搜索環境變量'LD_RUN_PATH'的內容.它也只
對本地連接器起作用.
4. 在SunOS上, '-rpath'選項不使用, 只搜索所有由'-L'指定的目錄.
5. 對于一個本地連接器,環境變量'LD_LIBRARY_PATH'的內容被搜索.
6. 對于一個本地ELF連接器,共享庫中的`DT_RUNPATH'和`DT_RPATH'操作符會被需要它的共享庫搜索. 如果'DT_RUNPATH'
存在了, 那'DT_RPATH'就會被忽略.
7. 缺省目錄, 常規的,如'/lib'和'/usr/lib'.
8. 對于ELF系統上的本地連接器, 如果文件'/etc/ld.so.conf'存在, 這個文件中有的目錄會被搜索.
如果需要的共享庫沒有被找到, 那連接器會發出一條警告信息,并繼續執行連接.
`-shared'
`-Bshareable'
創建一個共享庫. 這個選項只在ELF, XCOFF和SunOS平臺上有用。 在SunOS上,如果'-e'選項沒有被使用,并在連接
中有未定義的符號,連接器會自動創建一個共享庫,
`--sort-common'
這個選項告訴'ld'當它把普通符號放到相應的輸出節中時按大小進行排序。排在最前面的是所有的一字節符號,然
后是所有的二字節,然后是所有的四字節, 然后是其它的。 這是為了避免因為對齊約束而在符號間產生的斷裂
`--split-by-file [SIZE]'
跟'--split-by-reloc'相似,但在SIZE達到時,為每一個輸入文件創建一個新的輸出節。如果沒有給出,SIZE缺省
地設置為1
`--split-by-reloc [COUNT]'
試圖在輸出文件中創建節外的節,這樣就沒有單個的輸出節含有多于COUNT個重定位符。這在產生巨大的用于COFF格
式的實時內核的可重定位文件時非常有用;因為COFF不能在一個節中表示多于65535個重定位。 注意,這在不支持
專有節的目標文件格式中會失敗,連接器不會把單個輸入節分割進行重分配, 所以,如果單個輸入節含有多于COUNT
個重定位符, 那一個輸出節會含有同樣多的可重定位符。COUNT缺省被設為32768.
`--stats'
計算并顯示關于連接器操作的統計信息, 比如執行時間,內存占用等.
`--traditional-format'
對于某些目標平臺, 'ld'的輸出會跟某些面有的連接器的輸出有所不同. 這個開關要求'ld'使用傳
統的格式.
比如, 在SunOS上, 'ld'會把符號串表中的兩上完全相同的入口合并起來. 這可以把一個帶有調試信息
的輸出文件的大小減小百發之三十. 不幸地是, SunOS的'dbx'程序不能讀取這個輸出的程序(gdb就沒
有問題).'--trafitinal-format'開關告訴'ld'不要把相同的入口合并起來.
`--section-start SECTIONNAME=ORG'
通過指定ORG, 指定節在輸出文件中的絕對地址. 你可以多次使用這個選項來定位多個節. ORG必須是
一個十六進制整數; 為了跟基他的連接器兼容,你可以忽略前導'0x'. 注意,在SECTIONNAME,等號,ORG
之間不允許有空格出現.
`-Tbss ORG'
`-Tdata ORG'
`-Ttext ORG'
跟-section-start同義, 不過把SECTIONNAME替換為'.bss', '.data'或'.text'.
`--dll-verbose'
`--verbose'
顯示'ld'的版本號,并列出支持的連接器模擬. 顯示哪些輸入文件能被打開,而哪些不能. 顯示連接器
使用的連接腳本.
`--version-script=VERSION-SCRIPTFILE'
指定連接器的腳本的版本名. 這個常在創建一個需要指定附加的關于版本層次的信息的共享庫時使用,
這個選項只有支持共享庫的ELF平臺上有效.
`--warn-common'
當一個普通符號跟另一個普通符號或會號定義合并起來時,警告. 類Unix連接器允許這個選項,有時比
較實用, 但是在其他的操作系統上的連接器不允許這個. 這個選項可以讓你在合并全局符號時發現某
些潛在的問題. 不幸的是,有些C庫使用這項特性,所以你可能會像在你的程序中一樣,在庫中得到一些
警告信息.
這里給出三種類型的全局符號的解釋(用C語言):
`int i = 1;'
一個定義, 它會存在于輸出文件中的已初始化數據節.
`extern int i;'
一個未定義符號,它不占用空間. 必須在另外某一處對它有一個定義,或一個普通符號
`int i;'
一個普通符號.如果對于一個變量只有(一個或多個)普通符號, 它進入輸出文件的未初始化數據域. 連
接器會把同一變量的多個普通符號合并成一個單一的符號. 如果他們有不同的大小, 它采用最大的一
個. 如果是對同一變量的定義,連接器把一個普通符號轉化為一個聲明.
'--warn-common'選項可以產生五種類型的警告. 每種警告由兩行組成: 第一行描述遇到的符號, 第二
行描述遇到的前一個具有相同名字的符號. 一個或兩個都可能成為普通符號.
1. 把一個普通符號轉化為一個引用, 因為這個符號已經有一個定義了.
FILE(SECTION): warning: common of `SYMBOL'
overridden by definition
FILE(SECTION): warning: defined here
2. 把一個普通符號轉化為一個引用,因為遇到了第二個關于符號的定義. 這跟前一種情況相同,除了符
號遇到的順序相反.
FILE(SECTION): warning: definition of `SYMBOL'
overriding common
FILE(SECTION): warning: common is here
3. 把一個普通符號跟前一個相同大小的普通符號合并.
FILE(SECTION): warning: multiple common
of `SYMBOL'
FILE(SECTION): warning: divvious common is here
4. 把一個普通符號跟前一個更大的普通符號合并.
FILE(SECTION): warning: common of `SYMBOL'
overridden by larger common
FILE(SECTION): warning: larger common is here
5. 把一個普通符號跟前一個更小的普通符號合并. 這跟前一種情況相同, 除了遇到的符號的順序不同.
FILE(SECTION): warning: common of `SYMBOL'
overriding smaller common
FILE(SECTION): warning: smaller common is here
`--warn-constructors'
如果有全局結構體被使用到了,警告. 這只對很少的一些目標文件格式有用. 對于COFF或ELF格式, 連
接器不同探測到全局結構體的使用.
`--warn-multiple-gp'
如果在輸出文件中,需要多個全局指針值,警告. 這只對特定的處理器有意義, 比如Alpha. 特別的,有
些處理器在特定的節中放入很大的常數值. 一個特殊的寄存器(全局指針)指向這個節的中間部分, 所
以通過一個基地址寄存器相關的地址模式,這個常數可以很容易地被載入. 因為這個基寄存器相關模式
的偏移值是固定的而且很小(比如,16位), 這會限制常量池的最大尺寸. 所以,一個很大的問題是,為了
能夠定位所有可能的常數,經常需要使用多個全局指針值. 這個選項在這種情況發生時產生一條警告.
`--warn-once'
對于每一個未定義符號只警告一次, 而不是在每一個用到它的模塊中警告一次.
`--warn-section-align'
如果輸出節的地址因為對齊被改變了,警告. 通常, 對齊會被輸入節設置. 如果'SECTION'命令沒有指
定節的起始地址, 地址就會被隱式改變.
`--whole-archive'
對于每一個在命令行中'--whole-archive'選項后面出現的檔案文件, 在連接中包含檔案文件中的所有
目標文件, 而不是為需要的目標文件搜索檔案文件. 這在把一個檔案文件轉化為一個共享庫時使用, 把
所有的目標放到最終的共享庫中. 這個選項可以被多次使用.
在GCC中使用這個選項需要注意兩點: 首先,GCC不知道這個選項, 所以,你必須使用'-Wl, -whole-archive'.
第二, 不要忘了在你的檔案文件列表的后面使用'-Wl, -no-whole-archive',因為GCC會把它自己的檔
案列表加到你的連接后面, 而這可能并不是你所預期的.
`--wrap SYMBOL'
對SYMBOL符號使用包裝函數. 任何未定義的對SYMBOL符號的引用會被解析成'_wrap_SYMBOL'. 而任何
未定義的對'_real_SYMBOL'的引用會被解析成SYMBOL.
這可以用來為系統函靈敏提供一個包裝. 包裝函靈敏應當被叫做'__wrap_SYMBOL'. 如果需要調用這個
函數, 那就應該調用'__real_SYMBOL'
這里是一個沒什么實用價值的例子:
void *
__wrap_malloc (int c)
{
printf ("malloc called with %ld\n", c);
return __real_malloc ?;
}
如果你使用'--wrap malloc'把這節代碼跟其他的代碼連接, 那所有的對'malloc'的調用都會調用
'__wrap_malloc'函數. 而在'__wrap_malloc'中的'__real_malloc'會調用真正的'malloc'函數.
你有可能也希望提供一個'__real_malloc'函數, 這樣,不帶有'--wrap'的連接器也會成功連接.如果
你這樣做了, 你不能把'__real_malloc'的定義跟'__wrap_malloc'放到同一個文件中;如果放在一起
匯編器會在連接器之前把調用解析成真正的'malloc'.
`--enable-new-dtags'
`--disable-new-dtags'
連接器可以在ELF中創建一個新的動態標簽. 但是舊的ELF系統可能不理解這個. 如果你指定了
'--enable-new-dtags',動態標簽會按需要被創建. 如果你指定了'--disable-new-dtags',那不會有
新的動態標簽被創建. 缺省地,新的動態標簽不會被創建. 注意這些選項只在ELF系統中有效.
i386 PE平臺的特定選項.
-----------------------------------
i386 PE連接器支持'-shared'選項, 它使輸出文件為一個動態鏈接庫(DLL),而不是一個普通的可執行文件. 在
使用這個選項的時候,你應當為輸出文件取名'*.dll',另外, 連接器完全支持標準的'*.def'文件, 這類文件可
以在連接器命令行上象一個目標文件一樣被指定(實際上, 它應當被放在它從中導出符號的那個檔案文件前面,
以保證它們象一個普通的目標文件一樣被連接進去.)
除了對所有平臺通用的那些選項外,i386 PE連接器支持一些只對i386平臺專有的命令行選面. 帶有值的選項應
當用空格或等號把它跟值分隔開.
`--add-stdcall-alias'
如果給出這個選項, 帶有標準調用后綴(@NN)的符號會被剝掉后綴后導出.
`--base-file FILE'
使用FILE作為文件名,該文件是存放用'dlltool'產生 DLL文件時所需的所有重定位符的基地址的.(這
個選面是i386 PE平臺所專有的]
`--dll'
創建一個DLL文件而不是一個常規可執行文件. 你可能在一個給出的'.def'文件中使用'-shared'或指
定'LIBRARY'.
`--enable-stdcall-fixup'
`--disable-stdcall-fixup'
如果連接器發現有符號不能解析, 它會試圖進行'失真連接',即尋找另一個定義的符號,它們只是在
符號名的格式上不同(cdecl vs stdcall),并把符號解析為找到的這個符號. 比如, 一個未定義的符
號'_foo'可能被連接到函數'_foo@12', 或者一個未定義的符號'_bar@16'可能被連接到函數'_bar'.
如果連接器這么做了, 它會打印出一條警告信息, 因為在正常情況下,這會連接失敗, 但有時,由第三
方庫產生的導入庫可能需要這個特性. 如果你指定了'--enable-stdcall-fixup', 這個特性會被完全
開啟,警告信息也不會打印出來. 如果你指定了'--disable-stdcall-fixup',這個特性被關閉,而且這
樣的錯誤匹配會被認為是個錯誤.
`--export-all-symbols'
如果給出這個選項,目標中所有由DLL建立的全局符號會被DLL導出. 注意這是缺省情況,否則沒有任何
符號被導出. 如果符號由DEF文件顯式地導出,或由函數本身的屬性隱式地導出, 缺省情況是除非選項
給出,否則不導出任何其他的符號. 注意符號`DllMain@12',`DllEntryPoint@0',
`DllMainCRTStartup@12'和`impure_ptr'不會自動被導出.而且,由其他的DLL導入的符號也不會被再
次導出, 還有指定DLL內部布局的符號,比如那些以'_head_'開頭,或者以'_iname'結尾的符號也不會
被導出.還有,'libgcc','libstd++','libmingw32'或'crtX.o'中的符號也不會被導出. ......
環境變量
=====================
你可以通過環境變量`GNUTARGET', `LDEMULATION'和`COLLECT_NO_DEMANGLE'改變'ld'的行為.
`GNUTARGET'在你沒有使用'-b'(或者它的同義詞'--format')的時候,決定輸入文件的格式. 它的值應當是BFD
中關于輸入格式的一個名字. 如果環境中沒有'GNUTARGET'變量, 'ld'使用目標平臺的缺省格式. 如果
'GNUTARGET'被設為'default', 那BFD就會通過檢查二進制的輸入文件來找到輸入格式; 這個方法通常會成功,
但會有潛在的不明確性, 因為沒有辦法保證指定一個目標文件格式的魔數總是唯一的. 但是, 在每一個系統上
的BFD配置程序會把這個系統的常規格式放在搜索列表的首位, 所以不明確性可以通過這種慣列來解決.
`LDEMULATION'在你沒有使用'-m'選項的時候決定缺省的模擬器. 模擬器可以影響到連接器行為的很多方面,
特別是連接器的缺省連接腳本. 你可以通過'--verbose'或'-V'選項列出所有可用的模擬器. 如果'-m'選項沒
有使用, 而且`LDEMULATION'環境變量沒有定義, 缺省的模擬器跟連接器如何被配置有關.
一般地,連接器缺省狀況下會重構符號.但是,如果在環境中設置了`COLLECT_NO_DEMANGLE', 那缺省狀態下就不
會重構符號.這個環境變量在GCC的連接包裝程序中會以相似的方式被使用. 這個缺省的行為可以被'--demangle'
或'--no-demangle'選項覆蓋.
連接腳本
**************
每個連接都被一個'連接腳本'所控制. 這個腳本是用連接命令語言書寫的.
連接腳本的一個主要目的是描述輸入文件中的節如何被映射到輸出文件中,并控制輸出文件的內存排布. 幾乎
所有的連接腳本只做這兩件事情. 但是,在需要的時候,連接器腳本還可以指示連接器執行很多其他的操作.這
通過下面描述的命令實現.
連接器總是使用連接器腳本的.如果你自己不提供, 連接器會使用一個缺省的腳本,這個腳本是被編譯進連接器
可執行文件的. 你可以使用'--verbose'命令行選項來顯示缺省的連接器腳本的內容. 某些命令行選項,比如
'-r'或'-N', 會影響缺省的連接腳本.
你可以過使用'-T'命令行選項來提供你自己的連接腳本. 當你這么做的時候, 你的連接腳本會替換缺省的連
接腳本.
你也可以通過把連接腳本作為一個連接器的輸入文件來隱式地使用它,就象它們是一個被連接的文件一樣.
基本的連接腳本的概念
============================
我們需要定義一些基本的概念與詞匯以描述連接腳本語言.
連接器把多個輸入文件合并成單個輸出文件. 輸出文件和輸入文件都以一種叫做'目標文件格式'的數據格式形
式存在. 每一個文件被叫做'目標文件'. 輸出文件經常被叫做'可執行文件',但是由于需要,我們也把它叫做目
標文件. 每一個目標文件中,在其它東西之間,有一個節列表.我們有時把輸入文件的節叫做輸入節; 相似的,輸
出文件中的一個節經常被叫做輸出節.
一個目標文件中的每一個節都有一個名字和一個大小尺寸. 大多數節還有一個相關的數據塊, 稱為節內容. 某
一個節可能被標式詎'loadable',含義是在輸出文件被執行時,這個節應當被載入到內存中去. 一個沒有內容的
節可能是'allocatable', 含義是內存中必須為這個節開辟一塊空間,但是沒有實際的內容載入到這里(在某些
情況下,這塊內存必須被標式詎零). 一個既不是loadable也不是allocatable的節一般含有一些調試信息.
每一個loadable或allocatable的輸出節有兩個地址. 第一個是'VMA'或稱為虛擬內存地址. 這是當輸出文件運
行時節所擁有的地址. 第二個是"LMA', 或稱為載入內存地址. 這個節即將要載入的內存地址. 這大多數情況下
這兩個地址是相同的. 它們兩個有可能不同的一個例子是當一個數據節在ROM中時, 當程序啟動時,被拷貝到RAM
中(這個技術經常被用在基于ROM的系統中進行全局變量的初始化). 在這種情況下, ROM地址就是LMA, 而RAM地
址就是VMA.
你可以通過使用帶有'-h'選項的'objdump'來察看目標文件中的節.
每一個目標文件還有一個關于符號的列表, 被稱為'符號表'. 一個符號可能是定義過了的,也可能是未定義的.
每一個符號有一個名字, 而且每一個定義的符號有一個地址. 如果你把一個C/C++程序編譯為一個目標文件,對
于每一個定義的函數和全局或靜態變量,你為得到一個定義的符號. 每一個在輸入文件中只是一個引用而未定義
的函數或全局變量會變成一個未定義的符號.
你可以使用'nm'程序來看一個目標文件中的符號, 或者使用'objdump'程序帶有'-t'選項.
連接腳本的格式
====================
連接腳本是文本文件.
你寫了一系列的命令作為一個連接腳本. 每一個命令是一個帶有參數的關鍵字,或者是一個對符號的賦值. 你可
以用分號分隔命令. 空格一般被忽略.
文件名或格式名之類的字符串一般可以被直接鍵入. 如果文件名含有特殊字符,比如一般作為分隔文件名用的逗
號, 你可以把文件名放到雙引號中. 文件名中間無法使用雙引號.
你可以象在C語言中一樣,在連接腳本中使用注釋, 用'/*'和'*/'隔開. 就像在C中,注釋在語法上等同于空格.
簡單的連接腳本示例
============================
許多腳本是相當的簡單的.
可能的最簡單的腳本只含有一個命令: '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'節之間創建一個小的缺口.
就這樣,這是一個簡單但完整的連接腳本.
簡單的連接腳本命令.
=============================
在本章中,我們會描述一些簡單的腳本命令.
設置入口點.
-----------------------
在運行一個程序時第一個被執行到的指令稱為"入口點". 你可以使用'ENTRY'連接腳本命令來設置入口點.參數
是一個符號名:
ENTRY(SYMBOL)
有多種不同的方法來設置入口點.連接器會通過按順序嘗試以下的方法來設置入口點, 如果成功了,就會停止.
* `-e'入口命令行選項;
* 連接腳本中的`ENTRY(SYMBOL)'命令;
* 如果定義了start, 就使用start的值;
* 如果存在,就使用'.text'節的首地址;
* 地址`0'.
處理文件的命令.
---------------------------
有幾個處理文件的連接腳本命令.
`INCLUDE FILENAME'
在當前點包含連接腳本文件FILENAME. 在當前路徑下或用'-L'選項指定的所有路徑下搜索這個文件,
你可以嵌套使用'INCLUDE'達10層.
`INPUT(FILE, FILE, ...)'
`INPUT(FILE FILE ...)'
'INPUT'命令指示連接器在連接時包含文件, 就像它們是在命令行上指定的一樣.
比如,如果你在連接的時候總是要包含文件'subr.o',但是你對每次連接時要在命令行上輸入感到厭煩
, 你就可以在你的連接腳本中輸入'INPUT (subr.o).
事實上,如果你喜歡,你可以把你所有的輸入文件列在連接腳本中, 然后在連接的時候什么也不需要,
只要一個'-T'選項就夠了.
在一個'系統根前綴'被配置的情況下, 一個文件名如果以'/'字符打頭, 并且腳本也存放在系統根
前綴的某個子目錄下, 文件名就會被在系統根前綴下搜索. 否則連接器就會企圖打開當前目錄下的文
件. 如果沒有發現, 連接器會通過檔案庫搜索路徑進行搜索.
如果你使用了'INPUT (-lFILE)', 'ld'會把文件名轉換為'libFILE.a', 就象命令行參數'-l'一樣.
當你在一個隱式連接腳本中使用'INPUT'命令的時候, 文件就會在連接時連接腳本文件被包含的點上
被包含進來. 這會影響到檔案搜索.
`GROUP(FILE, FILE, ...)'
`GROUP(FILE FILE ...)'
除了文件必須全是檔案文件之外, 'GROUP'命令跟'INPUT'相似, 它們會被反復搜索,直至沒有未定義
的引用被創建.
`OUTPUT(FILENAME)'
'OUTPUT'命令命名輸出文件. 在連接腳本中使用'OUTPUT(FILENAME)'命令跟在命令行中使用'-o
FILENAME'命令是完全等效的. 如果兩個都使用了, 那命令行選項優先.
你可以使用'OUTPUT'命令為輸出文件創建一個缺省的文件名,而不是常用的'a.out'.
`SEARCH_DIR(PATH)'
`SEARCH_DIR'命令給'ld'用于搜索檔案文件的路徑中再增加新的路徑. 使用`SEARCH_DIR(PATH)'跟在
命令行上使用'-L PATH'選項是完全等效的. 如果兩個都使用了, 那連接器會兩個路徑都搜索. 用命
令行選項指定的路徑首先被搜索.
`STARTUP(FILENAME)'
除了FILENAME會成為第一個被連接的輸入文件, 'STARTUP'命令跟'INPUT'命令完全相似, 就象這個文
件是在命令行上第一個被指定的文件一樣. 如果在一個系統中, 入口點總是存在于第一個文件中,那
這個就很有用.
處理目標文件格式的命令.
-----------------------------------------
有兩個處理目標文件格式的連接腳本命令.
`OUTPUT_formAT(BFDNAME)'
`OUTPUT_formAT(DEFAULT, BIG, LITTLE)'
`OUTPUT_formAT'命令為輸出文件使用的BFD格式命名. 使用`OUTPUT_formAT(BFDNAME)'跟在命令行上
使用'-oformat BFDNAME'是完全等效的. 如果兩個都使用了, 命令行選項優先.
你可在使用`OUTPUT_formAT'時帶有三個參數以使用不同的基于'-EB'和'-EL'的命令行選項的格式.
如果'-EB'和'-EL'都沒有使用, 那輸出格式會是第一個參數DEFAULT, 如果使用了'-EB',輸出格式會是
第二個參數BIG, 如果使用了'-EL', 輸出格式會是第三個參數, LITTLE.
比如, 缺省的基于MIPS ELF平臺連接腳本使用如下命令:
OUTPUT_formAT(elf32-bigmips, elf32-bigmips, elf32-littlemips)
這表示缺省的輸出文件格式是'elf32-bigmips', 但是當用戶使用'-EL'命令行選項的時候, 輸出文件就會
被以`elf32-littlemips'格式創建.
`TARGET(BFDNAME)'
'TARGET'命令在讀取輸入文件時命名BFD格式. 它會影響到后來的'INPUT'和'GROUP'命令. 這個命令跟
在命令行上使用`-b BFDNAME'相似. 如果使用了'TARGET'命令但`OUTPUT_formAT'沒有指定, 最后的
'TARGET'命令也被用來設置輸出文件的格式.
其它的連接腳本命令.
----------------------------
還有一些其它的連接腳本命令.
`ASSERT(EXP, MESSAGE)'
確保EXP不等于零,如果等于零, 連接器就會返回一個錯誤碼退出,并打印出MESSAGE.
`EXTERN(SYMBOL SYMBOL ...)'
強制SYMBOL作為一個無定義的符號輸入到輸出文件中去. 這樣做了,可能會引發從標準庫中連接一些
節外的庫. 你可以為每一個EXTERN'列出幾個符號, 而且你可以多次使用'EXTERN'. 這個命令跟'-u'
命令行選項具有相同的效果.
`FORCE_COMMON_ALLOCATION'
這個命令跟命令行選項'-d'具有相同的效果: 就算指定了一個可重定位的輸出文件('-r'),也讓'ld'
為普通符號分配空間.
`INHIBIT_COMMON_ALLOCATION'
這個命令跟命令行選項`--no-define-common'具有相同的效果: 就算是一個不可重位輸出文件, 也讓
'ld'忽略為普通符號分配的空間.
`NOCROSSREFS(SECTION SECTION ...)'
這個命令在遇到在某些特定的節之間引用的時候會產生一條錯誤信息.
在某些特定的程序中, 特別是在使用覆蓋技術的嵌入式系統中, 當一個節被載入內存時,另外一個節
就不會在內存中. 任何在兩個節之間的直接引用都會是一個錯誤. 比如, 如果節1中的代碼調用了另
一個節中的一個函數,這就會產生一個錯誤.
`NOCROSSREFS'命令帶有一個輸出節名字的列表. 如果'ld'遇到任何在這些節之間的交叉引用, 它就
會報告一個錯誤,并返回一個非零退出碼. 注意, `NOCROSSREFS'命令使用輸出節名,而不是輸入節名.
`OUTPUT_ARCH(BFDARCH)'
指定一個特定的輸出機器架構. 這個參數是BFD庫中使用的一個名字. 你可以通過使用帶有'-f'選項
的'objdump'程序來查看一個目標文件的架構.
為符號賦值.
===========================
你可以在一個連接腳本中為一個符號賦一個值. 這會把一個符號定義為一個全局符號.
簡單的賦值.
------------------
你可以使用所有的C賦值符號為一個符號賦值.
`SYMBOL = EXdivSSION ;'
`SYMBOL += EXdivSSION ;'
`SYMBOL -= EXdivSSION ;'
`SYMBOL *= EXdivSSION ;'
`SYMBOL /= EXdivSSION ;'
`SYMBOL <<= EXdivSSION ;'
`SYMBOL >>= EXdivSSION ;'
`SYMBOL &= EXdivSSION ;'
`SYMBOL |= EXdivSSION ;'
第一個情況會把SYMBOL定義為值EXdivSSION. 其它情況下, SYMBOL必須是已經定義了的, 而值會作出相應的調
整.
特殊符號名'.'表示定位計數器. 你只可以在'SECTIONS'命令中使用它.
EXdivSSION后面的分號是必須的.
表達式下面會定義.
你在寫表達式賦值的時候,可以把它們作為單獨的部分,也可以作為'SECTIONS'命令中的一個語句,或者作為
'SECTIONS'命令中輸出節描述的一個部分.
符號所在的節會被設置成表達式所在的節.
下面是一個關于在三處地方使用符號賦值的例子:
floating_point = 0;
SECTIONS
{
.text :
{
*(.text)
_etext = .;
}
_bdata = (. + 3) & ~ 3;
.data : { *(.data) }
}
在這個例子中, 符號`floating_point'被定義為零. 符號'-etext'會被定義為前面一個'.text'節尾部的地址.
而符號'_bdata'會被定義為'.text'輸出節后面的一個向上對齊到4字節邊界的一個地址值.
PROVIDE
-------
在某些情況下, 一個符號被引用到的時候只在連接腳本中定義,而不在任何一個被連接進來的目標文件中定
義. 這種做法是比較明智的. 比如, 傳統的連接器定義了一個符號'etext'. 但是, ANSI C需要用戶能夠把
'etext'作為一個函數使用而不會產生錯誤. 'PROVIDE'關鍵字可以被用來定義一個符號, 比如'etext', 這個
定義只在它被引用到的時候有效,而在它被定義的時候無效.語法是 `PROVIDE(SYMBOL = EXdivSSION)'.
下面是一個關于使用'PROVIDE'定義'etext'的例子:
SECTIONS
{
.text :
{
*(.text)
_etext = .;
PROVIDE(etext = .);
}
}
在這個例子中, 如果程序定義了一個'_etext'(帶有一個前導下劃線), 連接器會給出一個重定義錯誤. 如果,
程序定義了一個'etext'(不帶前導下劃線), 連接器會默認使用程序中的定義. 如果程序引用了'etext'但不
定義它, 連接器會使用連接腳本中的定義.
SECTIONS命令
================
'SECTIONS'命令告訴連接器如何把輸入節映射到輸出節, 并如何把輸出節放入到內存中.
'SECTIONS'命令的格式如下:
SECTIONS
{
SECTIONS-COMMAND
SECTIONS-COMMAND
...
}
每一個SECTIONS-COMMAND可能是如下的一種:
* 一個'ENTRY'命令.
* 一個符號賦值.
* 一個輸出節描述.
* 一個重疊描述.
'ENTRY'命令和符號賦值在'SECTIONS'命令中是允許的, 這是為了方便在這些命令中使用定位計數器. 這也可
以讓連接腳本更容易理解, 因為你可以在更有意義的地方使用這些命令來控制輸出文件的布局.
輸出節描述和重疊描述在下面描述.
如果你在連接腳本中不使用'SECTIONS'命令, 連接器會按在輸入文件中遇到的節的順序把每一個輸入節放到同
名的輸出節中. 如果所有的輸入節都在第一個文件中存在,那輸出文件中的節的順序會匹配第一個輸入文件中
的節的順序. 第一個節會在地址零處.
輸出節描述
--------------------------
一個完整的輸出節的描述應該是這個樣子的:
SECTION [ADDRESS] [(TYPE)] : [AT(LMA)]
{
OUTPUT-SECTION-COMMAND
OUTPUT-SECTION-COMMAND
...
} [>REGION] [AT>LMA_REGION] [:PHDR :PHDR ...] [=FILLEXP]
大多數輸出節不使用這里的可選節屬性.
SECTION邊上的空格是必須的, 所以節名是明確的. 冒號跟花括號也是必須的. 斷行和其他的空格是可選的.
每一個OUTPUT-SECTION-COMMAND可能是如下的情況:
* 一個符號賦值.
* 一個輸入節描述.
* 直接包含的數據值.
* 一個特定的輸出節關鍵字.
輸出節名.
-------------------
輸出節的名字是SECTION. SECTION必須滿足你的輸出格式的約束. 在一個只支持限制數量的節的格式中,比如
'a.out',這個名字必須是格式支持的節名中的一個(比如, 'a.out'只允許'.text', '.data'或'.bss').如果
輸出格式支持任意數量的節, 但是只支持數字,而沒有名字(就像Oasys中的情況), 名字應當以一個雙引號中的
數值串的形式提供.一個節名可以由任意數量的字符組成,但是一個含有任意非常用字符(比如逗號)的字句必須
用雙引號引起來.
輸出節描述
--------------------------
ADDRESS是關于輸出節中VMS的一個表達式. 如果你不提供ADDRESS, 連接器會基于REGION(如果存在)設置它,或
者基于定位計數器的當前值.
如果你提供了ADDRESS, 那輸出節的地址會被精確地設為這個值. 如果你既不提供ADDRESS也不提供REGION, 那
輸出節的地址會被設為當前的定位計數器向上對齊到輸出節需要的對齊邊界的值. 輸出節的對齊要求是所有輸
入節中含有的對齊要求中最嚴格的一個.
比如:
.text . : { *(.text) }
和
.text : { *(.text) }
有細微的不同. 第一個會把'.text'輸出節的地址設為當前定位計數器的值. 第二個會把它設為定位計數器的
當前值向上對齊到'.text'輸入節中對齊要求最嚴格的一個邊界.
ADDRESS可以是任意表達式; 比如,如果你需要把節對齊對0x10字節邊界,這樣就可以讓低四字節的節地址值為
零, 你可以這樣做:
.text ALIGN(0x10) : { *(.text) }
這個語句可以正常工作,因為'ALIGN'返回當前的定位計數器,并向上對齊到指定的值.
指定一個節的地址會改變定位計數器的值.
輸入節描述
-------------------------
最常用的輸出節命令是輸入節描述.
輸入節描述是最基本的連接腳本操作. 你使用輸出節來告訴連接器在內存中如何布局你的程序. 你使用輸入節
來告訴連接器如何把輸入文件映射到你的內存中.
輸入節基礎
---------------------------
一個輸入節描述由一個文件名后跟有可選的括號中的節名列表組成.
文件名和節名可以通配符形式出現, 這個我們以后再介紹.
最常用的輸入節描述是包含在輸出節中的所有具有特定名字的輸入節. 比如, 包含所有輸入'.text'節,你可以
這樣寫:
*(.text)
這里,'*'是一個通配符,匹配所有的文件名. 為把一部分文件排除在匹配的名字通配符之外, EXCLUDE_FILE可
以用來匹配所有的除了在EXCLUDE_FILE列表中指定的文件.比如:
(*(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors))
會讓除了`crtend.o'文件和`otherfile.o'文件之外的所有的文件中的所有的.ctors節被包含進來.
有兩種方法包含多于一個的節:
*(.text .rdata)
*(.text) *(.rdata)
上面兩句的區別在于'.text'和'.rdata'輸入節的輸出節中出現的順序不同. 在第一個例子中, 兩種節會交替
出現,并以連接器的輸入順序排布. 在第二個例子中,所有的'.text'輸入節會先出現,然后是所有的'.rdata'節.
你可以指定文件名,以從一個特定的文件中包含節. 如果一個或多個你的文件含有特殊的數據在內存中需要特
殊的定位,你可以這樣做. 比如:
data.o(.data)
如果你使用一個不帶有節列表的文件名, 那輸入文件中的所有的節會被包含到輸出節中. 通常不會這樣做, 但
是在某些場合下這個可能非常有用. 比如:
data.o
當你使用一個不含有任何通配符的文件名時, 連接器首先會查看你是否在連接命令行上指定了文件名或者在
'INPUT'命令中. 如果你沒有, 連接器會試圖把這個文件作為一個輸入文件打開, 就像它在命令行上出現一樣.
注意這跟'INPUT'命令不一樣, 因為連接器會在檔案搜索路徑中搜索文件.
輸入節通配符
---------------------------------
在一個輸入節描述中, 文件名或者節名,或者兩者同時都可以是通配符形式.
文件名通配符'*'在很多例子中都可以看到,這是一個簡單的文件名通配符形式.
通配符形式跟Unix Shell中使用的一樣.
`*'
匹配任意數量的字符.
`?'
匹配單個字符.
`[CHARS]'
匹配CHARS中的任意單個字符; 字符'-'可以被用來指定字符的方訌, 比如[a-z]匹配任意小字字符.
`\'
轉義其后的字符.
當一個文件名跟一個通配符匹配時, 通配符字符不會匹配一個'/'字符(在UNIX系統中用來分隔目錄名), 一個
含有單個'*'字符的形式是個例外; 它總是匹配任意文件名, 不管它是否含有'/'. 在一個節名中, 通配符字
符會匹配'/'字符.
文件名通配符只匹配那些在命令行或在'INPUT'命令上顯式指定的文件. 連接器不會通過搜索目錄來展開通配
符.
如果一個文件名匹配多于一個通配符, 或者如果一個文件名顯式出現同時又匹配了一個通配符, 連接器會使用
第一次匹配到的連接腳本. 比如, 下面的輸入節描述序列很可能就是錯誤的,因為'data.o'規則沒有被使用:
.data : { *(.data) }
.data1 : { data.o(.data) }
通常, 連接器會把匹配通配符的文件和節按在連接中被看到的順序放置. 你可以通過'SORT'關鍵字改變它, 它
出現在括號中的通配符之前(比如, 'SORT(.text*)'). 當'SORT'關鍵字被使用時, 連接器會在把文件和節放到
輸出文件中之前按名字順序重新排列它們.
如果你對于輸入節被放置到哪里去了感到很困惑, 那可以使用'-M'連接選項來產生一個位圖文件. 位圖文件會
精確顯示輸入節是如何被映射到輸出節中的.
這
個例子顯示了通配符是如何被用來區分文件的. 這個連接腳本指示連接器把所有的'.text'節放到'.text'中,
把所有的'.bss'節放到'.bss'. 連接器會把所有的來自文件名以一個大寫字母開始的文件中的'.data'節放進'.DATA'節中;
對于所有其他文件, 連接器會把'.data'節放進'.data'節中.
SECTIONS {
.text : { *(.text) }
.DATA : { [A-Z]*(.data) }
.data : { *(.data) }
.bss : { *(.bss) }
}
輸入節中的普通符號.
-----------------------------------
對于普通符號,需要一個特殊的標識, 因為在很多目標格式中, 普通符號沒有一個特定的輸入節. 連接器會把
普通符號處理成好像它們在一個叫做'COMMON'的節中.
你可能像使用帶有其他輸入節的文件名一樣使用帶有'COMMON'節的文件名。你可以通過這個把來自一個特定輸
入文件的普通符號放入一個節中,同時把來自其它輸入文件的普通符號放入另一個節中。
在大多數情況下,輸入文件中的普通符號會被放到輸出文件的'.bss'節中。比如:
.bss { *(.bss) *(COMMON) }
有些目標文件格式具有多于一個的普通符號。比如,MIPS ELF目標文件格式區分標準普通符號和小普通符號。
在這種情況下,連接器會為其他類型的普通符號使用一個不同的特殊節名。 在MIPS ELF的情況中, 連接器
為標準普通符號使用'COMMON',并且為小普通符號使用'.common'。這就允許你把不同類型的普通符號映射到
內存的不同位置。
在一些老的連接腳本上,你有時會看到'[COMMON]'。這個符號現在已經過時了, 它等效于'*(COMMON)'。
輸入節和垃圾收集
---------------------------------------
當連接時垃圾收集正在使用中時('--gc-sections'),這在標識那些不應該被排除在外的節時非常有用。這
是通過在輸入節的通配符入口外面加上'KEEP()'實現的,比如'KEEP(*(.init))'或者'KEEP(SORT(*)(.sorts))
'。
輸入節示例
---------------------
接下來的例子是一個完整的連接腳本。它告訴連接器去讀取文件'all.o'中的所有節,并把它們放到輸出節
'outputa'的開始位置處, 該輸出節是從位置'0x10000'處開始的。 從文件'foo.o'中來的所有節'.input1'
在同一個輸出節中緊密排列。 從文件'foo.o'中來的所有節'.input2'全部放入到輸出節'outputb'中,后面
跟上從'foo1.o'中來的節'.input1'。來自所有文件的所有余下的'.input1'和'.input2'節被寫入到輸出節
'outputc'中。
SECTIONS {
outputa 0x10000 :
{
all.o
foo.o (.input1)
}
outputb :
{
foo.o (.input2)
foo1.o (.input1)
}
outputc :
{
*(.input1)
*(.input2)
}
}
輸出節數據
-------------------
你可以通過使用輸出節命令'BYTE','SHORT','LONG','QUAD',或者'SQUAD'在輸出節中顯式包含幾個字節的數據
每一個關鍵字后面都跟上一個圓括號中的要存入的值。表達式的值被存在當前的定位計數器的值處。
‘BYTE’,‘SHORT’,‘LONG’‘QUAD’命令分別存儲一個,兩個,四個,八個字節。存入字節后,定位計
數器的值加上被存入的字節數。
比如,下面的命令會存入一字節的內容1,后面跟上四字節,其內容是符號'addr'的值。
BYTE(1)
LONG(addr)
當使用64位系統時,‘QUAD’和‘SQUAD’是相同的;它們都會存儲8字節,或者說是64位的值。而如果軟硬件
系統都是32位的,一個表達式就會被作為32位計算。在這種情況下,‘QUAD’存儲一個32位值,并把它零擴展
到64位, 而‘SQUAD’會把32位值符號擴展到64位。
如果輸出文件的目標文件格式有一個顯式的endianness,它在正常的情況下,值就會被以這種endianness存儲
當一個目標文件格式沒有一個顯式的endianness時, 值就會被以第一個輸入目標文件的endianness存儲。
注意, 這些命令只在一個節描述內部才有效,而不是在它們之間, 所以,下面的代碼會使連接器產生一個錯
誤信息:
SECTIONS { .text : { *(.text) } LONG(1) .data : { *(.data) } }
而這個才是有效的:
SECTIONS { .text : { *(.text) ; LONG(1) } .data : { *(.data) } }
你可能使用‘FILL’命令來為當前節設置填充樣式。它后面跟有一個括號中的表達式。任何未指定的節內內存
區域(比如,因為輸入節的對齊要求而造成的裂縫)會以這個表達式的值進行填充。一個'FILL'語句會覆蓋到
它本身在節定義中出現的位置后面的所有內存區域;通過引入多個‘FILL’語句,你可以在輸出節的不同位置
擁有不同的填充樣式。
這個例子顯示如何在未被指定的內存區域填充'0x90':
FILL(0x90909090)
‘FILL’命令跟輸出節的‘=FILLEXP’屬性相似,但它只影響到節內跟在‘FILL’命令后面的部分,而不是
整個節。如果兩個都用到了,那‘FILL’命令優先。
輸出節關鍵字
-----------------------
有兩個關鍵字作為輸出節命令的形式出現。
`CREATE_OBJECT_SYMBOLS'
這個命令告訴連接器為每一個輸入文件創建一個符號。而符號的名字正好就是相關輸入文件的名字。
而每一個符號的節就是`CREATE_OBJECT_SYMBOLS'命令出現的那個節。
這個命令一直是a.out目標文件格式特有的。 它一般不為其它的目標文件格式所使用。
`CONSTRUCTORS'
當使用a.out目標文件格式進行連接的時候, 連接器使用一組不常用的結構以支持C++的全局構造函
數和析構函數。當連接不支持專有節的目標文件格式時, 比如ECOFF和XCOFF,連接器會自動辯識C++
全局構造函數和析構函數的名字。對于這些目標文件格式,‘CONSTRUCTORS’命令告訴連接器把構造
函數信息放到‘CONSTRUCTORS’命令出現的那個輸出節中。對于其它目標文件格式,‘CONSTRUCTORS’
命令被忽略。
符號`__CTOR_LIST__'標識全局構造函數的開始,而符號`__DTOR_LIST'標識結束。這個列表的第一個
WORD是入口的數量,緊跟在后面的是每一個構造函數和析構函數的地址,再然后是一個零WORD。編譯
器必須安排如何實際運行代碼。對于這些目標文件格式,GNU C++通常從一個`__main'子程序中調用
構造函數,而對`__main'的調用自動被插入到`main'的啟動代碼中。GNU C++通常使用'atexit'運行
析構函數,或者直接從函數'exit'中運行。
對于像‘COFF’或‘ELF’這樣支持專有節名的目標文件格式,GNU C++通常會把全局構造函數與析構
函數的地址值放到'.ctors'和'.dtors'節中。把下面的代碼序列放到你的連接腳本中去,這樣會構建
出GNU C++運行時代碼希望見到的表類型。
__CTOR_LIST__ = .;
LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
*(.ctors)
LONG(0)
__CTOR_END__ = .;
__DTOR_LIST__ = .;
LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
*(.dtors)
LONG(0)
__DTOR_END__ = .;
如果你正使用GNU C++支持來進行優先初始化,那它提供一些可以控制全局構造函數運行順序的功能,
你必須在連接時給構造函數排好序以保證它們以正確的順序被執行。當使用'CONSTRUCTORS'命令時,
替代為`SORT(CONSTRUCTORS)'。當使用'.ctors'和'dtors'節時,使用`*(SORT(.ctors))'和
`*(SORT(.dtors))' 而不是`*(.ctors)'和`*(.dtors)'。
通常,編譯器和連接器會自動處理這些事情,并且你不必親自關心這些事情。但是,當你正在使用
C++,并自己編寫連接腳本時,你可能就要考慮這些事情了。
輸出節的丟棄。
-------------------------
連接器不會創建那些不含有任何內容的輸出節。這是為了引用那些可能出現或不出現在任何輸入文件中的輸入
節時方便。比如:
.foo { *(.foo) }
如果至少在一個輸入文件中有'.foo'節,它才會在輸出文件中創建一個'.foo'節
如果你使用了其它的而不是一個輸入節描述作為一個輸出節命令,比如一個符號賦值,那這個輸出節總是被
創建,即使沒有匹配的輸入節也會被創建。
一個特殊的輸出節名`/DISCARD/'可以被用來丟棄輸入節。任何被分配到名為`/DISCARD/'的輸出節中的輸入
節不包含在輸出文件中。
輸出節屬性
-------------------------
上面,我們已經展示了一個完整的輸出節描述,看下去就象這樣:
SECTION [ADDRESS] [(TYPE)] : [AT(LMA)]
{
OUTPUT-SECTION-COMMAND
OUTPUT-SECTION-COMMAND
...
} [>REGION] [AT>LMA_REGION] [:PHDR :PHDR ...] [=FILLEXP]
我們已經介紹了SECTION, ADDRESS, 和OUTPUT-SECTION-COMMAND. 在這一節中,我們將介紹余下的節屬性。
輸出節類型
...................
每一個輸出節可以有一個類型。類型是一個放在括號中的關鍵字,已定義的類型如下所示:
`NOLOAD'
這個節應當被標式詎不可載入,所以當程序運行時,它不會被載入到內存中。
`DSECT'
`COPY'
`INFO'
`OVERLAY'
支持這些類型名只是為了向下兼容,它們很少使用。它們都具有相同的效果:這個節應當被標式詎不
可分配,所以當程序運行時,沒有內存為這個節分配。
連接器通常基于映射到輸出節的輸入節來設置輸出節的屬性。你可以通過使用節類型來重設這個屬性,
比如,在下面的腳本例子中,‘ROM’節被定址在內存地址零處,并且在程序運行時不需要被載入。
‘ROM’節的內容會正常出現在連接輸出文件中。
SECTIONS {
ROM 0 (NOLOAD) : { ... }
...
}
輸出節LMA
..................
每一個節有一個虛地址(VMA)和一個載入地址(LMA);出現在輸出節描述中的地址表達式設置VMS
連接器通常把LMA跟VMA設成相等。你可以通過使用‘AT’關鍵字改變這個。跟在關鍵字‘AT’后面的表達式
LMA指定節的載入地址。或者,通過`AT>LMA_REGION'表達式, 你可以為節的載入地址指定一個內存區域。
這個特性是為了便于建立ROM映像而設計的。比如,下面的連接腳本創建了三個輸出節:一個叫做‘.text’
從地址‘0x1000’處開始,一個叫‘.mdata’,盡管它的VMA是'0x2000',它會被載入到'.text'節的后面,最
后一個叫做‘.bss’是用來放置未初始化的數據的,其地址從'0x3000'處開始。符號'_data'被定義為值
'0x2000', 它表示定位計數器的值是VMA的值,而不是LMA。
SECTIONS
{
.text 0x1000 : { *(.text) _etext = . ; }
.mdata 0x2000 :
AT ( ADDR (.text) + SIZEOF (.text) )
{ _data = . ; *(.data); _edata = . ; }
.bss 0x3000 :
{ _bstart = . ; *(.bss) *(COMMON) ; _bend = . ;}
}
這個連接腳本產生的程序使用的運行時初始化代碼會包含象下面所示的一些東西,以把初始化后的數據從ROM
映像中拷貝到它的運行時地址中去。注意這節代碼是如何利用好連接腳本定義的符號的。
extern char _etext, _data, _edata, _bstart, _bend;
char *src = &_etext;
char *dst = &_data;
/* ROM has data at end of text; copy it. */
while (dst < &_edata) {
*dst++ = *src++;
}
/* Zero bss */
for (dst = &_bstart; dst< &_bend; dst++)
*dst = 0;
輸出節區域
.....................
你可以通過使用`>REGION'把一個節賦給前面已經定義的一個內存區域。
這里有一個簡單的例子:
MEMORY { rom : ORIGIN = 0x1000, LENGTH = 0x1000 }
SECTIONS { ROM : { *(.text) } >rom }
輸出節Phdr
...
|