From 6114d85a7db0571bfade96a710f34a01bb2037c8 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sun, 7 Oct 2018 21:58:28 +0200 Subject: RPATH: Add option for using $ORIGIN in build tree This makes binaries independent of the build directory by not embedding the build directory via RPATH. The tests are partially based on the existing RuntimePath test, but with the check moved into a POST_BUILD command such that it can be skipped when the platform lacks support. Fixes: #18413 --- Help/manual/cmake-properties.7.rst | 1 + Help/manual/cmake-variables.7.rst | 1 + Help/prop_tgt/BUILD_RPATH_USE_ORIGIN.rst | 24 +++++++++ Help/release/dev/relative-rpath.rst | 8 +++ Help/variable/CMAKE_BUILD_RPATH_USE_ORIGIN.rst | 7 +++ Modules/Platform/Linux.cmake | 1 + Source/cmComputeLinkInformation.cxx | 29 +++++++++-- Source/cmTarget.cxx | 1 + Tests/RunCMake/RuntimePath/Relative.cmake | 69 ++++++++++++++++++++++++++ Tests/RunCMake/RuntimePath/RelativeCheck.cmake | 4 ++ Tests/RunCMake/RuntimePath/RunCMakeTest.cmake | 14 ++++++ Tests/RunCMake/RuntimePath/main.c | 4 +- 12 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 Help/prop_tgt/BUILD_RPATH_USE_ORIGIN.rst create mode 100644 Help/release/dev/relative-rpath.rst create mode 100644 Help/variable/CMAKE_BUILD_RPATH_USE_ORIGIN.rst create mode 100644 Tests/RunCMake/RuntimePath/Relative.cmake create mode 100644 Tests/RunCMake/RuntimePath/RelativeCheck.cmake diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 5c3eb81..1651114 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -138,6 +138,7 @@ Properties on Targets /prop_tgt/AUTORCC_OPTIONS /prop_tgt/BINARY_DIR /prop_tgt/BUILD_RPATH + /prop_tgt/BUILD_RPATH_USE_ORIGIN /prop_tgt/BUILD_WITH_INSTALL_NAME_DIR /prop_tgt/BUILD_WITH_INSTALL_RPATH /prop_tgt/BUNDLE_EXTENSION diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 9dd36ed..b88c661 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -322,6 +322,7 @@ Variables that Control the Build /variable/CMAKE_AUTOUIC_OPTIONS /variable/CMAKE_AUTOUIC_SEARCH_PATHS /variable/CMAKE_BUILD_RPATH + /variable/CMAKE_BUILD_RPATH_USE_ORIGIN /variable/CMAKE_BUILD_WITH_INSTALL_NAME_DIR /variable/CMAKE_BUILD_WITH_INSTALL_RPATH /variable/CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY diff --git a/Help/prop_tgt/BUILD_RPATH_USE_ORIGIN.rst b/Help/prop_tgt/BUILD_RPATH_USE_ORIGIN.rst new file mode 100644 index 0000000..511de7a --- /dev/null +++ b/Help/prop_tgt/BUILD_RPATH_USE_ORIGIN.rst @@ -0,0 +1,24 @@ +BUILD_RPATH_USE_ORIGIN +---------------------- + +Whether to use relative paths for the build ``RPATH``. + +This property is initialized by the value of the variable +:variable:`CMAKE_BUILD_RPATH_USE_ORIGIN`. + +On platforms that support runtime paths (``RPATH``) with the +``$ORIGIN`` token, setting this property to ``TRUE`` enables relative +paths in the build ``RPATH`` for executables that point to shared +libraries in the same build tree. + +Normally the build ``RPATH`` of an executable contains absolute paths +to the directory of shared libraries. Directories contained within the +build tree can be made relative to enable relocatable builds and to +help achieving reproducible builds by omitting the build directory +from the build environment. + +This property has no effect on platforms that do not support the +``$ORIGIN`` token in ``RPATH``, or when the :variable:`CMAKE_SKIP_RPATH` +variable is set. The runtime path set through the +:prop_tgt:`BUILD_RPATH` target property is also unaffected by this +property. diff --git a/Help/release/dev/relative-rpath.rst b/Help/release/dev/relative-rpath.rst new file mode 100644 index 0000000..5c62b10 --- /dev/null +++ b/Help/release/dev/relative-rpath.rst @@ -0,0 +1,8 @@ +relative-rpath +-------------- + +* A :variable:`CMAKE_BUILD_RPATH_USE_ORIGIN` variable and corresponding + :prop_tgt:`BUILD_RPATH_USE_ORIGIN` target property were added to + enable use of relative runtime paths (RPATHs). This helps achieving + relocatable and reproducible builds that are invariant of the build + directory. diff --git a/Help/variable/CMAKE_BUILD_RPATH_USE_ORIGIN.rst b/Help/variable/CMAKE_BUILD_RPATH_USE_ORIGIN.rst new file mode 100644 index 0000000..e34ede6 --- /dev/null +++ b/Help/variable/CMAKE_BUILD_RPATH_USE_ORIGIN.rst @@ -0,0 +1,7 @@ +CMAKE_BUILD_RPATH_USE_ORIGIN +---------------------------- + +Whether to use relative paths for the build ``RPATH``. + +This is used to initialize the :prop_tgt:`BUILD_RPATH_USE_ORIGIN` target +property for all targets, see that property for more details. diff --git a/Modules/Platform/Linux.cmake b/Modules/Platform/Linux.cmake index 1f8c1b4..b5d5464 100644 --- a/Modules/Platform/Linux.cmake +++ b/Modules/Platform/Linux.cmake @@ -1,6 +1,7 @@ set(CMAKE_DL_LIBS "dl") set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,") set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP ":") +set(CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN "\$ORIGIN") set(CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG "-Wl,-rpath-link,") set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,") set(CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic") diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index 0e48ca8..27b8599 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -421,7 +421,8 @@ std::string cmComputeLinkInformation::GetRPathLinkString() const return ""; } - // Construct the linker runtime search path. + // Construct the linker runtime search path. These MUST NOT contain tokens + // such as $ORIGIN, see https://sourceware.org/bugzilla/show_bug.cgi?id=16936 return cmJoin(this->OrderDependentRPath->GetOrderedDirectories(), ":"); } @@ -1702,6 +1703,14 @@ void cmComputeLinkInformation::GetRPath(std::vector& runtimeDirs, !this->Makefile->IsOn("CMAKE_SKIP_INSTALL_RPATH") && this->Target->GetPropertyAsBool("INSTALL_RPATH_USE_LINK_PATH"); + // Select whether to use $ORIGIN in RPATHs for artifacts in the build tree. + std::string const& originToken = this->Makefile->GetSafeDefinition( + "CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN"); + std::string targetOutputDir = this->Target->GetDirectory(this->Config); + bool use_relative_build_rpath = + this->Target->GetPropertyAsBool("BUILD_RPATH_USE_ORIGIN") && + !originToken.empty() && !targetOutputDir.empty(); + // Construct the RPATH. std::set emitted; if (use_install_rpath) { @@ -1711,6 +1720,8 @@ void cmComputeLinkInformation::GetRPath(std::vector& runtimeDirs, if (use_build_rpath) { // Add directories explicitly specified by user if (const char* build_rpath = this->Target->GetProperty("BUILD_RPATH")) { + // This will not resolve entries to use $ORIGIN, the user is expected to + // do that if necessary. cmCLI_ExpandListUnique(build_rpath, runtimeDirs, emitted); } } @@ -1728,6 +1739,8 @@ void cmComputeLinkInformation::GetRPath(std::vector& runtimeDirs, this->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); cmSystemTools::ConvertToUnixSlashes(rootPath); std::vector const& rdirs = this->GetRuntimeSearchPath(); + std::string const& topBinaryDir = + this->CMakeInstance->GetHomeOutputDirectory(); for (std::string const& ri : rdirs) { // Put this directory in the rpath if using build-tree rpath // support or if using the link path as an rpath. @@ -1741,6 +1754,18 @@ void cmComputeLinkInformation::GetRPath(std::vector& runtimeDirs, d += "/"; d += suffix; cmSystemTools::ConvertToUnixSlashes(d); + } else if (use_relative_build_rpath) { + // If expansion of the $ORIGIN token is supported and permitted per + // policy, use relative paths in the RPATH. + if (cmSystemTools::ComparePath(d, topBinaryDir) || + cmSystemTools::IsSubDirectory(d, topBinaryDir)) { + d = cmSystemTools::RelativePath(targetOutputDir, d); + if (!d.empty()) { + d = originToken + "/" + d; + } else { + d = originToken; + } + } } if (emitted.insert(d).second) { runtimeDirs.push_back(std::move(d)); @@ -1749,8 +1774,6 @@ void cmComputeLinkInformation::GetRPath(std::vector& runtimeDirs, // Do not add any path inside the source or build tree. std::string const& topSourceDir = this->CMakeInstance->GetHomeDirectory(); - std::string const& topBinaryDir = - this->CMakeInstance->GetHomeOutputDirectory(); if (!cmSystemTools::ComparePath(ri, topSourceDir) && !cmSystemTools::ComparePath(ri, topBinaryDir) && !cmSystemTools::IsSubDirectory(ri, topSourceDir) && diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 1458f01..987bdb3 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -218,6 +218,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->SetPropertyDefault("ANDROID_ASSETS_DIRECTORIES", nullptr); this->SetPropertyDefault("ANDROID_ANT_ADDITIONAL_OPTIONS", nullptr); this->SetPropertyDefault("BUILD_RPATH", nullptr); + this->SetPropertyDefault("BUILD_RPATH_USE_ORIGIN", nullptr); this->SetPropertyDefault("INSTALL_NAME_DIR", nullptr); this->SetPropertyDefault("INSTALL_RPATH", ""); this->SetPropertyDefault("INSTALL_RPATH_USE_LINK_PATH", "OFF"); diff --git a/Tests/RunCMake/RuntimePath/Relative.cmake b/Tests/RunCMake/RuntimePath/Relative.cmake new file mode 100644 index 0000000..203241f --- /dev/null +++ b/Tests/RunCMake/RuntimePath/Relative.cmake @@ -0,0 +1,69 @@ +enable_language(C) + +if(NOT CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN) + if(CMAKE_C_PLATFORM_ID STREQUAL "Linux") + # Sanity check for platform that is definitely known to support $ORIGIN. + message(FATAL_ERROR "Platform fails to report relative RPATH support") + else() + message(STATUS "Platform does not support relative RPATHs, skipping") + endif() + return() +endif() +set(CMAKE_BUILD_RPATH_USE_ORIGIN ON) + +function(CheckRpath target rpath) + add_custom_command( + TARGET ${target} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -Dfile=$ -Drpath=${rpath} + -P "${CMAKE_CURRENT_SOURCE_DIR}/RelativeCheck.cmake" + VERBATIM + ) +endfunction() + +if(CMAKE_C_COMPILER_ID STREQUAL "XL" AND CMAKE_BINARY_DIR MATCHES " ") + # XL 16.1.0.0 fails building the library if the output path contains a space. + set(externDir) + message(STATUS "Skipping external library test because of a toolchain bug") +else() + get_filename_component(externDir "${CMAKE_BINARY_DIR}" DIRECTORY) + set(externDir "${externDir}/Relative-extern") +endif() + +add_library(utils SHARED A.c) +add_library(utils-sub SHARED A.c) +set_property(TARGET utils-sub PROPERTY LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libs) +if(externDir) + add_library(utils-extern SHARED A.c) + set_property(TARGET utils-extern PROPERTY LIBRARY_OUTPUT_DIRECTORY ${externDir}) +endif() + +add_executable(main main.c) +target_link_libraries(main utils) +CheckRpath(main "\$ORIGIN") + +add_executable(main-norel main.c) +target_link_libraries(main-norel utils) +set_property(TARGET main-norel PROPERTY BUILD_RPATH_USE_ORIGIN OFF) +CheckRpath(main-norel "${CMAKE_CURRENT_BINARY_DIR}") + +add_executable(mainsub main.c) +target_link_libraries(mainsub utils) +set_property(TARGET mainsub PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) +CheckRpath(mainsub "\$ORIGIN/../") + +add_executable(main-sub main.c) +target_link_libraries(main-sub utils-sub) +CheckRpath(main-sub "\$ORIGIN/libs") + +add_executable(mainsub-sub main.c) +target_link_libraries(mainsub-sub utils-sub) +set_property(TARGET mainsub-sub PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) +CheckRpath(mainsub-sub "\$ORIGIN/../libs") + +if(externDir) + # Binaries linking to libraries outside the build tree should have an absolute RPATH. + add_executable(main-extern main.c) + target_link_libraries(main-extern utils-extern) + CheckRpath(main-extern "${externDir}") +endif() diff --git a/Tests/RunCMake/RuntimePath/RelativeCheck.cmake b/Tests/RunCMake/RuntimePath/RelativeCheck.cmake new file mode 100644 index 0000000..9ee403f --- /dev/null +++ b/Tests/RunCMake/RuntimePath/RelativeCheck.cmake @@ -0,0 +1,4 @@ +file(RPATH_CHECK FILE "${file}" RPATH "${rpath}") +if(NOT EXISTS "${file}") + message(FATAL_ERROR "RPATH for ${file} did not contain the expected value") +endif() diff --git a/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake b/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake index 3f238f2..6f1baa1 100644 --- a/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake +++ b/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake @@ -16,3 +16,17 @@ function(run_SymlinkImplicit) ${CMAKE_COMMAND} -Ddir=${RunCMake_TEST_BINARY_DIR} -P ${RunCMake_SOURCE_DIR}/SymlinkImplicitCheck.cmake) endfunction() run_SymlinkImplicit() + +function(run_Relative) + # Use a single build tree for a few tests without cleaning. + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Relative-build) + set(RunCMake_TEST_NO_CLEAN 1) + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) + endif() + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + run_cmake(Relative) + run_cmake_command(Relative-build ${CMAKE_COMMAND} --build . --config Debug) +endfunction() +run_Relative() diff --git a/Tests/RunCMake/RuntimePath/main.c b/Tests/RunCMake/RuntimePath/main.c index 8488f4e..181cd09 100644 --- a/Tests/RunCMake/RuntimePath/main.c +++ b/Tests/RunCMake/RuntimePath/main.c @@ -1,4 +1,6 @@ +extern int libA(void); + int main(void) { - return 0; + return libA(); } -- cgit v0.12 From c9b8c792719b2c46abf33f0ff0223db51453bf8f Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sun, 7 Oct 2018 22:03:18 +0200 Subject: RPATH: Record support for $ORIGIN on Haiku and Solaris Solaris 7 and Haiku (since 2013) both support $ORIGIN as documented at https://lekensteyn.nl/rpath.html --- Modules/Platform/Haiku.cmake | 1 + Modules/Platform/SunOS-GNU.cmake | 1 + 2 files changed, 2 insertions(+) diff --git a/Modules/Platform/Haiku.cmake b/Modules/Platform/Haiku.cmake index dfc2664..7e0af61 100644 --- a/Modules/Platform/Haiku.cmake +++ b/Modules/Platform/Haiku.cmake @@ -11,6 +11,7 @@ set(CMAKE_SHARED_LIBRARY_C_FLAGS "-fPIC") set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared") set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,") set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP ":") +set(CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN "\$ORIGIN") set(CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG "-Wl,-rpath-link,") set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,") set(CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic") diff --git a/Modules/Platform/SunOS-GNU.cmake b/Modules/Platform/SunOS-GNU.cmake index 485f235..47334d6 100644 --- a/Modules/Platform/SunOS-GNU.cmake +++ b/Modules/Platform/SunOS-GNU.cmake @@ -11,6 +11,7 @@ set(__SUNOS_COMPILER_GNU 1) macro(__sunos_compiler_gnu lang) set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG "-Wl,-R") set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP ":") + set(CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN "\$ORIGIN") set(CMAKE_SHARED_LIBRARY_SONAME_${lang}_FLAG "-Wl,-h") # Initialize C link type selection flags. These flags are used when -- cgit v0.12 From 95bd6317bc199135775a2f19ee7890d3e6991e3d Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sun, 7 Oct 2018 22:06:11 +0200 Subject: RPATH: Record support for $ORIGIN on various *BSD All of NetBSD, FreeBSD, OpenBSD and DragonFly BSD support `$ORIGIN`, but the last two require `-z origin` as documented at https://lekensteyn.nl/rpath.html The `-z origin` option causes a flag bit to be set and has no effect if the `RPATH` does not contain expandable tokens. --- Modules/Platform/DragonFly.cmake | 4 ++++ Modules/Platform/FreeBSD.cmake | 2 ++ Modules/Platform/NetBSD.cmake | 1 + Modules/Platform/OpenBSD.cmake | 4 ++++ 4 files changed, 11 insertions(+) diff --git a/Modules/Platform/DragonFly.cmake b/Modules/Platform/DragonFly.cmake index c22677b..12e5f3c 100644 --- a/Modules/Platform/DragonFly.cmake +++ b/Modules/Platform/DragonFly.cmake @@ -3,3 +3,7 @@ # see http://archive.netbsd.se/?ml=dfbsd-users&a=2007-07&m=4678361 include(Platform/FreeBSD) + +# DragonFly BSD requires -z origin to enable $ORIGIN expansion in RPATH. +# This is not required for FreeBSD since 10.2-RELEASE. +set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-z,origin,-rpath,") diff --git a/Modules/Platform/FreeBSD.cmake b/Modules/Platform/FreeBSD.cmake index ce4d3ce..082e9f6 100644 --- a/Modules/Platform/FreeBSD.cmake +++ b/Modules/Platform/FreeBSD.cmake @@ -6,6 +6,8 @@ set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared") # -shared set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") # +s, flag for exe link to use shared lib set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,") # -rpath set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP ":") # : or empty +# Does not require -z origin since 10.2-RELEASE +set(CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN "\$ORIGIN") set(CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG "-Wl,-rpath-link,") set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,") set(CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic") diff --git a/Modules/Platform/NetBSD.cmake b/Modules/Platform/NetBSD.cmake index 1004eb3..a8f4cc8 100644 --- a/Modules/Platform/NetBSD.cmake +++ b/Modules/Platform/NetBSD.cmake @@ -6,6 +6,7 @@ set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared") # -shared set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") # +s, flag for exe link to use shared lib set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,") # -rpath set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP ":") # : or empty +set(CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN "\$ORIGIN") set(CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG "-Wl,-rpath-link,") set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,") set(CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic") diff --git a/Modules/Platform/OpenBSD.cmake b/Modules/Platform/OpenBSD.cmake index 6466a0a..97e2a6a 100644 --- a/Modules/Platform/OpenBSD.cmake +++ b/Modules/Platform/OpenBSD.cmake @@ -15,6 +15,10 @@ if(NOT CMAKE_PLATFORM_RUNTIME_PATH) "${LDCONFIG_HINTS}") endif() +# OpenBSD requires -z origin to enable $ORIGIN expansion in RPATH. +# This is not required for NetBSD. +set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-z,origin,-rpath,") + set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_OPENBSD_VERSIONING 1) # OpenBSD has no multilib -- cgit v0.12