Skip to content

Commit 6e20a0f

Browse files
committed
Merge #32: cmake: Add hardening, reduce exports, werror and install
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
2 parents f4162d0 + 45f8a3c commit 6e20a0f

7 files changed

+234
-94
lines changed

CMakeLists.txt

+92-33
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ cmake_dependent_option(BUILD_WALLET_TOOL "Build bitcoin-wallet tool." ON "ENABLE
5353

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

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

6770
option(BUILD_TESTS "Build test_bitcoin executable." ON)
6871
option(BUILD_BENCH "Build bench_bitcoin executable." ON)
72+
option(INSTALL_MAN "Install man pages." ON)
6973

7074
if(CXX20)
7175
set(CMAKE_CXX_STANDARD 20)
@@ -135,10 +139,10 @@ if(WIN32)
135139
_WINDOWS
136140
_MT
137141
)
138-
try_append_linker_flag(core "-static")
142+
try_append_linker_flag("-static" TARGET core)
139143
# We require Windows 7 (NT 6.1) or later.
140-
try_append_linker_flag(core "-Wl,--major-subsystem-version,6")
141-
try_append_linker_flag(core "-Wl,--minor-subsystem-version,1")
144+
try_append_linker_flag("-Wl,--major-subsystem-version,6" TARGET core)
145+
try_append_linker_flag("-Wl,--minor-subsystem-version,1" TARGET core)
142146
endif()
143147
endif()
144148

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

165169
if(CMAKE_CROSSCOMPILING)
@@ -212,25 +216,89 @@ else()
212216
replace_c_flag_in_config(Release -O3 -O2)
213217
replace_cxx_flag_in_config(Release -O3 -O2)
214218

215-
set(debug_c_flags "")
216-
set(debug_cxx_flags "")
217-
try_append_cxx_flags(debug_cxx_flags "-O0" RESULT_VAR compiler_supports_O0)
218-
if(compiler_supports_O0)
219-
string(STRIP "${debug_c_flags} -O0" debug_c_flags)
219+
set(debug_flags)
220+
try_append_cxx_flags("-O0" VAR debug_flags)
221+
try_append_cxx_flags("-g3" VAR debug_flags RESULT_VAR compiler_supports_g3)
222+
if(NOT compiler_supports_g3)
223+
try_append_cxx_flags("-g" VAR debug_flags)
220224
endif()
221-
try_append_cxx_flags(debug_cxx_flags "-g3" RESULT_VAR compiler_supports_g3)
222-
if(compiler_supports_g3)
223-
string(STRIP "${debug_c_flags} -g3" debug_c_flags)
225+
set(CMAKE_C_FLAGS_DEBUG "${debug_flags}")
226+
try_append_cxx_flags("-ftrapv" VAR debug_flags)
227+
set(CMAKE_CXX_FLAGS_DEBUG "${debug_flags}")
228+
unset(debug_flags)
229+
endif()
230+
231+
include(cmake/optional.cmake)
232+
233+
# Don't allow extended (non-ASCII) symbols in identifiers. This is easier for code review.
234+
try_append_cxx_flags("-fno-extended-identifiers" TARGET core)
235+
236+
# Currently all versions of gcc are subject to a class of bugs, see the
237+
# gccbug_90348 test case (only reproduces on GCC 11 and earlier) and
238+
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111843. To work around that, set
239+
# -fstack-reuse=none for all gcc builds. (Only gcc understands this flag).
240+
try_append_cxx_flags("-fstack-reuse=none" TARGET core)
241+
242+
if(HARDENING)
243+
add_library(hardening INTERFACE)
244+
if(MSVC)
245+
try_append_linker_flag("/DYNAMICBASE" TARGET hardening)
246+
try_append_linker_flag("/HIGHENTROPYVA" TARGET hardening)
247+
try_append_linker_flag("/NXCOMPAT" TARGET hardening)
224248
else()
225-
try_append_cxx_flags(debug_cxx_flags "-g")
226-
string(STRIP "${debug_c_flags} -g" debug_c_flags)
249+
target_compile_options(hardening INTERFACE
250+
$<$<NOT:$<CONFIG:Debug>>:-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3>
251+
)
252+
253+
try_append_cxx_flags("-Wstack-protector" TARGET hardening)
254+
try_append_cxx_flags("-fstack-protector-all" TARGET hardening)
255+
try_append_cxx_flags("-fcf-protection=full" TARGET hardening)
256+
257+
if(MINGW)
258+
# stack-clash-protection doesn't compile with GCC 10 and earlier.
259+
# In any case, it is a no-op for Windows.
260+
# See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90458 for more details.
261+
else()
262+
try_append_cxx_flags("-fstack-clash-protection" TARGET hardening)
263+
endif()
264+
265+
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
266+
try_append_cxx_flags("-mbranch-protection=bti" TARGET hardening)
267+
endif()
268+
269+
try_append_linker_flag("-Wl,--enable-reloc-section" TARGET hardening)
270+
try_append_linker_flag("-Wl,--dynamicbase" TARGET hardening)
271+
try_append_linker_flag("-Wl,--nxcompat" TARGET hardening)
272+
try_append_linker_flag("-Wl,--high-entropy-va" TARGET hardening)
273+
try_append_linker_flag("-Wl,-z,relro" TARGET hardening)
274+
try_append_linker_flag("-Wl,-z,now" TARGET hardening)
275+
try_append_linker_flag("-Wl,-z,separate-code" TARGET hardening)
276+
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
277+
try_append_linker_flag("-Wl,-bind_at_load" TARGET hardening)
278+
try_append_linker_flag("-Wl,-fixup_chains" TARGET hardening)
279+
endif()
227280
endif()
228-
try_append_cxx_flags(debug_cxx_flags "-ftrapv")
229-
set(CMAKE_C_FLAGS_DEBUG "${debug_c_flags}")
230-
set(CMAKE_CXX_FLAGS_DEBUG "${debug_cxx_flags}")
281+
target_link_libraries(core INTERFACE hardening)
231282
endif()
232283

