linux搜索文件顺序

举报
ZhjDayDayUp 发表于 2021/11/01 18:45:47 2021/11/01
【摘要】 了解我们写的代码在编译,链接,运行等阶段时,是搜索哪些目录,以及搜索顺序,对于程序员来讲是很有必要的。

一、预处理时搜索头文件顺序

1、编译脚本指定的-I路径
2、gcc的环境变量(C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH)
3、gcc的安装目录
4、/usr/include
示例如下:
测试源码:

# test.cpp
#include<iostream>
using namespace std;
int main()
{
    cout << "hello world" << endl;
    return 0;
}

添加CPLUS_INCLUDE_PATH

vim /etc/profile
export CPLUS_INCLUDE_PATH=/home/tmp1:$CPLUS_INCLUDE_PATH
source /etc/profile

查看结果:

g++ test.cpp  --verbose -I/home/tmp
# 结果
...
#include <...> search starts here:
/home/tmp
/home/tmp1
/usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../include/c++/10.2.0
/usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../include/c++/10.2.0/x86_64-pc-linux-gnu
/usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../include/c++/10.2.0/backward
/usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include
/usr/local/include
/usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include-fixed
/usr/include
End of search list
...

二、链接时搜索库文件顺序

1、编译脚本指定:-L指定目录,-l指定动态链接库名
2、gcc的安装路径
3、环境变量LIBRARY_PATH (在gcc下编译无效,clang下有效)
4、/lib64或者/lib
5、/usr/lib64或者/usr/lib
6、当前目录
实例如下:
源码:

# test1/test.cpp
# 放在test1目录下
#include<iostream>
using namespace std;
void test()
{
    cout << "This is test1" << endl;
}
# test2/test.cpp
# 放在gcc的安装目录下
#include<iostream>
using namespace std;
void test()
{
    cout << "This is test22" << endl;
}
# test3/test.cpp
# 放在test3目录下,设置环境变量LIBRARY_PATH为test3目录
#include<iostream>
using namespace std;
void test()
{
    cout << "This is test33" << endl;
}
# main.cpp
#include <iostream>
using namespace std;
#include "test.h"
int main()
{
    test();
    cout << "hello world" << endl;    
}
# 编译生成动态库
g++ -c test.cpp -o libtest.so

编译:

# 不显示指定-L
g++ main.cpp -ltest -v
COLLECT_GCC_OPTIONS='-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/local/libexec/gcc/x86_64-pc-linux-gnu/10.2.0/collect2 -plugin /usr/local/libexec/gcc/x86_64-pc-linux-gnu/10.2.0/liblto_plugin.so -plugin-opt=/usr/local/libexec/gcc/x86_64-pc-linux-gnu/10.2.0/lto-wrapper -plugin-opt=-fresolution=/tmp/cczIpa36.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /lib/../lib64/crt1.o /lib/../lib64/crti.o /usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/crtbegin.o -L/usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0 -L/usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L. -L/usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../.. /tmp/ccrLG8PQ.o -ltest -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/crtend.o /lib/../lib64/crtn.o
从-L可以看出,先找的是gcc的安装目录
# 显示指定-L
g++ main.cpp -L./test1 -ltest -v 
COLLECT_GCC_OPTIONS='-L./test1' '-v' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /usr/local/libexec/gcc/x86_64-pc-linux-gnu/10.2.0/collect2 -plugin /usr/local/libexec/gcc/x86_64-pc-linux-gnu/10.2.0/liblto_plugin.so -plugin-opt=/usr/local/libexec/gcc/x86_64-pc-linux-gnu/10.2.0/lto-wrapper -plugin-opt=-fresolution=/tmp/ccpha42N.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /lib/../lib64/crt1.o /lib/../lib64/crti.o /usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/crtbegin.o -L./test1 -L/usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0 -L/usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L. -L/usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../.. /tmp/ccm7u9CG.o -ltest -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/local/lib/gcc/x86_64-pc-linux-gnu/10.2.0/crtend.o /lib/../lib64/crtn.o
从-L可以看出,先找的是-L指定的test1目录
# clang的情况
clang++ main.cpp -L./test1 -ltest -v
 "/usr/bin/ld" --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out /lib/../lib64/crt1.o /lib/../lib64/crti.o /usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/10.2.0/crtbegin.o -L./test1 -L/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/10.2.0 -L/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib64 -L/usr/local/bin/../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../.. -L/usr/local/bin/../lib -L/lib -L/usr/lib -L/home/test3 -L/home/test3 -L. /tmp/main-b5ac75.o -ltest -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/10.2.0/crtend.o /lib/../lib64/crtn.o
