Skip to content

Commit 4c1906a

Browse files
committed
Merge bitcoin/bitcoin#31992: cmake: Avoid fuzzer "multiple definition of `main'" errors
57d8b1f cmake: Avoid fuzzer "multiple definition of `main'" errors (Ryan Ofsky) Pull request description: This change builds libraries with `-fsanitize=fuzzer-no-link` instead of `-fsanitize=fuzzer` when the cmake `-DSANITIZERS=fuzzer` option is specified. This is necessary to make fuzzing and IPC cmake options compatible with each other and avoid CI failures in #30975 which enables IPC in the fuzzer CI build: https://cirrus-ci.com/task/5366255504326656?logs=ci#L2817 https://cirrus-ci.com/task/5233064575500288?logs=ci#L2384 The failures can also be reproduced by checking out #31741 and building with `cmake -B build -DBUILD_FOR_FUZZING=ON -DSANITIZERS=fuzzer -DENABLE_IPC=ON` with this fix reverted. The fix updates the cmake build so when `-DSANITIZERS=fuzzer` is specified, the fuzz test binary is built with `-fsanitize=fuzzer` (so it can use libFuzzer's main function), and libraries are built with `-fsanitize=fuzzer-no-link` (so they can be linked into other executables with their own main functions). Previously when `-DSANITIZERS=fuzzer` was specified, `-fsanitize=fuzzer` was applied to ALL libraries and executables. This was inappropriate because it made it impossible to build any executables other than the fuzz test executable without triggering link errors: - `` multiple definition of `main' `` - `` "undefined reference to `LLVMFuzzerTestOneInput' `` if they depended on any libraries instrumented for fuzzing. This was especially a problem when the `ENABLE_IPC` option was set because it made building the `mpgen` code generator impossible so nothing else that depended on generated sources, including the fuzz test binary, could be built either. This commit was previously part of bitcoin/bitcoin#31741 and had some discussion there starting in bitcoin/bitcoin#31741 (review) --- This PR is part of the [process separation project](bitcoin/bitcoin#28722). ACKs for top commit: hebasto: ACK 57d8b1f, tested on Ubuntu 24.04. Tree-SHA512: 4011adbc0b08742e83cf7c0560d3d5b5694a863358e6ac9a21239626b4a8fedceca66db34b5a46136a7b26849bb1d8710c894689322ae97e1c407687c3f57d50
2 parents 9acc25b + 57d8b1f commit 4c1906a

File tree

2 files changed

+25
-5
lines changed

2 files changed

+25
-5
lines changed

CMakeLists.txt

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -346,13 +346,28 @@ target_link_libraries(core_interface INTERFACE
346346
Threads::Threads
347347
)
348348

349+
# Define sanitize_interface with -fsanitize flags intended to apply to all
350+
# libraries and executables.
349351
add_library(sanitize_interface INTERFACE)
350352
target_link_libraries(core_interface INTERFACE sanitize_interface)
351353
if(SANITIZERS)
354+
# Transform list of sanitizers into -fsanitize flags, replacing "fuzzer" with
355+
# "fuzzer-no-link" in sanitize_interface flags, and moving "fuzzer" to
356+
# fuzzer_interface flags. If -DSANITIZERS=fuzzer is specified, the fuzz test
357+
# binary should be built with -fsanitize=fuzzer (so it can use libFuzzer's
358+
# main function), but libraries should be built with -fsanitize=fuzzer-no-link
359+
# (so they can be linked into other executables that have their own main
360+
# functions).
361+
string(REGEX REPLACE "(^|,)fuzzer($|,)" "\\1fuzzer-no-link\\2" sanitize_opts "${SANITIZERS}")
362+
set(fuzz_flag "")
363+
if(NOT sanitize_opts STREQUAL SANITIZERS)
364+
set(fuzz_flag "-fsanitize=fuzzer")
365+
endif()
366+
352367
# First check if the compiler accepts flags. If an incompatible pair like
353368
# -fsanitize=address,thread is used here, this check will fail. This will also
354369
# fail if a bad argument is passed, e.g. -fsanitize=undfeined
355-
try_append_cxx_flags("-fsanitize=${SANITIZERS}" TARGET sanitize_interface
370+
try_append_cxx_flags("-fsanitize=${sanitize_opts}" TARGET sanitize_interface
356371
RESULT_VAR cxx_supports_sanitizers
357372
SKIP_LINK
358373
)
@@ -365,12 +380,11 @@ if(SANITIZERS)
365380
# flag. This is a separate check so we can give a better error message when
366381
# the sanitize flags are supported by the compiler but the actual sanitizer
367382
# libs are missing.
368-
try_append_linker_flag("-fsanitize=${SANITIZERS}" VAR SANITIZER_LDFLAGS
383+
try_append_linker_flag("-fsanitize=${sanitize_opts}" VAR SANITIZER_LDFLAGS
369384
SOURCE "
370385
#include <cstdint>
371386
#include <cstddef>
372387
extern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return 0; }
373-
__attribute__((weak)) // allow for libFuzzer linking
374388
int main() { return 0; }
375389
"
376390
RESULT_VAR linker_supports_sanitizers
@@ -382,18 +396,23 @@ if(SANITIZERS)
382396
endif()
383397
target_link_options(sanitize_interface INTERFACE ${SANITIZER_LDFLAGS})
384398

399+
# Define fuzzer_interface with flags intended to apply to the fuzz test binary,
400+
# and perform a test compilation to determine correct value of
401+
# FUZZ_BINARY_LINKS_WITHOUT_MAIN_FUNCTION.
385402
if(BUILD_FUZZ_BINARY)
386-
target_link_libraries(core_interface INTERFACE ${FUZZ_LIBS})
387403
include(CheckSourceCompilesWithFlags)
388404
check_cxx_source_compiles_with_flags("
389405
#include <cstdint>
390406
#include <cstddef>
391407
extern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return 0; }
392408
// No main() function.
393409
" FUZZ_BINARY_LINKS_WITHOUT_MAIN_FUNCTION
394-
LDFLAGS ${SANITIZER_LDFLAGS}
410+
LDFLAGS ${SANITIZER_LDFLAGS} ${fuzz_flag}
395411
LINK_LIBRARIES ${FUZZ_LIBS}
396412
)
413+
add_library(fuzzer_interface INTERFACE)
414+
target_link_options(fuzzer_interface INTERFACE ${fuzz_flag})
415+
target_link_libraries(fuzzer_interface INTERFACE ${FUZZ_LIBS})
397416
endif()
398417

399418
include(AddBoostIfNeeded)

src/test/fuzz/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ add_executable(fuzz
138138
)
139139
target_link_libraries(fuzz
140140
core_interface
141+
fuzzer_interface
141142
test_fuzz
142143
bitcoin_cli
143144
bitcoin_common

0 commit comments

Comments
 (0)