proc_darwin.go 11.0 KB
Newer Older
1 2
//+build darwin,macnative

3
package native
D
Derek Parker 已提交
4

D
Derek Parker 已提交
5
// #include "proc_darwin.h"
6
// #include "threads_darwin.h"
7
// #include "exec_darwin.h"
8
// #include <stdlib.h>
D
Derek Parker 已提交
9 10
import "C"
import (
11
	"errors"
D
Derek Parker 已提交
12 13
	"fmt"
	"os"
14
	"os/exec"
15
	"path/filepath"
D
Derek Parker 已提交
16 17 18
	"unsafe"

	sys "golang.org/x/sys/unix"
19

20
	"github.com/go-delve/delve/pkg/proc"
D
Derek Parker 已提交
21 22
)

23 24
// osProcessDetails holds Darwin specific information.
type osProcessDetails struct {
25 26 27
	task             C.task_t      // mach task for the debugged process.
	exceptionPort    C.mach_port_t // mach port for receiving mach exceptions.
	notificationPort C.mach_port_t // mach port for dead name notification (process exit).
28
	initialized      bool
29
	halt             bool
30 31 32 33

	// the main port we use, will return messages from both the
	// exception and notification ports.
	portSet C.mach_port_t
D
Derek Parker 已提交
34 35
}

D
Derek Parker 已提交
36
// Launch creates and begins debugging a new process. Uses a
37
// custom fork/exec process in order to take advantage of
D
Derek Parker 已提交
38 39
// PT_SIGEXC on Darwin which will turn Unix signals into
// Mach exceptions.
40
func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target, error) {
41 42
	// check that the argument to Launch is an executable file
	if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
43
		return nil, proc.ErrNotExecutable
44
	}
45
	argv0Go, err := filepath.Abs(cmd[0])
46 47 48
	if err != nil {
		return nil, err
	}
49
	// Make sure the binary exists.
50 51 52 53 54 55 56 57 58
	if filepath.Base(cmd[0]) == cmd[0] {
		if _, err := exec.LookPath(cmd[0]); err != nil {
			return nil, err
		}
	}
	if _, err := os.Stat(argv0Go); err != nil {
		return nil, err
	}

59
	argv0 := C.CString(argv0Go)
60
	argvSlice := make([]*C.char, 0, len(cmd)+1)
61 62 63
	for _, arg := range cmd {
		argvSlice = append(argvSlice, C.CString(arg))
	}
64 65
	// argv array must be null terminated.
	argvSlice = append(argvSlice, nil)
66

67
	dbp := newProcess(0)
68 69
	var pid int
	dbp.execPtraceFunc(func() {
70
		ret := C.fork_exec(argv0, &argvSlice[0], C.int(len(argvSlice)),
E
Evgeny L 已提交
71
			C.CString(wd),
72 73
			&dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort,
			&dbp.os.notificationPort)
74 75
		pid = int(ret)
	})
D
Derek Parker 已提交
76
	if pid <= 0 {
D
Derek Parker 已提交
77
		return nil, fmt.Errorf("could not fork/exec")
78
	}
D
Derek Parker 已提交
79
	dbp.pid = pid
80
	dbp.childProcess = true
81 82 83
	for i := range argvSlice {
		C.free(unsafe.Pointer(argvSlice[i]))
	}
D
Derek Parker 已提交
84

85 86 87 88
	// Initialize enough of the Process state so that we can use resume and
	// trapWait to wait until the child process calls execve.

	for {
89 90 91 92 93 94 95 96 97 98 99
		task := C.get_task_for_pid(C.int(dbp.pid))
		// The task_for_pid call races with the fork call. This can
		// result in the parent task being returned instead of the child.
		if task != dbp.os.task {
			err = dbp.updateThreadListForTask(task)
			if err == nil {
				break
			}
			if err != couldNotGetThreadCount && err != couldNotGetThreadList {
				return nil, err
			}
100 101 102 103 104 105 106
		}
	}

	if err := dbp.resume(); err != nil {
		return nil, err
	}

D
Derek Parker 已提交
107
	for _, th := range dbp.threads {
108
		th.CurrentBreakpoint.Clear()
109 110 111 112 113 114
	}

	trapthread, err := dbp.trapWait(-1)
	if err != nil {
		return nil, err
	}
115
	if err := dbp.stop(nil); err != nil {
116 117 118 119
		return nil, err
	}

	dbp.os.initialized = true
