3.15 配置外部工具程序和參數
[adie: 這篇文章原是 GCC 手冊中的一節, 3.15 是它在手冊中的編號.]
gcc 是一個(gè)驅動(dòng)式的程序. 它調用其它程序來(lái)依次進(jìn)行編譯, 匯編和鏈接. GCC 分析命令行參數, 然后決定該調用哪一個(gè)子程序, 哪些參數應該傳遞給子程序. 所有這些行為都是由 SPEC 字符串(spec strings)來(lái)控制的. 通常情況下, 每一個(gè) GCC 可以調用的子程序都對應著(zhù)一個(gè) SPEC 字符串, 不過(guò)有少數的子程序需要多個(gè) SPEC 字符串來(lái)控制他們的行為. 編譯到 GCC 中的 SPEC 字符串可以被覆蓋, 方法是使用 -specs= 命令行參數來(lái)指定一個(gè) SPEC 文件(spec file).
Spec 文件(Spec files) 就是用來(lái)配置 SPEC 字符串的. 它包含了一系列用空行分隔的指令. 指令的類(lèi)型由一行的第一個(gè)非空格字符決定, 它們可能是:
- %command
- 定義 SPEC 文件的預處理命令, 預處理命令包括以下的命令:
- %include <file>
- 搜索文件, 并將其內容插入到 SPEC 文件的當前位置.
- %include_noerr <file>
- 和 '%include' 一樣, 但是在沒(méi)有找到文件的情況下不會(huì )產(chǎn)生錯誤消息.
- %renameold_name new_name
-
- 將 SPEC 字符串 old_name 改名為 new_name.
- *[spec_name]:
- 這條命令讓編譯器創(chuàng )建,覆蓋或刪除指定名字的 SEPC 字符串. 所有在這條指令之后到下一條指令或空行之前的文本都被認為是這個(gè) SPEC 字符串的內容. 如果它的內容為空, 則這個(gè)名字將被刪除(如果該名字不存在, 則什么都不會(huì )發(fā)生). 內容不為空時(shí), 如果該名字不存在, 則會(huì )創(chuàng )建一個(gè)新的 SPEC 字符串, 如果已經(jīng)存在了, 他們的內容將會(huì )被替換. 如果內容的第一個(gè)字符為 '+', 則這些內容會(huì )被追加到原來(lái)定義的內容后面, 而不是覆蓋了.
- [suffix]:
- 創(chuàng )建一個(gè)新的 '[suffix] spec'(后綴處理) 組合. 這個(gè)指令之后到下一個(gè)指令或空行之前的行組成了這個(gè)后綴的 SPEC 字符串. 當編譯器遇到指這種后綴的輸入文件時(shí), 它會(huì )依次執行 SPEC 字符串中的指令, 這些指令指明了如何編譯那個(gè)文件. 例如:
.ZZ: z-compile -input %i
它的意思是: 任何以 '.ZZ' 結尾的輸入文件都將使用 'z-compile' 程序進(jìn)行處理, 調用的時(shí)候將會(huì )使用 -input 開(kāi)關(guān)以及 '%i' 替換后的結果(詳見(jiàn)下文) 作為命令行參數.
作為 SPEC 字符串的內容, suffix 指令后的文本還可以是下面的某一個(gè):
- @language
- 它表明這個(gè)后綴是某種已知語(yǔ)言的別名. 它和 GCC 命令行中用于指定語(yǔ)言的 -x 選項類(lèi)似. 比如:
.ZZ: @c++
這說(shuō)明 .ZZ 文件實(shí)際上是 C++ 的源代碼文件.
- #name
- 這會(huì )產(chǎn)生一條錯誤消息:
當前系統沒(méi)有安裝 name 編譯器.
GCC 已經(jīng)內建了一份巨大的后綴列表. 這條指令將后綴添加到列表的結尾處, 由于這個(gè)列表使用的時(shí)候是從結尾向后搜索的, 實(shí)際上可以使用這種技術(shù)來(lái)覆蓋之前的條目.
GCC 已經(jīng)內置了如下的 SPEC 字符串. SPEC 文件可以覆蓋他們或者創(chuàng )建新的. 注意, 某些 GCC 的實(shí)現也可能添加它們自己的 SEPC 字符串到這個(gè)列表里面.
asm 傳遞給匯編器的選項 asm_final 傳遞給匯編后處理器的選項 cpp 傳遞給 C 預處理器的選項 cc1 傳遞給 C 編譯器的選項 cc1plus 傳遞給 C++ 編譯器的選項 endfile 鏈接的最后需要包含的目標文件 link 傳遞給鏈接器的選項 lib 命令行傳遞給鏈接器的要包含的庫 libgcc 決定給鏈接器傳遞哪個(gè) GCC 支持庫 linker 設置鏈接器的名字 predefines 傳遞給 C 預處理器的宏定義 signed_char 傳遞給 CPP 的用于說(shuō)明 char 默認是否是有符號類(lèi)型的宏 startfile 一開(kāi)始就需要傳遞給鏈接器的目標文件
下面是一個(gè)簡(jiǎn)單的 SPEC 文件的例子:
%rename lib old_lib *lib: --start-group -lgcc -lc -leval1 --end-group %(old_lib)
這個(gè)例子把 'lib' 改名為 'old_lib' 然后用一個(gè)新的定義覆蓋了之前的 'lib' 定義. 新定義在包含舊的定義文本之前添加了一些額外的命令行選項.
SEPC 字符串是傳遞給相應程序的命令行選項的列表. 另外, SEPC 字符串可以包含 '%' 作為前綴的字符串來(lái)表示變量. 變量可以用來(lái)代替一串文本, 或者作為向命令行插入文本的條件. 使用這些概念可以產(chǎn)生非常復雜的命令行.
下面是所有的用于 SPEC 字符串的預定義變量. 注意,這些變量在表示文本時(shí)不會(huì )自動(dòng)在結果兩邊產(chǎn)生空格. 你可以在鏈接這些變量時(shí)使用常量字符串來(lái)一起鏈接.
- %%
- 在程序名字或參數中用于表示一個(gè) '%'.
- %i
- 用來(lái)表示當前正在處理的輸入文件名
- %b
- 表示輸入文件的基本名字. 它不包含最后一個(gè)點(diǎn)號以及之后的內容, 也不包含路徑名.
- %B
- 和 '%b' 相同, 但是它包含了后綴名(最后一個(gè)點(diǎn)號后的內容).
- %d
- 用包含或附加 '%d' 來(lái)標記一個(gè)臨時(shí)文件名, GCC 正常退出后會(huì )自動(dòng)刪除帶有這種標記的文件. 和 '%g' 不同, 這個(gè)變量不會(huì )在參數中產(chǎn)生文本內容.
- %gsuffix
- 產(chǎn)生指定后綴 suffix 的文件名, 每次編譯產(chǎn)生一個(gè). 和 '%d' 一樣, 它也會(huì )被標記為臨時(shí)文件. 為了減少拒絕服務(wù)攻擊的可能性, 即使上一次產(chǎn)生的文件名已經(jīng)知道了, 下一次產(chǎn)生的文件名也是無(wú)法預料的. 例如, '%g.s ... %g.o ... %g.s' 可能被轉換成 'ccUVUUAU.s ccXYAXZ12.o ccUVUUAU.s'. 后綴名需要可以和 '[.A-Za-z]*' 這樣的正則表達式匹配或者使用特定的 '%O'. '%O' 使用的時(shí)候假設它已經(jīng)被處理過(guò)了. 以前, '%g' 只是簡(jiǎn)單的每次出現都被替換成一個(gè)文件名, 沒(méi)有后綴的情況下使得攻擊很容易成功.
- %usuffix
- 和 '%g' 一樣, 但是每次出現就會(huì )產(chǎn)生一個(gè)新的臨時(shí)文件名, 而不是每次編譯的時(shí)候一個(gè).
- %Usuffix
- 生成上一次使用 '%usuffix' 時(shí)產(chǎn)生的文件名, 如果之前沒(méi)有過(guò), 就生成一個(gè)新的. 在沒(méi)有使用過(guò) '%usuffix' 的情況下, 它和 '%gsuffix' 一樣, 只是他們不會(huì )共享后綴名字空間. 因此, '%g.s ... %U.s ... %g.s ... %U.s' 會(huì )生成兩個(gè)不同的文件名, 一個(gè)是 '%g.s' 產(chǎn)生的, 另一個(gè)是 '%U.s'. 以前, '%U' 只是替換成上一個(gè)的 '%u' 產(chǎn)生的文件名, 不會(huì )帶任何的后綴.
- %jsuffix
- 如果存在 HOST_BIT_BUCKET, 并且它可寫(xiě), 也沒(méi)有使用 -save-temps, 這會(huì )被替換成 HOST_BIT_BUCKET 的名字. 否則, 產(chǎn)生一個(gè)臨時(shí)文件名, 和 '%u' 效果相同. 這個(gè)臨時(shí)文件并不用來(lái)在兩個(gè)進(jìn)程間通信, 只是作為垃圾進(jìn)行處理.
[adie: HOST_BIT_BUCKET 是 GCC 中配置宿主系統環(huán)境時(shí)用到一個(gè)參數. 它是由宿主系統定義的一個(gè)路徑名, 可以被當作一個(gè)文件來(lái)進(jìn)行寫(xiě)入, 但是所有寫(xiě)入的內容都會(huì )被丟棄. 這通常被稱(chēng)為 bit bucket(比特流垃圾桶) 或 null device(空設備), 在 UNIX 中它通常就是 /dev/null.
- %|suffix
- %msuffix
- 和 '%g' 類(lèi)似, 只是可以使用 -pipe (管道). 使用管道時(shí), '%|' 被替換成一個(gè)破折號, '%m' 被替換成空. 這是讓程序從標準輸入讀取數據和往標準輸出寫(xiě)數據的常用方式. 如果你需要處理更復雜的情況, 你還可以使用 '%{pipe:X}': 參考 f/lang-specs.h 中的例子.
- %.SUFFIX
- 對于匹配后綴的參數進(jìn)行替換. 后綴到空格或下一個(gè) % 結束.
- %w
- 對包含或跟隨有 '%w' 的參數標記為此次編譯的輸出文件. 這會(huì )將這個(gè)參數放到 '%o' 的參數序列里面.
- %o
- 替換所有輸出文件的名字, 并自動(dòng)在他們周?chē)胖每崭? 你仍然應該在 '%o' 的周?chē)鷷?shū)寫(xiě)空格, 否則結果將是未定義的. '%o' 用于運行鏈接器. 沒(méi)有可識別后綴的輸入文件將不會(huì )被編譯, 但是他們被包含在輸出文件里, 因此, 他們可以被鏈接.
- %O
- 為目標文件替換后綴. 注意, 當它緊接在 '%g, %u, 和 %U' 后面時(shí)會(huì )被特別處理, 因為它們需要完整的文件名. 處理的方法是假設 '%O' 已經(jīng)被處理過(guò)了, 除非 '%g, %u, 和 %U' 當前不支持使用特殊的 '%O' 后綴, 例如, '.o'.
- %p
- 替換標準的預定義宏為當前的目標類(lèi)型. 運行 cpp 程序時(shí)使用.
- %P
- 和 '%p' 類(lèi)似, 只是會(huì )在每個(gè)預定義宏的名字前后加上 '__', 除非宏的名字是以 '__' 或 '_L' 開(kāi)頭的. 其中 L 是一個(gè)大寫(xiě)字母. 這是 ISO C 所要求的.
- %I
- 貼換所有的 -iprefix(來(lái)自 GCC_EXEC_PREFIX), -isysroot(來(lái)自 TARGET_SYSTEM_ROOT), -isystem(來(lái)自 COMPILER_PATH 和 -B 選項) 以及所需的 -imultilib.
- %s
- 當前參數是某個(gè)庫或可執行文件的名字. 搜索標準的目錄列表來(lái)查找這個(gè)文件名, 如果找到了, 就用全名來(lái)進(jìn)行替換. 當前目錄被包含在掃描的目錄列表的第一位.
- %T
- 當前參數是一個(gè)連接腳本. 在庫文件的目錄列表里面搜索那個(gè)文件. 如果文件找到了, 插入一個(gè) --script 選項和文件的全路徑名到命令行中. 如果文件沒(méi)找到將會(huì )產(chǎn)生一條錯誤信息. 注意, 當前目錄不會(huì )被搜索.
- %estr
- 把 str 作為錯誤消息打印出來(lái). str 使用換行作為結束. 檢測到不一致的選項時(shí)可以使用它.
- %(name)
- 貼換名字為 name 的 SPEC 字符串內容到當前位置.
- %x{option}
- 為 '%X' 添加一個(gè)選項.
- %X
- 輸出用 -W1 或 '%x' 添加的所有鏈接器選項.