X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gdb%2Fdarwin-nat.c;h=6247b0656c9507de3a8f89e1b74a42b8b4dc30a5;hb=96a86c01d119372f4af5aff2501d3104e6c1a8e3;hp=f0935a6a53f15a29f024af52728574c2695f66b5;hpb=41bf6acad7b02f67240f4cf84f066078f9ed7116;p=binutils-gdb.git diff --git a/gdb/darwin-nat.c b/gdb/darwin-nat.c index f0935a6a53f..6247b0656c9 100644 --- a/gdb/darwin-nat.c +++ b/gdb/darwin-nat.c @@ -1,5 +1,5 @@ /* Darwin support for GDB, the GNU debugger. - Copyright (C) 2008-2014 Free Software Foundation, Inc. + Copyright (C) 2008-2019 Free Software Foundation, Inc. Contributed by AdaCore. @@ -25,7 +25,6 @@ #include "symfile.h" #include "symtab.h" #include "objfiles.h" -#include "gdb.h" #include "gdbcmd.h" #include "gdbcore.h" #include "gdbthread.h" @@ -33,20 +32,19 @@ #include "event-top.h" #include "inf-loop.h" #include -#include "exceptions.h" #include "inf-child.h" #include "value.h" #include "arch-utils.h" #include "bfd.h" #include "bfd/mach-o.h" +#include #include #include -#include +#include #include #include #include -#include #include #include #include @@ -64,7 +62,12 @@ #include #include "darwin-nat.h" +#include "filenames.h" #include "common/filestuff.h" +#include "common/gdb_unlinker.h" +#include "common/pathstuff.h" +#include "common/scoped_fd.h" +#include "nat/fork-inferior.h" /* Quick overview. Darwin kernel is Mach + BSD derived kernel. Note that they share the @@ -85,38 +88,19 @@ #define PTRACE(CMD, PID, ADDR, SIG) \ darwin_ptrace(#CMD, CMD, (PID), (ADDR), (SIG)) -extern boolean_t exc_server (mach_msg_header_t *in, mach_msg_header_t *out); - -static void darwin_stop (ptid_t); - -static void darwin_resume_to (struct target_ops *ops, ptid_t ptid, int step, - enum gdb_signal signal); -static void darwin_resume (ptid_t ptid, int step, - enum gdb_signal signal); - -static ptid_t darwin_wait_to (struct target_ops *ops, ptid_t ptid, - struct target_waitstatus *status, int options); static ptid_t darwin_wait (ptid_t ptid, struct target_waitstatus *status); -static void darwin_mourn_inferior (struct target_ops *ops); - -static void darwin_kill_inferior (struct target_ops *ops); - static void darwin_ptrace_me (void); static void darwin_ptrace_him (int pid); -static void darwin_create_inferior (struct target_ops *ops, char *exec_file, - char *allargs, char **env, int from_tty); - -static void darwin_files_info (struct target_ops *ops); - -static char *darwin_pid_to_str (struct target_ops *ops, ptid_t tpid); - -static int darwin_thread_alive (struct target_ops *ops, ptid_t tpid); +static void darwin_encode_reply (mig_reply_error_t *reply, + mach_msg_header_t *hdr, integer_t code); -/* Target operations for Darwin. */ -static struct target_ops *darwin_ops; +static void darwin_setup_request_notification (struct inferior *inf); +static void darwin_deallocate_exception_ports (darwin_inferior *inf); +static void darwin_setup_exceptions (struct inferior *inf); +static void darwin_deallocate_threads (struct inferior *inf); /* Task identifier of gdb. */ static task_t gdb_task; @@ -127,7 +111,7 @@ mach_port_t darwin_host_self; /* Exception port. */ mach_port_t darwin_ex_port; -/* Port set. */ +/* Port set, to wait for answer on all ports. */ mach_port_t darwin_port_set; /* Page size. */ @@ -140,6 +124,10 @@ static int enable_mach_exceptions; /* Inferior that should report a fake stop event. */ static struct inferior *darwin_inf_fake_stop; +/* If non-NULL, the shell we actually invoke. See maybe_cache_shell + for details. */ +static const char *copied_shell; + #define PAGE_TRUNC(x) ((x) & ~(mach_page_size - 1)) #define PAGE_ROUND(x) PAGE_TRUNC((x) + mach_page_size - 1) @@ -149,10 +137,8 @@ static unsigned int darwin_debug_flag = 0; /* Create a __TEXT __info_plist section in the executable so that gdb could be signed. This is required to get an authorization for task_for_pid. - Once gdb is built, you can either: - * make it setgid procmod - * or codesign it with any system-trusted signing authority. - See taskgated(8) for details. */ + Once gdb is built, you must codesign it with any system-trusted signing + authority. See taskgated(8) for details. */ static const unsigned char info_plist[] __attribute__ ((section ("__TEXT,__info_plist"),used)) = "\n" @@ -174,6 +160,9 @@ __attribute__ ((section ("__TEXT,__info_plist"),used)) = "\n" "\n"; +static void inferior_debug (int level, const char *fmt, ...) + ATTRIBUTE_PRINTF (2, 3); + static void inferior_debug (int level, const char *fmt, ...) { @@ -245,17 +234,17 @@ unparse_exception_type (unsigned int i) static int darwin_ptrace (const char *name, - int request, int pid, PTRACE_TYPE_ARG3 arg3, int arg4) + int request, int pid, caddr_t arg3, int arg4) { int ret; errno = 0; - ret = ptrace (request, pid, (caddr_t) arg3, arg4); + ret = ptrace (request, pid, arg3, arg4); if (ret == -1 && errno == 0) ret = 0; - inferior_debug (4, _("ptrace (%s, %d, 0x%x, %d): %d (%s)\n"), - name, pid, arg3, arg4, ret, + inferior_debug (4, _("ptrace (%s, %d, 0x%lx, %d): %d (%s)\n"), + name, pid, (unsigned long) arg3, arg4, ret, (ret != 0) ? safe_strerror (errno) : _("no error")); return ret; } @@ -272,13 +261,15 @@ static void darwin_check_new_threads (struct inferior *inf) { kern_return_t kret; - unsigned int i; thread_array_t thread_list; unsigned int new_nbr; unsigned int old_nbr; unsigned int new_ix, old_ix; - darwin_inferior *darwin_inf = inf->private; - VEC (darwin_thread_t) *thread_vec; + darwin_inferior *darwin_inf = get_darwin_inferior (inf); + std::vector new_thread_vec; + + if (darwin_inf == nullptr) + return; /* Get list of threads. */ kret = task_threads (darwin_inf->task, &thread_list, &new_nbr); @@ -290,36 +281,44 @@ darwin_check_new_threads (struct inferior *inf) if (new_nbr > 1) qsort (thread_list, new_nbr, sizeof (thread_t), cmp_thread_t); - if (darwin_inf->threads) - old_nbr = VEC_length (darwin_thread_t, darwin_inf->threads); - else - old_nbr = 0; + old_nbr = darwin_inf->threads.size (); /* Quick check for no changes. */ if (old_nbr == new_nbr) { + size_t i; + for (i = 0; i < new_nbr; i++) - if (thread_list[i] - != VEC_index (darwin_thread_t, darwin_inf->threads, i)->gdb_port) + if (thread_list[i] != darwin_inf->threads[i]->gdb_port) break; if (i == new_nbr) { + /* Deallocate ports. */ + for (i = 0; i < new_nbr; i++) + { + kret = mach_port_deallocate (mach_task_self (), thread_list[i]); + MACH_CHECK_ERROR (kret); + } + + /* Deallocate the buffer. */ kret = vm_deallocate (gdb_task, (vm_address_t) thread_list, new_nbr * sizeof (int)); MACH_CHECK_ERROR (kret); + return; } } - thread_vec = VEC_alloc (darwin_thread_t, new_nbr); + /* Full handling: detect new threads, remove dead threads. */ + + new_thread_vec.reserve (new_nbr); for (new_ix = 0, old_ix = 0; new_ix < new_nbr || old_ix < old_nbr;) { - thread_t new_id = (new_ix < new_nbr) ? - thread_list[new_ix] : THREAD_NULL; - darwin_thread_t *old = (old_ix < old_nbr) ? - VEC_index (darwin_thread_t, darwin_inf->threads, old_ix) : NULL; - thread_t old_id = old ? old->gdb_port : THREAD_NULL; + thread_t new_id = (new_ix < new_nbr) ? thread_list[new_ix] : THREAD_NULL; + darwin_thread_t *old + = (old_ix < old_nbr) ? darwin_inf->threads[old_ix] : NULL; + thread_t old_id = old != NULL ? old->gdb_port : THREAD_NULL; inferior_debug (12, _(" new_ix:%d/%d, old_ix:%d/%d, new_id:0x%x old_id:0x%x\n"), @@ -328,12 +327,14 @@ darwin_check_new_threads (struct inferior *inf) if (old_id == new_id) { /* Thread still exist. */ - VEC_safe_push (darwin_thread_t, thread_vec, old); + new_thread_vec.push_back (old); new_ix++; old_ix++; - kret = mach_port_deallocate (gdb_task, old_id); + /* Deallocate the port. */ + kret = mach_port_deallocate (gdb_task, new_id); MACH_CHECK_ERROR (kret); + continue; } if (new_ix < new_nbr && new_id == MACH_PORT_DEAD) @@ -347,30 +348,23 @@ darwin_check_new_threads (struct inferior *inf) if (new_ix < new_nbr && (old_ix == old_nbr || new_id < old_id)) { /* A thread was created. */ - struct thread_info *tp; - struct private_thread_info *pti; + darwin_thread_info *pti = new darwin_thread_info; - pti = XCNEW (struct private_thread_info); pti->gdb_port = new_id; pti->msg_state = DARWIN_RUNNING; - /* Add a new thread unless this is the first one ever met. */ - if (!(old_nbr == 0 && new_ix == 0)) - tp = add_thread_with_info (ptid_build (inf->pid, 0, new_id), pti); - else - { - tp = find_thread_ptid (ptid_build (inf->pid, 0, 0)); - gdb_assert (tp); - tp->private = pti; - } - VEC_safe_push (darwin_thread_t, thread_vec, pti); + /* Add the new thread. */ + add_thread_with_info (ptid_t (inf->pid, 0, new_id), pti); + new_thread_vec.push_back (pti); new_ix++; continue; } if (old_ix < old_nbr && (new_ix == new_nbr || new_id > old_id)) { /* A thread was removed. */ - delete_thread (ptid_build (inf->pid, 0, old_id)); + struct thread_info *thr + = find_thread_ptid (ptid_t (inf->pid, 0, old_id)); + delete_thread (thr); kret = mach_port_deallocate (gdb_task, old_id); MACH_CHECK_ERROR (kret); old_ix++; @@ -379,10 +373,9 @@ darwin_check_new_threads (struct inferior *inf) gdb_assert_not_reached ("unexpected thread case"); } - if (darwin_inf->threads) - VEC_free (darwin_thread_t, darwin_inf->threads); - darwin_inf->threads = thread_vec; + darwin_inf->threads = std::move (new_thread_vec); + /* Deallocate the buffer. */ kret = vm_deallocate (gdb_task, (vm_address_t) thread_list, new_nbr * sizeof (int)); MACH_CHECK_ERROR (kret); @@ -391,13 +384,15 @@ darwin_check_new_threads (struct inferior *inf) static int find_inferior_task_it (struct inferior *inf, void *port_ptr) { - return inf->private->task == *(task_t*)port_ptr; + darwin_inferior *priv = get_darwin_inferior (inf); + + return priv != nullptr && priv->task == *(task_t *)port_ptr; } static int -find_inferior_notify_it (struct inferior *inf, void *port_ptr) +find_inferior_pid_it (struct inferior *inf, void *pid_ptr) { - return inf->private->notify_port == *(task_t*)port_ptr; + return inf->pid == *(int *)pid_ptr; } /* Return an inferior by task port. */ @@ -407,25 +402,26 @@ darwin_find_inferior_by_task (task_t port) return iterate_over_inferiors (&find_inferior_task_it, &port); } -/* Return an inferior by notification port. */ +/* Return an inferior by pid port. */ static struct inferior * -darwin_find_inferior_by_notify (mach_port_t port) +darwin_find_inferior_by_pid (int pid) { - return iterate_over_inferiors (&find_inferior_notify_it, &port); + return iterate_over_inferiors (&find_inferior_pid_it, &pid); } /* Return a thread by port. */ static darwin_thread_t * darwin_find_thread (struct inferior *inf, thread_t thread) { - darwin_thread_t *t; - int k; + darwin_inferior *priv = get_darwin_inferior (inf); + + if (priv != nullptr) + for (darwin_thread_t *t : priv->threads) + { + if (t->gdb_port == thread) + return t; + } - for (k = 0; - VEC_iterate (darwin_thread_t, inf->private->threads, k, t); - k++) - if (t->gdb_port == thread) - return t; return NULL; } @@ -434,14 +430,16 @@ darwin_find_thread (struct inferior *inf, thread_t thread) static void darwin_suspend_inferior (struct inferior *inf) { - if (!inf->private->suspended) + darwin_inferior *priv = get_darwin_inferior (inf); + + if (priv != nullptr && !priv->suspended) { kern_return_t kret; - kret = task_suspend (inf->private->task); + kret = task_suspend (priv->task); MACH_CHECK_ERROR (kret); - inf->private->suspended = 1; + priv->suspended = 1; } } @@ -450,14 +448,16 @@ darwin_suspend_inferior (struct inferior *inf) static void darwin_resume_inferior (struct inferior *inf) { - if (inf->private->suspended) + darwin_inferior *priv = get_darwin_inferior (inf); + + if (priv != nullptr && priv->suspended) { kern_return_t kret; - kret = task_resume (inf->private->task); + kret = task_resume (priv->task); MACH_CHECK_ERROR (kret); - inf->private->suspended = 0; + priv->suspended = 0; } } @@ -492,7 +492,7 @@ darwin_dump_message (mach_msg_header_t *hdr, int disp_body) if (disp_body) { const unsigned char *data; - const unsigned long *ldata; + const unsigned int *ldata; int size; int i; @@ -538,13 +538,78 @@ darwin_dump_message (mach_msg_header_t *hdr, int disp_body) } printf_unfiltered (_(" data:")); - ldata = (const unsigned long *)data; - for (i = 0; i < size / sizeof (unsigned long); i++) - printf_unfiltered (" %08lx", ldata[i]); + ldata = (const unsigned int *)data; + for (i = 0; i < size / sizeof (unsigned int); i++) + printf_unfiltered (" %08x", ldata[i]); printf_unfiltered (_("\n")); } } +/* Adjust inferior data when a new task was created. */ + +static struct inferior * +darwin_find_new_inferior (task_t task_port, thread_t thread_port) +{ + int task_pid; + struct inferior *inf; + kern_return_t kret; + mach_port_t prev; + + /* Find the corresponding pid. */ + kret = pid_for_task (task_port, &task_pid); + if (kret != KERN_SUCCESS) + { + MACH_CHECK_ERROR (kret); + return NULL; + } + + /* Find the inferior for this pid. */ + inf = darwin_find_inferior_by_pid (task_pid); + if (inf == NULL) + return NULL; + + darwin_inferior *priv = get_darwin_inferior (inf); + + /* Deallocate saved exception ports. */ + darwin_deallocate_exception_ports (priv); + + /* No need to remove dead_name notification, but still... */ + kret = mach_port_request_notification (gdb_task, priv->task, + MACH_NOTIFY_DEAD_NAME, 0, + MACH_PORT_NULL, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &prev); + if (kret != KERN_INVALID_ARGUMENT) + MACH_CHECK_ERROR (kret); + + /* Replace old task port. */ + kret = mach_port_deallocate (gdb_task, priv->task); + MACH_CHECK_ERROR (kret); + priv->task = task_port; + + darwin_setup_request_notification (inf); + darwin_setup_exceptions (inf); + + return inf; +} + +/* Check data representation. */ + +static int +darwin_check_message_ndr (NDR_record_t *ndr) +{ + if (ndr->mig_vers != NDR_PROTOCOL_2_0 + || ndr->if_vers != NDR_PROTOCOL_2_0 + || ndr->mig_encoding != NDR_record.mig_encoding + || ndr->int_rep != NDR_record.int_rep + || ndr->char_rep != NDR_record.char_rep + || ndr->float_rep != NDR_record.float_rep) + return -1; + return 0; +} + +/* Decode an exception message. */ + static int darwin_decode_exception_message (mach_msg_header_t *hdr, struct inferior **pinf, @@ -561,8 +626,8 @@ darwin_decode_exception_message (mach_msg_header_t *hdr, kern_return_t kret; int i; - /* Check message identifier. 2401 is exc. */ - if (hdr->msgh_id != 2401) + /* Check message destination. */ + if (hdr->msgh_local_port != darwin_ex_port) return -1; /* Check message header. */ @@ -581,33 +646,76 @@ darwin_decode_exception_message (mach_msg_header_t *hdr, /* Check data representation. */ ndr = (NDR_record_t *)(desc + 2); - if (ndr->mig_vers != NDR_PROTOCOL_2_0 - || ndr->if_vers != NDR_PROTOCOL_2_0 - || ndr->mig_encoding != NDR_record.mig_encoding - || ndr->int_rep != NDR_record.int_rep - || ndr->char_rep != NDR_record.char_rep - || ndr->float_rep != NDR_record.float_rep) + if (darwin_check_message_ndr (ndr) != 0) return -1; /* Ok, the hard work. */ data = (integer_t *)(ndr + 1); - /* Find process by port. */ task_port = desc[1].name; thread_port = desc[0].name; + + /* Find process by port. */ inf = darwin_find_inferior_by_task (task_port); - if (inf == NULL) - return -1; *pinf = inf; + if (inf == NULL && data[0] == EXC_SOFTWARE && data[1] == 2 + && data[2] == EXC_SOFT_SIGNAL && data[3] == SIGTRAP) + { + /* Not a known inferior, but a sigtrap. This happens on darwin 16.1.0, + as a new Mach task is created when a process exec. */ + inf = darwin_find_new_inferior (task_port, thread_port); + *pinf = inf; + + if (inf == NULL) + { + /* Deallocate task_port, unless it was saved. */ + kret = mach_port_deallocate (mach_task_self (), task_port); + MACH_CHECK_ERROR (kret); + } + } + else + { + /* We got new rights to the task, get rid of it. Do not get rid of + thread right, as we will need it to find the thread. */ + kret = mach_port_deallocate (mach_task_self (), task_port); + MACH_CHECK_ERROR (kret); + } + + if (inf == NULL) + { + /* Not a known inferior. This could happen if the child fork, as + the created process will inherit its exception port. + FIXME: should the exception port be restored ? */ + kern_return_t kret; + mig_reply_error_t reply; + + inferior_debug + (4, _("darwin_decode_exception_message: unknown task 0x%x\n"), + task_port); + + /* Free thread port (we don't know it). */ + kret = mach_port_deallocate (mach_task_self (), thread_port); + MACH_CHECK_ERROR (kret); + + darwin_encode_reply (&reply, hdr, KERN_SUCCESS); + + kret = mach_msg (&reply.Head, MACH_SEND_MSG | MACH_SEND_INTERRUPT, + reply.Head.msgh_size, 0, + MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + MACH_CHECK_ERROR (kret); + + return 0; + } + /* Find thread by port. */ /* Check for new threads. Do it early so that the port in the exception message can be deallocated. */ darwin_check_new_threads (inf); - /* We got new rights to the task and the thread. Get rid of them. */ - kret = mach_port_deallocate (mach_task_self (), task_port); - MACH_CHECK_ERROR (kret); + /* Free the thread port (as gdb knows the thread, it has already has a right + for it, so this just decrement a reference counter). */ kret = mach_port_deallocate (mach_task_self (), thread_port); MACH_CHECK_ERROR (kret); @@ -616,8 +724,8 @@ darwin_decode_exception_message (mach_msg_header_t *hdr, return -1; *pthread = thread; - /* The thread should be running. However we have observed cases where a thread - got a SIGTTIN message after being stopped. */ + /* The thread should be running. However we have observed cases where a + thread got a SIGTTIN message after being stopped. */ gdb_assert (thread->msg_state != DARWIN_MESSAGE); /* Finish decoding. */ @@ -639,14 +747,54 @@ darwin_decode_exception_message (mach_msg_header_t *hdr, return 0; } +/* Decode dead_name notify message. */ + +static int +darwin_decode_notify_message (mach_msg_header_t *hdr, struct inferior **pinf) +{ + NDR_record_t *ndr = (NDR_record_t *)(hdr + 1); + integer_t *data = (integer_t *)(ndr + 1); + struct inferior *inf; + task_t task_port; + + /* Check message header. */ + if (hdr->msgh_bits & MACH_MSGH_BITS_COMPLEX) + return -1; + + /* Check descriptors. */ + if (hdr->msgh_size < (sizeof (*hdr) + sizeof (*ndr) + sizeof (integer_t))) + return -2; + + /* Check data representation. */ + if (darwin_check_message_ndr (ndr) != 0) + return -3; + + task_port = data[0]; + + /* Find process by port. */ + inf = darwin_find_inferior_by_task (task_port); + *pinf = inf; + + /* Check message destination. */ + if (inf != NULL) + { + darwin_inferior *priv = get_darwin_inferior (inf); + if (hdr->msgh_local_port != priv->notify_port) + return -4; + } + + return 0; +} + static void darwin_encode_reply (mig_reply_error_t *reply, mach_msg_header_t *hdr, integer_t code) { mach_msg_header_t *rh = &reply->Head; - rh->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(hdr->msgh_bits), 0); + + rh->msgh_bits = MACH_MSGH_BITS (MACH_MSGH_BITS_REMOTE (hdr->msgh_bits), 0); rh->msgh_remote_port = hdr->msgh_remote_port; - rh->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t); + rh->msgh_size = (mach_msg_size_t) sizeof (mig_reply_error_t); rh->msgh_local_port = MACH_PORT_NULL; rh->msgh_id = hdr->msgh_id + 100; @@ -659,6 +807,7 @@ darwin_send_reply (struct inferior *inf, darwin_thread_t *thread) { kern_return_t kret; mig_reply_error_t reply; + darwin_inferior *priv = get_darwin_inferior (inf); darwin_encode_reply (&reply, &thread->event.header, KERN_SUCCESS); @@ -668,16 +817,27 @@ darwin_send_reply (struct inferior *inf, darwin_thread_t *thread) MACH_PORT_NULL); MACH_CHECK_ERROR (kret); - inf->private->pending_messages--; + priv->pending_messages--; +} + +/* Wrapper around the __pthread_kill syscall. We use this instead of the + pthread_kill function to be able to send a signal to any kind of thread, + including GCD threads. */ + +static int +darwin_pthread_kill (darwin_thread_t *thread, int nsignal) +{ + DIAGNOSTIC_PUSH; + DIAGNOSTIC_IGNORE_DEPRECATED_DECLARATIONS; + int res = syscall (SYS___pthread_kill, thread->gdb_port, nsignal); + DIAGNOSTIC_POP; + return res; } static void darwin_resume_thread (struct inferior *inf, darwin_thread_t *thread, int step, int nsignal) { - kern_return_t kret; - int res; - inferior_debug (3, _("darwin_resume_thread: state=%d, thread=0x%x, step=%d nsignal=%d\n"), thread->msg_state, thread->gdb_port, step, nsignal); @@ -689,8 +849,8 @@ darwin_resume_thread (struct inferior *inf, darwin_thread_t *thread, && thread->event.ex_data[0] == EXC_SOFT_SIGNAL) { /* Either deliver a new signal or cancel the signal received. */ - res = PTRACE (PT_THUPDATE, inf->pid, - (void *)(uintptr_t)thread->gdb_port, nsignal); + int res = PTRACE (PT_THUPDATE, inf->pid, + (caddr_t) (uintptr_t) thread->gdb_port, nsignal); if (res < 0) inferior_debug (1, _("ptrace THUP: res=%d\n"), res); } @@ -698,20 +858,17 @@ darwin_resume_thread (struct inferior *inf, darwin_thread_t *thread, { /* Note: ptrace is allowed only if the process is stopped. Directly send the signal to the thread. */ - res = syscall (SYS___pthread_kill, thread->gdb_port, nsignal); + int res = darwin_pthread_kill (thread, nsignal); inferior_debug (4, _("darwin_resume_thread: kill 0x%x %d: %d\n"), thread->gdb_port, nsignal, res); thread->signaled = 1; } /* Set or reset single step. */ - if (step != thread->single_step) - { - inferior_debug (4, _("darwin_set_sstep (thread=0x%x, enable=%d)\n"), - thread->gdb_port, step); - darwin_set_sstep (thread->gdb_port, step); - thread->single_step = step; - } + inferior_debug (4, _("darwin_set_sstep (thread=0x%x, enable=%d)\n"), + thread->gdb_port, step); + darwin_set_sstep (thread->gdb_port, step); + thread->single_step = step; darwin_send_reply (inf, thread); thread->msg_state = DARWIN_RUNNING; @@ -721,7 +878,7 @@ darwin_resume_thread (struct inferior *inf, darwin_thread_t *thread, break; case DARWIN_STOPPED: - kret = thread_resume (thread->gdb_port); + kern_return_t kret = thread_resume (thread->gdb_port); MACH_CHECK_ERROR (kret); thread->msg_state = DARWIN_RUNNING; @@ -734,13 +891,11 @@ darwin_resume_thread (struct inferior *inf, darwin_thread_t *thread, static void darwin_resume_inferior_threads (struct inferior *inf, int step, int nsignal) { - darwin_thread_t *thread; - int k; + darwin_inferior *priv = get_darwin_inferior (inf); - for (k = 0; - VEC_iterate (darwin_thread_t, inf->private->threads, k, thread); - k++) - darwin_resume_thread (inf, thread, step, nsignal); + if (priv != nullptr) + for (darwin_thread_t *thread : priv->threads) + darwin_resume_thread (inf, thread, step, nsignal); } struct resume_inferior_threads_param @@ -765,40 +920,36 @@ darwin_resume_inferior_threads_it (struct inferior *inf, void *param) static void darwin_suspend_inferior_threads (struct inferior *inf) { - darwin_thread_t *thread; - kern_return_t kret; - int k; + darwin_inferior *priv = get_darwin_inferior (inf); - for (k = 0; - VEC_iterate (darwin_thread_t, inf->private->threads, k, thread); - k++) - switch (thread->msg_state) - { - case DARWIN_STOPPED: - case DARWIN_MESSAGE: - break; - case DARWIN_RUNNING: - kret = thread_suspend (thread->gdb_port); - MACH_CHECK_ERROR (kret); - thread->msg_state = DARWIN_STOPPED; - break; - } + for (darwin_thread_t *thread : priv->threads) + { + switch (thread->msg_state) + { + case DARWIN_STOPPED: + case DARWIN_MESSAGE: + break; + case DARWIN_RUNNING: + { + kern_return_t kret = thread_suspend (thread->gdb_port); + MACH_CHECK_ERROR (kret); + thread->msg_state = DARWIN_STOPPED; + break; + } + } + } } -static void -darwin_resume (ptid_t ptid, int step, enum gdb_signal signal) +void +darwin_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signal) { struct target_waitstatus status; - int pid; - kern_return_t kret; - int res; int nsignal; - struct inferior *inf; inferior_debug - (2, _("darwin_resume: pid=%d, tid=0x%x, step=%d, signal=%d\n"), - ptid_get_pid (ptid), ptid_get_tid (ptid), step, signal); + (2, _("darwin_resume: pid=%d, tid=0x%lx, step=%d, signal=%d\n"), + ptid.pid (), ptid.tid (), step, signal); if (signal == GDB_SIGNAL_0) nsignal = 0; @@ -810,7 +961,7 @@ darwin_resume (ptid_t ptid, int step, enum gdb_signal signal) ptid = inferior_ptid; /* minus_one_ptid is RESUME_ALL. */ - if (ptid_equal (ptid, minus_one_ptid)) + if (ptid == minus_one_ptid) { struct resume_inferior_threads_param param; @@ -824,8 +975,8 @@ darwin_resume (ptid_t ptid, int step, enum gdb_signal signal) } else { - struct inferior *inf = find_inferior_pid (ptid_get_pid (ptid)); - long tid = ptid_get_tid (ptid); + struct inferior *inf = find_inferior_ptid (ptid); + long tid = ptid.tid (); /* Stop the inferior (should be useless). */ darwin_suspend_inferior (inf); @@ -850,13 +1001,6 @@ darwin_resume (ptid_t ptid, int step, enum gdb_signal signal) } } -static void -darwin_resume_to (struct target_ops *ops, ptid_t ptid, int step, - enum gdb_signal signal) -{ - return darwin_resume (ptid, step, signal); -} - static ptid_t darwin_decode_message (mach_msg_header_t *hdr, darwin_thread_t **pthread, @@ -866,8 +1010,8 @@ darwin_decode_message (mach_msg_header_t *hdr, darwin_thread_t *thread; struct inferior *inf; - /* Exception message. */ - if (hdr->msgh_local_port == darwin_ex_port) + /* Exception message. 2401 == 0x961 is exc. */ + if (hdr->msgh_id == 2401) { int res; @@ -880,12 +1024,20 @@ darwin_decode_message (mach_msg_header_t *hdr, printf_unfiltered (_("darwin_wait: ill-formatted message (id=0x%x)\n"), hdr->msgh_id); /* FIXME: send a failure reply? */ - status->kind = TARGET_WAITKIND_SPURIOUS; + status->kind = TARGET_WAITKIND_IGNORE; + return minus_one_ptid; + } + if (inf == NULL) + { + status->kind = TARGET_WAITKIND_IGNORE; return minus_one_ptid; } *pinf = inf; *pthread = thread; - inf->private->pending_messages++; + + darwin_inferior *priv = get_darwin_inferior (inf); + + priv->pending_messages++; status->kind = TARGET_WAITKIND_STOPPED; thread->msg_state = DARWIN_MESSAGE; @@ -941,58 +1093,82 @@ darwin_decode_message (mach_msg_header_t *hdr, break; } - return ptid_build (inf->pid, 0, thread->gdb_port); + return ptid_t (inf->pid, 0, thread->gdb_port); } + else if (hdr->msgh_id == 0x48) + { + /* MACH_NOTIFY_DEAD_NAME: notification for exit. */ + int res; - *pinf = NULL; - *pthread = NULL; + res = darwin_decode_notify_message (hdr, &inf); - inf = darwin_find_inferior_by_notify (hdr->msgh_local_port); - if (inf != NULL) - { - if (!inf->private->no_ptrace) + if (res < 0) { - pid_t res; - int wstatus; + /* Should not happen... */ + printf_unfiltered + (_("darwin_wait: ill-formatted message (id=0x%x, res=%d)\n"), + hdr->msgh_id, res); + } - res = wait4 (inf->pid, &wstatus, 0, NULL); - if (res < 0 || res != inf->pid) - { - printf_unfiltered (_("wait4: res=%d: %s\n"), - res, safe_strerror (errno)); - status->kind = TARGET_WAITKIND_SPURIOUS; - return minus_one_ptid; - } - if (WIFEXITED (wstatus)) + *pinf = NULL; + *pthread = NULL; + + if (res < 0 || inf == NULL) + { + status->kind = TARGET_WAITKIND_IGNORE; + return minus_one_ptid; + } + + if (inf != NULL) + { + darwin_inferior *priv = get_darwin_inferior (inf); + + if (!priv->no_ptrace) { - status->kind = TARGET_WAITKIND_EXITED; - status->value.integer = WEXITSTATUS (wstatus); + pid_t res; + int wstatus; + + res = wait4 (inf->pid, &wstatus, 0, NULL); + if (res < 0 || res != inf->pid) + { + printf_unfiltered (_("wait4: res=%d: %s\n"), + res, safe_strerror (errno)); + status->kind = TARGET_WAITKIND_IGNORE; + return minus_one_ptid; + } + if (WIFEXITED (wstatus)) + { + status->kind = TARGET_WAITKIND_EXITED; + status->value.integer = WEXITSTATUS (wstatus); + } + else + { + status->kind = TARGET_WAITKIND_SIGNALLED; + status->value.sig = gdb_signal_from_host (WTERMSIG (wstatus)); + } + + inferior_debug (4, _("darwin_wait: pid=%d exit, status=0x%x\n"), + res, wstatus); + + /* Looks necessary on Leopard and harmless... */ + wait4 (inf->pid, &wstatus, 0, NULL); + + inferior_ptid = ptid_t (inf->pid, 0, 0); + return inferior_ptid; } else { - status->kind = TARGET_WAITKIND_SIGNALLED; - status->value.sig = WTERMSIG (wstatus); + inferior_debug (4, _("darwin_wait: pid=%d\n"), inf->pid); + status->kind = TARGET_WAITKIND_EXITED; + status->value.integer = 0; /* Don't know. */ + return ptid_t (inf->pid, 0, 0); } - - inferior_debug (4, _("darwin_wait: pid=%d exit, status=0x%x\n"), - res, wstatus); - - /* Looks necessary on Leopard and harmless... */ - wait4 (inf->pid, &wstatus, 0, NULL); - - return ptid_build (inf->pid, 0, 0); - } - else - { - inferior_debug (4, _("darwin_wait: pid=%d\n"), inf->pid); - status->kind = TARGET_WAITKIND_EXITED; - status->value.integer = 0; /* Don't know. */ - return ptid_build (inf->pid, 0, 0); } } - printf_unfiltered (_("Bad local-port: 0x%x\n"), hdr->msgh_local_port); - status->kind = TARGET_WAITKIND_SPURIOUS; + /* Unknown message. */ + warning (_("darwin: got unknown message, id: 0x%x"), hdr->msgh_id); + status->kind = TARGET_WAITKIND_IGNORE; return minus_one_ptid; } @@ -1008,14 +1184,14 @@ cancel_breakpoint (ptid_t ptid) tripped on it. */ struct regcache *regcache = get_thread_regcache (ptid); - struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch *gdbarch = regcache->arch (); CORE_ADDR pc; pc = regcache_read_pc (regcache) - gdbarch_decr_pc_after_break (gdbarch); - if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc)) + if (breakpoint_inserted_here_p (regcache->aspace (), pc)) { - inferior_debug (4, "cancel_breakpoint for thread 0x%x\n", - ptid_get_tid (ptid)); + inferior_debug (4, "cancel_breakpoint for thread 0x%lx\n", + (unsigned long) ptid.tid ()); /* Back up the PC if necessary. */ if (gdbarch_decr_pc_after_break (gdbarch)) @@ -1042,7 +1218,7 @@ darwin_wait (ptid_t ptid, struct target_waitstatus *status) inferior_debug (2, _("darwin_wait: waiting for a message pid=%d thread=%lx\n"), - ptid_get_pid (ptid), ptid_get_tid (ptid)); + ptid.pid (), ptid.tid ()); /* Handle fake stop events at first. */ if (darwin_inf_fake_stop != NULL) @@ -1050,11 +1226,13 @@ darwin_wait (ptid_t ptid, struct target_waitstatus *status) inf = darwin_inf_fake_stop; darwin_inf_fake_stop = NULL; + darwin_inferior *priv = get_darwin_inferior (inf); + status->kind = TARGET_WAITKIND_STOPPED; status->value.sig = GDB_SIGNAL_TRAP; - thread = VEC_index (darwin_thread_t, inf->private->threads, 0); + thread = priv->threads[0]; thread->msg_state = DARWIN_STOPPED; - return ptid_build (inf->pid, 0, thread->gdb_port); + return ptid_t (inf->pid, 0, thread->gdb_port); } do @@ -1085,7 +1263,10 @@ darwin_wait (ptid_t ptid, struct target_waitstatus *status) darwin_dump_message (hdr, darwin_debug_flag > 11); res = darwin_decode_message (hdr, &thread, &inf, status); + if (res == minus_one_ptid) + continue; + /* Early return in case an inferior has exited. */ if (inf == NULL) return res; } @@ -1113,13 +1294,17 @@ darwin_wait (ptid_t ptid, struct target_waitstatus *status) break; } + /* Debug: display message. */ + if (darwin_debug_flag > 10) + darwin_dump_message (hdr, darwin_debug_flag > 11); + ptid2 = darwin_decode_message (hdr, &thread, &inf, &status2); if (inf != NULL && thread != NULL && thread->event.ex_type == EXC_BREAKPOINT) { if (thread->single_step - || cancel_breakpoint (ptid_build (inf->pid, 0, thread->gdb_port))) + || cancel_breakpoint (ptid_t (inf->pid, 0, thread->gdb_port))) { gdb_assert (thread->msg_state == DARWIN_MESSAGE); darwin_send_reply (inf, thread); @@ -1136,61 +1321,65 @@ darwin_wait (ptid_t ptid, struct target_waitstatus *status) return res; } -static ptid_t -darwin_wait_to (struct target_ops *ops, - ptid_t ptid, struct target_waitstatus *status, int options) +ptid_t +darwin_nat_target::wait (ptid_t ptid, struct target_waitstatus *status, + int options) { return darwin_wait (ptid, status); } -static void -darwin_stop (ptid_t t) +void +darwin_nat_target::interrupt () { struct inferior *inf = current_inferior (); + darwin_inferior *priv = get_darwin_inferior (inf); /* FIXME: handle in no_ptrace mode. */ - gdb_assert (!inf->private->no_ptrace); - kill (inf->pid, SIGINT); + gdb_assert (!priv->no_ptrace); + ::kill (inf->pid, SIGINT); } +/* Deallocate threads port and vector. */ + static void -darwin_mourn_inferior (struct target_ops *ops) +darwin_deallocate_threads (struct inferior *inf) +{ + darwin_inferior *priv = get_darwin_inferior (inf); + + for (darwin_thread_t *t : priv->threads) + { + kern_return_t kret = mach_port_deallocate (gdb_task, t->gdb_port); + MACH_CHECK_ERROR (kret); + } + + priv->threads.clear (); +} + +void +darwin_nat_target::mourn_inferior () { struct inferior *inf = current_inferior (); + darwin_inferior *priv = get_darwin_inferior (inf); kern_return_t kret; mach_port_t prev; - int i; - - unpush_target (darwin_ops); /* Deallocate threads. */ - if (inf->private->threads) - { - int k; - darwin_thread_t *t; - for (k = 0; - VEC_iterate (darwin_thread_t, inf->private->threads, k, t); - k++) - { - kret = mach_port_deallocate (gdb_task, t->gdb_port); - MACH_CHECK_ERROR (kret); - } - VEC_free (darwin_thread_t, inf->private->threads); - inf->private->threads = NULL; - } + darwin_deallocate_threads (inf); + /* Remove notify_port from darwin_port_set. */ kret = mach_port_move_member (gdb_task, - inf->private->notify_port, MACH_PORT_NULL); + priv->notify_port, MACH_PORT_NULL); MACH_CHECK_ERROR (kret); - kret = mach_port_request_notification (gdb_task, inf->private->task, + /* Remove task port dead_name notification. */ + kret = mach_port_request_notification (gdb_task, priv->task, MACH_NOTIFY_DEAD_NAME, 0, MACH_PORT_NULL, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev); /* This can fail if the task is dead. */ inferior_debug (4, "task=0x%x, prev=0x%x, notify_port=0x%x\n", - inf->private->task, prev, inf->private->notify_port); + priv->task, prev, priv->notify_port); if (kret == KERN_SUCCESS) { @@ -1198,37 +1387,28 @@ darwin_mourn_inferior (struct target_ops *ops) MACH_CHECK_ERROR (kret); } - kret = mach_port_destroy (gdb_task, inf->private->notify_port); + /* Destroy notify_port. */ + kret = mach_port_destroy (gdb_task, priv->notify_port); MACH_CHECK_ERROR (kret); - /* Deallocate saved exception ports. */ - for (i = 0; i < inf->private->exception_info.count; i++) - { - kret = mach_port_deallocate - (gdb_task, inf->private->exception_info.ports[i]); - MACH_CHECK_ERROR (kret); - } - inf->private->exception_info.count = 0; + darwin_deallocate_exception_ports (priv); - kret = mach_port_deallocate (gdb_task, inf->private->task); + /* Deallocate task port. */ + kret = mach_port_deallocate (gdb_task, priv->task); MACH_CHECK_ERROR (kret); - xfree (inf->private); - inf->private = NULL; + inf->priv = NULL; - generic_mourn_inferior (); + inf_child_target::mourn_inferior (); } static void darwin_reply_to_all_pending_messages (struct inferior *inf) { - int k; - darwin_thread_t *t; + darwin_inferior *priv = get_darwin_inferior (inf); - for (k = 0; - VEC_iterate (darwin_thread_t, inf->private->threads, k, t); - k++) + for (darwin_thread_t *t : priv->threads) { if (t->msg_state == DARWIN_MESSAGE) darwin_resume_thread (inf, t, 0, 0); @@ -1240,9 +1420,8 @@ darwin_stop_inferior (struct inferior *inf) { struct target_waitstatus wstatus; ptid_t ptid; - kern_return_t kret; - int status; int res; + darwin_inferior *priv = get_darwin_inferior (inf); gdb_assert (inf != NULL); @@ -1250,7 +1429,7 @@ darwin_stop_inferior (struct inferior *inf) darwin_reply_to_all_pending_messages (inf); - if (inf->private->no_ptrace) + if (priv->no_ptrace) return; res = kill (inf->pid, SIGSTOP); @@ -1300,179 +1479,259 @@ darwin_restore_exception_ports (darwin_inferior *inf) return KERN_SUCCESS; } +/* Deallocate saved exception ports. */ + +static void +darwin_deallocate_exception_ports (darwin_inferior *inf) +{ + int i; + kern_return_t kret; + + for (i = 0; i < inf->exception_info.count; i++) + { + kret = mach_port_deallocate (gdb_task, inf->exception_info.ports[i]); + MACH_CHECK_ERROR (kret); + } + inf->exception_info.count = 0; +} + static void -darwin_kill_inferior (struct target_ops *ops) +darwin_setup_exceptions (struct inferior *inf) +{ + darwin_inferior *priv = get_darwin_inferior (inf); + kern_return_t kret; + exception_mask_t mask; + + kret = darwin_save_exception_ports (priv); + if (kret != KERN_SUCCESS) + error (_("Unable to save exception ports, task_get_exception_ports" + "returned: %d"), + kret); + + /* Set exception port. */ + if (enable_mach_exceptions) + mask = EXC_MASK_ALL; + else + mask = EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT; + kret = task_set_exception_ports (priv->task, mask, darwin_ex_port, + EXCEPTION_DEFAULT, THREAD_STATE_NONE); + if (kret != KERN_SUCCESS) + error (_("Unable to set exception ports, task_set_exception_ports" + "returned: %d"), + kret); +} + +void +darwin_nat_target::kill () { struct inferior *inf = current_inferior (); + darwin_inferior *priv = get_darwin_inferior (inf); struct target_waitstatus wstatus; ptid_t ptid; kern_return_t kret; - int status; int res; - if (ptid_equal (inferior_ptid, null_ptid)) + if (inferior_ptid == null_ptid) return; gdb_assert (inf != NULL); - kret = darwin_restore_exception_ports (inf->private); + kret = darwin_restore_exception_ports (priv); MACH_CHECK_ERROR (kret); darwin_reply_to_all_pending_messages (inf); - res = kill (inf->pid, 9); + res = ::kill (inf->pid, 9); if (res == 0) { + /* On MacOS version Sierra, the darwin_restore_exception_ports call + does not work as expected. + When the kill function is called, the SIGKILL signal is received + by gdb whereas it should have been received by the kernel since + the exception ports have been restored. + This behavior is not the expected one thus gdb does not reply to + the received SIGKILL message. This situation leads to a "busy" + resource from the kernel point of view and the inferior is never + released, causing it to remain as a zombie process, even after + GDB exits. + To work around this, we mark all the threads of the inferior as + signaled thus darwin_decode_message function knows that the kill + signal was sent by gdb and will take the appropriate action + (cancel signal and reply to the signal message). */ + darwin_inferior *priv = get_darwin_inferior (inf); + for (darwin_thread_t *thread : priv->threads) + thread->signaled = 1; + darwin_resume_inferior (inf); - + ptid = darwin_wait (inferior_ptid, &wstatus); } else if (errno != ESRCH) warning (_("Failed to kill inferior: kill (%d, 9) returned [%s]"), inf->pid, safe_strerror (errno)); - target_mourn_inferior (); + target_mourn_inferior (inferior_ptid); } static void -darwin_attach_pid (struct inferior *inf) +darwin_setup_request_notification (struct inferior *inf) { + darwin_inferior *priv = get_darwin_inferior (inf); kern_return_t kret; - mach_port_t prev_port; - int traps_expected; mach_port_t prev_not; - exception_mask_t mask; - - inf->private = XCNEW (darwin_inferior); - kret = task_for_pid (gdb_task, inf->pid, &inf->private->task); + kret = mach_port_request_notification (gdb_task, priv->task, + MACH_NOTIFY_DEAD_NAME, 0, + priv->notify_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &prev_not); if (kret != KERN_SUCCESS) + error (_("Termination notification request failed, " + "mach_port_request_notification\n" + "returned: %d"), + kret); + if (prev_not != MACH_PORT_NULL) { - int status; + /* This is unexpected, as there should not be any previously + registered notification request. But this is not a fatal + issue, so just emit a warning. */ + warning (_("\ +A task termination request was registered before the debugger registered\n\ +its own. This is unexpected, but should otherwise not have any actual\n\ +impact on the debugging session.")); + } +} + +static void +darwin_attach_pid (struct inferior *inf) +{ + kern_return_t kret; - if (!inf->attach_flag) + darwin_inferior *priv = new darwin_inferior; + inf->priv.reset (priv); + + TRY + { + kret = task_for_pid (gdb_task, inf->pid, &priv->task); + if (kret != KERN_SUCCESS) { - kill (inf->pid, 9); - waitpid (inf->pid, &status, 0); - } + int status; + + if (!inf->attach_flag) + { + kill (inf->pid, 9); + waitpid (inf->pid, &status, 0); + } - error (_("Unable to find Mach task port for process-id %d: %s (0x%lx).\n" + error + (_("Unable to find Mach task port for process-id %d: %s (0x%lx).\n" " (please check gdb is codesigned - see taskgated(8))"), - inf->pid, mach_error_string (kret), (unsigned long) kret); - } + inf->pid, mach_error_string (kret), (unsigned long) kret); + } - inferior_debug (2, _("inferior task: 0x%x, pid: %d\n"), - inf->private->task, inf->pid); + inferior_debug (2, _("inferior task: 0x%x, pid: %d\n"), + priv->task, inf->pid); - if (darwin_ex_port == MACH_PORT_NULL) - { - /* Create a port to get exceptions. */ - kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_RECEIVE, - &darwin_ex_port); - if (kret != KERN_SUCCESS) - error (_("Unable to create exception port, mach_port_allocate " - "returned: %d"), - kret); + if (darwin_ex_port == MACH_PORT_NULL) + { + /* Create a port to get exceptions. */ + kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_RECEIVE, + &darwin_ex_port); + if (kret != KERN_SUCCESS) + error (_("Unable to create exception port, mach_port_allocate " + "returned: %d"), + kret); - kret = mach_port_insert_right (gdb_task, darwin_ex_port, darwin_ex_port, - MACH_MSG_TYPE_MAKE_SEND); - if (kret != KERN_SUCCESS) - error (_("Unable to create exception port, mach_port_insert_right " - "returned: %d"), - kret); + kret = mach_port_insert_right (gdb_task, darwin_ex_port, + darwin_ex_port, + MACH_MSG_TYPE_MAKE_SEND); + if (kret != KERN_SUCCESS) + error (_("Unable to create exception port, mach_port_insert_right " + "returned: %d"), + kret); + + /* Create a port set and put ex_port in it. */ + kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_PORT_SET, + &darwin_port_set); + if (kret != KERN_SUCCESS) + error (_("Unable to create port set, mach_port_allocate " + "returned: %d"), + kret); - /* Create a port set and put ex_port in it. */ - kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_PORT_SET, - &darwin_port_set); + kret = mach_port_move_member (gdb_task, darwin_ex_port, + darwin_port_set); + if (kret != KERN_SUCCESS) + error (_("Unable to move exception port into new port set, " + "mach_port_move_member\n" + "returned: %d"), + kret); + } + + /* Create a port to be notified when the child task terminates. */ + kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_RECEIVE, + &priv->notify_port); if (kret != KERN_SUCCESS) - error (_("Unable to create port set, mach_port_allocate " + error (_("Unable to create notification port, mach_port_allocate " "returned: %d"), kret); - kret = mach_port_move_member (gdb_task, darwin_ex_port, darwin_port_set); + kret = mach_port_move_member (gdb_task, + priv->notify_port, darwin_port_set); if (kret != KERN_SUCCESS) - error (_("Unable to move exception port into new port set, " + error (_("Unable to move notification port into new port set, " "mach_port_move_member\n" "returned: %d"), kret); - } - - /* Create a port to be notified when the child task terminates. */ - kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_RECEIVE, - &inf->private->notify_port); - if (kret != KERN_SUCCESS) - error (_("Unable to create notification port, mach_port_allocate " - "returned: %d"), - kret); - kret = mach_port_move_member (gdb_task, - inf->private->notify_port, darwin_port_set); - if (kret != KERN_SUCCESS) - error (_("Unable to move notification port into new port set, " - "mach_port_move_member\n" - "returned: %d"), - kret); + darwin_setup_request_notification (inf); - kret = mach_port_request_notification (gdb_task, inf->private->task, - MACH_NOTIFY_DEAD_NAME, 0, - inf->private->notify_port, - MACH_MSG_TYPE_MAKE_SEND_ONCE, - &prev_not); - if (kret != KERN_SUCCESS) - error (_("Termination notification request failed, " - "mach_port_request_notification\n" - "returned: %d"), - kret); - if (prev_not != MACH_PORT_NULL) + darwin_setup_exceptions (inf); + } + CATCH (ex, RETURN_MASK_ALL) { - /* This is unexpected, as there should not be any previously - registered notification request. But this is not a fatal - issue, so just emit a warning. */ - warning (_("\ -A task termination request was registered before the debugger registered\n\ -its own. This is unexpected, but should otherwise not have any actual\n\ -impact on the debugging session.")); + exit_inferior (inf); + inferior_ptid = null_ptid; + + throw_exception (ex); } + END_CATCH - kret = darwin_save_exception_ports (inf->private); - if (kret != KERN_SUCCESS) - error (_("Unable to save exception ports, task_get_exception_ports" - "returned: %d"), - kret); + target_ops *darwin_ops = get_native_target (); + if (!target_is_pushed (darwin_ops)) + push_target (darwin_ops); +} - /* Set exception port. */ - if (enable_mach_exceptions) - mask = EXC_MASK_ALL; - else - mask = EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT; - kret = task_set_exception_ports (inf->private->task, mask, darwin_ex_port, - EXCEPTION_DEFAULT, THREAD_STATE_NONE); - if (kret != KERN_SUCCESS) - error (_("Unable to set exception ports, task_set_exception_ports" - "returned: %d"), - kret); +/* Get the thread_info object corresponding to this darwin_thread_info. */ + +static struct thread_info * +thread_info_from_private_thread_info (darwin_thread_info *pti) +{ + for (struct thread_info *it : all_threads ()) + { + darwin_thread_info *iter_pti = get_darwin_thread_info (it); + + if (iter_pti->gdb_port == pti->gdb_port) + return it; + } - push_target (darwin_ops); + gdb_assert_not_reached ("did not find gdb thread for darwin thread"); } static void darwin_init_thread_list (struct inferior *inf) { - darwin_thread_t *thread; - ptid_t new_ptid; - darwin_check_new_threads (inf); - gdb_assert (inf->private->threads - && VEC_length (darwin_thread_t, inf->private->threads) > 0); - thread = VEC_index (darwin_thread_t, inf->private->threads, 0); + darwin_inferior *priv = get_darwin_inferior (inf); + + gdb_assert (!priv->threads.empty ()); - /* Note: fork_inferior automatically add a thead but it uses a wrong ptid. - Fix up. */ - new_ptid = ptid_build (inf->pid, 0, thread->gdb_port); - thread_change_ptid (inferior_ptid, new_ptid); - inferior_ptid = new_ptid; + darwin_thread_info *first_pti = priv->threads.front (); + struct thread_info *first_thread + = thread_info_from_private_thread_info (first_pti); + + inferior_ptid = first_thread->ptid; } /* The child must synchronize with gdb: gdb must set the exception port @@ -1487,22 +1746,28 @@ darwin_ptrace_me (void) char c; /* Close write end point. */ - close (ptrace_fds[1]); + if (close (ptrace_fds[1]) < 0) + trace_start_error_with_name ("close"); /* Wait until gdb is ready. */ res = read (ptrace_fds[0], &c, 1); if (res != 0) - error (_("unable to read from pipe, read returned: %d"), res); - close (ptrace_fds[0]); + trace_start_error (_("unable to read from pipe, read returned: %d"), res); + + if (close (ptrace_fds[0]) < 0) + trace_start_error_with_name ("close"); /* Get rid of privileges. */ - setegid (getgid ()); + if (setegid (getgid ()) < 0) + trace_start_error_with_name ("setegid"); /* Set TRACEME. */ - PTRACE (PT_TRACE_ME, 0, 0, 0); + if (PTRACE (PT_TRACE_ME, 0, 0, 0) < 0) + trace_start_error_with_name ("PTRACE"); /* Redirect signals to exception port. */ - PTRACE (PT_SIGEXC, 0, 0, 0); + if (PTRACE (PT_SIGEXC, 0, 0, 0) < 0) + trace_start_error_with_name ("PTRACE"); } /* Dummy function to be sure fork_inferior uses fork(2) and not vfork(2). */ @@ -1523,10 +1788,6 @@ darwin_pre_ptrace (void) static void darwin_ptrace_him (int pid) { - task_t itask; - kern_return_t kret; - mach_port_t prev_port; - int traps_expected; struct inferior *inf = current_inferior (); darwin_attach_pid (inf); @@ -1540,7 +1801,7 @@ darwin_ptrace_him (int pid) darwin_init_thread_list (inf); - startup_inferior (START_INFERIOR_TRAPS_EXPECTED); + gdb_startup_inferior (pid, START_INFERIOR_TRAPS_EXPECTED); } static void @@ -1577,17 +1838,171 @@ darwin_execvp (const char *file, char * const argv[], char * const env[]) posix_spawnp (NULL, argv[0], NULL, &attr, argv, env); } +/* Read kernel version, and return TRUE if this host may have System + Integrity Protection (Sierra or later). */ + +static bool +may_have_sip () +{ + char str[16]; + size_t sz = sizeof (str); + int ret; + + ret = sysctlbyname ("kern.osrelease", str, &sz, NULL, 0); + if (ret == 0 && sz < sizeof (str)) + { + unsigned long ver = strtoul (str, NULL, 10); + if (ver >= 16) + return true; + } + return false; +} + +/* A helper for maybe_cache_shell. This copies the shell to the + cache. It will throw an exception on any failure. */ + static void -darwin_create_inferior (struct target_ops *ops, char *exec_file, - char *allargs, char **env, int from_tty) +copy_shell_to_cache (const char *shell, const std::string &new_name) { - /* Do the hard work. */ - fork_inferior (exec_file, allargs, env, darwin_ptrace_me, darwin_ptrace_him, - darwin_pre_ptrace, NULL, darwin_execvp); + scoped_fd from_fd (gdb_open_cloexec (shell, O_RDONLY, 0)); + if (from_fd.get () < 0) + error (_("Could not open shell (%s) for reading: %s"), + shell, safe_strerror (errno)); + + std::string new_dir = ldirname (new_name.c_str ()); + if (!mkdir_recursive (new_dir.c_str ())) + error (_("Could not make cache directory \"%s\": %s"), + new_dir.c_str (), safe_strerror (errno)); + + gdb::char_vector temp_name = make_temp_filename (new_name); + scoped_fd to_fd (gdb_mkostemp_cloexec (&temp_name[0])); + gdb::unlinker unlink_file_on_error (temp_name.data ()); + + if (to_fd.get () < 0) + error (_("Could not open temporary file \"%s\" for writing: %s"), + temp_name.data (), safe_strerror (errno)); + + if (fcopyfile (from_fd.get (), to_fd.get (), nullptr, + COPYFILE_STAT | COPYFILE_DATA) != 0) + error (_("Could not copy shell to cache as \"%s\": %s"), + temp_name.data (), safe_strerror (errno)); + + /* Be sure that the caching is atomic so that we don't get bad + results from multiple copies of gdb running at the same time. */ + if (rename (temp_name.data (), new_name.c_str ()) != 0) + error (_("Could not rename shell cache file to \"%s\": %s"), + new_name.c_str (), safe_strerror (errno)); + + unlink_file_on_error.keep (); +} - /* Return now in case of error. */ - if (ptid_equal (inferior_ptid, null_ptid)) - return; +/* If $SHELL is restricted, try to cache a copy. Starting with El + Capitan, macOS introduced System Integrity Protection. Among other + things, this prevents certain executables from being ptrace'd. In + particular, executables in /bin, like most shells, are affected. + To work around this, while preserving command-line glob expansion + and redirections, gdb will cache a copy of the shell. Return true + if all is well -- either the shell is not subject to SIP or it has + been successfully cached. Returns false if something failed. */ + +static bool +maybe_cache_shell () +{ + /* SF_RESTRICTED is defined in sys/stat.h and lets us determine if a + given file is subject to SIP. */ +#ifdef SF_RESTRICTED + + /* If a check fails we want to revert -- maybe the user deleted the + cache while gdb was running, or something like that. */ + copied_shell = nullptr; + + const char *shell = get_shell (); + if (!IS_ABSOLUTE_PATH (shell)) + { + warning (_("This version of macOS has System Integrity Protection.\n\ +Normally gdb would try to work around this by caching a copy of your shell,\n\ +but because your shell (%s) is not an absolute path, this is being skipped."), + shell); + return false; + } + + struct stat sb; + if (stat (shell, &sb) < 0) + { + warning (_("This version of macOS has System Integrity Protection.\n\ +Normally gdb would try to work around this by caching a copy of your shell,\n\ +but because gdb could not stat your shell (%s), this is being skipped.\n\ +The error was: %s"), + shell, safe_strerror (errno)); + return false; + } + + if ((sb.st_flags & SF_RESTRICTED) == 0) + return true; + + /* Put the copy somewhere like ~/Library/Caches/gdb/bin/sh. */ + std::string new_name = get_standard_cache_dir (); + /* There's no need to insert a directory separator here, because + SHELL is known to be absolute. */ + new_name.append (shell); + + /* Maybe it was cached by some earlier gdb. */ + if (stat (new_name.c_str (), &sb) != 0 || !S_ISREG (sb.st_mode)) + { + TRY + { + copy_shell_to_cache (shell, new_name); + } + CATCH (ex, RETURN_MASK_ERROR) + { + warning (_("This version of macOS has System Integrity Protection.\n\ +Because `startup-with-shell' is enabled, gdb tried to work around SIP by\n\ +caching a copy of your shell. However, this failed:\n\ +%s\n\ +If you correct the problem, gdb will automatically try again the next time\n\ +you \"run\". To prevent these attempts, you can use:\n\ + set startup-with-shell off"), + ex.message); + return false; + } + END_CATCH + + printf_filtered (_("Note: this version of macOS has System Integrity Protection.\n\ +Because `startup-with-shell' is enabled, gdb has worked around this by\n\ +caching a copy of your shell. The shell used by \"run\" is now:\n\ + %s\n"), + new_name.c_str ()); + } + + /* We need to make sure that the new name has the correct lifetime. */ + static std::string saved_shell = std::move (new_name); + copied_shell = saved_shell.c_str (); + +#endif /* SF_RESTRICTED */ + + return true; +} + +void +darwin_nat_target::create_inferior (const char *exec_file, + const std::string &allargs, + char **env, int from_tty) +{ + gdb::optional> restore_startup_with_shell; + + if (startup_with_shell && may_have_sip ()) + { + if (!maybe_cache_shell ()) + { + warning (_("startup-with-shell is now temporarily disabled")); + restore_startup_with_shell.emplace (&startup_with_shell, 0); + } + } + + /* Do the hard work. */ + fork_inferior (exec_file, allargs, env, darwin_ptrace_me, + darwin_ptrace_him, darwin_pre_ptrace, copied_shell, + darwin_execvp); } @@ -1600,6 +2015,7 @@ darwin_create_inferior (struct target_ops *ops, char *exec_file, static void darwin_setup_fake_stop_event (struct inferior *inf) { + darwin_inferior *priv = get_darwin_inferior (inf); darwin_thread_t *thread; kern_return_t kret; @@ -1612,22 +2028,18 @@ darwin_setup_fake_stop_event (struct inferior *inf) as well. Otherwise, we'll try resuming it when resuming the inferior, and get a warning because the thread's suspend count is already zero, making the resume request useless. */ - thread = VEC_index (darwin_thread_t, inf->private->threads, 0); + thread = priv->threads[0]; kret = thread_suspend (thread->gdb_port); MACH_CHECK_ERROR (kret); } /* Attach to process PID, then initialize for debugging it and wait for the trace-trap that results from attaching. */ -static void -darwin_attach (struct target_ops *ops, char *args, int from_tty) +void +darwin_nat_target::attach (const char *args, int from_tty) { pid_t pid; - pid_t pid2; - int wstatus; - int res; struct inferior *inf; - kern_return_t kret; pid = parse_pid_to_attach (args); @@ -1640,37 +2052,34 @@ darwin_attach (struct target_ops *ops, char *args, int from_tty) if (exec_file) printf_unfiltered (_("Attaching to program: %s, %s\n"), exec_file, - target_pid_to_str (pid_to_ptid (pid))); + target_pid_to_str (ptid_t (pid)).c_str ()); else printf_unfiltered (_("Attaching to %s\n"), - target_pid_to_str (pid_to_ptid (pid))); - - gdb_flush (gdb_stdout); + target_pid_to_str (ptid_t (pid)).c_str ()); } - if (pid == 0 || kill (pid, 0) < 0) + if (pid == 0 || ::kill (pid, 0) < 0) error (_("Can't attach to process %d: %s (%d)"), pid, safe_strerror (errno), errno); - inferior_ptid = pid_to_ptid (pid); + inferior_ptid = ptid_t (pid); inf = current_inferior (); inferior_appeared (inf, pid); inf->attach_flag = 1; - /* Always add a main thread. */ - add_thread_silent (inferior_ptid); - darwin_attach_pid (inf); darwin_suspend_inferior (inf); darwin_init_thread_list (inf); - darwin_check_osabi (inf->private, ptid_get_tid (inferior_ptid)); + darwin_inferior *priv = get_darwin_inferior (inf); + + darwin_check_osabi (priv, inferior_ptid.tid ()); darwin_setup_fake_stop_event (inf); - inf->private->no_ptrace = 1; + priv->no_ptrace = 1; } /* Take a program previously attached to and detaches it. @@ -1680,33 +2089,25 @@ darwin_attach (struct target_ops *ops, char *args, int from_tty) to work, it may be necessary for the process to have been previously attached. It *might* work if the program was started via fork. */ -static void -darwin_detach (struct target_ops *ops, const char *args, int from_tty) + +void +darwin_nat_target::detach (inferior *inf, int from_tty) { - pid_t pid = ptid_get_pid (inferior_ptid); - struct inferior *inf = current_inferior (); + darwin_inferior *priv = get_darwin_inferior (inf); kern_return_t kret; int res; /* Display message. */ - if (from_tty) - { - char *exec_file = get_exec_file (0); - if (exec_file == 0) - exec_file = ""; - printf_unfiltered (_("Detaching from program: %s, %s\n"), exec_file, - target_pid_to_str (pid_to_ptid (pid))); - gdb_flush (gdb_stdout); - } + target_announce_detach (from_tty); /* If ptrace() is in use, stop the process. */ - if (!inf->private->no_ptrace) + if (!priv->no_ptrace) darwin_stop_inferior (inf); - kret = darwin_restore_exception_ports (inf->private); + kret = darwin_restore_exception_ports (priv); MACH_CHECK_ERROR (kret); - if (!inf->private->no_ptrace) + if (!priv->no_ptrace) { res = PTRACE (PT_DETACH, inf->pid, 0, 0); if (res != 0) @@ -1719,37 +2120,28 @@ darwin_detach (struct target_ops *ops, const char *args, int from_tty) /* When using ptrace, we have just performed a PT_DETACH, which resumes the inferior. On the other hand, when we are not using ptrace, we need to resume its execution ourselves. */ - if (inf->private->no_ptrace) + if (priv->no_ptrace) darwin_resume_inferior (inf); - darwin_mourn_inferior (ops); -} - -static void -darwin_files_info (struct target_ops *ops) -{ + mourn_inferior (); } -static char * -darwin_pid_to_str (struct target_ops *ops, ptid_t ptid) +std::string +darwin_nat_target::pid_to_str (ptid_t ptid) { - static char buf[80]; - long tid = ptid_get_tid (ptid); + long tid = ptid.tid (); if (tid != 0) - { - snprintf (buf, sizeof (buf), _("Thread 0x%lx of process %u"), - tid, ptid_get_pid (ptid)); - return buf; - } + return string_printf (_("Thread 0x%lx of process %u"), + tid, ptid.pid ()); return normal_pid_to_str (ptid); } -static int -darwin_thread_alive (struct target_ops *ops, ptid_t ptid) +bool +darwin_nat_target::thread_alive (ptid_t ptid) { - return 1; + return true; } /* If RDADDR is not NULL, read inferior task's LEN bytes from ADDR and @@ -1757,56 +2149,56 @@ darwin_thread_alive (struct target_ops *ops, ptid_t ptid) If WRADDR is not NULL, write gdb's LEN bytes from WRADDR and copy it to ADDR in inferior task's address space. Return 0 on failure; number of bytes read / writen otherwise. */ + static int darwin_read_write_inferior (task_t task, CORE_ADDR addr, gdb_byte *rdaddr, const gdb_byte *wraddr, - int length) + ULONGEST length) { kern_return_t kret; - mach_vm_address_t offset = addr & (mach_page_size - 1); - mach_vm_address_t low_address = (mach_vm_address_t) (addr - offset); - mach_vm_size_t aligned_length = (mach_vm_size_t) PAGE_ROUND (offset + length); - pointer_t copied; - mach_msg_type_number_t copy_count; - mach_vm_size_t remaining_length; - mach_vm_address_t region_address; - mach_vm_size_t region_length; - - inferior_debug (8, _("darwin_read_write_inferior(task=0x%x, %s, len=%d)\n"), - task, core_addr_to_string (addr), length); - - /* Get memory from inferior with page aligned addresses. */ - kret = mach_vm_read (task, low_address, aligned_length, - &copied, ©_count); - if (kret != KERN_SUCCESS) - { - inferior_debug - (1, _("darwin_read_write_inferior: mach_vm_read failed at %s: %s"), - core_addr_to_string (addr), mach_error_string (kret)); - return 0; - } + mach_vm_size_t res_length = 0; + + inferior_debug (8, _("darwin_read_write_inferior(task=0x%x, %s, len=%s)\n"), + task, core_addr_to_string (addr), pulongest (length)); + /* First read. */ if (rdaddr != NULL) - memcpy (rdaddr, (char *)copied + offset, length); + { + mach_vm_size_t count; + + /* According to target.h(to_xfer_partial), one and only one may be + non-null. */ + gdb_assert (wraddr == NULL); - if (wraddr == NULL) - goto out; + kret = mach_vm_read_overwrite (task, addr, length, + (mach_vm_address_t) rdaddr, &count); + if (kret != KERN_SUCCESS) + { + inferior_debug + (1, _("darwin_read_write_inferior: mach_vm_read failed at %s: %s"), + core_addr_to_string (addr), mach_error_string (kret)); + return 0; + } + return count; + } - memcpy ((char *)copied + offset, wraddr, length); + /* See above. */ + gdb_assert (wraddr != NULL); - /* Do writes atomically. - First check for holes and unwritable memory. */ - for (region_address = low_address, remaining_length = aligned_length; - region_address < low_address + aligned_length; - region_address += region_length, remaining_length -= region_length) + while (length != 0) { + mach_vm_address_t offset = addr & (mach_page_size - 1); + mach_vm_address_t region_address = (mach_vm_address_t) (addr - offset); + mach_vm_size_t aligned_length = + (mach_vm_size_t) PAGE_ROUND (offset + length); vm_region_submap_short_info_data_64_t info; + mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; + natural_t region_depth = 1000; mach_vm_address_t region_start = region_address; - mach_msg_type_number_t count; - natural_t region_depth; + mach_vm_size_t region_length; + mach_vm_size_t write_length; - region_depth = 100000; - count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; + /* Read page protection. */ kret = mach_vm_region_recurse (task, ®ion_start, ®ion_length, ®ion_depth, (vm_region_recurse_info_t) &info, &count); @@ -1817,7 +2209,7 @@ darwin_read_write_inferior (task_t task, CORE_ADDR addr, "mach_vm_region_recurse failed at %s: %s\n"), core_addr_to_string (region_address), mach_error_string (kret)); - goto out; + return res_length; } inferior_debug @@ -1834,131 +2226,165 @@ darwin_read_write_inferior (task_t task, CORE_ADDR addr, core_addr_to_string (region_address), core_addr_to_string (region_start), (unsigned)region_length); - length = 0; - goto out; + return res_length; } /* Adjust the length. */ region_length -= (region_address - region_start); + if (region_length > aligned_length) + region_length = aligned_length; - if (!(info.max_protection & VM_PROT_WRITE)) + /* Make the pages RW. */ + if (!(info.protection & VM_PROT_WRITE)) { - kret = mach_vm_protect - (task, region_address, region_length, - TRUE, info.max_protection | VM_PROT_WRITE | VM_PROT_COPY); + vm_prot_t prot = VM_PROT_READ | VM_PROT_WRITE; + + kret = mach_vm_protect (task, region_address, region_length, + FALSE, prot); + if (kret != KERN_SUCCESS) + { + prot |= VM_PROT_COPY; + kret = mach_vm_protect (task, region_address, region_length, + FALSE, prot); + } if (kret != KERN_SUCCESS) { - warning (_("darwin_read_write_inf: " - "mach_vm_protect max failed at %s: %s"), + warning (_("darwin_read_write_inferior: " + "mach_vm_protect failed at %s " + "(len=0x%lx, prot=0x%x): %s"), core_addr_to_string (region_address), + (unsigned long) region_length, (unsigned) prot, mach_error_string (kret)); - length = 0; - goto out; + return res_length; } } + if (offset + length > region_length) + write_length = region_length - offset; + else + write_length = length; + + /* Write. */ + kret = mach_vm_write (task, addr, (vm_offset_t) wraddr, write_length); + if (kret != KERN_SUCCESS) + { + warning (_("darwin_read_write_inferior: mach_vm_write failed: %s"), + mach_error_string (kret)); + return res_length; + } + + /* Restore page rights. */ if (!(info.protection & VM_PROT_WRITE)) { kret = mach_vm_protect (task, region_address, region_length, - FALSE, info.protection | VM_PROT_WRITE); + FALSE, info.protection); if (kret != KERN_SUCCESS) { - warning (_("darwin_read_write_inf: " - "mach_vm_protect failed at %s (len=0x%lx): %s"), + warning (_("darwin_read_write_inferior: " + "mach_vm_protect restore failed at %s " + "(len=0x%lx): %s"), core_addr_to_string (region_address), - (unsigned long)region_length, mach_error_string (kret)); - length = 0; - goto out; + (unsigned long) region_length, + mach_error_string (kret)); } } - } - - kret = mach_vm_write (task, low_address, copied, aligned_length); - if (kret != KERN_SUCCESS) - { - warning (_("darwin_read_write_inferior: mach_vm_write failed: %s"), - mach_error_string (kret)); - length = 0; + addr += write_length; + wraddr += write_length; + res_length += write_length; + length -= write_length; } -out: - mach_vm_deallocate (mach_task_self (), copied, copy_count); - return length; + + return res_length; } /* Read LENGTH bytes at offset ADDR of task_dyld_info for TASK, and copy them - to RDADDR. + to RDADDR (in big endian). Return 0 on failure; number of bytes read / written otherwise. */ #ifdef TASK_DYLD_INFO_COUNT /* This is not available in Darwin 9. */ -static int +static enum target_xfer_status darwin_read_dyld_info (task_t task, CORE_ADDR addr, gdb_byte *rdaddr, - int length) + ULONGEST length, ULONGEST *xfered_len) { struct task_dyld_info task_dyld_info; mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; - int sz = TASK_DYLD_INFO_COUNT * sizeof (natural_t); kern_return_t kret; - if (addr >= sz) - return 0; + if (addr != 0 || length > sizeof (mach_vm_address_t)) + return TARGET_XFER_EOF; - kret = task_info (task, TASK_DYLD_INFO, (task_info_t) &task_dyld_info, &count); + kret = task_info (task, TASK_DYLD_INFO, + (task_info_t) &task_dyld_info, &count); MACH_CHECK_ERROR (kret); if (kret != KERN_SUCCESS) - return -1; - /* Truncate. */ - if (addr + length > sz) - length = sz - addr; - memcpy (rdaddr, (char *)&task_dyld_info + addr, length); - return length; + return TARGET_XFER_E_IO; + + store_unsigned_integer (rdaddr, length, BFD_ENDIAN_BIG, + task_dyld_info.all_image_info_addr); + *xfered_len = (ULONGEST) length; + return TARGET_XFER_OK; } #endif -static LONGEST -darwin_xfer_partial (struct target_ops *ops, - enum target_object object, const char *annex, - gdb_byte *readbuf, const gdb_byte *writebuf, - ULONGEST offset, LONGEST len) +enum target_xfer_status +darwin_nat_target::xfer_partial (enum target_object object, const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, ULONGEST len, + ULONGEST *xfered_len) { struct inferior *inf = current_inferior (); + darwin_inferior *priv = get_darwin_inferior (inf); inferior_debug - (8, _("darwin_xfer_partial(%s, %d, rbuf=%s, wbuf=%s) pid=%u\n"), - core_addr_to_string (offset), (int)len, + (8, _("darwin_xfer_partial(%s, %s, rbuf=%s, wbuf=%s) pid=%u\n"), + core_addr_to_string (offset), pulongest (len), host_address_to_string (readbuf), host_address_to_string (writebuf), inf->pid); switch (object) { case TARGET_OBJECT_MEMORY: - return darwin_read_write_inferior (inf->private->task, offset, - readbuf, writebuf, len); + { + int l = darwin_read_write_inferior (priv->task, offset, + readbuf, writebuf, len); + + if (l == 0) + return TARGET_XFER_EOF; + else + { + gdb_assert (l > 0); + *xfered_len = (ULONGEST) l; + return TARGET_XFER_OK; + } + } #ifdef TASK_DYLD_INFO_COUNT case TARGET_OBJECT_DARWIN_DYLD_INFO: if (writebuf != NULL || readbuf == NULL) { /* Support only read. */ - return -1; + return TARGET_XFER_E_IO; } - return darwin_read_dyld_info (inf->private->task, offset, readbuf, len); + return darwin_read_dyld_info (priv->task, offset, readbuf, len, + xfered_len); #endif default: - return -1; + return TARGET_XFER_E_IO; } } static void -set_enable_mach_exceptions (char *args, int from_tty, +set_enable_mach_exceptions (const char *args, int from_tty, struct cmd_list_element *c) { - if (!ptid_equal (inferior_ptid, null_ptid)) + if (inferior_ptid != null_ptid) { struct inferior *inf = current_inferior (); + darwin_inferior *priv = get_darwin_inferior (inf); exception_mask_t mask; kern_return_t kret; @@ -1966,24 +2392,21 @@ set_enable_mach_exceptions (char *args, int from_tty, mask = EXC_MASK_ALL; else { - darwin_restore_exception_ports (inf->private); + darwin_restore_exception_ports (priv); mask = EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT; } - kret = task_set_exception_ports (inf->private->task, mask, darwin_ex_port, + kret = task_set_exception_ports (priv->task, mask, darwin_ex_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE); MACH_CHECK_ERROR (kret); } } -static char * -darwin_pid_to_exec_file (int pid) +char * +darwin_nat_target::pid_to_exec_file (int pid) { - char *path; + static char path[PATH_MAX]; int res; - path = xmalloc (PATH_MAX); - make_cleanup (xfree, path); - res = proc_pidinfo (pid, PROC_PIDPATHINFO, 0, path, PATH_MAX); if (res >= 0) return path; @@ -1991,13 +2414,11 @@ darwin_pid_to_exec_file (int pid) return NULL; } -static ptid_t -darwin_get_ada_task_ptid (long lwp, long thread) +ptid_t +darwin_nat_target::get_ada_task_ptid (long lwp, long thread) { - int i; - darwin_thread_t *t; - int k; struct inferior *inf = current_inferior (); + darwin_inferior *priv = get_darwin_inferior (inf); kern_return_t kret; mach_port_name_array_t names; mach_msg_type_number_t names_count; @@ -2006,16 +2427,16 @@ darwin_get_ada_task_ptid (long lwp, long thread) long res = 0; /* First linear search. */ - for (k = 0; - VEC_iterate (darwin_thread_t, inf->private->threads, k, t); - k++) - if (t->inf_port == lwp) - return ptid_build (ptid_get_pid (inferior_ptid), 0, t->gdb_port); + for (darwin_thread_t *t : priv->threads) + { + if (t->inf_port == lwp) + return ptid_t (inferior_ptid.pid (), 0, t->gdb_port); + } /* Maybe the port was never extract. Do it now. */ /* First get inferior port names. */ - kret = mach_port_names (inf->private->task, &names, &names_count, &types, + kret = mach_port_names (priv->task, &names, &names_count, &types, &types_count); MACH_CHECK_ERROR (kret); if (kret != KERN_SUCCESS) @@ -2024,51 +2445,48 @@ darwin_get_ada_task_ptid (long lwp, long thread) /* For each name, copy the right in the gdb space and then compare with our view of the inferior threads. We don't forget to deallocate the right. */ - for (i = 0; i < names_count; i++) + for (int i = 0; i < names_count; i++) { mach_port_t local_name; mach_msg_type_name_t local_type; /* We just need to know the corresponding name in gdb name space. So extract and deallocate the right. */ - kret = mach_port_extract_right (inf->private->task, names[i], + kret = mach_port_extract_right (priv->task, names[i], MACH_MSG_TYPE_COPY_SEND, &local_name, &local_type); if (kret != KERN_SUCCESS) continue; mach_port_deallocate (gdb_task, local_name); - for (k = 0; - VEC_iterate (darwin_thread_t, inf->private->threads, k, t); - k++) - if (t->gdb_port == local_name) - { - t->inf_port = names[i]; - if (names[i] == lwp) - res = t->gdb_port; - } + for (darwin_thread_t *t : priv->threads) + { + if (t->gdb_port == local_name) + { + t->inf_port = names[i]; + if (names[i] == lwp) + res = t->gdb_port; + } + } } vm_deallocate (gdb_task, (vm_address_t) names, names_count * sizeof (mach_port_t)); if (res) - return ptid_build (ptid_get_pid (inferior_ptid), 0, res); + return ptid_t (inferior_ptid.pid (), 0, res); else return null_ptid; } -static int -darwin_supports_multi_process (void) +bool +darwin_nat_target::supports_multi_process () { - return 1; + return true; } -/* -Wmissing-prototypes */ -extern initialize_file_ftype _initialize_darwin_inferior; - void -_initialize_darwin_inferior (void) +_initialize_darwin_nat () { kern_return_t kret; @@ -2083,36 +2501,8 @@ _initialize_darwin_inferior (void) MACH_CHECK_ERROR (kret); } - darwin_ops = inf_child_target (); - - darwin_ops->to_shortname = "darwin-child"; - darwin_ops->to_longname = _("Darwin child process"); - darwin_ops->to_doc = - _("Darwin child process (started by the \"run\" command)."); - darwin_ops->to_create_inferior = darwin_create_inferior; - darwin_ops->to_attach = darwin_attach; - darwin_ops->to_attach_no_wait = 0; - darwin_ops->to_detach = darwin_detach; - darwin_ops->to_files_info = darwin_files_info; - darwin_ops->to_wait = darwin_wait_to; - darwin_ops->to_mourn_inferior = darwin_mourn_inferior; - darwin_ops->to_kill = darwin_kill_inferior; - darwin_ops->to_stop = darwin_stop; - darwin_ops->to_resume = darwin_resume_to; - darwin_ops->to_thread_alive = darwin_thread_alive; - darwin_ops->to_pid_to_str = darwin_pid_to_str; - darwin_ops->to_pid_to_exec_file = darwin_pid_to_exec_file; - darwin_ops->to_load = NULL; - darwin_ops->to_xfer_partial = darwin_xfer_partial; - darwin_ops->to_supports_multi_process = darwin_supports_multi_process; - darwin_ops->to_get_ada_task_ptid = darwin_get_ada_task_ptid; - - darwin_complete_target (darwin_ops); - - add_target (darwin_ops); - - inferior_debug (2, _("GDB task: 0x%lx, pid: %d\n"), mach_task_self (), - getpid ()); + inferior_debug (2, _("GDB task: 0x%lx, pid: %d\n"), + (unsigned long) mach_task_self (), getpid ()); add_setshow_zuinteger_cmd ("darwin", class_obscure, &darwin_debug_flag, _("\