I'm using libclang from Java via the Java Native Interface (JNI). When calling the clang_createIndex function the first thing it does is check whether the environment variable 'LIBCLANG_DISABLE_CRASH_RECOVERY' is set if it isn't then it enables crash recovery.
// We use crash recovery to make some of our APIs more reliable, implicitly // enable it. CXIndex clang_createIndex(int excludeDeclarationsFromPCH, int displayDiagnostics) { if (!getenv("LIBCLANG_DISABLE_CRASH_RECOVERY")) llvm::CrashRecoveryContext::Enable(); ...
The crash recovery code installs it's own signal handler for catching several signals one of them being the segmentation fault signal 11. If the handler CrashRecoverySignalHandler is triggered and it doesn't recognise the signal coming from a call in LLVM then it reinstalls the previous handler (by calling CrashRecoveryContext::Disable()) for the signals and reraises the signal:
static void CrashRecoverySignalHandler(int Signal) { // Lookup the current thread local recovery object. const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); if (!CRCI) { // We didn't find a crash recovery context -- this means either we got a // signal on a thread we didn't expect it on, the application got a signal // outside of a crash recovery context, or something else went horribly // wrong. // // Disable crash recovery and raise the signal again. The assumption here is // that the enclosing application will terminate soon, and we won't want to // attempt crash recovery again. // // This call of Disable isn't thread safe, but it doesn't actually matter. CrashRecoveryContext::Disable(); raise(Signal); // The signal will be thrown once the signal mask is restored. return; }
The JVM installs it's own segfault handler which catches operations on null objects and raises a NullPointerException that produces a Java stacktrace. When my Java application calls clang_createIndex() with crash recovery enabled it replaces the JVM's segfault handler with CrashRecoverySignalHandler and now this handler gets all the segfault signals that would have normally been sent to the JVM and when it does and tries to restore the previous segfault hanlder (which is the JVMs) it doesn't install the right one because the JVM ends up crashing and producing a core dump. Solution to this problem is to disable crash recovery when using libclang from Java.
I could disable crash recovery by calling clang_toggleCrashRecovery(false) after clang_createIndex has been called but this doesn't work because the right JVM handler isn't reinstalled so I still get a core dump when the JVM receives a segfault signal which it would have normally treated as a NullPointerException.
The only way to correctly solve my problem is to set the environment variable 'LIBCLANG_DISABLE_CRASH_RECOVERY' but I find this to not be a very nice way to control the program behaviour so I've got a patch below which allows users to control whether it's enabled/disable by setting a function argument. I've kept the old check in for the environment variable LIBCLANG_DISABLE_CRASH_RECOVERY for backwards compatibility.