github.com/mangodowner/go-gm@v0.0.0-20180818020936-8baa2bd4408c/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  }