233-
include(cmake/optional.cmake)
284+
if(REDUCE_EXPORTS)
285+
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
286+
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
287+
try_append_linker_flag("-Wl,--exclude-libs,ALL" TARGET core)
288+
endif()
289+
290+
if(WERROR)
291+
if(MSVC)
292+
set(werror_flag "/WX")
293+
else()
294+
set(werror_flag "-Werror")
295+
endif()
296+
try_append_cxx_flags(${werror_flag} TARGET core RESULT_VAR compiler_supports_werror)
297+
if(NOT compiler_supports_werror)
298+
message(FATAL_ERROR "WERROR set but ${werror_flag} is not usable.")
299+
endif()
300+
unset(werror_flag)
301+
endif()
234302

235303
find_package(Python3 3.9 COMPONENTS Interpreter)
236304
set(PYTHON_COMMAND ${Python3_EXECUTABLE})
@@ -279,25 +347,16 @@ message("C++ compiler .......................... ${CMAKE_CXX_COMPILER}")
279347
list(JOIN DEPENDS_CXX_COMPILER_FLAGS " " depends_cxx_flags)
280348
string(STRIP "${CMAKE_CXX_FLAGS} ${depends_cxx_flags}" combined_cxx_flags)
281349
message("CXXFLAGS .............................. ${combined_cxx_flags}")
282-
get_target_property(common_compile_options core INTERFACE_COMPILE_OPTIONS)
283-
if(common_compile_options)
284-
list(JOIN common_compile_options " " common_compile_options)
285-
else()
286-
set(common_compile_options)
287-
endif()
288-
string(GENEX_STRIP "${common_compile_options}" common_compile_options)
350+
get_target_interface(common_compile_options core COMPILE_OPTIONS)
289351
message("Common compile options ................ ${common_compile_options}")
290-
get_target_property(common_link_options core INTERFACE_LINK_OPTIONS)
291-
if(common_link_options)
292-
list(JOIN common_link_options " " common_link_options)
293-
else()
294-
set(common_link_options)
295-
endif()
352+
get_target_interface(common_link_options core LINK_OPTIONS)
296353
message("Common link options ................... ${common_link_options}")
297354
message("Linker flags for executables .......... ${CMAKE_EXE_LINKER_FLAGS}")
298355
message("Linker flags for shared libraries ..... ${CMAKE_SHARED_LINKER_FLAGS}")
299356
print_config_flags()
300357
message("Use assembly routines ................. ${ASM}")
358+
message("Attempt to harden executables ......... ${HARDENING}")
359+
message("Treat compiler warnings as errors ..... ${WERROR}")
301360
message("Use ccache for compiling .............. ${CCACHE}")
302361
message("\n")
303362
if(configure_warnings)