120
	dbp.currentThread = trapthread
121

122 123
	tgt, err := dbp.initialize(argv0Go, []string{})
	if err != nil {
124 125 126
		return nil, err
	}

127
	return tgt, err
128 129
}

130
// Attach to an existing process with the given PID.
131
func Attach(pid int, _ []string) (*proc.Target, error) {
132
	dbp := newProcess(pid)
133 134 135 136 137 138 139 140 141

	kret := C.acquire_mach_task(C.int(pid),
		&dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort,
		&dbp.os.notificationPort)

	if kret != C.KERN_SUCCESS {
		return nil, fmt.Errorf("could not attach to %d", pid)
	}

142 143
	dbp.os.initialized = true

144
	var err error
145
	dbp.execPtraceFunc(func() { err = ptraceAttach(dbp.pid) })
146 147 148 149 150 151 152 153
	if err != nil {
		return nil, err
	}
	_, _, err = dbp.wait(dbp.pid, 0)
	if err != nil {
		return nil, err
	}

154
	tgt, err := dbp.initialize("", []string{})
155 156 157 158
	if err != nil {
		dbp.Detach(false)
		return nil, err
	}
159
	return tgt, nil
160 161
}

D
Derek Parker 已提交
162
// Kill kills the process.
163
func (dbp *nativeProcess) kill() (err error) {
164 165 166
	if dbp.exited {
		return nil
	}
D
Derek Parker 已提交
167
	err = sys.Kill(-dbp.pid, sys.SIGKILL)
168 169 170
	if err != nil {
		return errors.New("could not deliver signal: " + err.Error())
	}
D
Derek Parker 已提交
171
	for port := range dbp.threads {
172 173 174 175 176
		if C.thread_resume(C.thread_act_t(port)) != C.KERN_SUCCESS {
			return errors.New("could not resume task")
		}
	}
	for {
177 178
		var task C.task_t
		port := C.mach_port_wait(dbp.os.portSet, &task, C.int(0))
179 180 181 182
		if port == dbp.os.notificationPort {
			break
		}
	}
183
	dbp.postExit()
184 185 186
	return
}

187
func (dbp *nativeProcess) requestManualStop() (err error) {
D
Derek Parker 已提交
188 189
	var (
		task          = C.mach_port_t(dbp.os.task)
190
		thread        = C.mach_port_t(dbp.currentThread.os.threadAct)
D
Derek Parker 已提交
191 192
		exceptionPort = C.mach_port_t(dbp.os.exceptionPort)
	)
193
	dbp.os.halt = true
D
Derek Parker 已提交
194 195
	kret := C.raise_exception(task, thread, exceptionPort, C.EXC_BREAKPOINT)
	if kret != C.KERN_SUCCESS {
D
Derek Parker 已提交
196
		return fmt.Errorf("could not raise mach exception")
D
Derek Parker 已提交
197 198 199 200
	}
	return nil
}

201 202 203
var couldNotGetThreadCount = errors.New("could not get thread count")
var couldNotGetThreadList = errors.New("could not get thread list")

204
func (dbp *nativeProcess) updateThreadList() error {
205 206 207
	return dbp.updateThreadListForTask(dbp.os.task)
}

208
func (dbp *nativeProcess) updateThreadListForTask(task C.task_t) error {
D
Derek Parker 已提交
209 210 211
	var (
		err   error
		kret  C.kern_return_t
212 213
		count C.int
		list  []uint32
D
Derek Parker 已提交
214 215
	)

216
	for {
217
		count = C.thread_count(task)
218
		if count == -1 {
219
			return couldNotGetThreadCount
220 221 222 223 224
		}
		list = make([]uint32, count)

		// TODO(dp) might be better to malloc mem in C and then free it here
		// instead of getting count above and passing in a slice
225
		kret = C.get_threads(task, unsafe.Pointer(&list[0]), count)
226 227 228
		if kret != -2 {
			break
		}
D
Derek Parker 已提交
229
	}
230
	if kret != C.KERN_SUCCESS {
231 232 233
		return couldNotGetThreadList
	}

D
Derek Parker 已提交
234
	for _, thread := range dbp.threads {
235
		thread.os.exists = false
D
Derek Parker 已提交
236 237 238
	}

	for _, port := range list {
D
Derek Parker 已提交
239
		thread, ok := dbp.threads[int(port)]
240 241
		if !ok {
			thread, err = dbp.addThread(int(port), false)
D
Derek Parker 已提交
242 243 244
			if err != nil {
				return err
			}
D
Derek Parker 已提交
245
		}
246 247 248
		thread.os.exists = true
	}

D
Derek Parker 已提交
249
	for threadID, thread := range dbp.threads {
250
		if !thread.os.exists {
D
Derek Parker 已提交
251
			delete(dbp.threads, threadID)
252
		}
D
Derek Parker 已提交
253 254 255 256 257
	}

	return nil
}

