http://www.yuanma.org/data/2006/0625/article_955.htm
http://www.shankka.net/post/110/
当我们给
$ gcc -o foo.o foo.c
|
gcc怎么知道去哪里找foo.c里面所include的header文件,连结数据库与系统定义呢? 总共有下列来源指定gcc去那找。
- 当初在编译时指定的(在~gcc/gcc/collect2.c:locatelib()
- 写在specs内的
- 后来用-D -I -L指定的
- gcc环境变量设定(编译的时候)
- ld.so的环境变量(这是run time的时候)
在
prefix/lib/gcc-lib/xxxx-xxx-xxx-gnulibc/2.9.5/
|
里面有个很重要的specs这个档案 gcc根据这个档,做一些内定的动作。 通常系统上的specs内定装起来是在
/usr/lib/gcc-lib/xxxx-gnulibc/version/
|
specs档看起来是像这样
*asm: %{v:-V} %{Qy:} %{!Qn:-Qy} %{n} %{T} %{Ym,*} %{Yd,*} %{Wa,*:%*}
*asm_final: %|
*cpp: %(cpp_cpu) %{fPIC:-D__PIC__ -D__pic__} %{fpic:-D__PIC__ -D__pic__} %{posix: -D_POSIX_SOURCE} %{pthread:-D_REENTRANT}
*cc1: %(cc1_cpu) %{profile:-p}
*cc1plus:
*endfile: %{!shared:crtend.o%s} %{shared:crtendS.o%s} crtn.o%s
*link: -m elf_i386 %{shared:-shared} %{!shared: %{!ibcs: %{!static: %{rdynamic:-export-dynamic} %{!dynamic-linker:-dynamic-linker /lib/ld-linux.so.2}} %{static:-static}}}
*lib: %{shared: -lc --version-script libgcc.map%s} %{!shared: %{mieee-fp:-lieee} %{pthread:-lpthread} %{profile:-lc_p} %{!profile: -lc}}
*libgcc: -lgcc
*startfile: %{!shared: %{pg:gcrt1.o%s} %{!pg:%{p:gcrt1.o%s} %{!p:%{profile:gcrt1.o%s} %{!profile:crt1.o%s}}}} crti.o%s %{!shared:crtbegin.o%s} %{shared:crtbeginS.o%s}
*switches_need_spaces:
*signed_char: %{funsigned-char:-D__CHAR_UNSIGNED__}
*predefines: -D__ELF__ -Dunix -Di386 -D__i386__ -Dlinux -Asystem(posix)
*cross_compile: 0
*version: egcs-2.91.66
*multilib: . ;
*multilib_defaults:
*multilib_extra:
*multilib_matches:
*linker: collect2
*cpp_cpu_default: -D__tune_i386__
*cpp_cpu: -Asystem(unix) -Acpu(i386) -Amachine(i386) %{!ansi:-Di386} -D__i386 -D__i386__ %{march=i486:-D__i486 -D__i486__} %{march=pentium|march=i586:-D__pentium -D__pentium__ } %{march=pentiumpro|march=i686:-D__pentiumpro -D__pentiumpro__ } %{m386|mcpu=i386:-D__tune_i386__ } %{m486|mcpu=i486:-D__tune_i486__ } %{mpentium|mcpu=pentium|mcpu=i586:-D__tune_pentium__ } %{mpentiumpro|mcpu=pentiumpro|mcpu=i686:-D__tune_pentiumpro__ } %{!mcpu*:%{!m386:%{!m486:%{!mpentium*:%(cpp_cpu_default)}}}}
*cc1_cpu: %{!mcpu*: %{m386:-mcpu=i386} %{mno-486:-mcpu=i386 -march=i386} %{m486:-mcpu=i486} %{mno-386:-mcpu=i486 -march=i486} %{mno-pentium:-mcpu=i486 -march=i486} %{mpentium:-mcpu=pentium} %{mno-pentiumpro:-mcpu=pentium} %{mpentiumpro:-mcpu=pentiumpro}}
|
在shell下用这行,-E 表示只做到preprocess就好
$ echo 'main(){}' | gcc -E -v -
|
你会看到gcc去读specs檔
Reading specs from /usr/lib/gcc-lib/i386-linux/2.95.2/specs gcc version 2.95.2 20000220 (Debian GNU/Linux) /usr/lib/gcc-lib/i386-linux/2.95.2/cpp -lang-c -v -D__GNUC__=2 -D__GNUC_MINOR__=95 -D__ELF__ -Dunix -D__i386__ -Dlinux -D__ELF__ -D__unix__ -D__i386__ -D__linux__ -D__unix -D__linux -Asystem(posix) -Acpu(i386) -Amachine(i386) -Di386 -D__i386 -D__i386__ - GNU CPP version 2.95.2 20000220 (Debian GNU/Linux) (i386 Linux/ELF) #include "..." search starts here: #include <...> search starts here: /usr/local/include /usr/lib/gcc-lib/i386-linux/2.95.2/include /usr/include End of search list. The following default directories have been omitted from the search path: /usr/lib/gcc-lib/i386-linux/2.95.2/../../../../include/g++-3 /usr/lib/gcc-lib/i386-linux/2.95.2/../../../../i386-linux/include End of omitted list. # 1 "" main(){}
|
所以有内定的定义,(就是用在#if defined #ifndef #define这些东西, 如果有定义这个字符串,就去编译等等。) -Dxxxx -Dxxxx -Axxxx。 还有内定的include文件的搜寻路径
/usr/include /usr/local/include /usr/lib/gcc-lib/i386-linux/2.95.2/include /usr/lib/gcc-lib/i386-linux/2.95.2/../../../../include/g++-3 /usr/lib/gcc-lib/i386-linux/2.95.2/../../../../i386-linux/include
|
但是如果装gcc的时候,是有给定的prefix的话,那么就是
/usr/include prefix/include prefix/xxx-xxx-xxx-gnulibc/include prefix/lib/gcc-lib/xxxx-xxx-xxx-gnulibc/2.8.1/include
|
所以header file的搜寻会从-I开始然后找gcc的环境变量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH 再找上述的内定目录
函式库
当我们用到数学函式cos(),cos这个symbol,gcc并不晓它到底是什么东西, 是变量,是函式,要预留多少空间给他等等,完全没有任何讯息,你必须标头 檔要#include <math.h>,gcc才知道。而且因为specs这个档里面只有要 link -lc也就是只有libc.so这个档内的symbol会被搜寻, 像printf scanf等都在这里面,可是像cos()等就没有了, 所以函式库的选项要多加 -lm ,这时ld才会来找libm这个函式库,
编译的时候,gcc会去找-L,再找gcc的环境变量LIBRARY_PATH,再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的, gcc环境变量与pass给ld的机制在~gcc/gcc/collect2.c下找得到。 这上面只是搜寻路径而已,如果要不加-lm 也能正确的主动搜寻某个特定的lib,例如libm, 就要去在specs这个档案改一下,把math这个函式库加进自动联结函式库 之一。就不用写-lm了。
RUN TIME的时候, 如果编译时没有指定-static这个选项,其实可执行文件并不是真的可执行, 它必须在执行(run time)时需要ld.so来做最后的连结动作,建造一个可执行的 image丢到内存。如果是静态连结,编译时ld会去找libm.a的檔 。如果是动态连结去找libm.so。 所以每次有新改版程序, 或新加动态函式库如果不在原本的/etc/ld.so.conf搜寻路径中,都要把路径 加进来,然后用
ldconfig -v
|
会重建cache并且显示它所参照的函式库。Run Time时ld.so才找得到lib"执行"。 ld与ld.so不一样喔。
一些重要的程序
ld :Link Editor 连结各obj写进一个可执行档(executable)。 ldd :秀出一个执行文件用了那些动态函式库。 ld.so :Dynamic Linker, 动态连结的话,是由ld.so完成执行时期symbol的 :参照与连结。 ld-linux.so :ELF文件的动态连结,跟ld.so一样。只是ld.so是给a.out format的。 :新的glicb2的ld-linux.so.2已经跟ld.so.2结合成单一程序了。 ldconfig :根据/etc/ld.so.conf内的目录,做出动态连结所需的cache檔。
|
ld 就是负责各个函式库文件的信息写进最后可执行档(executable),所以它叫做 link editor,编译时根据flags -L搜寻需要的lib,gcc也会把他的设定pass下来。 ld.so ld-linux.so.2是负责最后动态连结,叫做dynamic linker, RUN Time 执行程序时,它根据这个顺序搜寻函式库。
- LD_LIBRARY_PATH 或LD_AOUT_LIBRARY_PATH环境变量所指的路径
- ldconfig所建立的cache
- /lib /usr/lib内的档
来找程序所需要的动态函式库
ldconfig会根据/etc/ld.so.conf这个档的设定,加上内定的两个目录 /lib /usr/lib来设定ld.so要用到所需要的连结 以及连结的cache到/etc/ld.so.cache。 所以如果换了新的函式库,新的kernel,内部的标头档可能会有变化, 都要跟着改变让gcc正确的找到,喔不,应该是cpp, ld, ld.so能正确的找到。 不然编出来的执行档可能是错误的,执行时还可能segmentation fault。==================================================
gcc查找include的头文件的优先级,首先会在当前目录下找,假如没有找到,会在以下几个地方找:
1.编译的时候指定
2.gcc的specs里
3.使用-I参数指定的路径
4.gcc环境变量设置(C_INCLUDE_PATH)
而在这四个当中,-I参数指定的路径优先级最高。在gcc的手册里是这么说的:
-I dir
Add the directory dir to the list of directories to be searched for header files. Directories named
by -I are searched before the standard system include directories. If the directory dir is a stan-
dard system include directory, the option is ignored to ensure that the default search order for sys-
tem directories and the special treatment of system headers are not defeated .
使用-I参数指定的路径会在标准的系统include路径之前被搜索。
简单写一行shell,就能看到include的搜索的顺序了。
echo 'main(){}' | gcc -E -v -
#include "..." 搜索从这里开始:
#include <...> 搜索从这里开始:
/usr/local/include
/usr/lib/gcc/i386-redhat-linux/4.1.2/include
/usr/include
加上-I参数
echo 'main(){}' | gcc -E -v -I /home/chengcheng/mmsapp/include -,结果则是
/home/chengcheng/mmsapp/include
/usr/local/include
/usr/lib/gcc/i386-redhat-linux/4.1.2/include
/usr/include