github.com/undoio/delve@v1.9.0/pkg/proc/native/proc_darwin.c (about)

     1  //+build darwin,macnative
     2  
     3  #include "proc_darwin.h"
     4  
     5  static const unsigned char info_plist[]
     6  __attribute__ ((section ("__TEXT,__info_plist"),used)) =
     7  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
     8  "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
     9  " \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
    10  "<plist version=\"1.0\">\n"
    11  "<dict>\n"
    12  "  <key>CFBundleIdentifier</key>\n"
    13  "  <string>org.dlv</string>\n"
    14  "  <key>CFBundleName</key>\n"
    15  "  <string>delve</string>\n"
    16  "  <key>CFBundleVersion</key>\n"
    17  "  <string>1.0</string>\n"
    18  "  <key>SecTaskAccess</key>\n"
    19  "  <array>\n"
    20  "    <string>allowed</string>\n"
    21  "    <string>debug</string>\n"
    22  "  </array>\n"
    23  "</dict>\n"
    24  "</plist>\n";
    25  
    26  kern_return_t
    27  acquire_mach_task(int tid,
    28  		task_t *task,
    29  		mach_port_t *port_set,
    30  		mach_port_t *exception_port,
    31  		mach_port_t *notification_port)
    32  {
    33  	kern_return_t kret;
    34  	mach_port_t prev_not;
    35  	mach_port_t self = mach_task_self();
    36  
    37  	kret = task_for_pid(self, tid, task);
    38  	if (kret != KERN_SUCCESS) return kret;
    39  
    40  	// Allocate exception port.
    41  	kret = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, exception_port);
    42  	if (kret != KERN_SUCCESS) return kret;
    43  
    44  	kret = mach_port_insert_right(self, *exception_port, *exception_port, MACH_MSG_TYPE_MAKE_SEND);
    45  	if (kret != KERN_SUCCESS) return kret;
    46  
    47  	kret = task_set_exception_ports(*task, EXC_MASK_BREAKPOINT|EXC_MASK_SOFTWARE, *exception_port,
    48  			EXCEPTION_DEFAULT, THREAD_STATE_NONE);
    49  	if (kret != KERN_SUCCESS) return kret;
    50  
    51  	// Allocate notification port to alert of when the process dies.
    52  	kret = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, notification_port);
    53  	if (kret != KERN_SUCCESS) return kret;
    54  
    55  	kret = mach_port_insert_right(self, *notification_port, *notification_port, MACH_MSG_TYPE_MAKE_SEND);
    56  	if (kret != KERN_SUCCESS) return kret;
    57  
    58  	kret = mach_port_request_notification(self, *task, MACH_NOTIFY_DEAD_NAME, 0, *notification_port,
    59  			MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev_not);
    60  	if (kret != KERN_SUCCESS) return kret;
    61  
    62  	// Create port set.
    63  	kret = mach_port_allocate(self, MACH_PORT_RIGHT_PORT_SET, port_set);
    64  	if (kret != KERN_SUCCESS) return kret;
    65  
    66  	// Move exception and notification ports to port set.
    67  	kret = mach_port_move_member(self, *exception_port, *port_set);
    68  	if (kret != KERN_SUCCESS) return kret;
    69  
    70  	return mach_port_move_member(self, *notification_port, *port_set);
    71  }
    72  
    73  kern_return_t
    74  reset_exception_ports(task_t task, mach_port_t *exception_port, mach_port_t *notification_port) {
    75  	kern_return_t kret;
    76  	mach_port_t prev_not;
    77  	mach_port_t self = mach_task_self();
    78  	
    79  	kret = task_set_exception_ports(task, EXC_MASK_BREAKPOINT|EXC_MASK_SOFTWARE, *exception_port,
    80  			EXCEPTION_DEFAULT, THREAD_STATE_NONE);
    81  	if (kret != KERN_SUCCESS) return kret;
    82  	
    83  	kret = mach_port_request_notification(self, task, MACH_NOTIFY_DEAD_NAME, 0, *notification_port,
    84  			MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev_not);
    85  	if (kret != KERN_SUCCESS) return kret;
    86  	
    87  	return KERN_SUCCESS;
    88  }
    89  
    90  char *
    91  find_executable(int pid) {
    92  	static char pathbuf[PATH_MAX];
    93  	proc_pidpath(pid, pathbuf, PATH_MAX);
    94  	return pathbuf;
    95  }
    96  
    97  kern_return_t
    98  get_threads(task_t task, void *slice, int limit) {
    99  	kern_return_t kret;
   100  	thread_act_array_t list;
   101  	mach_msg_type_number_t count;
   102  
   103  	kret = task_threads(task, &list, &count);
   104  	if (kret != KERN_SUCCESS) {
   105  		return kret;
   106  	}
   107  
   108  	if (count > limit) {
   109  		vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
   110  		return -2;
   111  	}
   112  
   113  	memcpy(slice, (void*)list, count*sizeof(list[0]));
   114  
   115  	kret = vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
   116  	if (kret != KERN_SUCCESS) return kret;
   117  
   118  	return (kern_return_t)0;
   119  }
   120  
   121  int
   122  thread_count(task_t task) {
   123  	kern_return_t kret;
   124  	thread_act_array_t list;
   125  	mach_msg_type_number_t count;
   126  
   127  	kret = task_threads(task, &list, &count);
   128  	if (kret != KERN_SUCCESS) return -1;
   129  
   130  	kret = vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
   131  	if (kret != KERN_SUCCESS) return -1;
   132  
   133  	return count;
   134  }
   135  
   136  mach_port_t
   137  mach_port_wait(mach_port_t port_set, task_t *task, int nonblocking) {
   138  	kern_return_t kret;
   139  	thread_act_t thread;
   140  	NDR_record_t *ndr;
   141  	integer_t *data;
   142  	union
   143  	{
   144  		mach_msg_header_t hdr;
   145  		char data[256];
   146  	} msg;
   147  	mach_msg_option_t opts = MACH_RCV_MSG|MACH_RCV_INTERRUPT;
   148  	if (nonblocking) {
   149  		opts |= MACH_RCV_TIMEOUT;
   150  	}
   151  
   152  	// Wait for mach msg.
   153  	kret = mach_msg(&msg.hdr, opts,
   154  			0, sizeof(msg.data), port_set, 10, MACH_PORT_NULL);
   155  	if (kret == MACH_RCV_INTERRUPTED) return kret;
   156  	if (kret != MACH_MSG_SUCCESS) return 0;
   157  
   158  
   159  	switch (msg.hdr.msgh_id) {
   160  		case 2401: { // Exception
   161  			// 2401 is the exception_raise event, defined in:
   162  			// http://opensource.apple.com/source/xnu/xnu-2422.1.72/osfmk/mach/exc.defs?txt
   163  			// compile this file with mig to get the C version of the description
   164  			
   165  			mach_msg_body_t *bod = (mach_msg_body_t*)(&msg.hdr + 1);
   166  			mach_msg_port_descriptor_t *desc = (mach_msg_port_descriptor_t *)(bod + 1);
   167  			thread = desc[0].name;
   168  			*task = desc[1].name;
   169  			ndr = (NDR_record_t *)(desc + 2);
   170  			data = (integer_t *)(ndr + 1);
   171  
   172  			if (thread_suspend(thread) != KERN_SUCCESS) return 0;
   173  			// Send our reply back so the kernel knows this exception has been handled.
   174  			kret = mach_send_reply(msg.hdr);
   175  			if (kret != MACH_MSG_SUCCESS) return 0;
   176  			if (data[2] == EXC_SOFT_SIGNAL) {
   177  				if (data[3] != SIGTRAP) {
   178  					if (thread_resume(thread) != KERN_SUCCESS) return 0;
   179  					return mach_port_wait(port_set, task, nonblocking);
   180  				}
   181  			}
   182  			return thread;
   183  		}
   184  
   185  		case 72: { // Death
   186  			// 72 is mach_notify_dead_name, defined in:
   187  			// https://opensource.apple.com/source/xnu/xnu-1228.7.58/osfmk/mach/notify.defs?txt
   188  			// compile this file with mig to get the C version of the description
   189  			ndr = (NDR_record_t *)(&msg.hdr + 1);
   190  			*task = *((mach_port_name_t *)(ndr + 1));
   191  			return msg.hdr.msgh_local_port;
   192  		}
   193  	}
   194  	return 0;
   195  }
   196  
   197  kern_return_t
   198  mach_send_reply(mach_msg_header_t hdr) {
   199  	mig_reply_error_t reply;
   200  	mach_msg_header_t *rh = &reply.Head;
   201  	rh->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(hdr.msgh_bits), 0);
   202  	rh->msgh_remote_port = hdr.msgh_remote_port;
   203  	rh->msgh_size = (mach_msg_size_t) sizeof(mig_reply_error_t);
   204  	rh->msgh_local_port = MACH_PORT_NULL;
   205  	rh->msgh_id = hdr.msgh_id + 100;
   206  
   207  	reply.NDR = NDR_record;
   208  	reply.RetCode = KERN_SUCCESS;
   209  
   210  	return mach_msg(&reply.Head, MACH_SEND_MSG|MACH_SEND_INTERRUPT, rh->msgh_size, 0,
   211  			MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
   212  }
   213  
   214  kern_return_t
   215  raise_exception(mach_port_t task, mach_port_t thread, mach_port_t exception_port, exception_type_t exception) {
   216  	return exception_raise(exception_port, thread, task, exception, 0, 0);
   217  }
   218  
   219  task_t
   220  get_task_for_pid(int pid) {
   221  	task_t task = 0;
   222  	mach_port_t self = mach_task_self();
   223  
   224  	task_for_pid(self, pid, &task);
   225  	return task;
   226  }
   227  
   228  int
   229  task_is_valid(task_t task) {
   230  	struct task_basic_info info;
   231  	mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
   232  	return task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count) == KERN_SUCCESS;
   233  }