258
func (dbp *nativeProcess) addThread(port int, attach bool) (*nativeThread, error) {
D
Derek Parker 已提交
259
	if thread, ok := dbp.threads[port]; ok {
D
Derek Parker 已提交
260 261
		return thread, nil
	}
262
	thread := &nativeThread{
D
Derek Parker 已提交
263
		ID:  port,
264
		dbp: dbp,
265
		os:  new(osSpecificDetails),
D
Derek Parker 已提交
266
	}
D
Derek Parker 已提交
267
	dbp.threads[port] = thread
D
Derek Parker 已提交
268
	thread.os.threadAct = C.thread_act_t(port)
269
	if dbp.currentThread == nil {
270
		dbp.currentThread = thread
D
Derek Parker 已提交
271
	}
D
Derek Parker 已提交
272 273 274
	return thread, nil
}

275
func findExecutable(path string, pid int) string {
276
	if path == "" {
277
		path = C.GoString(C.find_executable(C.int(pid)))
D
Derek Parker 已提交
278
	}
279
	return path
D
Derek Parker 已提交
280 281
}

282
func (dbp *nativeProcess) trapWait(pid int) (*nativeThread, error) {
283
	for {
284 285
		task := dbp.os.task
		port := C.mach_port_wait(dbp.os.portSet, &task, C.int(0))
286

287 288
		switch port {
		case dbp.os.notificationPort:
289 290 291 292 293 294 295 296
			// on macOS >= 10.12.1 the task_t changes after an execve, we could
			// receive the notification for the death of the pre-execve task_t,
			// this could also happen *before* we are notified that our task_t has
			// changed.
			if dbp.os.task != task {
				continue
			}
			if !dbp.os.initialized {
D
Derek Parker 已提交
297
				if pidtask := C.get_task_for_pid(C.int(dbp.pid)); pidtask != 0 && dbp.os.task != pidtask {
298 299 300
					continue
				}
			}
D
Derek Parker 已提交
301
			_, status, err := dbp.wait(dbp.pid, 0)
302 303 304
			if err != nil {
				return nil, err
			}
305
			dbp.postExit()
306
			return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: status.ExitStatus()}
307

308
		case C.MACH_RCV_INTERRUPTED:
309 310 311
			dbp.stopMu.Lock()
			halt := dbp.os.halt
			dbp.stopMu.Unlock()
312
			if !halt {
313 314 315
				// Call trapWait again, it seems
				// MACH_RCV_INTERRUPTED is emitted before
				// process natural death _sometimes_.
316
				continue
317
			}
D
Derek Parker 已提交
318
			return nil, nil
319

320
		case 0:
D
Derek Parker 已提交
321
			return nil, fmt.Errorf("error while waiting for task")
322 323
		}

324 325 326 327 328 329 330 331 332 333 334
		// In macOS 10.12.1 if we received a notification for a task other than
		// the inferior's task and the inferior's task is no longer valid, this
		// means inferior called execve and its task_t changed.
		if dbp.os.task != task && C.task_is_valid(dbp.os.task) == 0 {
			dbp.os.task = task
			kret := C.reset_exception_ports(dbp.os.task, &dbp.os.exceptionPort, &dbp.os.notificationPort)
			if kret != C.KERN_SUCCESS {
				return nil, fmt.Errorf("could not follow task across exec: %d\n", kret)
			}
		}

335 336 337
		// Since we cannot be notified of new threads on OS X
		// this is as good a time as any to check for them.
		dbp.updateThreadList()
D
Derek Parker 已提交
338
		th, ok := dbp.threads[int(port)]
A
aarzilli 已提交
339
		if !ok {
340 341 342
			dbp.stopMu.Lock()
			halt := dbp.os.halt
			dbp.stopMu.Unlock()
343
			if halt {
344
				dbp.os.halt = false
A
aarzilli 已提交
345
				return th, nil
346
			}
A
aarzilli 已提交
347
			if dbp.firstStart || th.singleStepping {
348
				dbp.firstStart = false
A
aarzilli 已提交
349
				return th, nil
350
			}
A
aarzilli 已提交
351
			if err := th.Continue(); err != nil {
352 353 354
				return nil, err
			}
			continue
355
		}
356
		return th, nil
D
Derek Parker 已提交
357 358 359
	}
}

