热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux动态链接那点事儿(`cmakefind_package,linuxsharedlibrary`路径详解)

Motivation经常在Linux下面写C程序,尤其是需要集成各种第三方库的工程,肯定对find_package指令不陌生。这是条很强大的指令。可

Motivation

经常在Linux下面写C++程序,尤其是需要集成各种第三方库的工程,肯定对find_package指令不陌生。

这是条很强大的指令。可以直接帮我们解决整个工程的依赖问题,自动把头文件和动态链接文件配置好。比如说,在Linux下面工程依赖了OpenCV,只需要下面几行就可以完全配置好:

add_executable(my_bin src/my_bin.cpp)
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(my_bin, ${OpenCV_LIBS})

工作流程如下:

  1. find_package在一些目录中查找OpenCV的配置文件。
  2. 找到后,find_package会将头文件目录设置到${OpenCV_INCLUDE_DIRS}中,将链接库设置到${OpenCV_LIBS}中。
  3. 设置可执行文件的链接库和头文件目录,编译文件。

到现在为止出现了第一个问题。那就是:
find_package会在哪些目录下面寻找OpenCV的配置文件?

find_package目录

为什么我们要知道这个问题呢?因为很多库,我们都是自己编译安装的。比如说,电脑中同时编译了OpenCV2OpenCV3,我该如何让cmake知道到底找哪个呢?

其实这个问题在CMake官方文档中有非常详细的解答。

首先是查找路径的根目录。我把几个重要的默认查找目录总结如下:

_DIR
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
PATH

其中,PATH中的路径如果以binsbin结尾,则自动回退到上一级目录。
找到根目录后,cmake会检查这些目录下的

/ (W)
/(cmake|CMake)/ (W)
/*/ (W)
/*/(cmake|CMake)/ (W)
/(lib/|lib|share)/cmake/*/ (U)
/(lib/|lib|share)/*/ (U)
/(lib/|lib|share)/*/(cmake|CMake)/ (U)

括号里的W代表Windows,U代表Unix。但是经过实测,Windows下的目录Linux也会检查。

cmake找到这些目录后,会开始依次找Config.cmakeFind.cmake文件。找到后即可执行该文件并生成相关链接信息。

现在回过头来看查找路径的根目录。我认为最重要的一个是PATH。由于/usr/bin/PATH中,cmake会自动去/usr/(lib/|lib|share)/cmake/*/寻找模块,这使得绝大部分我们直接通过apt-get安装的库可以被找到。

另外一个比较重要的是_DIR。我们可以在调用cmake时将这个目录传给cmake。由于其优先级最高,因此cmake会优先从该目录中寻找,这样我们就可以随心所欲的配置cmake使其找到我们希望它要找到的包。如我在3rd_parties目录下编译了一个OpenCV,那么执行cmake时可以使用

OpenCV_DIR=../../3rd-party/opencv-3.3.4/build/ cmake ..

另一种方式是使用
cmake -D CMAKE_PREFIX_PATH=../../3rd-party/opencv-3.3.4/build/
这种做法比第一种优先级还要高,而且要更常用一些。

这样做以后,cmake会优先从该目录寻找OpenCV

配置好编译好了以后,我感兴趣的是另一个问题:
我现在编译出了可执行文件,并且这个可执行文件依赖于opencv里的动态库。这个动态库是在cmake时显式给出的。那么,

  1. 该执行文件在运行时是如何找到这个动态库的?
  2. 如果我把可执行文件移动了,如何让这个可执行文件依然能找到动态库?
  3. 如果我把该动态库位置移动了,如何让这个可执行文件依然能找到动态库?
  4. 如果我把可执行文件复制到别的电脑上使用,我该把其链接的动态库放到新电脑的什么位置?

可执行文件如何寻找动态库

在ld的官方文档中,对这个问题有详尽的描述。

The linker uses the following search paths to locate required
shared libraries:

