现在的位置: 首页 > 综合 > 正文

C语言头文件、库文件的查找路径

2013年10月01日 ⁄ 综合 ⁄ 共 2883字 ⁄ 字号 评论关闭

一、讨论环境

*操作系统:Redhat5/Fedora14

*编译器:gcc
4.5.1

以下言论仅确保在以上环境中适用。别的环境,大家可以通过类比方法,得到启示。


二、C语言头文件的查找路径

C语言,使用include指令,包含头文件,但又细分两种形式:

1、形式一:#include
“file1”

  gcc先在当前目录(指包含本条#include指令的源文件所在的目录)寻找file1,如果找不到,继续在由-iquote选项(如果有的话)指定的目录中寻找file1

  例如,在文件/usr/include/sys/stat.h中,包含指令#include
“types.h”,那么gcc先在/usr/include/sys目录下寻找types.h文件。嗯,在该目录下,确实存在一个types.h的文件。现假设我们把这个文件移动到另一个目录:mv
/usr/include/sys/types.h
/bar/foo/,我们在编译时,可以通过-iquote选项,在不改变stat.h的情况下,正常编译(当然,通常不建议这样做):

gcc
-iquote /bar/foo -I/usr/include/sys *.o

2、形式二:#include
<file2>

  gcc按照以下顺序查找file2

  1. -Idir1 -Idir2
    ...
  2. /usr/local/include
  3. libdir/gcc/<target>/<version>/include
  4. /usr/<target>/include
  5. /usr/include

第一行中,-Idir1
-Idir2 ...
是用户通过gcc-I选项指定的目录。值得一提的是,放在/usr/local/include/下的头文件也会被gcc自动的检索,这与/usr/local/lib/目录下的库处理方式是不一样的(gcc的链接器在运行时阶段不会自动查找该目录下的库文件,下一节会提到)。


三、C语言库文件的查找路径

C语言库文件的查找路径,又分为两个阶段:链接阶段、运行时阶段。

1、链接阶段(link
time

  此阶段,需要告诉编译器,在哪里找到库文件?以静态还是动态的方式链接库文件?默认情况下使用动态方式链接,这要求存在对应的.so动态库文件,如果不存在,则寻找相应的.a静态库文件。若在编译时向gcc传入-static选项,则使用静态方式链接,这要求所有库文件都必须有对应的*.a静态库。

  那么,是否可以令某些库使用动态链接,另一些库使用静态链接?不太确定,请参考ld的使用手册,我没有这样用过。

  切入正题,在链接阶段,gcc编译器如何寻找库文件呢(linker本身并没有默认的查找路径,这些查找路径是由gcc传递给linker的)?大家可以在编译时,向gcc加入-v选项来观察它向linker传递的库文件查找路径(观察LIBRARY_PATH变量的值),通常查找路径如下:

  1. 任何由-rpath-link-rpath选项指定的目录
  2. LD_RUN_PATH(如果没有找到-rpath-rpath-link选项)
  3. -Ldir1 -Ldir2
    ...
  4. /usr/lib/gcc/<target>/<version>/
  5. /usr/lib/

第一行-rpath-link-rpath选项的区别在于,-rpath选项指定的目录被硬编码到可执行文件中,-rpath-link选项指定的目录只在链接阶段生效。由于这两个选项都是链接器ld的选项,如何从gcc中向ld传递这两个选项?方法如下(更从细节参考gcc-Wl选项):

gcc -Wl, -rpath, /usr/local/lib

这相当于向ld向传递了如下参数:

ld -rpath /usr/local/lib

第二行,如果没有设置-rpath或-rpath-link选项,则查找LD_RUN_PATH环境变量指定的目录,并把它当作-rpath选项来处理。第三行-Ldir1
-Ldir2
...,是我们通过gcc-L选项向其指定的库文件查找路径,查找顺序按照我们传递的-L参数从左到右进行搜索;第四行属于gcc自己的库目录;第五行/usr/lib/Linux系统默认的系统库文件的目录。第四、第五行,都是gcc自动向linker传递的查找目录。例如我现在的机器上,使用gcc
-v可以看到LIBRARY_PATH变量值为:

LIBRARY_PATH=/usr/lib/gcc/i686-redhat-linux/4.5.1/:/usr/lib/

但是这并不是全部真理!根据我自己的测试,我发现gcc会把/usr/local/lib/目录也作为链接阶段的查找路径,这正是问题的根源——我们在链接过程中,使用到了/usr/local/lib/里面的一些库文件,但在运行时阶段,却说找不到该库文件。

2、运行时阶段(runtime

  仅当可执行程序采用动态的方式链接库文件时,才会存在运行时库文件的查找问题。对于这种可执行程序,它本身只是记录动态库的名称。所以在运行该程序时,操作系统的加载程序(ld.so)需要根据库的名称,在必要时加载库文件到内存中。

  在linux中,在运行时阶段,动态库(又叫共享库)的查找路径如下:

  1. -rpath选项指定的目录(已被硬编码到可执行文件中)
  2. LD_LIBRARY_PATH
  3. /lib或/usr/lib
  4. 系统默认的查找路径

我们可以通过readelf查看被硬编码到可执行文件中的rpath

$ readelf -d <可执行文件名>                #Display the dynamic section (if present)

LD_LIBRARY_PATH则没有这个问题,但是通常我们不建议使用这个环境变量,因为修改这个变量意味着影响所有依赖于这个环境变量的程序(如果非要使用,请把这个环境变量写在启动脚本中,并且让它只影响脚本中的程序)。

  那么系统默认的查找路径又是怎样的?在Redhat5/Fedora14中,ld.so通过读取/etc/ld.so.cache文件来查找库文件的位置,如果没有找到则继续从/etc/ld.so.conf文件中指定的目录查找。这个ld.so.cache文件相当于一个key-value的数据库,key就是动态库的名称,value就是这些库的存放路径。

  那么/etc/ld.so.cache文件是怎么生成的呢?这就要谈到ldconfig这个工具程序了。ldconfig是动态链接库的配置工具,使用它可以更新/etc/ld.so.cache文件,也可以查看这个文件中的key-value信息(使用ldconfig
-p),ldconfig的使用细节,请参考它的使用手册。总结一下系统默认的查找路径:

  1. /etc/ld.so.cache
  2. /etc/ld.so.conf文件中指定的目录

四、参考资料

 

抱歉!评论已关闭.