signal_handler_darwin.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. //go:build darwin && badlinkname
  2. package libbox
  3. /*
  4. #include <signal.h>
  5. #include <stdint.h>
  6. #include <string.h>
  7. static struct sigaction _go_sa[32];
  8. static struct sigaction _plcrash_sa[32];
  9. static int _saved = 0;
  10. static int _signals[] = {SIGSEGV, SIGBUS, SIGFPE, SIGILL, SIGTRAP};
  11. static const int _signal_count = sizeof(_signals) / sizeof(_signals[0]);
  12. static void _save_go_handlers(void) {
  13. if (_saved) return;
  14. for (int i = 0; i < _signal_count; i++)
  15. sigaction(_signals[i], NULL, &_go_sa[_signals[i]]);
  16. _saved = 1;
  17. }
  18. static void _combined_handler(int sig, siginfo_t *info, void *uap) {
  19. // Step 1: PLCrashReporter writes .plcrash, resets all handlers to SIG_DFL,
  20. // and calls raise(sig) which pends (signal is blocked, no SA_NODEFER).
  21. if ((_plcrash_sa[sig].sa_flags & SA_SIGINFO) &&
  22. (uintptr_t)_plcrash_sa[sig].sa_sigaction > 1)
  23. _plcrash_sa[sig].sa_sigaction(sig, info, uap);
  24. // SIGTRAP does not rely on sigreturn -> sigpanic. Once Go's trap trampoline
  25. // is force-installed, we can chain into it directly after PLCrashReporter.
  26. if (sig == SIGTRAP &&
  27. (_go_sa[sig].sa_flags & SA_SIGINFO) &&
  28. (uintptr_t)_go_sa[sig].sa_sigaction > 1) {
  29. _go_sa[sig].sa_sigaction(sig, info, uap);
  30. return;
  31. }
  32. // Step 2: Restore Go's handler via sigaction (overwrites PLCrashReporter's SIG_DFL).
  33. // Do NOT call Go's handler directly — Go's preparePanic only modifies the
  34. // ucontext and returns. The actual crash output is written by sigpanic, which
  35. // only runs when the KERNEL restores the modified ucontext via sigreturn.
  36. // A direct C function call has no sigreturn, so sigpanic would never execute.
  37. sigaction(sig, &_go_sa[sig], NULL);
  38. // Step 3: Return. The kernel restores the original ucontext and re-executes
  39. // the faulting instruction. Two signals are now pending/imminent:
  40. // a) PLCrashReporter's raise() (SI_USER) — Go's handler ignores it
  41. // (sighandler: sigFromUser() → return).
  42. // b) The re-executed fault (SEGV_MAPERR) — Go's handler processes it:
  43. // preparePanic → kernel sigreturn → sigpanic → crash output written
  44. // via debug.SetCrashOutput.
  45. }
  46. static void _reinstall_handlers(void) {
  47. if (!_saved) return;
  48. for (int i = 0; i < _signal_count; i++) {
  49. int sig = _signals[i];
  50. struct sigaction current;
  51. sigaction(sig, NULL, &current);
  52. // Only save the handler if it's not one of ours
  53. if (current.sa_sigaction != _combined_handler) {
  54. // If current handler is still Go's, PLCrashReporter wasn't installed
  55. if ((current.sa_flags & SA_SIGINFO) &&
  56. (uintptr_t)current.sa_sigaction > 1 &&
  57. current.sa_sigaction == _go_sa[sig].sa_sigaction)
  58. memset(&_plcrash_sa[sig], 0, sizeof(_plcrash_sa[sig]));
  59. else
  60. _plcrash_sa[sig] = current;
  61. }
  62. struct sigaction sa;
  63. memset(&sa, 0, sizeof(sa));
  64. sa.sa_sigaction = _combined_handler;
  65. sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
  66. sigemptyset(&sa.sa_mask);
  67. sigaction(sig, &sa, NULL);
  68. }
  69. }
  70. */
  71. import "C"
  72. import (
  73. "reflect"
  74. _ "unsafe"
  75. )
  76. const (
  77. _sigtrap = 5
  78. _nsig = 32
  79. )
  80. //go:linkname runtimeGetsig runtime.getsig
  81. func runtimeGetsig(i uint32) uintptr
  82. //go:linkname runtimeSetsig runtime.setsig
  83. func runtimeSetsig(i uint32, fn uintptr)
  84. //go:linkname runtimeCgoSigtramp runtime.cgoSigtramp
  85. func runtimeCgoSigtramp()
  86. //go:linkname runtimeFwdSig runtime.fwdSig
  87. var runtimeFwdSig [_nsig]uintptr
  88. //go:linkname runtimeHandlingSig runtime.handlingSig
  89. var runtimeHandlingSig [_nsig]uint32
  90. func forceGoSIGTRAPHandler() {
  91. runtimeFwdSig[_sigtrap] = runtimeGetsig(_sigtrap)
  92. runtimeHandlingSig[_sigtrap] = 1
  93. runtimeSetsig(_sigtrap, reflect.ValueOf(runtimeCgoSigtramp).Pointer())
  94. }
  95. // PrepareCrashSignalHandlers captures Go's original synchronous signal handlers.
  96. //
  97. // In gomobile/c-archive embeddings, package init runs on the first Go entry.
  98. // That means a native crash reporter installed before the first Go call would
  99. // otherwise be captured as the "Go" handler and break handler restoration on
  100. // SIGSEGV. Go skips SIGTRAP in c-archive mode, so install its trap trampoline
  101. // before saving handlers. Call this before installing PLCrashReporter.
  102. func PrepareCrashSignalHandlers() {
  103. forceGoSIGTRAPHandler()
  104. C._save_go_handlers()
  105. }
  106. // ReinstallCrashSignalHandlers installs a combined signal handler that chains
  107. // PLCrashReporter (native crash report) and Go's runtime handler (Go crash log).
  108. //
  109. // Call PrepareCrashSignalHandlers before installing PLCrashReporter, then call
  110. // this after PLCrashReporter has been installed.
  111. //
  112. // Flow on SIGSEGV:
  113. // 1. Combined handler calls PLCrashReporter's saved handler → .plcrash written
  114. // 2. Combined handler restores Go's handler via sigaction
  115. // 3. Combined handler returns — kernel re-executes faulting instruction
  116. // 4. PLCrashReporter's pending raise() (SI_USER) is ignored by Go's handler
  117. // 5. Hardware fault → Go's handler → preparePanic → kernel sigreturn →
  118. // sigpanic → crash output via debug.SetCrashOutput
  119. //
  120. // Flow on SIGTRAP:
  121. // 1. PrepareCrashSignalHandlers force-installs Go's cgo trap trampoline
  122. // 2. Combined handler calls PLCrashReporter's saved handler → .plcrash written
  123. // 3. Combined handler directly calls the saved Go trap trampoline
  124. func ReinstallCrashSignalHandlers() {
  125. C._reinstall_handlers()
  126. }