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

     1  //+build darwin,macnative
     2  
     3  #include "exec_darwin.h"
     4  #include "stdio.h"
     5  
     6  extern char** environ;
     7  
     8  int
     9  close_exec_pipe(int fd[2]) {
    10  	if (pipe(fd) < 0) return -1;
    11  	if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0) return -1;
    12  	if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0) return -1;
    13  	return 0;
    14  }
    15  
    16  int
    17  fork_exec(char *argv0, char **argv, int size,
    18  		char *wd,
    19  		task_t *task,
    20  		mach_port_t *port_set,
    21  		mach_port_t *exception_port,
    22  		mach_port_t *notification_port)
    23  {
    24  	// Since we're using mach exceptions instead of signals,
    25  	// we need to coordinate between parent and child via pipes
    26  	// to ensure that the parent has set the exception ports on
    27  	// the child task before it execs.
    28  	int fd[2];
    29  	if (close_exec_pipe(fd) < 0) return -1;
    30  
    31  	// Create another pipe to signal the parent on exec.
    32  	int efd[2];
    33  	if (close_exec_pipe(efd) < 0) return -1;
    34  
    35  	kern_return_t kret;
    36  	pid_t pid = fork();
    37  	if (pid > 0) {
    38  		// In parent.
    39  		close(fd[0]);
    40  		close(efd[1]);
    41  		kret = acquire_mach_task(pid, task, port_set, exception_port, notification_port);
    42  		if (kret != KERN_SUCCESS) return -1;
    43  
    44  		char msg = 'c';
    45  		write(fd[1], &msg, 1);
    46  		close(fd[1]);
    47  
    48  		char w;
    49  		size_t n = read(efd[0], &w, 1);
    50  		close(efd[0]);
    51  		if (n != 0) {
    52  			// Child died, reap it.
    53  			waitpid(pid, NULL, 0);
    54  			return -1;
    55  		}
    56  		return pid;
    57  	}
    58  
    59  	// Fork succeeded, we are in the child.
    60  	int pret, cret;
    61  	char sig;
    62  
    63  	close(fd[1]);
    64  	read(fd[0], &sig, 1);
    65  	close(fd[0]);
    66  
    67  	// Create a new process group.
    68  	if (setpgid(0, 0) < 0) {
    69  		perror("setpgid");
    70  		exit(1);
    71  	}
    72  
    73  	// Set errno to zero before a call to ptrace.
    74  	// It is documented that ptrace can return -1 even
    75  	// for successful calls.
    76  	errno = 0;
    77  	pret = ptrace(PT_TRACE_ME, 0, 0, 0);
    78  	if (pret != 0 && errno != 0) {
    79  		perror("ptrace");
    80  		exit(1);
    81  	}
    82  
    83  	// Change working directory if wd is not empty.
    84  	if (wd && wd[0]) {
    85  		errno = 0;
    86  		cret = chdir(wd);
    87  		if (cret != 0 && errno != 0) {
    88  			char *error_msg;
    89  			asprintf(&error_msg, "%s '%s'", "chdir", wd);
    90  			perror(error_msg);
    91  			exit(1);
    92  		}
    93  	}
    94  
    95  	errno = 0;
    96  	pret = ptrace(PT_SIGEXC, 0, 0, 0);
    97  	if (pret != 0 && errno != 0) {
    98  		perror("ptrace");
    99  		exit(1);
   100  	}
   101  
   102  	sleep(1);
   103  
   104  	// Create the child process.
   105  	execve(argv0, argv, environ);
   106  
   107  	// We should never reach here, but if we did something went wrong.
   108  	// Write a message to parent to alert that exec failed.
   109  	char msg = 'd';
   110  	write(efd[1], &msg, 1);
   111  	close(efd[1]);
   112  
   113  	exit(1);
   114  }