From 5e02dda08990d06bd2e0d6e1e0eda6a8af76cf05 Mon Sep 17 00:00:00 2001 From: Jim Borden Date: Tue, 2 Jun 2026 14:59:23 +0900 Subject: [PATCH 1/2] Fix issues with .NET using signals as runtime instruments E.g. SIGXCPU is used to trigger garbage collection, and would spam stderr if left in place (not to mention terminate when it shouldn't). SIGSEGV is used for checking null pointer exceptions, etc. --- Fleece/Support/Backtrace+signals-posix.cc | 53 +++++++++++++---------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/Fleece/Support/Backtrace+signals-posix.cc b/Fleece/Support/Backtrace+signals-posix.cc index a88c59ca..a59235a8 100644 --- a/Fleece/Support/Backtrace+signals-posix.cc +++ b/Fleece/Support/Backtrace+signals-posix.cc @@ -62,9 +62,10 @@ namespace fleece { for ( const int signal : signals) { struct sigaction action{}; - action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND; + // NOTE: No SA_RESETHAND since on managed platforms signals are recoverable and + // we want our handler to keep working in the event that they are not + action.sa_flags = SA_SIGINFO | SA_ONSTACK; sigfillset(&action.sa_mask); - sigdelset(&action.sa_mask, signal); #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdisabled-macro-expansion" @@ -91,11 +92,11 @@ namespace fleece { unique_ptr _stack_content; #ifdef __APPLE__ - static constexpr array signals = { -#else static constexpr array signals = { +#else + static constexpr array signals = { #endif - SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGQUIT, SIGSEGV, SIGSYS, SIGTRAP, SIGXCPU, SIGXFSZ, + SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGQUIT, SIGSEGV, SIGSYS, SIGTRAP, SIGXFSZ, #if defined(__APPLE__) SIGEMT, #endif @@ -111,37 +112,41 @@ namespace fleece { NOINLINE static void crash_handler_immediate(siginfo_t* info, void* context) { void* buffer[50]; int n = Backtrace::raw_capture(buffer, 50, context); - const char* name = strsignal(info->si_signo); - write_to_and_stderr(sLogFD, "\n\n******************** Process Crash: ", 38); - if ( name ) { - write_to_and_stderr(sLogFD, name); - } else { - write_to_and_stderr(sLogFD, "Signal: ", 8); - write_long(info->si_signo, sLogFD); - } + write_to_and_stderr(sLogFD, "\n\n******************** Signal caught: ", 38); + write_long(info->si_signo, sLogFD); write_to_and_stderr(sLogFD, " Timestamp: ", 12); - char timestamp[20]; - snprintf(timestamp, 20, "%lld", static_cast(time(nullptr))); write_long(time(nullptr), sLogFD); write_to_and_stderr(sLogFD, " *******************\n", 21); Backtrace::writeTo(buffer + 3, n - 3, sLogFD); - write_to_and_stderr(sLogFD, "\n******************** Now terminating ********************\n", 59); if ( sLogFD != -1 ) { fsync(sLogFD); - close(sLogFD); } } - [[noreturn]] static void sig_handler(int signo, siginfo_t* info, void* context) { - crash_handler_immediate(info, context); - - auto default_action = BacktraceSignalHandlerPosix::defaultActionFor(signo); - sigaction(signo, &default_action, nullptr); - raise(signo); + static void chain_to_previous(int signo, siginfo_t* info, void* context) { + const auto& prev = defaultActionFor(signo); + if ((prev.sa_flags & SA_SIGINFO) && prev.sa_sigaction && prev.sa_sigaction != &sig_handler) { + // Original fault context: the runtime can recover (and modify *context, + // which our return then resumes into) or die inside this call. + prev.sa_sigaction(signo, info, context); + } else if (prev.sa_handler && prev.sa_handler != SIG_IGN + && prev.sa_handler != SIG_DFL) { + // SIG_IGN and SIG_DFL are not actual functions, don't try to call them + prev.sa_handler(signo); + } else if (prev.sa_handler == SIG_DFL) { + sigaction(signo, &prev, nullptr); // restore default and let it act + raise(signo); // fatal default terminates here + } + //SIG_IGN means ignore, so do just that + } - _exit(128 + signo); + static void sig_handler(int signo, siginfo_t* info, void* context) { + crash_handler_immediate(info, context); + chain_to_previous(signo, info, context); + write_to_and_stderr(sLogFD, + "\n********** Signal handled; execution continuing **********\n", 60); } }; From 50bde7ab8100f17a3631706dcb639ed58d4199ab Mon Sep 17 00:00:00 2001 From: Jim Borden Date: Tue, 2 Jun 2026 15:50:09 +0900 Subject: [PATCH 2/2] Find libbacktrace when running either GCC or clang They both support finding the file, and for our image it will be present --- cmake/platform_linux.cmake | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/cmake/platform_linux.cmake b/cmake/platform_linux.cmake index 6f885ec8..af2afa4f 100644 --- a/cmake/platform_linux.cmake +++ b/cmake/platform_linux.cmake @@ -1,19 +1,16 @@ include("${CMAKE_CURRENT_LIST_DIR}/platform_base.cmake") include(CheckCXXSourceCompiles) -# libbacktrace is bundled inside GCC's private library directory. -# Ask GCC where it keeps the library and header so that builds with -# other compilers (e.g. Clang) can find it. -find_program(_gcc NAMES gcc) -if(_gcc) +# Both GCC and Clang support -print-file-name to locate bundled libraries. +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") execute_process( - COMMAND ${_gcc} -print-file-name=libbacktrace.a - OUTPUT_VARIABLE _gcc_bt_lib OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) - if(_gcc_bt_lib AND NOT _gcc_bt_lib STREQUAL "libbacktrace.a") - get_filename_component(_gcc_lib_dir "${_gcc_bt_lib}" DIRECTORY) - find_path(BACKTRACE_INCLUDE_DIR backtrace.h HINTS "${_gcc_lib_dir}/include") + COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=libbacktrace.a + OUTPUT_VARIABLE _bt_lib OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) + if(_bt_lib AND NOT _bt_lib STREQUAL "libbacktrace.a") + get_filename_component(_bt_lib_dir "${_bt_lib}" DIRECTORY) + find_path(BACKTRACE_INCLUDE_DIR backtrace.h HINTS "${_bt_lib_dir}/include") if(BACKTRACE_INCLUDE_DIR) - set(BACKTRACE_LIBRARY "${_gcc_bt_lib}") + set(BACKTRACE_LIBRARY "${_bt_lib}") set(CMAKE_REQUIRED_INCLUDES "${BACKTRACE_INCLUDE_DIR}") set(CMAKE_REQUIRED_LIBRARIES "${BACKTRACE_LIBRARY}") check_cxx_source_compiles(" @@ -26,11 +23,10 @@ if(_gcc) unset(CMAKE_REQUIRED_INCLUDES) unset(CMAKE_REQUIRED_LIBRARIES) endif() + unset(_bt_lib_dir) endif() - unset(_gcc_bt_lib) - unset(_gcc_lib_dir) + unset(_bt_lib) endif() -unset(_gcc) function(set_source_files) set(oneValueArgs RESULT)