也是先找的-L指定的目录,然后是安装目录,接着是LIBRARY_PATH目录。

三、运行时搜索库文件顺序

1、rpath(编译代码时指定的动态库搜索路径)
如果在编译时制定了这个路径,在linux的elf文件中会有对应路径,表示了可执行文件或者动态库在运行时首先查到动态库的路径。
指定路径的方式是:(cmake的方式不同,这里就不记录了)

-Wl,--rpath /libpath1:/libpath2 # elf生成RPATH
-Wl,--disable-new-dtags,--rpath /libpath1:/libpath2 # elf生成RPATH
-Wl,--enable-new-dtags,--rpath /libpath1:/libpath2 # elf生成RUNPATH

2、LD_LIBRARY_PATH指定的路径
3、runpath(也是在编译时指定)
生成的elf文件中会有该路径

-Wl,--enable-new-dtags,--rpath /libpath1:/libpath2 # elf生成RUNPATH

4、配置文件/etc/ld.so.conf指定的路径(这里修改完后需要执行ldconfig,使得重新加载下文件中指定的路径)
5、/lib64或/lib
6、/usr/lib64或/usr/lib
实例:
源码同样是上述例子,编译成libtest.so的时候,注意采用如下脚本:

# 得到.o
g++ -c test.cpp -fPIC -o test.o
# 得到.so
g++ -shared -o libtest.so test.o
# 指定了rpath
g++ main.cpp -L./test1 -ltest -v -Wl,-rpath ./test1
# 查看动态库搜索路径
ldd ./a.out
linux-vdso.so.1 =>  (0x00007ffcf132e000)
libtest.so => ./test1/libtest.so (0x00007f98d9081000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f98d8cb4000)
libm.so.6 => /lib64/libm.so.6 (0x00007f98d89b2000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f98d879b000)
libc.so.6 => /lib64/libc.so.6 (0x00007f98d83ca000)
/lib64/ld-linux-x86-64.so.2 (0x00007f98d9283000)
# 查看.dynamic段的内容
readelf -d ./a.out
Dynamic section at offset 0xdd8 contains 29 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libtest.so]
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [./test1]
 0x000000000000000c (INIT)               0x400718
 0x000000000000000d (FINI)               0x4009b4
 0x0000000000000019 (INIT_ARRAY)         0x600dc0
 0x000000000000001b (INIT_ARRAYSZ)       16 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x600dd0
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x0000000000000004 (HASH)               0x400278
 0x0000000000000005 (STRTAB)             0x400450
 0x0000000000000006 (SYMTAB)             0x4002d0
 0x000000000000000a (STRSZ)              374 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x601000
 0x0000000000000002 (PLTRELSZ)           192 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x400658
 0x0000000000000007 (RELA)               0x400628
 0x0000000000000008 (RELASZ)             48 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x4005e8
 0x000000006fffffff (VERNEEDNUM)         2
 0x000000006ffffff0 (VERSYM)             0x4005c6
 0x0000000000000000 (NULL)               0x0

由ldd结果可见,运行时使用到的库是RPATH指定的库。
使用如下脚本更换为RUNPATH

g++ main.cpp -L./test1 -ltest -v -Wl,enable-new-dtags,-rpath ./test2

通过readelf查看,结果也是肯定的。

0x0000000000000001 (NEEDED)             Shared library: [libtest.so]
0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
0x000000000000001d (RUNPATH)            Library runpath: [./test2]

接着,更改LD_LIBRARY_PATH,试着修改查找顺序。

export LD_LIBRARY_PATH=/home/tmp/test1:$LD_LIBRARY_PATH
ldd ./a.out 
linux-vdso.so.1 =>  (0x00007ffe481be000)
libtest.so => /home/tmp/test1/libtest.so (0x00007fd2b5350000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fd2b4f83000)
libm.so.6 => /lib64/libm.so.6 (0x00007fd2b4c81000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fd2b4a6a000)
libc.so.6 => /lib64/libc.so.6 (0x00007fd2b4699000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd2b5552000)

可见,原先的查找顺序已经被覆盖了。而RPATH不会被覆盖。
同样,可以在不同目录下放入libtest.so文件,然后逐个删除,看下具体的依赖顺序,具体不再贴上了。
总之:上面说的查询顺序是对的。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。