github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/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 // If you want to debug a segfault under lldb, compile the standard library 19 // with the build tag lldb: 20 // 21 // go test -tags lldb -installsuffix lldb 22 23 // +build !lldb 24 // +build darwin 25 // +build arm arm64 26 27 #include <limits.h> 28 #include <pthread.h> 29 #include <stdio.h> 30 #include <signal.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 34 #include <mach/arm/thread_status.h> 35 #include <mach/exception_types.h> 36 #include <mach/mach.h> 37 #include <mach/mach_init.h> 38 #include <mach/mach_port.h> 39 #include <mach/thread_act.h> 40 #include <mach/thread_status.h> 41 42 #include "libcgo.h" 43 44 uintptr_t x_cgo_panicmem; 45 46 static pthread_mutex_t mach_exception_handler_port_set_mu; 47 static mach_port_t mach_exception_handler_port_set = MACH_PORT_NULL; 48 49 kern_return_t 50 catch_exception_raise( 51 mach_port_t exception_port, 52 mach_port_t thread, 53 mach_port_t task, 54 exception_type_t exception, 55 exception_data_t code_vector, 56 mach_msg_type_number_t code_count) 57 { 58 kern_return_t ret; 59 arm_unified_thread_state_t thread_state; 60 mach_msg_type_number_t state_count = ARM_UNIFIED_THREAD_STATE_COUNT; 61 62 // Returning KERN_SUCCESS intercepts the exception. 63 // 64 // Returning KERN_FAILURE lets the exception fall through to the 65 // next handler, which is the standard signal emulation code 66 // registered on the task port. 67 68 if (exception != EXC_BAD_ACCESS) { 69 return KERN_FAILURE; 70 } 71 72 ret = thread_get_state(thread, ARM_UNIFIED_THREAD_STATE, (thread_state_t)&thread_state, &state_count); 73 if (ret) { 74 fprintf(stderr, "runtime/cgo: thread_get_state failed: %d\n", ret); 75 abort(); 76 } 77 78 // Bounce call to sigpanic through asm that makes it look like 79 // we call sigpanic directly from the faulting code. 80 #ifdef __arm64__ 81 thread_state.ts_64.__x[1] = thread_state.ts_64.__lr; 82 thread_state.ts_64.__x[2] = thread_state.ts_64.__pc; 83 thread_state.ts_64.__pc = x_cgo_panicmem; 84 #else 85 thread_state.ts_32.__r[1] = thread_state.ts_32.__lr; 86 thread_state.ts_32.__r[2] = thread_state.ts_32.__pc; 87 thread_state.ts_32.__pc = x_cgo_panicmem; 88 #endif 89 90 if (0) { 91 // Useful debugging logic when panicmem is broken. 92 // 93 // Sends the first SIGSEGV and lets lldb catch the 94 // second one, avoiding a loop that locks up iOS 95 // devices requiring a hard reboot. 96 fprintf(stderr, "runtime/cgo: caught exc_bad_access\n"); 97 fprintf(stderr, "__lr = %llx\n", thread_state.ts_64.__lr); 98 fprintf(stderr, "__pc = %llx\n", thread_state.ts_64.__pc); 99 static int pass1 = 0; 100 if (pass1) { 101 return KERN_FAILURE; 102 } 103 pass1 = 1; 104 } 105 106 ret = thread_set_state(thread, ARM_UNIFIED_THREAD_STATE, (thread_state_t)&thread_state, state_count); 107 if (ret) { 108 fprintf(stderr, "runtime/cgo: thread_set_state failed: %d\n", ret); 109 abort(); 110 } 111 112 return KERN_SUCCESS; 113 } 114 115 void 116 darwin_arm_init_thread_exception_port() 117 { 118 // Called by each new OS thread to bind its EXC_BAD_ACCESS exception 119 // to mach_exception_handler_port_set. 120 int ret; 121 mach_port_t port = MACH_PORT_NULL; 122 123 ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 124 if (ret) { 125 fprintf(stderr, "runtime/cgo: mach_port_allocate failed: %d\n", ret); 126 abort(); 127 } 128 ret = mach_port_insert_right( 129 mach_task_self(), 130 port, 131 port, 132 MACH_MSG_TYPE_MAKE_SEND); 133 if (ret) { 134 fprintf(stderr, "runtime/cgo: mach_port_insert_right failed: %d\n", ret); 135 abort(); 136 } 137 138 ret = thread_set_exception_ports( 139 mach_thread_self(), 140 EXC_MASK_BAD_ACCESS, 141 port, 142 EXCEPTION_DEFAULT, 143 THREAD_STATE_NONE); 144 if (ret) { 145 fprintf(stderr, "runtime/cgo: thread_set_exception_ports failed: %d\n", ret); 146 abort(); 147 } 148 149 ret = pthread_mutex_lock(&mach_exception_handler_port_set_mu); 150 if (ret) { 151 fprintf(stderr, "runtime/cgo: pthread_mutex_lock failed: %d\n", ret); 152 abort(); 153 } 154 ret = mach_port_move_member( 155 mach_task_self(), 156 port, 157 mach_exception_handler_port_set); 158 if (ret) { 159 fprintf(stderr, "runtime/cgo: mach_port_move_member failed: %d\n", ret); 160 abort(); 161 } 162 ret = pthread_mutex_unlock(&mach_exception_handler_port_set_mu); 163 if (ret) { 164 fprintf(stderr, "runtime/cgo: pthread_mutex_unlock failed: %d\n", ret); 165 abort(); 166 } 167 } 168 169 static void* 170 mach_exception_handler(void *port) 171 { 172 // Calls catch_exception_raise. 173 extern boolean_t exc_server(); 174 mach_msg_server(exc_server, 2048, (mach_port_t)port, 0); 175 abort(); // never returns 176 } 177 178 void 179 darwin_arm_init_mach_exception_handler() 180 { 181 pthread_mutex_init(&mach_exception_handler_port_set_mu, NULL); 182 183 // Called once per process to initialize a mach port server, listening 184 // for EXC_BAD_ACCESS thread exceptions. 185 int ret; 186 pthread_t thr = NULL; 187 pthread_attr_t attr; 188 189 ret = mach_port_allocate( 190 mach_task_self(), 191 MACH_PORT_RIGHT_PORT_SET, 192 &mach_exception_handler_port_set); 193 if (ret) { 194 fprintf(stderr, "runtime/cgo: mach_port_allocate failed for port_set: %d\n", ret); 195 abort(); 196 } 197 198 // Start a thread to handle exceptions. 199 uintptr_t port_set = (uintptr_t)mach_exception_handler_port_set; 200 pthread_attr_init(&attr); 201 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 202 ret = pthread_create(&thr, &attr, mach_exception_handler, (void*)port_set); 203 if (ret) { 204 fprintf(stderr, "runtime/cgo: pthread_create failed: %d\n", ret); 205 abort(); 206 } 207 pthread_attr_destroy(&attr); 208 }