C++ 可执行文件在 scp 后无法 link 到共享库
C++ executable fails to link to shared library after scp
所以我正在开发一个旨在 运行 在远程服务器上的项目。我在本地电脑上开发程序,编译它,然后上传到远程服务器。本地电脑和远程服务器在 CentOS 7.7 上都是 运行。
该程序是使用 CLion IDE 开发的,配置了 CMake。该程序依赖于一些共享库,根据我在 CMake 中编写的内容,这些共享库应该 link 到可执行文件。在我的本地 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
的远程服务器上运行 运行。我想知道这里到底发生了什么,而不是变通。谢谢!
如果我对您的问题的理解正确,您希望将已编译的 YS_Test
程序与一些依赖项一起发送,并将其 运行 放在远程服务器上。默认情况下,可执行文件只会查找 /etc/ld.so
中配置的目录,其中不包括部署路径。
注意:通常您不会部署 整个构建目录 ,而只会部署已编译的工件和依赖项。对于这个答案,我假设您将二进制文件及其依赖项部署到 same 目录。
您有两个选择:
- 要求您的程序的用户自行或通过包装脚本设置
LD_LIBRARY_PATH
。该变量将指示动态链接器也查看指定的目录。即使您不喜欢这个解决方案,它也是迄今为止最常用的方法。
将 -Wl,-rpath='$ORIGIN'
添加到您的链接器选项。这会将 DT_RUNPATH
属性添加到可执行文件的动态部分。当您使用 CMake 时,您还可以使用 BUILD_RPATH
and/or INSTALL_RPATH
目标属性进行设置。
ld.so 联机帮助页对此属性的描述如下:
If a shared object dependency does not contain a slash, then it is
searched for in the following order:
- ...
- Using the directories specified in the DT_RUNPATH dynamic section
attribute of the binary if present.
$ORIGIN
部分扩展到包含程序或共享的目录
对象.
如果你真的坚持发布你的构建目录(例如在开发期间),你可以看看CMake BUILD_RPATH_USE_ORIGIN
属性(和它通常的全局对应 CMAKE_BUILD_RPATH_USE_ORIGIN),这会将 relative 路径而不是绝对路径嵌入到二进制文件中。
由于您不想要解决方法(@Botje 已经给了您两个),我将尝试解释一下。在您的开发机器中,如果您使用此命令:
ldd YS_Test
您将看到您的程序使用的所有共享库及其相应的路径。 libTapQuoteAPI.so libTapTradeAPI.so libTapDataCollectAPI.so 位于您的 'api/lib/linux' 目录中,但已使用完整的绝对路径解析。如果您在服务器上执行相同的操作,一些共享对象将无法解析,因为它们不在同一位置。
如果您使用这些命令之一(不确定哪些在 Centos 中可用):
chrpath --list YS_Test
或
patchelf --print-rpath YS_Test
您将看到 RPATH
或 RUNPATH
标记嵌入到您的程序中。这是 Linux 链接器用来定位标准 ld
位置之外的依赖项的路径。您可能会在 Internet 上找到关于此的扩展解释,例如 this one or the Wikipedia article.
违背我的诺言,我给你第三种解决方法:在你的服务器上 scp
之后使用 patchelf
或 chrpath
来更改嵌入的 RPATH
标签,指向它相对于 $ORIGIN
(代表程序位置)。
所以我正在开发一个旨在 运行 在远程服务器上的项目。我在本地电脑上开发程序,编译它,然后上传到远程服务器。本地电脑和远程服务器在 CentOS 7.7 上都是 运行。
该程序是使用 CLion IDE 开发的,配置了 CMake。该程序依赖于一些共享库,根据我在 CMake 中编写的内容,这些共享库应该 link 到可执行文件。在我的本地 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
的远程服务器上运行 运行。我想知道这里到底发生了什么,而不是变通。谢谢!
如果我对您的问题的理解正确,您希望将已编译的 YS_Test
程序与一些依赖项一起发送,并将其 运行 放在远程服务器上。默认情况下,可执行文件只会查找 /etc/ld.so
中配置的目录,其中不包括部署路径。
注意:通常您不会部署 整个构建目录 ,而只会部署已编译的工件和依赖项。对于这个答案,我假设您将二进制文件及其依赖项部署到 same 目录。
您有两个选择:
- 要求您的程序的用户自行或通过包装脚本设置
LD_LIBRARY_PATH
。该变量将指示动态链接器也查看指定的目录。即使您不喜欢这个解决方案,它也是迄今为止最常用的方法。 将
-Wl,-rpath='$ORIGIN'
添加到您的链接器选项。这会将DT_RUNPATH
属性添加到可执行文件的动态部分。当您使用 CMake 时,您还可以使用BUILD_RPATH
and/orINSTALL_RPATH
目标属性进行设置。 ld.so 联机帮助页对此属性的描述如下:If a shared object dependency does not contain a slash, then it is searched for in the following order:
- ...
- Using the directories specified in the DT_RUNPATH dynamic section attribute of the binary if present.
$ORIGIN
部分扩展到包含程序或共享的目录 对象.
如果你真的坚持发布你的构建目录(例如在开发期间),你可以看看CMake BUILD_RPATH_USE_ORIGIN
属性(和它通常的全局对应 CMAKE_BUILD_RPATH_USE_ORIGIN),这会将 relative 路径而不是绝对路径嵌入到二进制文件中。
由于您不想要解决方法(@Botje 已经给了您两个),我将尝试解释一下。在您的开发机器中,如果您使用此命令:
ldd YS_Test
您将看到您的程序使用的所有共享库及其相应的路径。 libTapQuoteAPI.so libTapTradeAPI.so libTapDataCollectAPI.so 位于您的 'api/lib/linux' 目录中,但已使用完整的绝对路径解析。如果您在服务器上执行相同的操作,一些共享对象将无法解析,因为它们不在同一位置。
如果您使用这些命令之一(不确定哪些在 Centos 中可用):
chrpath --list YS_Test
或
patchelf --print-rpath YS_Test
您将看到 RPATH
或 RUNPATH
标记嵌入到您的程序中。这是 Linux 链接器用来定位标准 ld
位置之外的依赖项的路径。您可能会在 Internet 上找到关于此的扩展解释,例如 this one or the Wikipedia article.
违背我的诺言,我给你第三种解决方法:在你的服务器上 scp
之后使用 patchelf
或 chrpath
来更改嵌入的 RPATH
标签,指向它相对于 $ORIGIN
(代表程序位置)。