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