“GCC”是一种方便用户调用的程序前端驱动程序可执行文件将在后台调用其他程序,比如cc1作为其工作或ld根据给定的命令行参数。SPECS文件是纯文本用于控制“GCC”前端的默认行为。SPECS文件通常是内置的灵活性的目的,但它可以覆盖与外部版本。
GCC通过以下命令将产生一个规范文件。
gcc -dumpspecs > specs
你可以使用你选择的文本编辑器来检查specs。
在编译时使用参数 -specs=<path_to_specs_file>
或者
将specs放入"gcc -print-libgcc-file-name"给出的路径,来默认使用这个specs文件 , 调用gcc -v来查看正在使用的specs配置
其实简单来说,gcc spec文件是用来控制gcc的默认行为的,一般被放在这个目录下可以找到这个文件:
# cd /x86toolchain/lib/gcc/i686-mot-linux-gnu/3.4.3
# ls specs
也可以通过这个命令来打印gcc spec:
# gcc -dumpspecs
如果希望使用自己的specs, 可以通过-specs参数来指定:
# g++ -O2 -specs=/tmp/specs 1.c
下面是用来修改gcc默认行为的几个例子:
使用 -O2 & -fomit-frame-pointer 进行编译
By default, 对于X86 GCC, 如果指定-O2, frame pointer并不会被omit掉,通过修改specs, 我们可以在-O2的情况下,同时omit frame pointer:
*cc1_options:
%{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}} %1 %{!Q:-quiet} -dumpbase %B %{d*} %{m*} %{a*} %{c|S:%{o*:-auxbase-strip %*}%{!o*:-auxbase %b}}%{!c:%{!S:-auxbase %b}} %{g*} %{O*} %{W*&pedantic*} %{w} %{std*} %{ansi} %{v:-version} %{pg:-p} %{p} %{f*} %{undef} %{Qn:-fno-ident} %{--help:--help} %{--target-help:--target-help} %{!fsyntax-only:%{S:%W{o*}%{!o*:-o %b.s}}} %{fsyntax-only:-o %j} %{-param*} %{O2:-O2 -fomit-frame-pointer} %{O3:-O3 -fomit-frame-pointer}
*cc1plus_options:
%{O2:-O2 -fomit-frame-pointer} %{O3:-O3 -fomit-frame-pointer}使gcc默认编译gdb调试debug程序
-g
By default, GCC不会在编译object的时候使用-g选项,可以通过修改specs来对所有的objects在编译的时候加上-g选项:
*cc1:
%(cc1_cpu) %{profile:-p} -g
*cc1plus:
-g
包括目录添加到搜索路径
* cpp:部分。默认情况下:
*cpp: %{posix:-D_POSIX_SOURCE} %{mthreads:-D_MT}如果你需要将"z:\libx\include" 加入默认头文件搜索路径,那么操作如下
*cpp: %{posix:-D_POSIX_SOURCE} %{mthreads:-D_MT} -I z:/libx/include
添加lib搜索路径*link_libgcc: 默认情况下:
*link_libgcc: %D如果你需要将"z:\libx\lib" 加入默认库文件搜索路径,那么操作如下
*link_libgcc: %D -L z:/libx/lib
使Gcc默认-muclibc
修改specs文件为:
*link:
%{!static:--eh-frame-hdr} %{h*} %{version:-v} %{b} %{static:-Bstatic} %{shared:-shared} %{symbolic:-Bsymbolic} %{rdynamic:-export-dynamic} %{!dynamic-linker:-dynamic-linker %{muclibc:%{mglibc:%e-mglibc and -muclibc used together}/lib/ld-uClibc.so.0;:/lib/ld-uClibc.so.0}} -X %{mbig-endian:-EB} %{mlittle-endian:-EL} -m armelf_linux_eabi
修改gcc默认的specs文件
specs文件修改了不会保存么?想让每次使用gcc都根据自己的意思来么?
办法1:
在.bashrc脚本中添加alias arm-linux-gcc="arm-linux-gcc -specs=~/gcc_specs"
那么以后每次调用gcc的时候都会根据自定义的specs编译了
方法2:
step1:调用命令:arm-linux-gcc -v
Using built-in specs. Target: arm-none-linux-gnueabi Configured with: ../../gcc-4.2.1/configure --prefix=/usr/local/arm_linux_4.2 --target=arm-none-linux-gnueabi --enable-languages=c,c++ --with-gnu-ld --with-gnu-as --disable-nls --with-float=soft --without-newlib --disable-libmudflap --disable-libssp --disable-libgomp Thread model: posix gcc version 4.2.1
发现当前是使用 Using built-in specs. gcc内建的specs
step2:调用命令:arm-linux-gcc -print-libgcc-file-name查看当前今天libc库连接的路径,将specs文件放入这个目录(注:名称只能是specs)
step3:再次arm-linux-gcc -v,发现现在使用的已经是自己的specs了
Reading specs from /mnt/exfs/dragon/usr/arm/arm_linux_4.2/bin/../lib/gcc/arm-none-linux-gnueabi/4.2.1/specs
Target: arm-none-linux-gnueabi
Configured with: ../../gcc-4.2.1/configure --prefix=/usr/local/arm_linux_4.2 --target=arm-none-linux-gnueabi --enable-languages=c,c++ --with-gnu-ld --with-gnu-as --disable-nls --with-float=soft --without-newlib --disable-libmudflap --disable-libssp --disable-libgomp
Thread model: posix
gcc version 4.2.1
gcc 是一个驱动式的程序. 它调用其它程序来依次进行编译, 汇编和链接. GCC 分析命令行参数, 然后决定该调用哪一个子程序, 哪些参数应该传递给子程序. 所有这些行为都是由 SPEC 字符串(spec strings)来控制的. 通常情况下, 每一个 GCC 可以调用的子程序都对应着一个 SPEC 字符串, 不过有少数的子程序需要多个 SPEC 字符串来控制他们的行为. 编译到 GCC 中的 SPEC 字符串可以被覆盖, 方法是使用 -specs= 命令行参数来指定一个 SPEC 文件(spec file).
Spec 文件(Spec files) 就是用来配置 SPEC 字符串的. 它包含了一系列用空行分隔的指令. 指令的类型由一行的第一个非空格字符决定, 它们可能是:
.ZZ:
z-compile -input %i
它的意思是: 任何以 '.ZZ' 结尾的输入文件都将使用 'z-compile' 程序进行处理, 调用的时候将会使用 -input 开关以及 '%i' 替换后的结果(详见下文) 作为命令行参数.
作为 SPEC 字符串的内容, suffix 指令后的文本还可以是下面的某一个:
.ZZ:
@c++
这说明 .ZZ 文件实际上是 C++ 的源代码文件.
当前系统没有安装 name 编译器.
GCC 已经内建了一份巨大的后缀列表. 这条指令将后缀添加到列表的结尾处, 由于这个列表使用的时候是从结尾向后搜索的, 实际上可以使用这种技术来覆盖之前的条目.
GCC 已经内置了如下的 SPEC 字符串. SPEC 文件可以覆盖他们或者创建新的. 注意, 某些 GCC 的实现也可能添加它们自己的 SEPC 字符串到这个列表里面.
asm 传递给汇编器的选项
asm_final 传递给汇编后处理器的选项
cpp 传递给 C 预处理器的选项
cc1 传递给 C 编译器的选项
cc1plus 传递给 C++ 编译器的选项
endfile 链接的最后需要包含的目标文件
link 传递给链接器的选项
lib 命令行传递给链接器的要包含的库
libgcc 决定给链接器传递哪个 GCC 支持库
linker 设置链接器的名字
predefines 传递给 C 预处理器的宏定义
signed_char 传递给 CPP 的用于说明 char 默认是否是有符号类型的宏
startfile 一开始就需要传递给链接器的目标文件
下面是一个简单的 SPEC 文件的例子:
%rename lib old_lib
*lib:
--start-group -lgcc -lc -leval1 --end-group %(old_lib)
这个例子把 'lib' 改名为 'old_lib' 然后用一个新的定义覆盖了之前的 'lib' 定义. 新定义在包含旧的定义文本之前添加了一些额外的命令行选项.
SEPC 字符串是传递给相应程序的命令行选项的列表. 另外, SEPC 字符串可以包含 '%' 作为前缀的字符串来表示变量. 变量可以用来代替一串文本, 或者作为向命令行插入文本的条件. 使用这些概念可以产生非常复杂的命令行.
下面是所有的用于 SPEC 字符串的预定义变量. 注意,这些变量在表示文本时不会自动在结果两边产生空格. 你可以在链接这些变量时使用常量字符串来一起链接.
[adie: HOST_BIT_BUCKET 是 GCC 中配置宿主系统环境时用到一个参数. 它是由宿主系统定义的一个路径名, 可以被当作一个文件来进行写入, 但是所有写入的内容都会被丢弃. 这通常被称为 bit bucket(比特流垃圾桶) 或 null device(空设备), 在 UNIX 中它通常就是 /dev/null.
下面是内建支持的 SPEC 函数:
%:getenv(TOPDIR /include)
将会展开成 /path/to/top/include.
*startfile:
crt0%O%s %:if-exists(crti%O%s) crtbegin%O%s
*startfile:
crt0%O%s %:if-exists(crti%O%s) \
%:if-exists-else(crtbeginT%O%s crtbegin%O%s)
%{fgnu-runtime:%:replace-outfile(-lobjc -lobjc-gnu)}
%:remove-outfile(-lm)
%:pass-through-libs(%G %L %G)
Assembler options
=================
Use "-Wa,OPTION" to pass "OPTION" to the assembler.
它用于在使用 --target-help 的输出中分隔编译器选项和汇编器选项.
例如, 像下面这样的 SEPC 字符串:
%{.c:-foo} %{!.c:-bar} %{.c|d:-baz} %{!.c|d:-boggle}
则输入的命令行和对应的输出如下:
fred.c -foo -baz
jim.d -bar -boggle
-d fred.c -foo -baz -boggle
-d jim.d -bar -baz -boggle
在条件语句 %{S:X} 以及类似的结构中的 X 可以包含嵌套的 '%' 结构, 空格, 甚至是换行. 向以上描述的那样, 它们照常工作. 在 X 结尾处的空格会被忽略. 空格也可以出现在冒号的左边, 但不能出现在 '.' 或 '*' 与匹配单词之间.
在这些结构中, -O, -f, -m, 以及 -W 选项会被特殊处理. 如果有其他的 -O 选项或者有与 -f, -m, -W 起作用相反的选项, 之前的值将会被忽略, 除非 {S*} 中的 S 只有一个字母. 只有一个字母时所有的匹配项都可以通过.
'|' 字符出现在谓词的开头表示接下来的命令应该使用管道, 不过只有使用了 -pipe 时才能生效.
哪些选项需要使用参数是内置到 GCC 里面的. (你或许在想, 通过使用不同的编译器 sepc 来定义哪些选项需要参数是很有用的. 但是, 这在目前是无法实现的. 如果不知道哪些选项需要参数的话, GCC 无法决定哪个是需要编译的输入文件. GCC 必须要知道输入文件才能决定使用哪个编译器).
GCC 也知道, 以 -l 开头的参数会被作为编译输出文件处理, 并按照它在其他输出文件中的位置传递给链接器.
另外,一些 toolchain ,因為平台的因素,無法使用 gcc 預設好的參數內容。於是要強迫使用者必需下一堆固定的參數,例如: Android 就在其 build system 裡,為 gcc 設定一堆和平台相關的參數,像是 -mthumb。於是,若使用該 toolchain 自行開發軟體,就誓必要找出這些參數,並正確的設定為 gcc 的參數。這完全是一堆苦工。其實,只要作 toolchain 的人多用點心,使用 toolchain 其實可以不用這麼累。
一般而言,gcc 會到 /usr/lib 和 /usr/include 下找 library 、 header files 和其它一些 start files 、 end files。當我們在 build gcc 設定 --prefix=/path/to/xxx ,則 gcc 安裝到 /path/to/xxx ,也在該目錄下的 lib/ 和 include/ 找 library 和 header 。旦,若裝 gcc 移到其它目錄時, gcc 會找不到這些 library 。這時,我們可以在呼叫 gcc 時,給予 --sysrooot=/new/path/to/xxx 參數。如此,gcc 就會改以 /new/path/to/xxx 為參考目錄,去搜尋所需的幅程式和 library 等。
然而,要注意的是, ld 必需要能 support --sysroot。這必需在 configure binutils 時,給予 --sysroot=xxx 參數。xxx 可以是任意目錄,不一定是實際安裝的位置。這個參數的主要目的是使 ld 啟動 --sysroot 的功能。
--sysroot 解決了一部分問題,但有更大部分的問題是 toolchain 往往要你設定一些固定參數。例如,你會需要設定一些 header file 的目錄,並且設定一些平台有關的參數。這些設定往往又臭又長,很容易出錯。其實,這些參數可以寫在 spec file 裡,使用者就不用一再的重復指定這些參數。例如,以下是我為 Android 設定的 spec file 的內容
%rename cc1_android old_cc1_android %rename cc1plus_android old_cc1plus_android *android_root: %R/../../../../../ *android_product: generic *ccflags: %{!march=:-march=armv5te} %{!mtune=:-mtune=xscale} -mthumb \ -fno-strict-aliasing -finline-limit=64 -mthumb-interwork \ -fno-short-enums \ -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5TE__ \ -isystem %(android_root)bionic/libc/arch-arm/include/ \ -isystem %(android_root)bionic/libc/include \ -isystem %(android_root)bionic/libstdc++/include \ -isystem %(android_root)bionic/libc/kernel/common \ -isystem %(android_root)bionic/libc/kernel/arch-arm \ -isystem %(android_root)bionic/libm/include \ -isystem %(android_root)bionic/libm/include/arch/arm \ -isystem %(android_root)bionic/libthread_db/include \ -include %(android_root)system/core/include/arch/linux-arm/AndroidConfig.h \ -isystem %(android_root)system/core/include/arch/linux-arm/ *cc1_android: %(old_cc1_android) %(ccflags) *cc1plus_android: %(old_cc1plus_android) %(ccflags)
像 Android 的 header files 就散置在一堆目錄,於是 build system 就指定了一堆參數。如果你自己寫個小程式,又不使用 Android 的 build system 時,就必需自己指定這些參數。於是,有人寫了一個 perl 的小程式,作為 gcc 的 wrapper ,幫你指定一些參數。其實不用這麼麻煩,只需像上例一樣,設定一個 spec file ,在執行 gcc 時加上一個參數就能自動套用這些參數
spec file 設計的很有彈性,可以對參數列的內容進行修改,也可以加入條件性的參數。像上例 {!march=:...} 就是指定,當參數列沒有指定 -march= 的參數時,就在 cc1 的參數例加入 -march=armv5te。另外我還指定了一些巨集和 -system 指定一些 header file 的路徑。而這些路徑是相對於 %R ,也就是 --sysroot 所指定的路。例如 --sysroot=/my/path/to/the/kkk/ ,則 %(android_root)bionic/libm/include/arch/arm 就會對應到 /my/path/to/th/kkk/../../../../../bionic/libm/include/arch/arm 。
--sysroot 的預設內容,其實是可以設定成相對於 gcc 本身的位置。在 configure 時,若
configure --with-sysroot='${exec_prefix}/xxx'
則編譯出來的 gcc ,其 sysroot 就會相對於其執行路徑。如此 user 甚至不必再手動指定 --sysroot 。
有了 spec files ,我們只需設定 -specs 和 --sysroot 兩個參數就能解決所有的問題。其實在建 toolchain 時,應該再多花一點時間,幫 user 作好 spec files 。這樣的 toolchain 才方便使用。 關於 spec file 更詳細的內容,請參考 Spec Files