POSITION_INDEPENDENT_CODE: Manage link flags for executables

Fixes: #14983, #16561
This commit is contained in:
Marc Chevrier 2018-10-02 17:34:57 +02:00
parent 724a0346f7
commit c4b4d8b3a6
43 changed files with 683 additions and 20 deletions

View File

@ -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

24
Help/policy/CMP0083.rst Normal file
View File

@ -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

View File

@ -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`.

View File

@ -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)

View File

@ -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})

View File

@ -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")

View File

@ -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()

View File

@ -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)

View File

@ -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=")

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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,")

View File

@ -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")

View File

@ -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 " ")

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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})

View File

@ -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)
std::string getTypedProperty<std::string>(
cmGeneratorTarget const* tgt, const std::string& prop,
cmGeneratorExpressionInterpreter* genexInterpreter)
{
return value ? "TRUE" : "FALSE";
}
template <>
std::string valueAsString<const char*>(const char* value)
{
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
{

View File

@ -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
{

View File

@ -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());

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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>")
]==])

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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>")
]==])

View File

@ -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()

View File

@ -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()

View File

@ -26,6 +26,7 @@
\* CMP0073
\* CMP0076
\* CMP0081
\* CMP0083
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)