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