【问题标题】:C++ executable fails to link to shared library after scpscp 后 C++ 可执行文件无法链接到共享库
【发布时间】:2019-11-21 07:52:41
【问题描述】:

所以我正在开发一个旨在在远程服务器上运行的项目。我在本地电脑上开发程序,编译它,然后上传到远程服务器。本地电脑和远程服务器都在 CentOS 7.7 上运行。

该程序是使用 CLion IDE 开发的,配置了 CMake。该程序依赖于一些共享库,它们应该根据我在 CMake 中编写的内容链接到可执行文件。在我的本地 PC 上,我可以完美地编译和运行程序。但是,我scp整个项目目录到远程服务器后,可执行文件无法运行。根据ldd 所说,它找不到任何“.so”文件。

这是我的 CMakeList.txt,每个路径都是相对路径,而不是绝对路径。

cmake_minimum_required(VERSION 3.15)
project(YS_Test)

set(CMAKE_CXX_STANDARD 11)

set(SOURCE_PATH_ src)

file(GLOB SOURCE_FILES_ ${SOURCE_PATH_}/*.*)

set(PROJECT_LIBS_ libTapQuoteAPI.so libTapTradeAPI.so libTapDataCollectAPI.so)

include_directories(api/include)
link_directories(api/lib/linux)

add_executable(YS_Test ${SOURCE_FILES_})

target_link_libraries(YS_Test ${PROJECT_LIBS_})

请不要告诉我设置LD_LIBRARY_PATH 来解决我的问题。该程序在没有LD_LIBRARY_PATH 的情况下在我的本地电脑上运行良好,所以我希望它可以在没有LD_LIBRARY_PATH 的远程服务器上运行。我想知道这里到底发生了什么,而不是解决问题。谢谢!

【问题讨论】:

  • 构建可执行文件时,CMake 默认将库的绝对路径作为 RPATH 嵌入其中。如果在 scp 之后更改库的路径,则需要手动控制 RPATH,CMake 提供了方法:gitlab.kitware.com/cmake/community/wikis/doc/cmake/…。 (如果您在问题中添加更多详细信息,该帮助可能会更详细。例如,确切的错误消息、PC 上可执行文件和库的确切位置以及scp 之后等等)。
  • 我不明白为什么这个问题被否决了。 OP 想知道 Linux 动态链接器在尝试解决依赖关系时是如何工作的。用“centos”或“linux”标记问题也很好,但每个问题只有 5 个标签可用!
  • @Pedro 我们开始 ^_^

标签: linux cmake centos shared-libraries


【解决方案1】:

如果我正确理解您的问题,您希望将编译后的YS_Test 程序连同一些依赖项一起发布,并让它在远程服务器上运行。默认情况下,可执行文件只会在/etc/ld.so 中配置的目录中查找,不会包含部署路径。

注意:通常您不会部署整个构建目录,而只会部署已编译的工件和依赖项。对于这个答案,我假设您将二进制文件及其依赖项部署到 same 目录。

你有两个选择:

  • 要求您的程序的用户自行设置LD_LIBRARY_PATH,或者通过包装脚本设置。该变量将指示动态链接器也查找指定的目录。即使您不喜欢这种解决方案,它也是迄今为止最常用的方法。
  • -Wl,-rpath='$ORIGIN' 添加到您的链接器选项中。这会将DT_RUNPATH 属性添加到可执行文件的动态部分。当您使用 CMake 时,您还可以使用 BUILD_RPATH 和/或 INSTALL_RPATH 目标属性进行设置。 ld.so 手册页对该属性的描述如下:

    如果共享对象依赖项不包含斜线,那么它是 按以下顺序搜索:

    • ...
    • 使用 DT_RUNPATH 动态部分中指定的目录 二进制文件的属性(如果存在)。

    $ORIGIN 部分扩展为包含程序或共享的目录 对象。

如果您真的坚持发送您的构建目录(例如在开发期间),您可以查看 CMake BUILD_RPATH_USE_ORIGIN 属性(及其通常的全球对应项 CMAKE_BUILD_RPATH_USE_ORIGIN),这将嵌入 relative 路径到二进制而不是绝对路径。

【讨论】:

  • 精彩的答案!但是,如果我可以在编译后使用 LD_LIBRARY_PATH 操作库路径,那么在 CMakeList.txt 中设置路径有什么意义。
  • 大概(我还没有测试过)告诉 CMake 在可执行文件中设置相对 RPATH 将允许您按照您的要求发送构建目录。除此之外,LD_LIBRARY_PATH 并不总是一个选项(例如,如果您提供的库可以加载其他库)
  • 如果某个答案帮助您解决了您的问题,请将其标记为已接受,以便其他人可以找到它。
【解决方案2】:

由于您不想要解决方法(@Botje 已经给了您两个),我将尝试解释一下。在你的开发机器上,如果你使用这个命令:

ldd YS_Test

您将看到您的程序使用的所有共享库及其对应的路径。 libTapQuoteAPI.so libTapTradeAPI.so libTapDataCollectAPI.so 位于您的“api/lib/linux”目录中,但使用完整的绝对路径解析。如果您在服务器上执行相同操作,则无法解析某些共享对象,因为它们不在同一位置。

如果您使用以下命令之一(不确定 Centos 中哪些可用):

chrpath --list YS_Test

patchelf --print-rpath YS_Test

您将看到程序中嵌入了RPATHRUNPATH 标记。这是 Linux 链接器用来定位标准 ld 位置之外的依赖项的路径。您可以在 Internet 上找到有关此的扩展说明,例如 this onethe Wikipedia article

违背我的承诺,我给你第三个解决方法:在你的服务器上使用patchelfchrpathscp 之后更改嵌入的RPATH 标签,指向它相对于$ORIGIN(代表程序位置)。

【讨论】:

  • 好文章!两个 cmets:你可以使用readelf -d 读取动态部分属性(例如DT_RUNPATH),如果可执行文件/库还没有运行路径,chrpath 拒绝添加它。
猜你喜欢
  • 2023-03-15
  • 2021-06-16
  • 1970-01-01
  • 2015-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-10
相关资源
最近更新 更多