So I am working on a project that is intended to run on a remote server. I develop the program on a local pc, compile it, then upload it to the remote server. Both the local pc and the remote server are run on CentOS 7.7.
The program is developed using the CLion IDE, configured with CMake. The program depends a few shared libraries, which are supposed to link to the executable according to what I wrote in CMake. At my local PC, I can compile and run the program perfectly. However, after I scp
the whole directory of the project to the remote server, the executable fails to run. It cannot find any of the “.so” files, according to what ldd
says.
This is my CMakeList.txt, with every path being relative path, instead of absolute path.
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_})
Please do not tell me to set LD_LIBRARY_PATH
to fix my issue. The program worked fine on my local pc without LD_LIBRARY_PATH
, so I expect it to run on the remote server without LD_LIBRARY_PATH
. I would like to know what is really going on here, instead of a work around. Thanks!
2
Answers
If I understand your problem correctly, you want to ship your compiled
YS_Test
program along with some dependencies and have it run on a remote server. By default an executable will only look in the directories configured in/etc/ld.so
, which will not include the deploy path.Note: Typically you do not deploy your entire build directory but only the compiled artifacts and dependencies. For this answer I will assume you deploy the binary and its dependencies to the same directory.
You have two options:
LD_LIBRARY_PATH
, either by themselves or by a wrapper script. This variable will instruct the dynamic linker to look in the specified directories as well. Even if you do not like this solution, it is by far the most common approach.Add
-Wl,-rpath='$ORIGIN'
to your linker options. This will add aDT_RUNPATH
attribute to the executable’s dynamic section. As you are using CMake you can also set this usingBUILD_RPATH
and/orINSTALL_RPATH
target properties.The ld.so manpage describes this attribute as follows:
The
$ORIGIN
part expands to the directory containing the program or sharedobject.
If you really insist on shipping your build directory (eg during development), you can take a look at the CMake
BUILD_RPATH_USE_ORIGIN
property (and its usual global counterpart CMAKE_BUILD_RPATH_USE_ORIGIN), this will embed relative paths into binaries instead of absolute paths.As you don’t want a workaround (@Botje has given you two already), I will try an explanation instead. In your development machine, if you use this command:
You will see all the shared libraries used by your program, with their corresponding paths. The libTapQuoteAPI.so libTapTradeAPI.so libTapDataCollectAPI.so are found at your ‘api/lib/linux’ directory, but resolved with full absolute paths. If you do the same at your server, some shared objects can’t be resolved because they aren’t at the same location.
If you use one of these commands (not sure which are available in Centos):
or
You will see the
RPATH
orRUNPATH
tags embedded in your program. This is the path used by the Linux linker to locate dependencies that are outside the standardld
locations. You may find extended explanations on Internet about this, like this one or the Wikipedia article.Breaking my promise, I give you a third workaround: use
patchelf
orchrpath
at your server afterscp
to change the embeddedRPATH
tag, pointing it relative to$ORIGIN
(which represents the program location).