github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/runtime/cgo/gcc_signal_darwin_armx.c (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Emulation of the Unix signal SIGSEGV. 6 // 7 // On iOS, Go tests and apps under development are run by lldb. 8 // The debugger uses a task-level exception handler to intercept signals. 9 // Despite having a 'handle' mechanism like gdb, lldb will not allow a 10 // SIGSEGV to pass to the running program. For Go, this means we cannot 11 // generate a panic, which cannot be recovered, and so tests fail. 12 // 13 // We work around this by registering a thread-level mach exception handler 14 // and intercepting EXC_BAD_ACCESS. The kernel offers thread handlers a 15 // chance to resolve exceptions before the task handler, so we can generate 16 // the panic and avoid lldb's SIGSEGV handler. 17 // 18 // The dist tool enables this by build flag when testing. 19 20 // +build lldb 21 // +build darwin 22 // +build arm arm64 23 24 #include <limits.h> 25 #include <pthread.h> 26 #include <stdio.h> 27 #include <signal.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 31 #include <mach/arm/thread_status.h> 32 #include <mach/exception_types.h> 33 #include <mach/mach.h> 34 #include <mach/mach_init.h> 35 #include <mach/mach_port.h> 36 #include <mach/thread_act.h> 37 #include <mach/thread_status.h> 38 39 #include "libcgo.h" 40 #include "libcgo_unix.h" 41 42 uintptr_t x_cgo_panicmem; 43 44 static pthread_mutex_t mach_exception_handler_port_set_mu; 45 static mach_port_t mach_exception_handler_port_set = MACH_PORT_NULL; 46 47 kern_return_t 48 catch_exception_raise( 49 mach_port_t exception_port, 50 mach_port_t thread, 51 mach_port_t task, 52 exception_type_t exception, 53 exception_data_t code_vector, 54 mach_msg_type_number_t code_count) 55 { 56 kern_return_t ret; 57 arm_unified_thread_state_t thread_state; 58 mach_msg_type_number_t state_count = ARM_UNIFIED_THREAD_STATE_COUNT; 59 60 // Returning KERN_SUCCESS intercepts the exception. 61 // 62 // Returning KERN_FAILURE lets the exception fall through to the 63 // next handler, which is the standard signal emulation code 64 // registered on the task port. 65 66 if (exception != EXC_BAD_ACCESS) { 67 return KERN_FAILURE; 68 } 69 70 ret = thread_get_state(thread, ARM_UNIFIED_THREAD_STATE, (thread_state_t)&thread_state, &state_count); 71 if (ret) { 72 fprintf(stderr, "runtime/cgo: thread_get_state failed: %d\n", ret); 73 abort(); 74 } 75 76 // Bounce call to sigpanic through asm that makes it look like 77 // we call sigpanic directly from the faulting code. 78 #ifdef __arm64__ 79 thread_state.ts_64.__x[1] = thread_state.ts_64.__lr; 80 thread_state.ts_64.__x[2] = thread_state.ts_64.__pc; 81 thread_state.ts_64.__pc = x_cgo_panicmem; 82 #else 83 thread_state.ts_32.__r[1] = thread_state.ts_32.__lr; 84 thread_state.ts_32.__r[2] = thread_state.ts_32.__pc; 85 thread_state.ts_32.__pc = x_cgo_panicmem; 86 #endif 87 88 if (0) { 89 // Useful debugging logic when panicmem is broken. 90 // 91 // Sends the first SIGSEGV and lets lldb catch the 92 // second one, avoiding a loop that locks up iOS 93 // devices requiring a hard reboot. 94 fprintf(stderr, "runtime/cgo: caught exc_bad_access\n"); 95 fprintf(stderr, "__lr = %llx\n", thread_state.ts_64.__lr); 96 fprintf(stderr, "__pc = %llx\n", thread_state.ts_64.__pc); 97 static int pass1 = 0; 98 if (pass1) { 99 return KERN_FAILURE; 100 } 101 pass1 = 1; 102 } 103 104 ret = thread_set_state(thread, ARM_UNIFIED_THREAD_STATE, (thread_state_t)&thread_state, state_count); 105 if (ret) { 106 fprintf(stderr, "runtime/cgo: thread_set_state failed: %d\n", ret); 107 abort(); 108 } 109 110 return KERN_SUCCESS; 111 } 112 113 void 114 darwin_arm_init_thread_exception_port() 115 { 116 // Called by each new OS thread to bind its EXC_BAD_ACCESS exception 117 // to mach_exception_handler_port_set. 118 int ret; 119 mach_port_t port = MACH_PORT_NULL; 120 121 ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 122 if (ret) { 123 fprintf(stderr, "runtime/cgo: mach_port_allocate failed: %d\n", ret); 124 abort(); 125 } 126 ret = mach_port_insert_right( 127 mach_task_self(), 128 port, 129 port, 130 MACH_MSG_TYPE_MAKE_SEND); 131 if (ret) { 132 fprintf(stderr, "runtime/cgo: mach_port_insert_right failed: %d\n", ret); 133 abort(); 134 } 135 136 ret = thread_set_exception_ports( 137 mach_thread_self(), 138 EXC_MASK_BAD_ACCESS, 139 port, 140 EXCEPTION_DEFAULT, 141 THREAD_STATE_NONE); 142 if (ret) { 143 fprintf(stderr, "runtime/cgo: thread_set_exception_ports failed: %d\n", ret); 144 abort(); 145 } 146 147 ret = pthread_mutex_lock(&mach_exception_handler_port_set_mu); 148 if (ret) { 149 fprintf(stderr, "runtime/cgo: pthread_mutex_lock failed: %d\n", ret); 150 abort(); 151 } 152 ret = mach_port_move_member( 153 mach_task_self(), 154 port, 155 mach_exception_handler_port_set); 156 if (ret) { 157 fprintf(stderr, "runtime/cgo: mach_port_move_member failed: %d\n", ret); 158 abort(); 159 } 160 ret = pthread_mutex_unlock(&mach_exception_handler_port_set_mu); 161 if (ret) { 162 fprintf(stderr, "runtime/cgo: pthread_mutex_unlock failed: %d\n", ret); 163 abort(); 164 } 165 } 166 167 static void* 168 mach_exception_handler(void *port) 169 { 170 // Calls catch_exception_raise. 171 extern boolean_t exc_server(); 172 mach_msg_server(exc_server, 2048, (mach_port_t)port, 0); 173 abort(); // never returns 174 } 175 176 void 177 darwin_arm_init_mach_exception_handler() 178 { 179 pthread_mutex_init(&mach_exception_handler_port_set_mu, NULL); 180 181 // Called once per process to initialize a mach port server, listening 182 // for EXC_BAD_ACCESS thread exceptions. 183 int ret; 184 pthread_t thr = NULL; 185 pthread_attr_t attr; 186 sigset_t ign, oset; 187 188 ret = mach_port_allocate( 189 mach_task_self(), 190 MACH_PORT_RIGHT_PORT_SET, 191 &mach_exception_handler_port_set); 192 if (ret) { 193 fprintf(stderr, "runtime/cgo: mach_port_allocate failed for port_set: %d\n", ret); 194 abort(); 195 } 196 197 // Block all signals to the exception handler thread 198 sigfillset(&ign); 199 pthread_sigmask(SIG_SETMASK, &ign, &oset); 200 201 // Start a thread to handle exceptions. 202 uintptr_t port_set = (uintptr_t)mach_exception_handler_port_set; 203 pthread_attr_init(&attr); 204 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 205 ret = _cgo_try_pthread_create(&thr, &attr, mach_exception_handler, (void*)port_set); 206 207 pthread_sigmask(SIG_SETMASK, &oset, nil); 208 209 if (ret) { 210 fprintf(stderr, "runtime/cgo: pthread_create failed: %d\n", ret); 211 abort(); 212 } 213 pthread_attr_destroy(&attr); 214 }