Linux下C/C++程序编译链接加载过程中的常见问题及解决方法

42 篇文章 2 订阅

Linux下C/C++程序编译链接加载过程中的常见问题及解决方法

1 头文件包含的问题

报错信息

该错误通常发生在编译时,常见报错信息如下:

run.cpp:2:10: fatal error: dlpack/dlpack.h: No such file or directory
 #include <dlpack/dlpack.h>
          ^~~~~~~~~~~~~~~~~
compilation terminated.

头文件包含的问题是新手常常会遇到的问题,在这里,我们要先搞懂 gcc 关于头文件搜索的相关过程、参数选项和环境变量,之后头文件包含的相关问题就迎刃而解了。

头文件的搜索顺序

我们先来看一下头文件的搜索顺序,我们知道,在 C/C++ 源文件中,包含头文件有两种形式,即分别是尖括号和双引号来包含:#inlcude <xxx>#include "xxx"

<>

对于尖括号包含的头文件,通常搜索顺序为:

  1. 搜索 -I 指定的目录
  2. 搜索 gcc 环境变量 C_INCLUDE_PATH(如果是C++程序,则为 CPLUS_INCLUDE_PATH) 中指定的目录
  3. 搜索 gcc 的默认目录:
    • /usr/include
    • /usr/local/include
    • /usr/lib/gcc/x86_64-linux-gnu/7.5.0/include
“”

而对于双引号包含的头文件,我们很清楚,主要是为了当前工作目录下去搜索,这种情况的详细搜索顺序是这样的:

  1. 搜索当前目录
  2. 搜索 -I 参数指定的目录
  3. 搜索 gcc 环境变量 C_INCLUDE_PATH(如果是C++程序,则为 CPLUS_INCLUDE_PATH) 中指定的目录
  4. 搜索 gcc 的默认目录:
    • /usr/include
    • /usr/local/include
    • /usr/lib/gcc/x86_64-linux-gnu/7.5.0/include

可以看到,相对于 <> 的包含方式,"" 就是先多搜索了当前目录。

另外,要指出,编译器会按照上述顺序搜索头文件,一旦找到了就不会就绪往下进行了,也就是说,如果有同名的头文件,顺序靠后的搜索目录中的头文件会被靠前的屏蔽掉。

解决方法:编译参数选项和环境变量

通过上面介绍的与包含头文件相关的编译参数选项和环境变量我们也不难发现,当面对找不到头文件的错误时,有两种方式可以告诉编译器去找所需的头文件的目录:即 通过编译参数选项或环境变量

  • 编译参数选项 -I 参数指定编译器会去搜索的头文件包含目录。
  • 环境变量 C_INCLUDE_PATHCPLUS_INCULDE_PATH 分别指定C代码和C++代码需要去搜索的头文件包含目录。

注意这里不是由 PATH 环境变量指定的,PATH 目录下都是一些可执行文件,他不是用来指定头文件的搜索目录的,

这里要说下 include 的默认目录,它也不是由 $ATH 环境变量指定的(也就是说在所有头文件包含的行为中,和 PATH 环境变量一毛钱关系没有),而是由g++的配置prefix指定的,可以通过 gcc -v 来查看:

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 7.5.0-3ubuntu1~18.04' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with-gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)

2 编译连接时的未定义引用的问题

报错信息

常见报错信息如下:

/tmp/ccWYumCZ.o: In function `main':
run.cpp:(.text+0x8f): undefined reference to `tvm::runtime::Module::LoadFromFile(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
collect2: error: ld returned 1 exit status

编译链接时库搜索顺序

首先还是要明确在编译连接时的库的搜索顺序:

  1. 搜索 -L + -l 参数指定的
  2. 搜索环境变量 LIBRARY_PATH + 参数-l指定的
  3. 搜索默认目录中的
    • /lib
    • /usr/lib
    • /usr/local/lib

解决方法:编译参数选项和环境变量

与上面头文件包含问题类似,解决方法还是靠添加编译参数选项和环境变量。这里需要注意的是:-L 参数选项和 LIBRARY_PATH 环境变量都可以负责指定库的搜索目录,而 -l 参数选项是来指定搜索目录下的具体的库文件的名字。也就是说, -LLIBRARY_PATH 二选一,再加上 -l 参数,才行。

比如我们在 /home/song/tvm/build/ 目录下,有两个库文件 libtvm.solibtvm_runtime.so,我们要用它们,有以下两种方法:

  • 方法一:

    export LIBRARY_PATH=/home/song/tvm/build/:$LIBRARY_PATH
    gcc main.cpp -ltvm -ltvm_runtime
    
  • 方法二:

    gcc main.cpp -L/home/song/tvm/build/ -ltvm -ltvm_runtime
    

可参考:gcc参数 -i, -L, -l, -include

3 运行时链接库的问题

报错信息

该问题发生在运行时,即我们已经得到可执行文件 a.out 了,但是在运行时却报错类似:

./a.out: error while loading shared libraries: libtvm.so: cannot open shared object file: No such file or directory

我们还可以借助工具 ldd 来查看 a.out 需要运行时链接的库是否都可用:

ldd a.out
# 输出:
	linux-vdso.so.1 (0x00007ffc0ebca000)
	libtvm.so => not found
	libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f1b829b7000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f1b8279f000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1b823ae000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f1b82010000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f1b82f45000)

