diff options
43 files changed, 683 insertions, 20 deletions
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 0587429..98279ef 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -57,6 +57,7 @@ Policies Introduced by CMake 3.14 .. toctree:: :maxdepth: 1 + CMP0083: Add PIE options when linking executable. </policy/CMP0083> CMP0082: Install rules from add_subdirectory() are interleaved with those in caller. </policy/CMP0082> Policies Introduced by CMake 3.13 diff --git a/Help/policy/CMP0083.rst b/Help/policy/CMP0083.rst new file mode 100644 index 0000000..7b467b0 --- /dev/null +++ b/Help/policy/CMP0083.rst @@ -0,0 +1,24 @@ +CMP0083 +------- + +To control generation of Position Independent Executable (``PIE``) or not, some +flags are required at link time. + +CMake 3.13 and lower did not add these link flags when +:prop_tgt:`POSITION_INDEPENDENT_CODE` is set. + +The ``OLD`` behavior for this policy is to not manage ``PIE`` link flags. The +``NEW`` behavior is to add link flags if :prop_tgt:`POSITION_INDEPENDENT_CODE` +is set: + +* Set to ``TRUE``: flags to produce a position independent executable are + passed to the linker step. For example ``-pie`` for ``GCC``. +* Set to ``FALSE``: flags not to produce a position independent executable are + passed to the linker step. For example ``-no-pie`` for ``GCC``. +* Not set: no flags are passed to the linker step. + +This policy was introduced in CMake version 3.14. CMake version +|release| warns when the policy is not set and uses ``OLD`` behavior. Use +the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly. + +.. include:: DEPRECATED.txt diff --git a/Help/release/dev/link-option-PIE.rst b/Help/release/dev/link-option-PIE.rst new file mode 100644 index 0000000..824ab2c --- /dev/null +++ b/Help/release/dev/link-option-PIE.rst @@ -0,0 +1,6 @@ +link-option-PIE +--------------- + +* Required link options to manage Position Independent Executable are now + added when :prop_tgt:`POSITION_INDEPENDENT_CODE` is set. These flags are + controlled by policy :policy:`CMP0083`. diff --git a/Modules/CMakeCXXInformation.cmake b/Modules/CMakeCXXInformation.cmake index 2975874..a896b99 100644 --- a/Modules/CMakeCXXInformation.cmake +++ b/Modules/CMakeCXXInformation.cmake @@ -105,6 +105,12 @@ endif() if(NOT CMAKE_CXX_COMPILE_OPTIONS_PIE) set(CMAKE_CXX_COMPILE_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE}) endif() +if(NOT CMAKE_CXX_LINK_OPTIONS_PIE) + set(CMAKE_CXX_LINK_OPTIONS_PIE ${CMAKE_C_LINK_OPTIONS_PIE}) +endif() +if(NOT CMAKE_CXX_LINK_OPTIONS_NO_PIE) + set(CMAKE_CXX_LINK_OPTIONS_NO_PIE ${CMAKE_C_LINK_OPTIONS_NO_PIE}) +endif() if(NOT CMAKE_CXX_COMPILE_OPTIONS_DLL) set(CMAKE_CXX_COMPILE_OPTIONS_DLL ${CMAKE_C_COMPILE_OPTIONS_DLL}) @@ -269,4 +275,3 @@ CMAKE_VERBOSE_MAKEFILE ) set(CMAKE_CXX_INFORMATION_LOADED 1) - diff --git a/Modules/CMakeFortranInformation.cmake b/Modules/CMakeFortranInformation.cmake index cceac83..ffa6a24 100644 --- a/Modules/CMakeFortranInformation.cmake +++ b/Modules/CMakeFortranInformation.cmake @@ -74,6 +74,12 @@ endif() if(NOT CMAKE_Fortran_COMPILE_OPTIONS_PIE) set(CMAKE_Fortran_COMPILE_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE}) endif() +if(NOT CMAKE_Fortran_LINK_OPTIONS_PIE) + set(CMAKE_Fortran_LINK_OPTIONS_PIE ${CMAKE_C_LINK_OPTIONS_PIE}) +endif() +if(NOT CMAKE_Fortran_LINK_OPTIONS_NO_PIE) + set(CMAKE_Fortran_LINK_OPTIONS_NO_PIE ${CMAKE_C_LINK_OPTIONS_NO_PIE}) +endif() if(NOT CMAKE_Fortran_COMPILE_OPTIONS_DLL) set(CMAKE_Fortran_COMPILE_OPTIONS_DLL ${CMAKE_C_COMPILE_OPTIONS_DLL}) diff --git a/Modules/Compiler/AppleClang-C.cmake b/Modules/Compiler/AppleClang-C.cmake index a48adec..8754951 100644 --- a/Modules/Compiler/AppleClang-C.cmake +++ b/Modules/Compiler/AppleClang-C.cmake @@ -1,6 +1,9 @@ include(Compiler/Clang) __compiler_clang(C) +set(CMAKE_C_LINK_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE} -Xlinker -pie) +set(CMAKE_C_LINK_OPTIONS_NO_PIE -Xlinker -no_pie) + if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.0) set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90") set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu90") diff --git a/Modules/Compiler/AppleClang-CXX.cmake b/Modules/Compiler/AppleClang-CXX.cmake index e5fd647..54c1388 100644 --- a/Modules/Compiler/AppleClang-CXX.cmake +++ b/Modules/Compiler/AppleClang-CXX.cmake @@ -1,6 +1,9 @@ include(Compiler/Clang) __compiler_clang(CXX) +set(CMAKE_CXX_LINK_OPTIONS_PIE ${CMAKE_CXX_COMPILE_OPTIONS_PIE} -Xlinker -pie) +set(CMAKE_CXX_LINK_OPTIONS_NO_PIE -Xlinker -no_pie) + if(NOT "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC") set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden") endif() diff --git a/Modules/Compiler/Clang.cmake b/Modules/Compiler/Clang.cmake index e23470b..7cee9c7 100644 --- a/Modules/Compiler/Clang.cmake +++ b/Modules/Compiler/Clang.cmake @@ -21,6 +21,26 @@ else() macro(__compiler_clang lang) __compiler_gnu(${lang}) set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "-fPIE") + # Link options for PIE are already set in 'Compiler/GNU.cmake' + # but clang may require alternate syntax on some platforms + if (NOT CMAKE_${lang}_FLAG_PIE) + cmake_check_compiler_flag(${lang} "${CMAKE_${lang}_COMPILE_OPTIONS_PIE};-Xlinker;-pie" + CMAKE_${lang}_FLAG_XLINKER_PIE) + if (CMAKE_${lang}_FLAG_XLINKER_PIE) + set(CMAKE_${lang}_LINK_OPTIONS_PIE ${CMAKE_${lang}_COMPILE_OPTIONS_PIE} "-Xlinker" "-pie") + else() + set(CMAKE_${lang}_LINK_OPTIONS_PIE "") + endif() + endif() + if (NOT CMAKE_${lang}_FLAG_NO_PIE) + cmake_check_compiler_flag(${lang} "-Xlinker;-no_pie" + CMAKE_${lang}_FLAG_XLINKER_NO_PIE) + if (CMAKE_${lang}_FLAG_XLINKER_NO_PIE) + set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "-Xlinker" "-no_pie") + else() + set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "") + endif() + endif() set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ") set(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY "-fvisibility=") if(CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.4.0) diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake index 4491d4b..688a1b5 100644 --- a/Modules/Compiler/GNU.cmake +++ b/Modules/Compiler/GNU.cmake @@ -9,6 +9,7 @@ endif() set(__COMPILER_GNU 1) include(Compiler/CMakeCommonCompilerMacros) +include(Internal/CMakeCheckCompilerFlag) macro(__compiler_gnu lang) # Feature flags. @@ -16,6 +17,21 @@ macro(__compiler_gnu lang) set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "-fPIC") if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.4) set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "-fPIE") + # Support of PIE at link stage depends on various elements : platform, compiler, linker + # so the easiest way is to check if compiler supports these flags + cmake_check_compiler_flag(${lang} "${CMAKE_${lang}_COMPILE_OPTIONS_PIE};-pie" + CMAKE_${lang}_FLAG_PIE) + if (CMAKE_${lang}_FLAG_PIE) + set(CMAKE_${lang}_LINK_OPTIONS_PIE ${CMAKE_${lang}_COMPILE_OPTIONS_PIE} "-pie") + else() + set(CMAKE_${lang}_LINK_OPTIONS_PIE "") + endif() + cmake_check_compiler_flag(${lang} "-no-pie" CMAKE_${lang}_FLAG_NO_PIE) + if (CMAKE_${lang}_FLAG_NO_PIE) + set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "-no-pie") + else() + set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "") + endif() endif() if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.0) set(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY "-fvisibility=") diff --git a/Modules/Compiler/SunPro-C.cmake b/Modules/Compiler/SunPro-C.cmake index 1988aae..75b8fe6 100644 --- a/Modules/Compiler/SunPro-C.cmake +++ b/Modules/Compiler/SunPro-C.cmake @@ -7,6 +7,8 @@ set(CMAKE_C_VERBOSE_FLAG "-#") set(CMAKE_C_COMPILE_OPTIONS_PIC -KPIC) set(CMAKE_C_COMPILE_OPTIONS_PIE "") +set(CMAKE_C_LINK_OPTIONS_PIE "") +set(CMAKE_C_LINK_OPTIONS_NO_PIE "") set(CMAKE_SHARED_LIBRARY_C_FLAGS "-KPIC") set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-G") set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-R") diff --git a/Modules/Compiler/SunPro-CXX.cmake b/Modules/Compiler/SunPro-CXX.cmake index f463be3..662ac30 100644 --- a/Modules/Compiler/SunPro-CXX.cmake +++ b/Modules/Compiler/SunPro-CXX.cmake @@ -7,6 +7,8 @@ set(CMAKE_CXX_VERBOSE_FLAG "-v") set(CMAKE_CXX_COMPILE_OPTIONS_PIC -KPIC) set(CMAKE_CXX_COMPILE_OPTIONS_PIE "") +set(CMAKE_CXX_LINK_OPTIONS_PIE "") +set(CMAKE_CXX_LINK_OPTIONS_NO_PIE "") set(CMAKE_SHARED_LIBRARY_CXX_FLAGS "-KPIC") set(CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS "-G") set(CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG "-R") diff --git a/Modules/Compiler/SunPro-Fortran.cmake b/Modules/Compiler/SunPro-Fortran.cmake index d3a415f..e110253 100644 --- a/Modules/Compiler/SunPro-Fortran.cmake +++ b/Modules/Compiler/SunPro-Fortran.cmake @@ -4,6 +4,8 @@ set(CMAKE_Fortran_FORMAT_FREE_FLAG "-free") set(CMAKE_Fortran_COMPILE_OPTIONS_PIC "-KPIC") set(CMAKE_Fortran_COMPILE_OPTIONS_PIE "") +set(CMAKE_Fortran_LINK_OPTIONS_PIE "") +set(CMAKE_Fortran_LINK_OPTIONS_NO_PIE "") set(CMAKE_SHARED_LIBRARY_Fortran_FLAGS "-KPIC") set(CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS "-G") set(CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG "-R") diff --git a/Modules/Internal/CMakeCheckCompilerFlag.cmake b/Modules/Internal/CMakeCheckCompilerFlag.cmake new file mode 100644 index 0000000..ca9b356 --- /dev/null +++ b/Modules/Internal/CMakeCheckCompilerFlag.cmake @@ -0,0 +1,146 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=[ + +NOTE: This function is used internally by CMake. Projects should not include + this file directly. + +The cmake_check_compiler_flag() function can be used to compile and link a +source file to check whether a specific compiler or linker flag is supported. +The function does not use the try_compile() command so as to avoid infinite +recursion. It may not work for all platforms or toolchains, the caller is +responsible for ensuring it is only called in valid situations. + +Parameters: + lang - Language to check. + flag - The flag to add to the compile/link command line. + result - Boolean output variable. It will be stored in the cache as an + internal variable and if true, will cause future tests that assign + to that variable to be bypassed. + +Optional parameters: + SRC_EXT - Overrides the extension of the source file used for the + check. Defaults are 'c' (C), 'cxx' (CXX), 'F' (Fortran). + COMMAND_PATTERN - Pattern to be used for the command line. The default is + '<FLAG> -o <OUTPUT> <SOURCE>' + FAIL_REGEX - List of additional regular expressions that, if matched by + the output, give a failed result for the check. A common + set of regular expressions will be included in addition to + those given by FAIL_REGEX. + +#]=] + +include_guard(GLOBAL) +include(CMakeCheckCompilerFlagCommonPatterns) + +function(CMAKE_CHECK_COMPILER_FLAG lang flag result) + # Cache results between runs similar to check_<lang>_source_compiles() + if(DEFINED ${result}) + return() + endif() + + set(comment "Is the '${flag}' option(s) supported") + string(REPLACE ";" " " comment "${comment}") + + if (NOT lang MATCHES "^(C|CXX|Fortran|ASM)$") + # other possible languages are not supported + # log message to keep trace of this problem... + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Function 'CMAKE_CHECK_COMPILER_FLAG' called with unsupported language: ${lang}\n") + set(${result} FALSE CACHE INTERNAL ${comment}) + return() + endif() + if (lang STREQUAL "ASM") + # assume ASM compiler is a multi-language compiler, so supports C language as well + set(check_lang C) + else() + set(check_lang ${lang}) + endif() + + cmake_parse_arguments(CCCF "" "SRC_EXT;COMMAND_PATTERN" "FAIL_REGEX" ${ARGN}) + + if (NOT CCCF_COMMAND_PATTERN) + set (CCCF_COMMAND_PATTERN "<FLAG> -o <OUTPUT> <SOURCE>") + endif() + + list (APPEND CCCF_FAIL_REGEX "argument unused during compilation") # clang + if (check_lang STREQUAL "C") + list(APPEND CCCF_FAIL_REGEX + "command line option .* is valid for .* but not for C") # GNU + elseif(check_lang STREQUAL "CXX") + list(APPEND CCCF_FAIL_REGEX + "command line option .* is valid for .* but not for C\\+\\+") # GNU + elseif(check_lang STREQUAL "Fortran") + list(APPEND CCCF_FAIL_REGEX + "command line option .* is valid for .* but not for Fortran") # GNU + endif() + + # Add patterns for common errors + check_compiler_flag_common_patterns(COMPILER_FLAG_COMMON_PATTERNS) + foreach(arg IN LISTS COMPILER_FLAG_COMMON_PATTERNS) + if(arg MATCHES "^FAIL_REGEX$") + continue() + endif() + list(APPEND CCCF_FAIL_REGEX "${arg}") + endforeach() + + if(NOT CCCF_SRC_EXT) + if (check_lang STREQUAL "C") + set(CCCF_SRC_EXT c) + elseif(check_lang STREQUAL "CXX") + set(CCCF_SRC_EXT cxx) + elseif(check_lang STREQUAL "Fortran") + set(CCCF_SRC_EXT F) + endif() + endif() + + # Compute the directory in which to run the test. + set(COMPILER_FLAG_DIR "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp") + # Compute source and output files. + set(COMPILER_FLAG_SRC + "${COMPILER_FLAG_DIR}/CompilerFlag${lang}.${CCCF_SRC_EXT}") + if(check_lang STREQUAL "Fortran") + file(WRITE "${COMPILER_FLAG_SRC}" + " program simple\n end program simple\n") + else() + file(WRITE "${COMPILER_FLAG_SRC}" "int main (void)\n{ return 0; }\n") + endif() + get_filename_component(COMPILER_FLAG_EXE "${COMPILER_FLAG_SRC}" NAME_WE) + string(APPEND COMPILER_FLAG_EXE "${CMAKE_EXECUTABLE_SUFFIX}") + + # Build command line + separate_arguments(CCCF_COMMAND_PATTERN UNIX_COMMAND + "${CCCF_COMMAND_PATTERN}") + list(TRANSFORM CCCF_COMMAND_PATTERN REPLACE "<SOURCE>" "${COMPILER_FLAG_SRC}") + list(TRANSFORM CCCF_COMMAND_PATTERN REPLACE "<OUTPUT>" "${COMPILER_FLAG_EXE}") + list(TRANSFORM CCCF_COMMAND_PATTERN REPLACE "<FLAG>" "${flag}") + + execute_process( + COMMAND "${CMAKE_COMMAND}" -E env LC_ALL=C LC_MESSAGES=C LANG=C + "${CMAKE_${lang}_COMPILER}" ${CCCF_COMMAND_PATTERN} + WORKING_DIRECTORY "${COMPILER_FLAG_DIR}" + OUTPUT_VARIABLE COMPILER_FLAG_OUTPUT + ERROR_VARIABLE COMPILER_FLAG_OUTPUT + RESULT_VARIABLE COMPILER_FLAG_RESULT) + + # Record result in the cache so we can avoid re-testing every CMake run + if (COMPILER_FLAG_RESULT) + set(${result} FALSE CACHE INTERNAL ${comment}) + else() + foreach(regex IN LISTS CCCF_FAIL_REGEX) + if(COMPILER_FLAG_OUTPUT MATCHES "${regex}") + set(${result} FALSE CACHE INTERNAL ${comment}) + endif() + endforeach() + endif() + if (DEFINED ${result}) + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determining if the ${flag} option " + "is supported for ${lang} language failed with the following output:\n" + "${COMPILER_FLAG_OUTPUT}\n") + return() + endif() + + set(${result} TRUE CACHE INTERNAL ${comment}) +endfunction() diff --git a/Modules/Platform/Android/abi-common.cmake b/Modules/Platform/Android/abi-common.cmake index 10fb897..a0b47f4 100644 --- a/Modules/Platform/Android/abi-common.cmake +++ b/Modules/Platform/Android/abi-common.cmake @@ -12,10 +12,6 @@ if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() -if(CMAKE_POSITION_INDEPENDENT_CODE) - string(APPEND _ANDROID_ABI_INIT_EXE_LDFLAGS " -fPIE -pie") -endif() - string(APPEND _ANDROID_ABI_INIT_EXE_LDFLAGS " -Wl,--gc-sections") if(NOT _ANDROID_ABI_INIT_EXE_LDFLAGS_NO_nocopyreloc) diff --git a/Modules/Platform/CYGWIN-GNU.cmake b/Modules/Platform/CYGWIN-GNU.cmake index 784c8c6..f55b80d 100644 --- a/Modules/Platform/CYGWIN-GNU.cmake +++ b/Modules/Platform/CYGWIN-GNU.cmake @@ -27,6 +27,8 @@ macro(__cygwin_compiler_gnu lang) # No -fPIC on cygwin set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "") set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "") + set(CMAKE_${lang}_LINK_OPTIONS_PIE "") + set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "") set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "") # Initialize C link type selection flags. These flags are used when diff --git a/Modules/Platform/FreeBSD.cmake b/Modules/Platform/FreeBSD.cmake index 082e9f6..4a4c00d 100644 --- a/Modules/Platform/FreeBSD.cmake +++ b/Modules/Platform/FreeBSD.cmake @@ -1,6 +1,7 @@ set(CMAKE_DL_LIBS "") set(CMAKE_C_COMPILE_OPTIONS_PIC "-fPIC") set(CMAKE_C_COMPILE_OPTIONS_PIE "-fPIE") +# PIE link options are managed in Compiler/<compiler>.cmake file set(CMAKE_SHARED_LIBRARY_C_FLAGS "-fPIC") # -pic 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 diff --git a/Modules/Platform/Fuchsia.cmake b/Modules/Platform/Fuchsia.cmake index 896da7b..7b33434 100644 --- a/Modules/Platform/Fuchsia.cmake +++ b/Modules/Platform/Fuchsia.cmake @@ -3,6 +3,8 @@ set(FUCHSIA 1) set(CMAKE_DL_LIBS "") set(CMAKE_C_COMPILE_OPTIONS_PIC "-fPIC") set(CMAKE_C_COMPILE_OPTIONS_PIE "-fPIE") +set(CMAKE_C_LINK_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE} "-pie") +set(CMAKE_C_LINK_OPTIONS_NO_PIE "-no-pie") set(CMAKE_SHARED_LIBRARY_C_FLAGS "-fPIC") set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared") set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,") diff --git a/Modules/Platform/Linux-Intel.cmake b/Modules/Platform/Linux-Intel.cmake index f712e2b..ab22b1d 100644 --- a/Modules/Platform/Linux-Intel.cmake +++ b/Modules/Platform/Linux-Intel.cmake @@ -23,6 +23,10 @@ endif() macro(__linux_compiler_intel lang) set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "-fPIC") set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "-fPIE") + if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 13.0) + set(CMAKE_${lang}_LINK_OPTIONS_PIE ${CMAKE_${lang}_COMPILE_OPTIONS_PIE} "-pie") + set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "-no-pie") + endif() set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "-fPIC") set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared") diff --git a/Modules/Platform/Linux-PGI.cmake b/Modules/Platform/Linux-PGI.cmake index db032c2..3e7e391 100644 --- a/Modules/Platform/Linux-PGI.cmake +++ b/Modules/Platform/Linux-PGI.cmake @@ -12,6 +12,8 @@ macro(__linux_compiler_pgi lang) # Shared library compile and link flags. set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "-fPIC") set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "") + set(CMAKE_${lang}_LINK_OPTIONS_PIE "") + set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "") set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "-fPIC") set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared") set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS " ") diff --git a/Modules/Platform/NetBSD.cmake b/Modules/Platform/NetBSD.cmake index a8f4cc8..d99cb4a 100644 --- a/Modules/Platform/NetBSD.cmake +++ b/Modules/Platform/NetBSD.cmake @@ -1,6 +1,7 @@ set(CMAKE_DL_LIBS "") set(CMAKE_C_COMPILE_OPTIONS_PIC "-fPIC") set(CMAKE_C_COMPILE_OPTIONS_PIE "-fPIE") +# PIE link options are managed in Compiler/<compiler>.cmake file set(CMAKE_SHARED_LIBRARY_C_FLAGS "-fPIC") # -pic 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 diff --git a/Modules/Platform/SINIX.cmake b/Modules/Platform/SINIX.cmake index c37a113..e44ceef 100644 --- a/Modules/Platform/SINIX.cmake +++ b/Modules/Platform/SINIX.cmake @@ -1,4 +1,6 @@ set(CMAKE_C_COMPILE_OPTIONS_PIC -K PIC) set(CMAKE_C_COMPILE_OPTIONS_PIE "") +set(CMAKE_C_LINK_OPTIONS_PIE "") +set(CMAKE_C_LINK_OPTIONS_NO_PIE "") set(CMAKE_SHARED_LIBRARY_C_FLAGS "-K PIC") include(Platform/UnixPaths) diff --git a/Modules/Platform/UNIX_SV.cmake b/Modules/Platform/UNIX_SV.cmake index 1ec96ae..433daf3 100644 --- a/Modules/Platform/UNIX_SV.cmake +++ b/Modules/Platform/UNIX_SV.cmake @@ -1,5 +1,7 @@ set(CMAKE_C_COMPILE_OPTIONS_PIC -K PIC) set(CMAKE_C_COMPILE_OPTIONS_PIE "") +set(CMAKE_C_LINK_OPTIONS_PIE "") +set(CMAKE_C_LINK_OPTIONS_NO_PIE "") set(CMAKE_SHARED_LIBRARY_C_FLAGS "-K PIC") set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-Wl,-Bexport") include(Platform/UnixPaths) diff --git a/Modules/Platform/UnixWare.cmake b/Modules/Platform/UnixWare.cmake index e649bd2..8c9d430 100644 --- a/Modules/Platform/UnixWare.cmake +++ b/Modules/Platform/UnixWare.cmake @@ -1,5 +1,7 @@ set(CMAKE_C_COMPILE_OPTIONS_PIC -K PIC) set(CMAKE_C_COMPILE_OPTIONS_PIE "") +set(CMAKE_C_LINK_OPTIONS_PIE "") +set(CMAKE_C_LINK_OPTIONS_NO_PIE "") set(CMAKE_SHARED_LIBRARY_C_FLAGS "-K PIC") set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-Wl,-Bexport") include(Platform/UnixPaths) diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake index cfb325b..2e854e5 100644 --- a/Modules/Platform/Windows-GNU.cmake +++ b/Modules/Platform/Windows-GNU.cmake @@ -72,6 +72,8 @@ macro(__windows_compiler_gnu lang) # No -fPIC on Windows set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "") set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "") + set(CMAKE_${lang}_LINK_OPTIONS_PIE "") + set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "") set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "") set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS ${__WINDOWS_GNU_LD_RESPONSE}) diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index a278a7f..0b28d1e 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -495,6 +495,36 @@ const char* cmGeneratorTarget::GetFeature(const std::string& feature, return this->LocalGenerator->GetFeature(feature, config); } +const char* cmGeneratorTarget::GetLinkPIEProperty( + const std::string& config) const +{ + static std::string PICValue; + + PICValue = this->GetLinkInterfaceDependentStringAsBoolProperty( + "POSITION_INDEPENDENT_CODE", config); + + if (PICValue == "(unset)") { + // POSITION_INDEPENDENT_CODE is not set + return nullptr; + } + + switch (this->GetPolicyStatusCMP0083()) { + case cmPolicies::WARN: { + std::ostringstream e; + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0083); + this->LocalGenerator->IssueMessage(cmake::AUTHOR_WARNING, e.str()); + CM_FALLTHROUGH; + } + case cmPolicies::OLD: + return nullptr; + default: + // nothing to do + break; + } + + return PICValue.c_str(); +} + bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang, std::string const& config) const { @@ -4237,6 +4267,29 @@ void cmGeneratorTarget::CheckPropertyCompatibility( } } +template <typename PropertyType> +std::string valueAsString(PropertyType); +template <> +std::string valueAsString<bool>(bool value) +{ + return value ? "TRUE" : "FALSE"; +} +template <> +std::string valueAsString<const char*>(const char* value) +{ + return value ? value : "(unset)"; +} +template <> +std::string valueAsString<std::string>(std::string value) +{ + return value; +} +template <> +std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/) +{ + return "(unset)"; +} + std::string compatibilityType(CompatibleType t) { switch (t) { @@ -4299,17 +4352,18 @@ const char* getTypedProperty<const char*>( return genexInterpreter->Evaluate(value, prop).c_str(); } -template <typename PropertyType> -std::string valueAsString(PropertyType); -template <> -std::string valueAsString<bool>(bool value) -{ - return value ? "TRUE" : "FALSE"; -} template <> -std::string valueAsString<const char*>(const char* value) +std::string getTypedProperty<std::string>( + cmGeneratorTarget const* tgt, const std::string& prop, + cmGeneratorExpressionInterpreter* genexInterpreter) { - return value ? value : "(unset)"; + const char* value = tgt->GetProperty(prop); + + if (genexInterpreter == nullptr) { + return valueAsString(value); + } + + return genexInterpreter->Evaluate(value, prop); } template <typename PropertyType> @@ -4324,6 +4378,12 @@ const char* impliedValue<const char*>(const char* /*unused*/) { return ""; } +template <> +std::string impliedValue<std::string>( + std::string /*unused*/) // NOLINT(clang-tidy) +{ + return std::string(); +} template <typename PropertyType> std::pair<bool, PropertyType> consistentProperty(PropertyType lhs, @@ -4344,6 +4404,13 @@ std::pair<bool, const char*> consistentStringProperty(const char* lhs, return std::make_pair(b, b ? lhs : nullptr); } +std::pair<bool, std::string> consistentStringProperty(const std::string& lhs, + const std::string& rhs) +{ + const bool b = lhs == rhs; + return std::make_pair(b, b ? lhs : valueAsString(nullptr)); +} + std::pair<bool, const char*> consistentNumberProperty(const char* lhs, const char* rhs, CompatibleType t) @@ -4386,9 +4453,10 @@ std::pair<bool, const char*> consistentProperty(const char* lhs, const char* const null_ptr = nullptr; switch (t) { - case BoolType: - assert(false && "consistentProperty for strings called with BoolType"); - return std::pair<bool, const char*>(false, null_ptr); + case BoolType: { + bool same = cmSystemTools::IsOn(lhs) == cmSystemTools::IsOn(rhs); + return std::make_pair(same, same ? lhs : nullptr); + } case StringType: return consistentStringProperty(lhs, rhs); case NumberMinType: @@ -4399,6 +4467,40 @@ std::pair<bool, const char*> consistentProperty(const char* lhs, return std::pair<bool, const char*>(false, null_ptr); } +std::pair<bool, std::string> consistentProperty(const std::string& lhs, + const std::string& rhs, + CompatibleType t) +{ + const std::string null_ptr = valueAsString(nullptr); + + if (lhs == null_ptr && rhs == null_ptr) { + return std::make_pair(true, lhs); + } + if (lhs == null_ptr) { + return std::make_pair(true, rhs); + } + if (rhs == null_ptr) { + return std::make_pair(true, lhs); + } + + switch (t) { + case BoolType: { + bool same = cmSystemTools::IsOn(lhs) == cmSystemTools::IsOn(rhs); + return std::make_pair(same, same ? lhs : null_ptr); + } + case StringType: + return consistentStringProperty(lhs, rhs); + case NumberMinType: + case NumberMaxType: { + auto value = consistentNumberProperty(lhs.c_str(), rhs.c_str(), t); + return std::make_pair( + value.first, value.first ? std::string(value.second) : null_ptr); + } + } + assert(false && "Unreachable!"); + return std::pair<bool, std::string>(false, null_ptr); +} + template <typename PropertyType> PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt, const std::string& p, @@ -4408,6 +4510,7 @@ PropertyType checkInterfacePropertyCompatibility(cmGeneratorTarget const* tgt, PropertyType* /*unused*/) { PropertyType propContent = getTypedProperty<PropertyType>(tgt, p); + std::vector<std::string> headPropKeys = tgt->GetPropertyKeys(); const bool explicitlySet = std::find(headPropKeys.begin(), headPropKeys.end(), p) != @@ -4552,6 +4655,13 @@ bool cmGeneratorTarget::GetLinkInterfaceDependentBoolProperty( BoolType, nullptr); } +std::string cmGeneratorTarget::GetLinkInterfaceDependentStringAsBoolProperty( + const std::string& p, const std::string& config) const +{ + return checkInterfacePropertyCompatibility<std::string>( + this, p, config, "FALSE", BoolType, nullptr); +} + const char* cmGeneratorTarget::GetLinkInterfaceDependentStringProperty( const std::string& p, const std::string& config) const { diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 5e7cf12..52defee 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -173,6 +173,8 @@ public: const char* GetFeature(const std::string& feature, const std::string& config) const; + const char* GetLinkPIEProperty(const std::string& config) const; + bool IsIPOEnabled(std::string const& lang, std::string const& config) const; bool IsLinkInterfaceDependentBoolProperty(const std::string& p, @@ -789,6 +791,9 @@ private: cmHeadToLinkInterfaceMap& GetHeadToLinkInterfaceUsageRequirementsMap( std::string const& config) const; + std::string GetLinkInterfaceDependentStringAsBoolProperty( + const std::string& p, const std::string& config) const; + // Cache import information from properties for each configuration. struct ImportInfo { diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index b7a361e..8a38f9b 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -1755,6 +1755,26 @@ void cmGlobalXCodeGenerator::CreateCustomRulesMakefile( } } +void cmGlobalXCodeGenerator::AddPositionIndependentLinkAttribute( + cmGeneratorTarget* target, cmXCodeObject* buildSettings, + const std::string& configName) +{ + // For now, only EXECUTABLE is concerned + if (target->GetType() != cmStateEnums::EXECUTABLE) { + return; + } + + const char* PICValue = target->GetLinkPIEProperty(configName); + if (PICValue == nullptr) { + // POSITION_INDEPENDENT_CODE is not set + return; + } + + buildSettings->AddAttribute( + "LD_NO_PIE", + this->CreateString(cmSystemTools::IsOn(PICValue) ? "NO" : "YES")); +} + void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, cmXCodeObject* buildSettings, const std::string& configName) @@ -1806,6 +1826,9 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, buildSettings->AddAttribute("LLVM_LTO", this->CreateString(ltoValue)); } + // Handle PIE linker configuration + this->AddPositionIndependentLinkAttribute(gtgt, buildSettings, configName); + // Add define flags this->CurrentLocalGenerator->AppendFlags( defFlags, this->CurrentMakefile->GetDefineFlags()); diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index 62f7030..9b0d4fe 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -171,6 +171,9 @@ private: const std::string& configName); cmXCodeObject* CreateUtilityTarget(cmGeneratorTarget* gtgt); void AddDependAndLinkInformation(cmXCodeObject* target); + void AddPositionIndependentLinkAttribute(cmGeneratorTarget* target, + cmXCodeObject* buildSettings, + const std::string& configName); void CreateBuildSettings(cmGeneratorTarget* gtgt, cmXCodeObject* buildSettings, const std::string& buildType); diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index a703f4d..da48950 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1201,6 +1201,8 @@ void cmLocalGenerator::GetTargetFlags( break; } + this->AppendPositionIndependentLinkerFlags(linkFlags, target, config, + linkLanguage); this->AppendIPOLinkerFlags(linkFlags, target, config, linkLanguage); } @@ -2027,6 +2029,36 @@ void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags, } } +void cmLocalGenerator::AppendPositionIndependentLinkerFlags( + std::string& flags, cmGeneratorTarget* target, const std::string& config, + const std::string& lang) +{ + // For now, only EXECUTABLE is concerned + if (target->GetType() != cmStateEnums::EXECUTABLE) { + return; + } + + const char* PICValue = target->GetLinkPIEProperty(config); + if (PICValue == nullptr) { + // POSITION_INDEPENDENT_CODE is not set + return; + } + + std::string name = "CMAKE_" + lang + "_LINK_OPTIONS_"; + name += cmSystemTools::IsOn(PICValue) ? "PIE" : "NO_PIE"; + + auto pieFlags = this->Makefile->GetSafeDefinition(name); + if (pieFlags.empty()) { + return; + } + + std::vector<std::string> flagsList; + cmSystemTools::ExpandListArgument(pieFlags, flagsList); + for (const auto& flag : flagsList) { + this->AppendFlagEscape(flags, flag); + } +} + void cmLocalGenerator::AppendCompileOptions(std::string& options, const char* options_list, const char* regex) const diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 3dd6929..2fa0070 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -126,6 +126,10 @@ public: void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target, const std::string& config, const std::string& lang); + void AppendPositionIndependentLinkerFlags(std::string& flags, + cmGeneratorTarget* target, + const std::string& config, + const std::string& lang); ///! Get the include flags for the current makefile and language std::string GetIncludeFlags(const std::vector<std::string>& includes, cmGeneratorTarget* target, diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 3c46784..9acae49 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -97,6 +97,9 @@ void cmMakefileTargetGenerator::GetTargetLinkFlags( this->GeneratorTarget->GetLinkOptions(opts, this->ConfigName, linkLanguage); // LINK_OPTIONS are escaped. this->LocalGenerator->AppendCompileOptions(flags, opts); + + this->LocalGenerator->AppendPositionIndependentLinkerFlags( + flags, this->GeneratorTarget, this->ConfigName, linkLanguage); } void cmMakefileTargetGenerator::CreateRuleFile() diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 5c27124..52ef470 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -244,7 +244,9 @@ class cmMakefile; SELECT(POLICY, CMP0082, \ "Install rules from add_subdirectory() are interleaved with those " \ "in caller.", \ - 3, 14, 0, cmPolicies::WARN) + 3, 14, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0083, "Add PIE options when linking executable.", 3, 14, \ + 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -271,7 +273,8 @@ class cmMakefile; F(CMP0069) \ F(CMP0073) \ F(CMP0076) \ - F(CMP0081) + F(CMP0081) \ + F(CMP0083) /** \class cmPolicies * \brief Handles changes in CMake behavior and policies diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index e23926f..b6b6519 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -190,7 +190,8 @@ add_RunCMake_test(PolicyScope) add_RunCMake_test(WriteCompilerDetectionHeader) add_RunCMake_test(SourceProperties) if(NOT WIN32) - add_RunCMake_test(PositionIndependentCode) + add_RunCMake_test(PositionIndependentCode -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} + -DCMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID}) endif() if(NOT CMAKE_GENERATOR MATCHES "Visual Studio") add_RunCMake_test(VisibilityPreset) diff --git a/Tests/RunCMake/PositionIndependentCode/CMP0083-cmp0083_new-check.cmake b/Tests/RunCMake/PositionIndependentCode/CMP0083-cmp0083_new-check.cmake new file mode 100644 index 0000000..255e63d --- /dev/null +++ b/Tests/RunCMake/PositionIndependentCode/CMP0083-cmp0083_new-check.cmake @@ -0,0 +1,22 @@ + +include ("${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/CMP0083_config.cmake") + + +# retrieve default type of executable +check_executable ("${cmp0083_ref}" ref) + +if (ref STREQUAL "PIE") + # check no_pie executable is really no position independent + check_executable ("${cmp0083_new_no_pie}" new_no_pie) + if (NOT new_no_pie STREQUAL "NO_PIE") + set (RunCMake_TEST_FAILED "CMP0083(NEW) do not produce expected executable.") + endif() +elseif (ref STREQUAL "NO_PIE") + # check pie executable is really position independent + check_executable ("${cmp0083_new_pie}" new_pie) + if (NOT new_pie MATCHES "PIE") + set (RunCMake_TEST_FAILED "CMP0083(NEW) do not produce expected executable.") + endif() +else() + set (RunCMake_TEST_FAILED "CMP0083(NEW) unexpected result.") +endif() diff --git a/Tests/RunCMake/PositionIndependentCode/CMP0083-cmp0083_old-check.cmake b/Tests/RunCMake/PositionIndependentCode/CMP0083-cmp0083_old-check.cmake new file mode 100644 index 0000000..b66b672 --- /dev/null +++ b/Tests/RunCMake/PositionIndependentCode/CMP0083-cmp0083_old-check.cmake @@ -0,0 +1,20 @@ + +include ("${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/CMP0083_config.cmake") + + +# retrieve default type of executable +check_executable ("${cmp0083_ref}" ref) + +# POSITION_INDEPENDENT_CODE must not have influence on executable +# pie and no_pie executable must have same type as reference +check_executable ("${cmp0083_old_pie}" old_pie) +if (NOT old_pie STREQUAL ref) + set (RunCMake_TEST_FAILED "CMP0083(OLD) do not produce expected executable.") + return() +endif() + +check_executable ("${cmp0083_old_no_pie}" old_no_pie) +if (NOT old_no_pie STREQUAL ref) + set (RunCMake_TEST_FAILED "CMP0083(OLD) do not produce expected executable.") + return() +endif() diff --git a/Tests/RunCMake/PositionIndependentCode/CMP0083.cmake b/Tests/RunCMake/PositionIndependentCode/CMP0083.cmake new file mode 100644 index 0000000..9713ea4 --- /dev/null +++ b/Tests/RunCMake/PositionIndependentCode/CMP0083.cmake @@ -0,0 +1,45 @@ + +# create reference to detect default : PIE or not +add_executable (cmp0083_ref main.cpp) + + +set (CMAKE_POSITION_INDEPENDENT_CODE ON) + +cmake_policy(SET CMP0083 NEW) +add_executable (cmp0083_new_pie main.cpp) + + +cmake_policy(SET CMP0083 OLD) +add_executable (cmp0083_old_pie main.cpp) + + +set (CMAKE_POSITION_INDEPENDENT_CODE OFF) + +cmake_policy(SET CMP0083 NEW) +add_executable (cmp0083_new_no_pie main.cpp) + + +cmake_policy(SET CMP0083 OLD) +add_executable (cmp0083_old_no_pie main.cpp) + +# high-level targets +add_custom_target(cmp0083_new) +add_dependencies(cmp0083_new cmp0083_ref cmp0083_new_pie cmp0083_new_no_pie) + +# high-level targets +add_custom_target(cmp0083_old) +add_dependencies(cmp0083_old cmp0083_ref cmp0083_old_pie cmp0083_old_no_pie) + + +# generate file holding paths to executables +file (GENERATE OUTPUT "${CMAKE_BINARY_DIR}/$<CONFIG>/CMP0083_config.cmake" + CONTENT +[==[ +include ("${RunCMake_TEST_SOURCE_DIR}/PIE_validator.cmake") + +set (cmp0083_ref "$<TARGET_FILE:cmp0083_ref>") +set (cmp0083_new_pie "$<TARGET_FILE:cmp0083_new_pie>") +set (cmp0083_old_pie "$<TARGET_FILE:cmp0083_old_pie>") +set (cmp0083_new_no_pie "$<TARGET_FILE:cmp0083_new_no_pie>") +set (cmp0083_old_no_pie "$<TARGET_FILE:cmp0083_old_no_pie>") +]==]) diff --git a/Tests/RunCMake/PositionIndependentCode/CheckPIESupported.cmake b/Tests/RunCMake/PositionIndependentCode/CheckPIESupported.cmake new file mode 100644 index 0000000..1e0a2c9 --- /dev/null +++ b/Tests/RunCMake/PositionIndependentCode/CheckPIESupported.cmake @@ -0,0 +1,12 @@ + +if (CMAKE_CXX_LINK_OPTIONS_PIE) + file(WRITE "${PIE_SUPPORTED}" "\nset(PIE_SUPPORTED TRUE)\n") +else() + file(WRITE "${PIE_SUPPORTED}" "\nset(PIE_SUPPORTED FALSE)\n") +endif() + +if (CMAKE_CXX_LINK_OPTIONS_NO_PIE) + file(APPEND "${PIE_SUPPORTED}" "\nset(NO_PIE_SUPPORTED TRUE)\n") +else() + file(APPEND "${PIE_SUPPORTED}" "\nset(NO_PIE_SUPPORTED FALSE)\n") +endif() diff --git a/Tests/RunCMake/PositionIndependentCode/PIE-pie_off-check.cmake b/Tests/RunCMake/PositionIndependentCode/PIE-pie_off-check.cmake new file mode 100644 index 0000000..096395c --- /dev/null +++ b/Tests/RunCMake/PositionIndependentCode/PIE-pie_off-check.cmake @@ -0,0 +1,7 @@ + +include ("${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/PIE_config.cmake") + +check_executable ("${pie_off}" status) +if (NOT status STREQUAL "NO_PIE") + set (RunCMake_TEST_FAILED "Executable is NOT 'no PIE' (${status}).") +endif() diff --git a/Tests/RunCMake/PositionIndependentCode/PIE-pie_on-check.cmake b/Tests/RunCMake/PositionIndependentCode/PIE-pie_on-check.cmake new file mode 100644 index 0000000..bf3d018 --- /dev/null +++ b/Tests/RunCMake/PositionIndependentCode/PIE-pie_on-check.cmake @@ -0,0 +1,7 @@ + +include ("${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/PIE_config.cmake") + +check_executable ("${pie_on}" status) +if (NOT status STREQUAL "PIE") + set (RunCMake_TEST_FAILED "Executable is NOT 'PIE' (${status}).") +endif() diff --git a/Tests/RunCMake/PositionIndependentCode/PIE.cmake b/Tests/RunCMake/PositionIndependentCode/PIE.cmake new file mode 100644 index 0000000..a9d579d --- /dev/null +++ b/Tests/RunCMake/PositionIndependentCode/PIE.cmake @@ -0,0 +1,19 @@ + +cmake_policy(SET CMP0083 NEW) + +add_executable (pie_on main.cpp) +set_property(TARGET pie_on PROPERTY POSITION_INDEPENDENT_CODE ON) + +add_executable (pie_off main.cpp) +set_property(TARGET pie_off PROPERTY POSITION_INDEPENDENT_CODE OFF) + + +# generate file holding paths to executables +file (GENERATE OUTPUT "${CMAKE_BINARY_DIR}/$<CONFIG>/PIE_config.cmake" + CONTENT +[==[ +include ("${RunCMake_TEST_SOURCE_DIR}/PIE_validator.cmake") + +set (pie_on "$<TARGET_FILE:pie_on>") +set (pie_off "$<TARGET_FILE:pie_off>") +]==]) diff --git a/Tests/RunCMake/PositionIndependentCode/PIE_validator.cmake b/Tests/RunCMake/PositionIndependentCode/PIE_validator.cmake new file mode 100644 index 0000000..7be35db --- /dev/null +++ b/Tests/RunCMake/PositionIndependentCode/PIE_validator.cmake @@ -0,0 +1,32 @@ + +include_guard(GLOBAL) + +function (CHECK_EXECUTABLE executable result) + set (${result} "UNKNOWN" PARENT_SCOPE) + + if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set (tool otool -hv) + else() + set (tool "${CMAKE_COMMAND}" -E env LANG=C LC_ALL=C readelf -lW) + endif() + + execute_process(COMMAND ${tool} "${executable}" + OUTPUT_VARIABLE output + ERROR_VARIABLE output) + + if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + if (output MATCHES "( |\t)PIE( |\n|$)") + set (${result} "PIE" PARENT_SCOPE) + else() + set (${result} "NO_PIE" PARENT_SCOPE) + endif() + else() + if (output MATCHES "Elf file type is DYN") + set (${result} "PIE" PARENT_SCOPE) + elseif (output MATCHES "Elf file type is EXEC") + set (${result} "NO_PIE" PARENT_SCOPE) + else() + message(SEND_ERROR "Did not find a known file type") + endif() + endif() +endfunction() diff --git a/Tests/RunCMake/PositionIndependentCode/RunCMakeTest.cmake b/Tests/RunCMake/PositionIndependentCode/RunCMakeTest.cmake index 20187d3..6efa0d4 100644 --- a/Tests/RunCMake/PositionIndependentCode/RunCMakeTest.cmake +++ b/Tests/RunCMake/PositionIndependentCode/RunCMakeTest.cmake @@ -9,3 +9,65 @@ run_cmake(Conflict6) run_cmake(Debug) run_cmake(Genex1) run_cmake(Genex2) + +set(RunCMake_TEST_OPTIONS "-DPIE_SUPPORTED=${RunCMake_BINARY_DIR}/PIESupported.cmake") +run_cmake(CheckPIESupported) +include ("${RunCMake_BINARY_DIR}/PIESupported.cmake" OPTIONAL) + +if (PIE_SUPPORTED OR NO_PIE_SUPPORTED) + if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|(Free|Net|Open)BSD)$") + # try to locate readelf needed for validation + find_program (READELF NAMES readelf) + endif() + if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + # try to locate otool needed for validation + find_program (OTOOL NAMES otool) + endif() + + if ((READELF OR OTOOL) AND + (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" + OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")) + macro(run_cmake_target test subtest) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_CONFIG Release) + run_cmake_command(${test}-${subtest} ${CMAKE_COMMAND} --build . --config Release --target ${subtest} ${ARGN}) + + unset(RunCMake_TEST_BINARY_DIR) + unset(RunCMake_TEST_NO_CLEAN) + endmacro() + + set(RunCMake_TEST_SOURCE_DIR "${RunCMake_SOURCE_DIR}") + set(RunCMake_TEST_OUTPUT_MERGE TRUE) + if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release) + endif() + + run_cmake(PIE) + if (PIE_SUPPORTED) + run_cmake_target(PIE pie_on) + endif() + if (NO_PIE_SUPPORTED) + run_cmake_target(PIE pie_off) + endif() + + run_cmake(CMP0083) + run_cmake_target(CMP0083 cmp0083_ref) + + # retrieve default mode + include("${RunCMake_SOURCE_DIR}/PIE_validator.cmake") + include("${RunCMake_BINARY_DIR}/CMP0083-build/Release/CMP0083_config.cmake") + check_executable("${cmp0083_ref}" cmp0083_ref_mode) + + if ((cmp0083_ref_mode STREQUAL "PIE" AND NO_PIE_SUPPORTED) + OR (cmp0083_ref_mode STREQUAL "NO_PIE" AND PIE_SUPPORTED)) + run_cmake_target(CMP0083 cmp0083_new) + endif() + run_cmake_target(CMP0083 cmp0083_old) + + unset(RunCMake_TEST_SOURCE_DIR) + unset(RunCMake_TEST_OPTIONS) + unset(RunCMake_TEST_OUTPUT_MERGE) + endif() +endif() diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt index 2441a9c..0bcf886 100644 --- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt +++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt @@ -26,6 +26,7 @@ \* CMP0073 \* CMP0076 \* CMP0081 + \* CMP0083 Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) |