1. Any directories specified by -rpath-link options.2. Any directories specified by -rpath options. The differencebetween -rpath and -rpath-link is that directories specified by-rpath options are included in the executable and used atruntime, whereas the -rpath-link option is only effective atlink time. Searching -rpath in this way is only supported bynative linkers and cross linkers which have been configuredwith the --with-sysroot option.3. On an ELF system, for native linkers, if the -rpath and-rpath-link options were not used, search the contents of theenvironment variable "LD_RUN_PATH".4. On SunOS, if the -rpath option was not used, search anydirectories specified using -L options.5. For a native linker, the search the contents of the environmentvariable "LD_LIBRARY_PATH".6. For a native ELF linker, the directories in "DT_RUNPATH" or"DT_RPATH" of a shared library are searched for sharedlibraries needed by it. The "DT_RPATH" entries are ignored if"DT_RUNPATH" entries exist.7. The default directories, normally /lib and /usr/lib.8. For a native linker on an ELF system, if the file/etc/ld.so.conf exists, the list of directories found in thatfile.If the required shared library is not found, the linker will issuea warning and continue with the link.

最重要的是第一条,即rpath。这个rpath会在编译时将动态库绝对路径或者相对路径(取决于该动态库的cmake)写到可执行文件中。chrpath工具可以查看这些路径。

>>> chrpath extract_gpu
extract_gpu: RPATH=/usr/local/cuda/lib64:/home/dechao_meng/data/github/temporal-segment-networks/3rd-party/opencv-3.4.4/build/lib

可以看到,OpenCV的动态库的绝对路径被写到了可执行文件中。因此即使可执行文件的位置发生移动,依然可以准确找到编译时的rpath

接下来的问题:如果我把可执行文件复制到了别人的电脑上,或者我的动态库文件的目录发生了改变,怎样让可执行文件继续找到这个动态库呢?其实是在第五条:LD_LIBRARY_PATH。只要将存储动态库的目录加入到LD_LIBRARY_PATH中,可执行文件就能正确找到该目录。

这种做法十分常见,比如我们在安装CUDA时,最后一步是在.bashrc中配置

export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH

这样做之后,依赖cuda的可执行文件就能够正常运行了。

总结

写这篇文章是因为从我第一次使用cmake以来,经常因为动态链接的问题而耽误很长时间。清楚理解find_package的运行机制在Linux的C++开发中是非常重要的,而相关的资料网上又比较稀少。其实官网上解释的非常清楚,不过之前一直没有认真查。做事情还是应该一步一个脚印,将原理搞清楚再放心使用。

Reference


  1. https://cmake.org/cmake/help/v3.0/command/find_package.html
  2. https://unix.stackexchange.com/questions/22926/where-do-executables-look-for-shared-objects-at-runtime
  3. https://codeyarns.com/2017/11/02/how-to-change-rpath-or-runpath-of-executable/

