Skip to content

Commit

Permalink
Merge #32: cmake: Add hardening, reduce exports, werror and install
Browse files Browse the repository at this point in the history
45f8a3c cmake: Implement `make install` (Hennadii Stepanov)
c9decae cmake: Add `WERROR` option (Hennadii Stepanov)
1ea5f1c cmake: Add `REDUCE_EXPORTS` option (Hennadii Stepanov)
643decf cmake: Add `HARDENING` option (Hennadii Stepanov)
574d66d cmake: Add general compile options (Hennadii Stepanov)
ac1e28e fixup! cmake: Redefine configuration flags (Hennadii Stepanov)
4375266 fixup! cmake: Add platform-specific linker flags (Hennadii Stepanov)
e3a378b fixup! cmake: Add `TryAppendLinkerFlag` module (Hennadii Stepanov)
40973d6 fixup! cmake: Add `TryAppendCXXFlags` module (Hennadii Stepanov)

Pull request description:

  New CMake variables that affect the build system configuration:
  - `HARDENING`
  - `REDUCE_EXPORTS`
  - `WERROR`

Top commit has no ACKs.

Tree-SHA512: cd849a28ff6a63730d6ee7062938d86096ca4d7b2fd4277a6026ba38855a88b1854476d429c5ad4cf79008f04cfcbc8ad947794b60e91f7f5b8853490d19e678
  • Loading branch information