可以看到,确实如同报错信息提示的那样,libtvm.so 它是找不到的。

运行时链接库的搜索顺序

我们还是先来看一下运行时链接库的搜索顺序:

  1. 编译目标代码时指定的动态库搜索路径

  2. 环境变量 LD_LIBRARY_PATH 指定的动态库搜索路径

  3. 配置文件 /etc/ld.so.conf 中指定的动态库搜索路径

  4. 默认的动态库搜索路径

    • /lib

    • /lib64

    • /usr/lib

应注意动态库搜寻路径并不包括当前文件夹,所以当即使可执行文件和其所需的so文件在同一文件夹,也会出现找不到so的问题,就像 #include <xxxx> 也不搜索当前目录

解决方法:编译选项参数、环境变量、配置文件、软链接

这里编译选项参数和环境变量的方法与上面类似,这里就不再赘述了,说说配置文件和软链接的方法:

修改/etc/ld.so.conf文件

/etc/ld.so.conf 文件中添加运行时库的路径。然后执行 ldconfig 命令。

或者在 /etc/ld.so.conf.d 目录下添加一个新建的 .conf 新文件,然后在文件中输入新的路径,然后再执行ldconfig 命令:

vim /etc/ld.so.conf.d/MyLibrary.conf		# 加上自己需要的搜索路径
sudo ldconfig

添加运行时库的软链接

可以用 ln 命令来创建运行时库的软链接,并把软链接放在系统默认路径下,然后程序链接时只需链接动态库的软链接就行。这样做的好处是当动态库升级时,只需替换掉原来的老软链接就行,无需修改编译命令。其实上面的问题2 也可以考虑用这种方法。

Ref:

http://blog.sina.com.cn/s/blog_7195909a0100zi7i.html

https://blog.csdn.net/Damocles_shi/article/details/104085803

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
链接器和加载器殿堂级经典书籍 不管你的编程语言是什么,不管你的平台是什么,你很可能总是会涉及链接器和加载器的功能。但是你知道如何最大限度地利用它们吗?只有现在,随着《链接器和加载器》的出版,总算有一本深入完整地彻底揭示编译和运行过程的权威著作了。 《链接器和加载器》首先通过实例深入浅出地阐述了在不同的编译器和操作系统链接加载过程的差异。在这个基础上,作者提出了清晰实用的忠告,来帮助你创建更快、更清晰的代码。你将会学习如可规避和Windows DLL相关的陌阱,充分利用UNX ELF库模式等。如果你对程序设计抱有非常认真的态度,那么你可以通过这本书充分地理解这个领域内最难惶的主题之一。《链接器和加载器》对于编译器和操作系统课程同样也是一本理想的补充读物, 本书特性 覆盖了 windows,UNIX,LInux Beos和其它操作系统的动态链接过程。 解释了Java链接模式,以及它是如何应用在网络小应用程序和可扩展Java代码的。 帮助你编写更优雅、更高效的代码,以及构建能够被更加高效地编译加载和运行的应用程序。 包含了一个用Perl构建链接器的练习项目,项目文件可以从网络下载得到。 John R. Levine是很多书籍的作者或合作者,包括Lex&Yacc;(O'Reilly), Programming for Graphics Files in C and C++(Wiley),以及The Internet for Dummies (IDG),他还是Journal ofcLanguage Translation的荣誉退休发行人、comp.compilers新闻组的长期仲裁人员,以及某个最早的商用Fortran 77编译器的创建者。他在耶鲁大学获得了计算机科学的博士学位。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值