推荐阅读
  • Android 构建基础流程详解
    Android 构建基础流程详解 ... [详细]
  • 为了在Hadoop 2.7.2中实现对Snappy压缩和解压功能的原生支持,本文详细介绍了如何重新编译Hadoop源代码,并优化其Native编译过程。通过这一优化,可以显著提升数据处理的效率和性能。此外,还探讨了编译过程中可能遇到的问题及其解决方案,为用户提供了一套完整的操作指南。 ... [详细]
  • MATLAB字典学习工具箱SPAMS:稀疏与字典学习的详细介绍、配置及应用实例
    SPAMS(Sparse Modeling Software)是一个强大的开源优化工具箱,专为解决多种稀疏估计问题而设计。该工具箱基于MATLAB,提供了丰富的算法和函数,适用于字典学习、信号处理和机器学习等领域。本文将详细介绍SPAMS的配置方法、核心功能及其在实际应用中的典型案例,帮助用户更好地理解和使用这一工具箱。 ... [详细]
  • 在Linux系统中,网络配置是至关重要的任务之一。本文详细解析了Firewalld和Netfilter机制,并探讨了iptables的应用。通过使用`ip addr show`命令来查看网卡IP地址(需要安装`iproute`包),当网卡未分配IP地址或处于关闭状态时,可以通过`ip link set`命令进行配置和激活。此外,文章还介绍了如何利用Firewalld和iptables实现网络流量控制和安全策略管理,为系统管理员提供了实用的操作指南。 ... [详细]
  • PHP预处理常量详解:如何定义与使用常量 ... [详细]
  • 本文详细介绍了在Linux系统上编译安装MySQL 5.5源码的步骤。首先,通过Yum安装必要的依赖软件包,如GCC、GCC-C++等,确保编译环境的完备。接着,下载并解压MySQL 5.5的源码包,配置编译选项,进行编译和安装。最后,完成安装后,进行基本的配置和启动测试,确保MySQL服务正常运行。 ... [详细]
  • SecureCRT是一款功能强大的终端仿真软件,支持SSH1和SSH2协议,适用于在Windows环境下高效连接和管理Linux服务器。该工具不仅提供了稳定的连接性能,还具备丰富的配置选项,能够满足不同用户的需求。通过SecureCRT,用户可以轻松实现对远程Linux系统的安全访问和操作。 ... [详细]
  • 深入浅出 webpack 系列(二):实现 PostCSS 代码的编译与优化
    在前一篇文章中,我们探讨了如何通过基础配置使 Webpack 完成 ES6 代码的编译。本文将深入讲解如何利用 Webpack 实现 PostCSS 代码的编译与优化,包括配置相关插件和加载器,以提升开发效率和代码质量。我们将详细介绍每个步骤,并提供实用示例,帮助读者更好地理解和应用这些技术。 ... [详细]
  • 在Cisco IOS XR系统中,存在提供服务的服务器和使用这些服务的客户端。本文深入探讨了进程与线程状态转换机制,分析了其在系统性能优化中的关键作用,并提出了改进措施,以提高系统的响应速度和资源利用率。通过详细研究状态转换的各个环节,本文为开发人员和系统管理员提供了实用的指导,旨在提升整体系统效率和稳定性。 ... [详细]
  • FreeBSD环境下PHP GD库安装问题的详细解决方案
    在 FreeBSD 环境下,安装 PHP GD 库时可能会遇到一些常见的问题。本文详细介绍了从配置到编译的完整步骤,包括解决依赖关系、配置选项以及常见错误的处理方法。通过这些详细的指导,开发者可以顺利地在 FreeBSD 上完成 PHP GD 库的安装,确保其正常运行。此外,本文还提供了一些优化建议,帮助提高安装过程的效率和稳定性。 ... [详细]
  • V8不仅是一款著名的八缸发动机,广泛应用于道奇Charger、宾利Continental GT和BossHoss摩托车中。自2008年以来,作为Chromium项目的一部分,V8 JavaScript引擎在性能优化和技术创新方面取得了显著进展。该引擎通过先进的编译技术和高效的垃圾回收机制,显著提升了JavaScript的执行效率,为现代Web应用提供了强大的支持。持续的优化和创新使得V8在处理复杂计算和大规模数据时表现更加出色,成为众多开发者和企业的首选。 ... [详细]
  • 该问题可能由守护进程配置不当引起,例如未识别的JVM选项或内存分配不足。建议检查并调整JVM参数,确保为对象堆预留足够的内存空间(至少1572864KB)。此外,还可以优化应用程序的内存使用,减少不必要的内存消耗。 ... [详细]
  • 本文深入探讨了Java多线程环境下的同步机制及其应用,重点介绍了`synchronized`关键字的使用方法和原理。`synchronized`关键字主要用于确保多个线程在访问共享资源时的互斥性和原子性。通过具体示例,如在一个类中使用`synchronized`修饰方法,展示了如何实现线程安全的代码块。此外,文章还讨论了`ReentrantLock`等其他同步工具的优缺点,并提供了实际应用场景中的最佳实践。 ... [详细]
  • 在本地环境中调试远程服务器上的网站代码执行问题,可以通过以下步骤实现:首先,在本地安装 Visual Studio 并配置远程调试工具。接着,确保服务器和本地机器之间的网络连接畅通,并正确设置防火墙规则以允许调试流量。最后,使用 Visual Studio 的远程调试功能连接到服务器,进行代码调试。这种方法不仅提高了开发效率,还减少了在服务器上直接操作的风险。 ... [详细]
  • Vue CLI 初始化 Webpack 项目时,main.js 文件是如何被调用的? ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有