360
func (dbp *nativeProcess) waitForStop() ([]int, error) {
D
Derek Parker 已提交
361
	ports := make([]int, 0, len(dbp.threads))
362
	count := 0
363
	for {
364 365 366
		var task C.task_t
		port := C.mach_port_wait(dbp.os.portSet, &task, C.int(1))
		if port != 0 && port != dbp.os.notificationPort && port != C.MACH_RCV_INTERRUPTED {
367 368 369
			count = 0
			ports = append(ports, int(port))
		} else {
370
			n := C.num_running_threads(dbp.os.task)
371 372 373 374 375
			if n == 0 {
				return ports, nil
			} else if n < 0 {
				return nil, fmt.Errorf("error waiting for thread stop %d", n)
			} else if count > 16 {
D
Derek Parker 已提交
376
				return nil, fmt.Errorf("could not stop process %d", n)
377
			}
378
		}
379 380 381
	}
}

382
func (dbp *nativeProcess) wait(pid, options int) (int, *sys.WaitStatus, error) {
D
Derek Parker 已提交
383 384 385 386
	var status sys.WaitStatus
	wpid, err := sys.Wait4(pid, &status, options, nil)
	return wpid, &status, err
}
387

L
Luke Hoban 已提交
388 389 390 391
func killProcess(pid int) error {
	return sys.Kill(pid, sys.SIGINT)
}

392
func (dbp *nativeProcess) exitGuard(err error) error {
393 394 395
	if err != ErrContinueThread {
		return err
	}
D
Derek Parker 已提交
396
	_, status, werr := dbp.wait(dbp.pid, sys.WNOHANG)
397 398
	if werr == nil && status.Exited() {
		dbp.postExit()
399
		return proc.ErrProcessExited{Pid: dbp.pid, Status: status.ExitStatus()}
400
	}
401 402
	return err
}
403

404
func (dbp *nativeProcess) resume() error {
405
	// all threads stopped over a breakpoint are made to step over it
D
Derek Parker 已提交
406
	for _, thread := range dbp.threads {
407
		if thread.CurrentBreakpoint.Breakpoint != nil {
408
			if err := thread.StepInstruction(); err != nil {
409 410
				return err
			}
411
			thread.CurrentBreakpoint.Clear()
412 413 414
		}
	}
	// everything is resumed
D
Derek Parker 已提交
415
	for _, thread := range dbp.threads {
416 417 418 419 420 421
		if err := thread.resume(); err != nil {
			return dbp.exitGuard(err)
		}
	}
	return nil
}
422

423
// stop stops all running threads and sets breakpoints
424
func (dbp *nativeProcess) stop(trapthread *nativeThread) (err error) {
425
	if dbp.exited {
426
		return &proc.ErrProcessExited{Pid: dbp.Pid()}
427 428 429
	}
	for _, th := range dbp.threads {
		if !th.Stopped() {
430
			if err := th.stop(); err != nil {
431 432 433 434 435 436 437 438 439 440 441 442
				return dbp.exitGuard(err)
			}
		}
	}

	ports, err := dbp.waitForStop()
	if err != nil {
		return err
	}
	if !dbp.os.initialized {
		return nil
	}
443
	trapthread.SetCurrentBreakpoint(true)
444 445
	for _, port := range ports {
		if th, ok := dbp.threads[port]; ok {
446
			err := th.SetCurrentBreakpoint(true)
447 448 449 450 451 452 453 454
			if err != nil {
				return err
			}
		}
	}
	return nil
}

455 456
func (dbp *nativeProcess) detach(kill bool) error {
	return ptraceDetach(dbp.pid, 0)
457
}
458

459
func (dbp *nativeProcess) EntryPoint() (uint64, error) {
460 461 462
	//TODO(aarzilli): implement this
	return 0, nil
}
463

464
func initialize(dbp *nativeProcess) error { return nil }