@@ -456,9 +456,48 @@ registers_from_uctx(const sentry_ucontext_t *uctx)
456456 return registers ;
457457}
458458
459+ #ifdef SENTRY_PLATFORM_LINUX
460+ static uintptr_t
461+ get_stack_pointer (const sentry_ucontext_t * uctx )
462+ {
463+ # if defined(__i386__ )
464+ return uctx -> user_context -> uc_mcontext .gregs [REG_ESP ];
465+ # elif defined(__x86_64__ )
466+ return uctx -> user_context -> uc_mcontext .gregs [REG_RSP ];
467+ # elif defined(__arm__ )
468+ return uctx -> user_context -> uc_mcontext .arm_sp ;
469+ # elif defined(__aarch64__ )
470+ return uctx -> user_context -> uc_mcontext .sp ;
471+ # else
472+ SENTRY_WARN ("get_stack_pointer is not implemented for this architecture. "
473+ "Signal chaining may not work as expected." );
474+ return NULL ;
475+ # endif
476+ }
477+
478+ static uintptr_t
479+ get_instruction_pointer (const sentry_ucontext_t * uctx )
480+ {
481+ # if defined(__i386__ )
482+ return uctx -> user_context -> uc_mcontext .gregs [REG_EIP ];
483+ # elif defined(__x86_64__ )
484+ return uctx -> user_context -> uc_mcontext .gregs [REG_RIP ];
485+ # elif defined(__arm__ )
486+ return uctx -> user_context -> uc_mcontext .arm_pc ;
487+ # elif defined(__aarch64__ )
488+ return uctx -> user_context -> uc_mcontext .pc ;
489+ # else
490+ SENTRY_WARN (
491+ "get_instruction_pointer is not implemented for this architecture. "
492+ "Signal chaining may not work as expected." );
493+ return NULL ;
494+ # endif
495+ }
496+ #endif
497+
459498static sentry_value_t
460- make_signal_event (
461- const struct signal_slot * sig_slot , const sentry_ucontext_t * uctx )
499+ make_signal_event (const struct signal_slot * sig_slot ,
500+ const sentry_ucontext_t * uctx , sentry_handler_strategy_t strategy )
462501{
463502 sentry_value_t event = sentry_value_new_event ();
464503 sentry_value_set_by_key (
@@ -496,8 +535,10 @@ make_signal_event(
496535 "captured backtrace from ucontext with %lu frames" , frame_count );
497536 // if unwinding from a ucontext didn't yield any results, try again with a
498537 // direct unwind. this is most likely the case when using `libbacktrace`,
499- // since that does not allow to unwind from a ucontext at all.
500- if (!frame_count ) {
538+ // since that does not allow to unwind from a ucontext at all. the fallback
539+ // is skipped with the "chain at start" strategy because `libbacktrace`
540+ // crashes, and would likely not provide helpful information anyway.
541+ if (!frame_count && strategy != SENTRY_HANDLER_STRATEGY_CHAIN_AT_START ) {
501542 frame_count = sentry_unwind_stack (NULL , & backtrace [0 ], MAX_FRAMES );
502543 }
503544 SENTRY_DEBUGF ("captured backtrace with %lu frames" , frame_count );
@@ -534,20 +575,7 @@ handle_ucontext(const sentry_ucontext_t *uctx)
534575
535576 SENTRY_INFO ("entering signal handler" );
536577
537- const struct signal_slot * sig_slot = NULL ;
538- for (int i = 0 ; i < SIGNAL_COUNT ; ++ i ) {
539- #ifdef SENTRY_PLATFORM_UNIX
540- if (SIGNAL_DEFINITIONS [i ].signum == uctx -> signum ) {
541- #elif defined SENTRY_PLATFORM_WINDOWS
542- if (SIGNAL_DEFINITIONS [i ].signum
543- == uctx -> exception_ptrs .ExceptionRecord -> ExceptionCode ) {
544- #else
545- # error Unsupported platform
546- #endif
547- sig_slot = & SIGNAL_DEFINITIONS [i ];
548- }
549- }
550-
578+ sentry_handler_strategy_t strategy = SENTRY_HANDLER_STRATEGY_DEFAULT ;
551579#ifdef SENTRY_PLATFORM_UNIX
552580 // inform the sentry_sync system that we're in a signal handler. This will
553581 // make mutexes spin on a spinlock instead as it's no longer safe to use a
@@ -556,42 +584,77 @@ handle_ucontext(const sentry_ucontext_t *uctx)
556584#endif
557585
558586 SENTRY_WITH_OPTIONS (options ) {
559- // Flush logs in a crash-safe manner before crash handling
560- if (options -> enable_logs ) {
561- sentry__logs_flush_crash_safe ();
562- }
563587#ifdef SENTRY_PLATFORM_LINUX
564588 // On Linux (and thus Android) CLR/Mono converts signals provoked by
565589 // AOT/JIT-generated native code into managed code exceptions. In these
566590 // cases, we shouldn't react to the signal at all and let their handler
567591 // discontinue the signal chain by invoking the runtime handler before
568592 // we process the signal.
569- if ( sentry_options_get_handler_strategy (options )
570- == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START ) {
593+ strategy = sentry_options_get_handler_strategy (options );
594+ if ( strategy == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START ) {
571595 SENTRY_DEBUG ("defer to runtime signal handler at start" );
572596 // there is a good chance that we won't return from the previous
573597 // handler and that would mean we couldn't enter this handler with
574598 // the next signal coming in if we didn't "leave" here.
575599 sentry__leave_signal_handler ();
600+ if (!options -> enable_logging_when_crashed ) {
601+ sentry__logger_enable ();
602+ }
603+
604+ uintptr_t ip = get_instruction_pointer (uctx );
605+ uintptr_t sp = get_stack_pointer (uctx );
576606
577607 // invoke the previous handler (typically the CLR/Mono
578608 // signal-to-managed-exception handler)
579609 invoke_signal_handler (
580610 uctx -> signum , uctx -> siginfo , (void * )uctx -> user_context );
581611
612+ // If the execution returns here in AOT mode, and the instruction
613+ // or stack pointer were changed, it means CLR/Mono converted the
614+ // signal into a managed exception and transferred execution to a
615+ // managed exception handler.
616+ // https://github.com/dotnet/runtime/blob/6d96e28597e7da0d790d495ba834cc4908e442cd/src/mono/mono/mini/exceptions-arm64.c#L538
617+ if (ip != get_instruction_pointer (uctx )
618+ || sp != get_stack_pointer (uctx )) {
619+ SENTRY_DEBUG ("runtime converted the signal to a managed "
620+ "exception, we do not handle the signal" );
621+ return ;
622+ }
623+
582624 // let's re-enter because it means this was an actual native crash
625+ if (!options -> enable_logging_when_crashed ) {
626+ sentry__logger_disable ();
627+ }
583628 sentry__enter_signal_handler ();
584629 SENTRY_DEBUG (
585630 "return from runtime signal handler, we handle the signal" );
586631 }
587632#endif
588633
634+ const struct signal_slot * sig_slot = NULL ;
635+ for (int i = 0 ; i < SIGNAL_COUNT ; ++ i ) {
636+ #ifdef SENTRY_PLATFORM_UNIX
637+ if (SIGNAL_DEFINITIONS [i ].signum == uctx -> signum ) {
638+ #elif defined SENTRY_PLATFORM_WINDOWS
639+ if (SIGNAL_DEFINITIONS [i ].signum
640+ == uctx -> exception_ptrs .ExceptionRecord -> ExceptionCode ) {
641+ #else
642+ # error Unsupported platform
643+ #endif
644+ sig_slot = & SIGNAL_DEFINITIONS [i ];
645+ }
646+ }
647+
589648#ifdef SENTRY_PLATFORM_UNIX
590649 // use a signal-safe allocator before we tear down.
591650 sentry__page_allocator_enable ();
592651#endif
652+ // Flush logs in a crash-safe manner before crash handling
653+ if (options -> enable_logs ) {
654+ sentry__logs_flush_crash_safe ();
655+ }
593656
594- sentry_value_t event = make_signal_event (sig_slot , uctx );
657+ sentry_value_t event = make_signal_event (sig_slot , uctx , strategy );
595658 bool should_handle = true;
596659 sentry__write_crash_marker (options );
597660
@@ -647,8 +710,10 @@ handle_ucontext(const sentry_ucontext_t *uctx)
647710 // forward as we're not restoring the page allocator.
648711 reset_signal_handlers ();
649712 sentry__leave_signal_handler ();
650- invoke_signal_handler (
651- uctx -> signum , uctx -> siginfo , (void * )uctx -> user_context );
713+ if (strategy != SENTRY_HANDLER_STRATEGY_CHAIN_AT_START ) {
714+ invoke_signal_handler (
715+ uctx -> signum , uctx -> siginfo , (void * )uctx -> user_context );
716+ }
652717#endif
653718}
654719
0 commit comments