cmake/module/ProcessConfigurations.cmake

+22-1
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,12 @@ endfunction()
9393
function(separate_by_configs options)
9494
list(JOIN ${options} " " ${options}_ALL)
9595
string(GENEX_STRIP "${${options}_ALL}" ${options}_ALL)
96+
string(STRIP "${${options}_ALL}" ${options}_ALL)
9697
set(${options}_ALL "${${options}_ALL}" PARENT_SCOPE)
9798

9899
get_all_configs(all_configs)
99100
foreach(config IN LISTS all_configs)
100-
string(REGEX MATCHALL "\\$<\\$<CONFIG:${config}>[^\n]*>" match "${${options}}")
101+
string(REGEX MATCHALL "\\$<\\$<CONFIG:${config}>:[^<>\n]*>" match "${${options}}")
101102
list(JOIN match " " match)
102103
string(REPLACE "\$<\$<CONFIG:${config}>:" "" match "${match}")
103104
string(REPLACE ">" "" match "${match}")
@@ -106,6 +107,26 @@ function(separate_by_configs options)
106107
endforeach()
107108
endfunction()
108109

110+
function(get_target_interface var target property)
111+
get_target_property(result ${target} INTERFACE_${property})
112+
if(result)
113+
string(GENEX_STRIP "${result}" result)
114+
list(JOIN result " " result)
115+
else()
116+
set(result)
117+
endif()
118+
119+
get_target_property(dependencies ${target} INTERFACE_LINK_LIBRARIES)
120+
if(dependencies)
121+
foreach(dependency IN LISTS dependencies)
122+
get_target_interface(dep_result ${dependency} ${property})
123+
string(STRIP "${result} ${dep_result}" result)
124+
endforeach()
125+
endif()
126+
127+
set(${var} "${result}" PARENT_SCOPE)
128+
endfunction()
129+
109130
function(print_config_flags)
110131
macro(print_flags config)
111132
string(TOUPPER "${config}" config_uppercase)

cmake/module/TryAppendCXXFlags.cmake

