summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Allison <dallison@google.com>2014-08-26 11:07:58 -0700
committerDave Allison <dallison@google.com>2014-08-27 12:47:44 -0700
commit8ce6b9040747054b444a7fa706503cd257801936 (patch)
tree04712170addb252d307ef9015abfc9bfc2b73581
parenta0a0da29e7d4d5c1bd471c49f1a4b6ec98fb767a (diff)
downloadart-8ce6b9040747054b444a7fa706503cd257801936.zip
art-8ce6b9040747054b444a7fa706503cd257801936.tar.gz
art-8ce6b9040747054b444a7fa706503cd257801936.tar.bz2
Handle nested signals
This allows for signals to be raised inside the ART signal handler. This can occur when the JavaStackTraceHandler attempts to generate a stack trace and something goes wrong. It also fixes an issue where the fault manager was not being correctly shut down inside the signal chaining code. In this case the signal handler was not restored to the original. Bug: 17006816 Bug: 17133266 (cherry picked from commit fabe91e0d558936ac26b98d2b4ee1af08f58831d) Change-Id: I10730ef52d5d8d34610a5293253b3be6caf4829e
-rw-r--r--runtime/arch/arm/fault_handler_arm.cc18
-rw-r--r--runtime/arch/arm64/fault_handler_arm64.cc14
-rw-r--r--runtime/arch/x86/fault_handler_x86.cc26
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S13
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S14
-rw-r--r--runtime/entrypoints_order_test.cc3
-rw-r--r--runtime/fault_handler.cc151
-rw-r--r--runtime/fault_handler.h1
-rw-r--r--runtime/thread.cc2
-rw-r--r--runtime/thread.h8
-rw-r--r--sigchainlib/sigchain.cc18
11 files changed, 250 insertions, 18 deletions
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index 28b69ec..564fcba 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -16,6 +16,7 @@
#include "fault_handler.h"
+
#include <sys/ucontext.h>
#include "base/macros.h"
#include "base/hex_dump.h"
@@ -46,6 +47,23 @@ static uint32_t GetInstructionSize(uint8_t* pc) {
return instr_size;
}
+void FaultManager::HandleNestedSignal(int sig, siginfo_t* info, void* context) {
+ // Note that in this handler we set up the registers and return to
+ // longjmp directly rather than going through an assembly language stub. The
+ // reason for this is that longjmp is (currently) in ARM mode and that would
+ // require switching modes in the stub - incurring an unwanted relocation.
+
+ struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ Thread* self = Thread::Current();
+ CHECK(self != nullptr); // This will cause a SIGABRT if self is nullptr.
+
+ sc->arm_r0 = reinterpret_cast<uintptr_t>(*self->GetNestedSignalState());
+ sc->arm_r1 = 1;
+ sc->arm_pc = reinterpret_cast<uintptr_t>(longjmp);
+ VLOG(signals) << "longjmp address: " << reinterpret_cast<void*>(sc->arm_pc);
+}
+
void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context,
mirror::ArtMethod** out_method,
uintptr_t* out_return_pc, uintptr_t* out_sp) {
diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc
index b5948cb..687d232 100644
--- a/runtime/arch/arm64/fault_handler_arm64.cc
+++ b/runtime/arch/arm64/fault_handler_arm64.cc
@@ -37,6 +37,20 @@ extern "C" void art_quick_implicit_suspend();
namespace art {
+void FaultManager::HandleNestedSignal(int sig, siginfo_t* info, void* context) {
+ // To match the case used in ARM we return directly to the longjmp function
+ // rather than through a trivial assembly language stub.
+
+ struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ Thread* self = Thread::Current();
+ CHECK(self != nullptr); // This will cause a SIGABRT if self is nullptr.
+
+ sc->regs[0] = reinterpret_cast<uintptr_t>(*self->GetNestedSignalState());
+ sc->regs[1] = 1;
+ sc->pc = reinterpret_cast<uintptr_t>(longjmp);
+}
+
void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context,
mirror::ArtMethod** out_method,
uintptr_t* out_return_pc, uintptr_t* out_sp) {
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index 65a48f6..fb26f5f 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -35,12 +35,14 @@
#define CTX_EIP uc_mcontext->__ss.__rip
#define CTX_EAX uc_mcontext->__ss.__rax
#define CTX_METHOD uc_mcontext->__ss.__rdi
+#define CTX_JMP_BUF uc_mcontext->__ss.__rdi
#else
// 32 bit mac build.
#define CTX_ESP uc_mcontext->__ss.__esp
#define CTX_EIP uc_mcontext->__ss.__eip
#define CTX_EAX uc_mcontext->__ss.__eax
#define CTX_METHOD uc_mcontext->__ss.__eax
+#define CTX_JMP_BUF uc_mcontext->__ss.__eax
#endif
#elif defined(__x86_64__)
@@ -49,12 +51,15 @@
#define CTX_EIP uc_mcontext.gregs[REG_RIP]
#define CTX_EAX uc_mcontext.gregs[REG_RAX]
#define CTX_METHOD uc_mcontext.gregs[REG_RDI]
+#define CTX_RDI uc_mcontext.gregs[REG_RDI]
+#define CTX_JMP_BUF uc_mcontext.gregs[REG_RDI]
#else
// 32 bit linux build.
#define CTX_ESP uc_mcontext.gregs[REG_ESP]
#define CTX_EIP uc_mcontext.gregs[REG_EIP]
#define CTX_EAX uc_mcontext.gregs[REG_EAX]
#define CTX_METHOD uc_mcontext.gregs[REG_EAX]
+#define CTX_JMP_BUF uc_mcontext.gregs[REG_EAX]
#endif
//
@@ -76,6 +81,12 @@ extern "C" void art_quick_test_suspend();
#define EXT_SYM(sym) sym
#endif
+// Note this is different from the others (no underscore on 64 bit mac) due to
+// the way the symbol is defined in the .S file.
+// TODO: fix the symbols for 64 bit mac - there is a double underscore prefix for some
+// of them.
+extern "C" void art_nested_signal_return();
+
// Get the size of an instruction in bytes.
// Return 0 if the instruction is not handled.
static uint32_t GetInstructionSize(const uint8_t* pc) {
@@ -215,6 +226,21 @@ static uint32_t GetInstructionSize(const uint8_t* pc) {
return pc - startpc;
}
+void FaultManager::HandleNestedSignal(int sig, siginfo_t* info, void* context) {
+ // For the Intel architectures we need to go to an assembly language
+ // stub. This is because the 32 bit call to longjmp is much different
+ // from the 64 bit ABI call and pushing things onto the stack inside this
+ // handler was unwieldy and ugly. The use of the stub means we can keep
+ // this code the same for both 32 and 64 bit.
+
+ Thread* self = Thread::Current();
+ CHECK(self != nullptr); // This will cause a SIGABRT if self is nullptr.
+
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ uc->CTX_JMP_BUF = reinterpret_cast<uintptr_t>(*self->GetNestedSignalState());
+ uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_nested_signal_return);
+}
+
void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context,
mirror::ArtMethod** out_method,
uintptr_t* out_return_pc, uintptr_t* out_sp) {
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index a21d672..337e5fe 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1369,5 +1369,18 @@ DEFINE_FUNCTION art_quick_string_compareto
ret
END_FUNCTION art_quick_string_compareto
+// Return from a nested signal:
+// Entry:
+// eax: address of jmp_buf in TLS
+
+DEFINE_FUNCTION art_nested_signal_return
+ SETUP_GOT_NOSAVE // sets %ebx for call into PLT
+ movl LITERAL(1), %ecx
+ pushl %ecx // second arg to longjmp (1)
+ pushl %eax // first arg to longjmp (jmp_buf)
+ call PLT_SYMBOL(longjmp)
+ int3 // won't get here.
+END_FUNCTION art_nested_signal_return
+
// TODO: implement these!
UNIMPLEMENTED art_quick_memcmp16
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index e9b5a72..e68cfbc 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1593,3 +1593,17 @@ DEFINE_FUNCTION art_quick_assignable_from_code
RESTORE_FP_CALLEE_SAVE_FRAME
ret
END_FUNCTION art_quick_assignable_from_code
+
+
+// Return from a nested signal:
+// Entry:
+// rdi: address of jmp_buf in TLS
+
+DEFINE_FUNCTION art_nested_signal_return
+ // first arg to longjmp is already in correct register
+ movq LITERAL(1), %rsi // second arg to longjmp (1)
+ call PLT_SYMBOL(longjmp)
+ int3 // won't get here
+END_FUNCTION art_nested_signal_return
+
+
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index a3014b3..305e5a2 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -15,6 +15,7 @@
*/
#include <memory>
+#include <setjmp.h>
#include "base/macros.h"
#include "common_runtime_test.h"
@@ -130,7 +131,7 @@ class EntrypointsOrderTest : public CommonRuntimeTest {
kPointerSize);
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_end, held_mutexes, kPointerSize);
EXPECT_OFFSET_DIFF(Thread, tlsPtr_.held_mutexes, Thread, wait_mutex_,
- kPointerSize * kLockLevelCount, thread_tlsptr_end);
+ kPointerSize * kLockLevelCount + kPointerSize, thread_tlsptr_end);
}
void CheckInterpreterEntryPoints() {
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 68fad7b..25f87c5 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -16,6 +16,7 @@
#include "fault_handler.h"
+#include <setjmp.h>
#include <sys/mman.h>
#include <sys/ucontext.h>
#include "mirror/art_method.h"
@@ -24,6 +25,47 @@
#include "thread-inl.h"
#include "verify_object-inl.h"
+// Note on nested signal support
+// -----------------------------
+//
+// Typically a signal handler should not need to deal with signals that occur within it.
+// However, when a SIGSEGV occurs that is in generated code and is not one of the
+// handled signals (implicit checks), we call a function to try to dump the stack
+// to the log. This enhances the debugging experience but may have the side effect
+// that it may not work. If the cause of the original SIGSEGV is a corrupted stack or other
+// memory region, the stack backtrace code may run into trouble and may either crash
+// or fail with an abort (SIGABRT). In either case we don't want that (new) signal to
+// mask the original signal and thus prevent useful debug output from being presented.
+//
+// In order to handle this situation, before we call the stack tracer we do the following:
+//
+// 1. shutdown the fault manager so that we are talking to the real signal management
+// functions rather than those in sigchain.
+// 2. use pthread_sigmask to allow SIGSEGV and SIGABRT signals to be delivered to the
+// thread running the signal handler.
+// 3. set the handler for SIGSEGV and SIGABRT to a secondary signal handler.
+// 4. save the thread's state to the TLS of the current thread using 'setjmp'
+//
+// We then call the stack tracer and one of two things may happen:
+// a. it completes successfully
+// b. it crashes and a signal is raised.
+//
+// In the former case, we fall through and everything is fine. In the latter case
+// our secondary signal handler gets called in a signal context. This results in
+// a call to FaultManager::HandledNestedSignal(), an archirecture specific function
+// whose purpose is to call 'longjmp' on the jmp_buf saved in the TLS of the current
+// thread. This results in a return with a non-zero value from 'setjmp'. We detect this
+// and write something to the log to tell the user that it happened.
+//
+// Regardless of how we got there, we reach the code after the stack tracer and we
+// restore the signal states to their original values, reinstate the fault manager (thus
+// reestablishing the signal chain) and continue.
+
+// This is difficult to test with a runtime test. To invoke the nested signal code
+// on any signal, uncomment the following line and run something that throws a
+// NullPointerException.
+// #define TEST_NESTED_SIGNAL
+
namespace art {
// Static fault manger object accessed by signal handler.
FaultManager fault_manager;
@@ -37,10 +79,14 @@ void art_sigsegv_fault() {
// Signal handler called on SIGSEGV.
static void art_fault_handler(int sig, siginfo_t* info, void* context) {
- // std::cout << "handling fault in ART handler\n";
fault_manager.HandleFault(sig, info, context);
}
+// Signal handler for dealing with a nested signal.
+static void art_nested_signal_handler(int sig, siginfo_t* info, void* context) {
+ fault_manager.HandleNestedSignal(sig, info, context);
+}
+
FaultManager::FaultManager() : initialized_(false) {
sigaction(SIGSEGV, nullptr, &oldaction_);
}
@@ -82,26 +128,36 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
// If malloc calls abort, it will be holding its lock.
// If the handler tries to call malloc, it will deadlock.
- // Also, there is only an 8K stack available here to logging can cause memory
- // overwrite issues if you are unlucky. If you want to enable logging and
- // are getting crashes, allocate more space for the alternate signal stack.
-
VLOG(signals) << "Handling fault";
if (IsInGeneratedCode(info, context, true)) {
VLOG(signals) << "in generated code, looking for handler";
for (const auto& handler : generated_code_handlers_) {
VLOG(signals) << "invoking Action on handler " << handler;
if (handler->Action(sig, info, context)) {
+#ifdef TEST_NESTED_SIGNAL
+ // In test mode we want to fall through to stack trace handler
+ // on every signal (in reality this will cause a crash on the first
+ // signal).
+ break;
+#else
+ // We have handled a signal so it's time to return from the
+ // signal handler to the appropriate place.
return;
+#endif
}
}
}
+
+ // We hit a signal we didn't handle. This might be something for which
+ // we can give more information about so call all registered handlers to see
+ // if it is.
for (const auto& handler : other_handlers_) {
if (handler->Action(sig, info, context)) {
return;
}
}
+ // Set a breakpoint in this function to catch unhandled signals.
art_sigsegv_fault();
// Pass this on to the next handler in the chain, or the default if none.
@@ -242,19 +298,90 @@ JavaStackTraceHandler::JavaStackTraceHandler(FaultManager* manager) : FaultHandl
bool JavaStackTraceHandler::Action(int sig, siginfo_t* siginfo, void* context) {
// Make sure that we are in the generated code, but we may not have a dex pc.
- if (manager_->IsInGeneratedCode(siginfo, context, false)) {
+
+#ifdef TEST_NESTED_SIGNAL
+ bool in_generated_code = true;
+#else
+ bool in_generated_code = manager_->IsInGeneratedCode(siginfo, context, false);
+#endif
+ if (in_generated_code) {
LOG(ERROR) << "Dumping java stack trace for crash in generated code";
mirror::ArtMethod* method = nullptr;
uintptr_t return_pc = 0;
uintptr_t sp = 0;
- manager_->GetMethodAndReturnPcAndSp(siginfo, context, &method, &return_pc, &sp);
Thread* self = Thread::Current();
- // Inside of generated code, sp[0] is the method, so sp is the frame.
- StackReference<mirror::ArtMethod>* frame =
- reinterpret_cast<StackReference<mirror::ArtMethod>*>(sp);
- self->SetTopOfStack(frame, 0); // Since we don't necessarily have a dex pc, pass in 0.
- self->DumpJavaStack(LOG(ERROR));
+
+ // Shutdown the fault manager so that it will remove the signal chain for
+ // SIGSEGV and we call the real sigaction.
+ fault_manager.Shutdown();
+
+ // The action for SIGSEGV should be the default handler now.
+
+ // Unblock the signals we allow so that they can be delivered in the signal handler.
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGSEGV);
+ sigaddset(&sigset, SIGABRT);
+ pthread_sigmask(SIG_UNBLOCK, &sigset, nullptr);
+
+ // If we get a signal in this code we want to invoke our nested signal
+ // handler.
+ struct sigaction action, oldsegvaction, oldabortaction;
+ action.sa_sigaction = art_nested_signal_handler;
+
+ // Explictly mask out SIGSEGV and SIGABRT from the nested signal handler. This
+ // should be the default but we definitely don't want these happening in our
+ // nested signal handler.
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGSEGV);
+ sigaddset(&action.sa_mask, SIGABRT);
+
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+#if !defined(__APPLE__) && !defined(__mips__)
+ action.sa_restorer = nullptr;
+#endif
+
+ // Catch SIGSEGV and SIGABRT to invoke our nested handler
+ int e1 = sigaction(SIGSEGV, &action, &oldsegvaction);
+ int e2 = sigaction(SIGABRT, &action, &oldabortaction);
+ if (e1 != 0 || e2 != 0) {
+ LOG(ERROR) << "Unable to register nested signal handler - no stack trace possible";
+ // If sigaction failed we have a serious problem. We cannot catch
+ // any failures in the stack tracer and it's likely to occur since
+ // the program state is bad. Therefore we don't even try to give
+ // a stack trace.
+ } else {
+ // Save the current state and try to dump the stack. If this causes a signal
+ // our nested signal handler will be invoked and this will longjmp to the saved
+ // state.
+ if (setjmp(*self->GetNestedSignalState()) == 0) {
+ manager_->GetMethodAndReturnPcAndSp(siginfo, context, &method, &return_pc, &sp);
+ // Inside of generated code, sp[0] is the method, so sp is the frame.
+ StackReference<mirror::ArtMethod>* frame =
+ reinterpret_cast<StackReference<mirror::ArtMethod>*>(sp);
+ self->SetTopOfStack(frame, 0); // Since we don't necessarily have a dex pc, pass in 0.
+#ifdef TEST_NESTED_SIGNAL
+ // To test the nested signal handler we raise a signal here. This will cause the
+ // nested signal handler to be called and perform a longjmp back to the setjmp
+ // above.
+ abort();
+#endif
+ self->DumpJavaStack(LOG(ERROR));
+ } else {
+ LOG(ERROR) << "Stack trace aborted due to nested signal - original signal being reported";
+ }
+
+ // Restore the signal handlers.
+ sigaction(SIGSEGV, &oldsegvaction, nullptr);
+ sigaction(SIGABRT, &oldabortaction, nullptr);
+ }
+
+ // Now put the fault manager back in place.
+ fault_manager.Init();
+
+ // And we're done.
}
+
return false; // Return false since we want to propagate the fault to the main signal handler.
}
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index 0e9b908..bb26780 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -42,6 +42,7 @@ class FaultManager {
void Shutdown();
void HandleFault(int sig, siginfo_t* info, void* context);
+ void HandleNestedSignal(int sig, siginfo_t* info, void* context);
void AddHandler(FaultHandler* handler, bool generated_code);
void RemoveHandler(FaultHandler* handler);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index f432c9c..03792b1 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1079,6 +1079,7 @@ Thread::Thread(bool daemon) : tls32_(daemon), wait_monitor_(nullptr), interrupte
tlsPtr_.single_step_control = new SingleStepControl;
tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
tlsPtr_.name = new std::string(kThreadNameDuringStartup);
+ tlsPtr_.nested_signal_state = static_cast<jmp_buf*>(malloc(sizeof(jmp_buf)));
CHECK_EQ((sizeof(Thread) % 4), 0U) << sizeof(Thread);
tls32_.state_and_flags.as_struct.flags = 0;
@@ -1226,6 +1227,7 @@ Thread::~Thread() {
delete tlsPtr_.instrumentation_stack;
delete tlsPtr_.name;
delete tlsPtr_.stack_trace_sample;
+ free(tlsPtr_.nested_signal_state);
Runtime::Current()->GetHeap()->AssertThreadLocalBuffersAreRevoked(this);
diff --git a/runtime/thread.h b/runtime/thread.h
index 5283ca6..aca4069 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -22,6 +22,7 @@
#include <iosfwd>
#include <list>
#include <memory>
+#include <setjmp.h>
#include <string>
#include "atomic.h"
@@ -848,6 +849,10 @@ class Thread {
tls32_.handling_signal_ = false;
}
+ jmp_buf* GetNestedSignalState() {
+ return tlsPtr_.nested_signal_state;
+ }
+
private:
explicit Thread(bool daemon);
~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_,
@@ -1136,6 +1141,9 @@ class Thread {
// Support for Mutex lock hierarchy bug detection.
BaseMutex* held_mutexes[kLockLevelCount];
+
+ // Recorded thread state for nested signals.
+ jmp_buf* nested_signal_state;
} tlsPtr_;
// Guards the 'interrupted_' and 'wait_monitor_' members.
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 2ba7405..c655226 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -45,8 +45,8 @@ class SignalAction {
// Unclaim the signal and restore the old action.
void Unclaim(int signal) {
- sigaction(signal, &action_, NULL); // Restore old action.
claimed_ = false;
+ sigaction(signal, &action_, NULL); // Restore old action.
}
// Get the action associated with this signal.
@@ -155,8 +155,12 @@ int sigaction(int signal, const struct sigaction* new_action, struct sigaction*
void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
if (linked_sigaction_sym == nullptr) {
- log("Unable to find next sigaction in signal chain");
- abort();
+ linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
+ if (linked_sigaction_sym == nullptr ||
+ linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
+ log("Unable to find next sigaction in signal chain");
+ abort();
+ }
}
typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
@@ -184,8 +188,12 @@ int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_se
void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
if (linked_sigprocmask_sym == nullptr) {
- log("Unable to find next sigprocmask in signal chain");
- abort();
+ linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
+ if (linked_sigprocmask_sym == nullptr ||
+ linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
+ log("Unable to find next sigprocmask in signal chain");
+ abort();
+ }
}
typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);