hebasto committed Oct 29, 2023
2 parents f4162d0 + 45f8a3c commit 6e20a0f
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 94 deletions.
125 changes: 92 additions & 33 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ cmake_dependent_option(BUILD_WALLET_TOOL "Build bitcoin-wallet tool." ON "ENABLE

cmake_dependent_option(CXX20 "Enable compilation in C++20 mode." OFF "NOT MSVC" ON)
option(THREADLOCAL "Enable features that depend on the C++ thread_local keyword (currently just thread names in debug logs)." ON)
option(HARDENING "Attempt to harden the resulting executables." ON)
option(REDUCE_EXPORTS "Attempt to reduce exported symbols in the resulting executables." OFF)
option(WERROR "Treat compiler warnings as errors." OFF)

tristate_option(CCACHE "Use ccache for compiling." "if ccache is found." AUTO)
tristate_option(WITH_NATPMP "Enable NAT-PMP." "if libnatpmp is found." AUTO)
Expand All @@ -66,6 +69,7 @@ tristate_option(WITH_USDT

option(BUILD_TESTS "Build test_bitcoin executable." ON)
option(BUILD_BENCH "Build bench_bitcoin executable." ON)
option(INSTALL_MAN "Install man pages." ON)

if(CXX20)
set(CMAKE_CXX_STANDARD 20)
Expand Down Expand Up @@ -135,10 +139,10 @@ if(WIN32)
_WINDOWS
_MT
)
try_append_linker_flag(core "-static")
try_append_linker_flag("-static" TARGET core)
# We require Windows 7 (NT 6.1) or later.
try_append_linker_flag(core "-Wl,--major-subsystem-version,6")
try_append_linker_flag(core "-Wl,--minor-subsystem-version,1")
try_append_linker_flag("-Wl,--major-subsystem-version,6" TARGET core)
try_append_linker_flag("-Wl,--minor-subsystem-version,1" TARGET core)
endif()
endif()

Expand All @@ -157,9 +161,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# These flags are specific to ld64, and may cause issues with other linkers.
# For example: GNU ld will interpret -dead_strip as -de and then try and use
# "ad_strip" as the symbol for the entry point.
try_append_linker_flag(core "-Wl,-dead_strip")
try_append_linker_flag(core "-Wl,-dead_strip_dylibs")
try_append_linker_flag(core "-Wl,-headerpad_max_install_names")
try_append_linker_flag("-Wl,-dead_strip" TARGET core)
try_append_linker_flag("-Wl,-dead_strip_dylibs" TARGET core)
try_append_linker_flag("-Wl,-headerpad_max_install_names" TARGET core)
endif()

if(CMAKE_CROSSCOMPILING)
Expand Down Expand Up @@ -212,25 +216,89 @@ else()
replace_c_flag_in_config(Release -O3 -O2)
replace_cxx_flag_in_config(Release -O3 -O2)

set(debug_c_flags "")
set(debug_cxx_flags "")
try_append_cxx_flags(debug_cxx_flags "-O0" RESULT_VAR compiler_supports_O0)
if(compiler_supports_O0)
string(STRIP "${debug_c_flags} -O0" debug_c_flags)
set(debug_flags)
try_append_cxx_flags("-O0" VAR debug_flags)
try_append_cxx_flags("-g3" VAR debug_flags RESULT_VAR compiler_supports_g3)
if(NOT compiler_supports_g3)
try_append_cxx_flags("-g" VAR debug_flags)
endif()
try_append_cxx_flags(debug_cxx_flags "-g3" RESULT_VAR compiler_supports_g3)
if(compiler_supports_g3)
string(STRIP "${debug_c_flags} -g3" debug_c_flags)
set(CMAKE_C_FLAGS_DEBUG "${debug_flags}")
try_append_cxx_flags("-ftrapv" VAR debug_flags)
set(CMAKE_CXX_FLAGS_DEBUG "${debug_flags}")
unset(debug_flags)
endif()

include(cmake/optional.cmake)

# Don't allow extended (non-ASCII) symbols in identifiers. This is easier for code review.
try_append_cxx_flags("-fno-extended-identifiers" TARGET core)

# Currently all versions of gcc are subject to a class of bugs, see the
# gccbug_90348 test case (only reproduces on GCC 11 and earlier) and
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111843. To work around that, set
# -fstack-reuse=none for all gcc builds. (Only gcc understands this flag).
try_append_cxx_flags("-fstack-reuse=none" TARGET core)

if(HARDENING)
add_library(hardening INTERFACE)
if(MSVC)
try_append_linker_flag("/DYNAMICBASE" TARGET hardening)
try_append_linker_flag("/HIGHENTROPYVA" TARGET hardening)
try_append_linker_flag("/NXCOMPAT" TARGET hardening)
else()
try_append_cxx_flags(debug_cxx_flags "-g")
string(STRIP "${debug_c_flags} -g" debug_c_flags)
target_compile_options(hardening INTERFACE
$<$<NOT:$<CONFIG:Debug>>:-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3>
)

try_append_cxx_flags("-Wstack-protector" TARGET hardening)
try_append_cxx_flags("-fstack-protector-all" TARGET hardening)
try_append_cxx_flags("-fcf-protection=full" TARGET hardening)

if(MINGW)
# stack-clash-protection doesn't compile with GCC 10 and earlier.
# In any case, it is a no-op for Windows.
# See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90458 for more details.
else()
try_append_cxx_flags("-fstack-clash-protection" TARGET hardening)
endif()

if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
try_append_cxx_flags("-mbranch-protection=bti" TARGET hardening)
endif()

try_append_linker_flag("-Wl,--enable-reloc-section" TARGET hardening)
try_append_linker_flag("-Wl,--dynamicbase" TARGET hardening)
try_append_linker_flag("-Wl,--nxcompat" TARGET hardening)
try_append_linker_flag("-Wl,--high-entropy-va" TARGET hardening)
try_append_linker_flag("-Wl,-z,relro" TARGET hardening)
try_append_linker_flag("-Wl,-z,now" TARGET hardening)
try_append_linker_flag("-Wl,-z,separate-code" TARGET hardening)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
try_append_linker_flag("-Wl,-bind_at_load" TARGET hardening)
try_append_linker_flag("-Wl,-fixup_chains" TARGET hardening)
endif()
endif()
try_append_cxx_flags(debug_cxx_flags "-ftrapv")
set(CMAKE_C_FLAGS_DEBUG "${debug_c_flags}")
set(CMAKE_CXX_FLAGS_DEBUG "${debug_cxx_flags}")
target_link_libraries(core INTERFACE hardening)
endif()

include(cmake/optional.cmake)
if(REDUCE_EXPORTS)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
try_append_linker_flag("-Wl,--exclude-libs,ALL" TARGET core)
endif()

if(WERROR)
if(MSVC)
set(werror_flag "/WX")
else()
set(werror_flag "-Werror")
endif()
try_append_cxx_flags(${werror_flag} TARGET core RESULT_VAR compiler_supports_werror)
if(NOT compiler_supports_werror)
message(FATAL_ERROR "WERROR set but ${werror_flag} is not usable.")
endif()
unset(werror_flag)
endif()

find_package(Python3 3.9 COMPONENTS Interpreter)
set(PYTHON_COMMAND ${Python3_EXECUTABLE})
Expand Down Expand Up @@ -279,25 +347,16 @@ message("C++ compiler .......................... ${CMAKE_CXX_COMPILER}")
list(JOIN DEPENDS_CXX_COMPILER_FLAGS " " depends_cxx_flags)
string(STRIP "${CMAKE_CXX_FLAGS} ${depends_cxx_flags}" combined_cxx_flags)
message("CXXFLAGS .............................. ${combined_cxx_flags}")
get_target_property(common_compile_options core INTERFACE_COMPILE_OPTIONS)
if(common_compile_options)
list(JOIN common_compile_options " " common_compile_options)
else()
set(common_compile_options)
endif()
string(GENEX_STRIP "${common_compile_options}" common_compile_options)
get_target_interface(common_compile_options core COMPILE_OPTIONS)
message("Common compile options ................ ${common_compile_options}")
get_target_property(common_link_options core INTERFACE_LINK_OPTIONS)
if(common_link_options)
list(JOIN common_link_options " " common_link_options)
else()
set(common_link_options)
endif()
get_target_interface(common_link_options core LINK_OPTIONS)
message("Common link options ................... ${common_link_options}")
message("Linker flags for executables .......... ${CMAKE_EXE_LINKER_FLAGS}")
message("Linker flags for shared libraries ..... ${CMAKE_SHARED_LINKER_FLAGS}")
print_config_flags()
message("Use assembly routines ................. ${ASM}")
message("Attempt to harden executables ......... ${HARDENING}")
message("Treat compiler warnings as errors ..... ${WERROR}")
message("Use ccache for compiling .............. ${CCACHE}")
message("\n")
if(configure_warnings)
Expand Down
23 changes: 22 additions & 1 deletion cmake/module/ProcessConfigurations.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,12 @@ endfunction()
function(separate_by_configs options)
list(JOIN ${options} " " ${options}_ALL)
string(GENEX_STRIP "${${options}_ALL}" ${options}_ALL)
string(STRIP "${${options}_ALL}" ${options}_ALL)
set(${options}_ALL "${${options}_ALL}" PARENT_SCOPE)

get_all_configs(all_configs)
foreach(config IN LISTS all_configs)
string(REGEX MATCHALL "\\$<\\$<CONFIG:${config}>[^\n]*>" match "${${options}}")
string(REGEX MATCHALL "\\$<\\$<CONFIG:${config}>:[^<>\n]*>" match "${${options}}")
list(JOIN match " " match)
string(REPLACE "\$<\$<CONFIG:${config}>:" "" match "${match}")
string(REPLACE ">" "" match "${match}")
Expand All @@ -106,6 +107,26 @@ function(separate_by_configs options)
endforeach()
endfunction()

function(get_target_interface var target property)
get_target_property(result ${target} INTERFACE_${property})
if(result)
string(GENEX_STRIP "${result}" result)
list(JOIN result " " result)
else()
set(result)
endif()

get_target_property(dependencies ${target} INTERFACE_LINK_LIBRARIES)
if(dependencies)
foreach(dependency IN LISTS dependencies)
get_target_interface(dep_result ${dependency} ${property})
string(STRIP "${result} ${dep_result}" result)
endforeach()
endif()

set(${var} "${result}" PARENT_SCOPE)
endfunction()

function(print_config_flags)
macro(print_flags config)
string(TOUPPER "${config}" config_uppercase)
Expand Down
76 changes: 49 additions & 27 deletions cmake/module/TryAppendCXXFlags.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,28 @@ include(CheckCXXSourceCompiles)
#[=[
Usage examples:

try_append_cxx_flags(warn_cxx_flags "-Wformat -Wformat-security")
try_append_cxx_flags("-Wformat -Wformat-security" VAR warn_cxx_flags)


try_append_cxx_flags(warn_cxx_flags "-Wsuggest-override"
try_append_cxx_flags("-Wsuggest-override" VAR warn_cxx_flags
SOURCE "struct A { virtual void f(); }; struct B : A { void f() final; };"
)


try_append_cxx_flags(sanitizers_cxx_flags "-fsanitize=${SANITIZERS}" RESULT_VAR cxx_supports_sanitizers)
try_append_cxx_flags("-fsanitize=${SANITIZERS}" TARGET core
RESULT_VAR cxx_supports_sanitizers
)
if(NOT cxx_supports_sanitizers)
message(FATAL_ERROR "Compiler did not accept requested flags.")
endif()


try_append_cxx_flags(warn_cxx_flags "-Wunused-parameter" IF_CHECK_PASSED "-Wno-unused-parameter")
try_append_cxx_flags("-Wunused-parameter" TARGET core
IF_CHECK_PASSED "-Wno-unused-parameter"
)


try_append_cxx_flags(error_cxx_flags "-Werror=return-type"
try_append_cxx_flags("-Werror=return-type" TARGET core
IF_CHECK_FAILED "-Wno-error=return-type"
SOURCE "#include <cassert>\nint f(){ assert(false); }"
)
Expand All @@ -37,49 +41,67 @@ In configuration output, this function prints a string by the following pattern:
-- Performing Test CXX_SUPPORTS_[flags] - Success

]=]
function(try_append_cxx_flags flags_var flags)
cmake_parse_arguments(PARSE_ARGV 2
function(try_append_cxx_flags flags)
cmake_parse_arguments(PARSE_ARGV 1
TACXXF # prefix
"" # options
"SOURCE;RESULT_VAR" # one_value_keywords
"TARGET;VAR;SOURCE;RESULT_VAR" # one_value_keywords
"IF_CHECK_PASSED;IF_CHECK_FAILED" # multi_value_keywords
)

string(MAKE_C_IDENTIFIER "${flags}" result)
string(TOUPPER "${result}" result)
set(result "CXX_SUPPORTS_${result}")

# Every subsequent check_cxx_source_compiles((<code> <resultVar>) run will re-use
# the cached result rather than performing the check again, even if the <code> changes.
# Removing the cached result in order to force the check to be re-evaluated.
unset(${result} CACHE)

if(NOT DEFINED TACXXF_SOURCE)
set(TACXXF_SOURCE "int main() { return 0; }")
string(PREPEND result CXX_SUPPORTS_)

set(source "int main() { return 0; }")
if(DEFINED TACXXF_SOURCE AND NOT TACXXF_SOURCE STREQUAL source)
set(source "${TACXXF_SOURCE}")
string(SHA256 source_hash "${source}")
string(SUBSTRING ${source_hash} 0 4 source_hash_head)
string(APPEND result _${source_hash_head})
endif()

# This avoids running a linker.
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(CMAKE_REQUIRED_FLAGS "${flags} ${working_compiler_werror_flag}")
check_cxx_source_compiles("${TACXXF_SOURCE}" ${result})
check_cxx_source_compiles("${source}" ${result})

if(${result})
if(DEFINED TACXXF_IF_CHECK_PASSED)
string(STRIP "${${flags_var}} ${TACXXF_IF_CHECK_PASSED}" ${flags_var})
if(DEFINED TACXXF_TARGET)
target_compile_options(${TACXXF_TARGET} INTERFACE ${TACXXF_IF_CHECK_PASSED})
endif()
if(DEFINED TACXXF_VAR)
string(STRIP "${${TACXXF_VAR}} ${TACXXF_IF_CHECK_PASSED}" ${TACXXF_VAR})
endif()
else()
string(STRIP "${${flags_var}} ${flags}" ${flags_var})
if(DEFINED TACXXF_TARGET)
target_compile_options(${TACXXF_TARGET} INTERFACE ${flags})
endif()
if(DEFINED TACXXF_VAR)
string(STRIP "${${TACXXF_VAR}} ${flags}" ${TACXXF_VAR})
endif()
endif()
elseif(DEFINED TACXXF_IF_CHECK_FAILED)
string(STRIP "${${flags_var}} ${TACXXF_IF_CHECK_FAILED}" ${flags_var})
if(DEFINED TACXXF_TARGET)
target_compile_options(${TACXXF_TARGET} INTERFACE ${TACXXF_IF_CHECK_FAILED})
endif()
if(DEFINED TACXXF_VAR)
string(STRIP "${${TACXXF_VAR}} ${TACXXF_IF_CHECK_FAILED}" ${TACXXF_VAR})
endif()
endif()

if(DEFINED TACXXF_VAR)
set(${TACXXF_VAR} "${${TACXXF_VAR}}" PARENT_SCOPE)
endif()

if(DEFINED TACXXF_RESULT_VAR)
set(${TACXXF_RESULT_VAR} "${${result}}" PARENT_SCOPE)
endif()
set(${flags_var} "${${flags_var}}" PARENT_SCOPE)
set(${TACXXF_RESULT_VAR} "${${result}}" PARENT_SCOPE)
endfunction()

if(MSVC)
set(warning_as_error_flag /WX)
try_append_cxx_flags("/WX /options:strict" VAR working_compiler_werror_flag)
else()
set(warning_as_error_flag -Werror)
try_append_cxx_flags("-Werror" VAR working_compiler_werror_flag)
endif()
try_append_cxx_flags(working_compiler_werror_flag ${warning_as_error_flag})
unset(warning_as_error_flag)
Loading

0 comments on commit 6e20a0f

Please sign in to comment.