+49-27
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,28 @@ include(CheckCXXSourceCompiles)
88
#[=[
99
Usage examples:
1010
11-
try_append_cxx_flags(warn_cxx_flags "-Wformat -Wformat-security")
11+
try_append_cxx_flags("-Wformat -Wformat-security" VAR warn_cxx_flags)
1212
1313
14-
try_append_cxx_flags(warn_cxx_flags "-Wsuggest-override"
14+
try_append_cxx_flags("-Wsuggest-override" VAR warn_cxx_flags
1515
SOURCE "struct A { virtual void f(); }; struct B : A { void f() final; };"
1616
)
1717
1818
19-
try_append_cxx_flags(sanitizers_cxx_flags "-fsanitize=${SANITIZERS}" RESULT_VAR cxx_supports_sanitizers)
19+
try_append_cxx_flags("-fsanitize=${SANITIZERS}" TARGET core
20+
RESULT_VAR cxx_supports_sanitizers
21+
)
2022
if(NOT cxx_supports_sanitizers)
2123
message(FATAL_ERROR "Compiler did not accept requested flags.")
2224
endif()
2325
2426
25-
try_append_cxx_flags(warn_cxx_flags "-Wunused-parameter" IF_CHECK_PASSED "-Wno-unused-parameter")
27+
try_append_cxx_flags("-Wunused-parameter" TARGET core
28+
IF_CHECK_PASSED "-Wno-unused-parameter"
29+
)
2630
2731
28-
try_append_cxx_flags(error_cxx_flags "-Werror=return-type"
32+
try_append_cxx_flags("-Werror=return-type" TARGET core
2933
IF_CHECK_FAILED "-Wno-error=return-type"
3034
SOURCE "#include <cassert>\nint f(){ assert(false); }"
3135
)
@@ -37,49 +41,67 @@ In configuration output, this function prints a string by the following pattern:
3741
-- Performing Test CXX_SUPPORTS_[flags] - Success
3842
3943
]=]
40-
function(try_append_cxx_flags flags_var flags)
41-
cmake_parse_arguments(PARSE_ARGV 2
44+
function(try_append_cxx_flags flags)
45+
cmake_parse_arguments(PARSE_ARGV 1
4246
TACXXF # prefix
4347
"" # options
44-
"SOURCE;RESULT_VAR" # one_value_keywords
48+
"TARGET;VAR;SOURCE;RESULT_VAR" # one_value_keywords
4549
"IF_CHECK_PASSED;IF_CHECK_FAILED" # multi_value_keywords
4650
)
4751

4852
string(MAKE_C_IDENTIFIER "${flags}" result)
4953
string(TOUPPER "${result}" result)
50-
set(result "CXX_SUPPORTS_${result}")
51-
52-
# Every subsequent check_cxx_source_compiles((<code> <resultVar>) run will re-use
53-
# the cached result rather than performing the check again, even if the <code> changes.
54-
# Removing the cached result in order to force the check to be re-evaluated.
55-
unset(${result} CACHE)
56-
57-
if(NOT DEFINED TACXXF_SOURCE)
58-
set(TACXXF_SOURCE "int main() { return 0; }")
54+
string(PREPEND result CXX_SUPPORTS_)
55+
56+
set(source "int main() { return 0; }")
57+
if(DEFINED TACXXF_SOURCE AND NOT TACXXF_SOURCE STREQUAL source)
58+
set(source "${TACXXF_SOURCE}")
59+
string(SHA256 source_hash "${source}")
60+
string(SUBSTRING ${source_hash} 0 4 source_hash_head)
61+
string(APPEND result _${source_hash_head})
5962
endif()
6063

6164
# This avoids running a linker.
6265
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
6366
set(CMAKE_REQUIRED_FLAGS "${flags} ${working_compiler_werror_flag}")
64-
check_cxx_source_compiles("${TACXXF_SOURCE}" ${result})
67+
check_cxx_source_compiles("${source}" ${result})
6568

6669
if(${result})
6770
if(DEFINED TACXXF_IF_CHECK_PASSED)
68-
string(STRIP "${${flags_var}} ${TACXXF_IF_CHECK_PASSED}" ${flags_var})
71+
if(DEFINED TACXXF_TARGET)
72+
target_compile_options(${TACXXF_TARGET} INTERFACE ${TACXXF_IF_CHECK_PASSED})
73+
endif()
74+
if(DEFINED TACXXF_VAR)
75+
string(STRIP "${${TACXXF_VAR}} ${TACXXF_IF_CHECK_PASSED}" ${TACXXF_VAR})
76+
endif()
6977
else()
70-
string(STRIP "${${flags_var}} ${flags}" ${flags_var})
78+
if(DEFINED TACXXF_TARGET)
79+
target_compile_options(${TACXXF_TARGET} INTERFACE ${flags})
80+
endif()
81+
if(DEFINED TACXXF_VAR)
82+
string(STRIP "${${TACXXF_VAR}} ${flags}" ${TACXXF_VAR})
83+
endif()
7184
endif()
7285
elseif(DEFINED TACXXF_IF_CHECK_FAILED)
73-
string(STRIP "${${flags_var}} ${TACXXF_IF_CHECK_FAILED}" ${flags_var})
86+
if(DEFINED TACXXF_TARGET)
87+
target_compile_options(${TACXXF_TARGET} INTERFACE ${TACXXF_IF_CHECK_FAILED})
88+
endif()
89+
if(DEFINED TACXXF_VAR)
90+
string(STRIP "${${TACXXF_VAR}} ${TACXXF_IF_CHECK_FAILED}" ${TACXXF_VAR})
91+
endif()
92+
endif()
93+
94+
if(DEFINED TACXXF_VAR)
95+
set(${TACXXF_VAR} "${${TACXXF_VAR}}" PARENT_SCOPE)
96+
endif()
97+
98+
if(DEFINED TACXXF_RESULT_VAR)
99+
set(${TACXXF_RESULT_VAR} "${${result}}" PARENT_SCOPE)
74100
endif()
75-
set(${flags_var} "${${flags_var}}" PARENT_SCOPE)
76-
set(${TACXXF_RESULT_VAR} "${${result}}" PARENT_SCOPE)
77101
endfunction()
78102

79103
if(MSVC)
80-
set(warning_as_error_flag /WX)
104+
try_append_cxx_flags("/WX /options:strict" VAR working_compiler_werror_flag)
81105
else()
82-
set(warning_as_error_flag -Werror)
106+
try_append_cxx_flags("-Werror" VAR working_compiler_werror_flag)
83107
endif()
84-
try_append_cxx_flags(working_compiler_werror_flag ${warning_as_error_flag})
85-
unset(warning_as_error_flag)

0 commit comments

Comments
 (0)