From d94e8653463e33dd3e10ce4001217e5d929d0883 Mon Sep 17 00:00:00 2001 From: Tristan Gingold Date: Wed, 9 Nov 2016 10:25:00 +0100 Subject: [PATCH 1/1] darwin-nat.c: handle Darwin 16 (aka Sierra). Support message from new task and dead name notification on task of an existing process. With Sierra, exec(2) terminate the current task and creates a new one. 'set startup-with-shell off' must still be used on Darwin 16. 2016-11-09 Tristan Gingold * darwin-nat.c (find_inferior_task_it): Fix indentation. (find_inferior_notify_it): Remove. (find_inferior_pid_it): New function. (darwin_find_inferior_by_notify): Remove. (darwin_find_inferior_by_pid): New function. (darwin_find_new_inferior): New function. (darwin_check_message_ndr): New function from darwin_decode_exception_message. (darwin_decode_exception_message): Call darwin_check_message_ndr. Handle SIGTRAP addressed to an unknown task (when a task spawned). (darwin_decode_notify_message): New function. (darwin_decode_message): Handle unknown task. (darwin_deallocate_threads): New function from darwin_mourn_inferior. (darwin_mourn_inferior): Use darwin_deallocate_threads and darwin_deallocate_exception_ports. (darwin_deallocate_exception_ports): New function from darwin_mourn_inferior. (darwin_setup_exceptions): New function from darwin_attach_pid. (darwin_setup_request_notification): Likewise. (darwin_attach_pid): Call darwin_setup_request_notification and darwin_setup_request_notification. --- gdb/darwin-nat.c | 342 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 267 insertions(+), 75 deletions(-) diff --git a/gdb/darwin-nat.c b/gdb/darwin-nat.c index 590c2ad..4fc1685 100644 --- a/gdb/darwin-nat.c +++ b/gdb/darwin-nat.c @@ -114,6 +114,11 @@ 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); +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); + /* Target operations for Darwin. */ static struct target_ops *darwin_ops; @@ -320,6 +325,7 @@ darwin_check_new_threads (struct inferior *inf) } } + /* Full handling: detect new threads, remove dead threads. */ thread_vec = VEC_alloc (darwin_thread_t, new_nbr); for (new_ix = 0, old_ix = 0; new_ix < new_nbr || old_ix < old_nbr;) @@ -365,15 +371,22 @@ darwin_check_new_threads (struct inferior *inf) 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 + if (old_nbr == 0 && new_ix == 0) { + /* A ptid is create when the inferior is started (see + fork-child.c) with lwp=tid=0. This ptid will be renamed + later by darwin_init_thread_list (). */ tp = find_thread_ptid (ptid_build (inf->pid, 0, 0)); gdb_assert (tp); + gdb_assert (tp->priv == NULL); tp->priv = pti; } + else + { + /* Add the new thread. */ + tp = add_thread_with_info + (ptid_build (inf->pid, 0, new_id), pti); + } VEC_safe_push (darwin_thread_t, thread_vec, pti); new_ix++; continue; @@ -403,13 +416,13 @@ darwin_check_new_threads (struct inferior *inf) static int find_inferior_task_it (struct inferior *inf, void *port_ptr) { - return inf->priv->task == *(task_t*)port_ptr; + return inf->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->priv->notify_port == *(task_t*)port_ptr; + return inf->pid == *(int *)pid_ptr; } /* Return an inferior by task port. */ @@ -419,11 +432,11 @@ 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. */ @@ -557,6 +570,69 @@ darwin_dump_message (mach_msg_header_t *hdr, int disp_body) } } +/* 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; + + /* Deallocate saved exception ports. */ + darwin_deallocate_exception_ports (inf->priv); + + /* No need to remove dead_name notification, but still... */ + kret = mach_port_request_notification (gdb_task, inf->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, inf->priv->task); + MACH_CHECK_ERROR (kret); + inf->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, @@ -593,12 +669,7 @@ 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. */ @@ -607,14 +678,33 @@ darwin_decode_exception_message (mach_msg_header_t *hdr, task_port = desc[1].name; thread_port = desc[0].name; - /* 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); - /* Find process by port. */ inf = darwin_find_inferior_by_task (task_port); *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 @@ -623,6 +713,10 @@ darwin_decode_exception_message (mach_msg_header_t *hdr, 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); @@ -676,6 +770,45 @@ 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; + darwin_thread_t *thread; + task_t task_port; + thread_t thread_port; + kern_return_t kret; + int i; + + /* 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 && hdr->msgh_local_port != inf->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) @@ -986,10 +1119,27 @@ darwin_decode_message (mach_msg_header_t *hdr, else if (hdr->msgh_id == 0x48) { /* MACH_NOTIFY_DEAD_NAME: notification for exit. */ + int res; + + res = darwin_decode_notify_message (hdr, &inf); + + if (res < 0) + { + /* Should not happen... */ + printf_unfiltered + (_("darwin_wait: ill-formatted message (id=0x%x, res=%d)\n"), + hdr->msgh_id, res); + } + *pinf = NULL; *pthread = NULL; - inf = darwin_find_inferior_by_notify (hdr->msgh_local_port); + if (res < 0 || inf == NULL) + { + status->kind = TARGET_WAITKIND_IGNORE; + return minus_one_ptid; + } + if (inf != NULL) { if (!inf->priv->no_ptrace) @@ -1022,7 +1172,8 @@ darwin_decode_message (mach_msg_header_t *hdr, /* Looks necessary on Leopard and harmless... */ wait4 (inf->pid, &wstatus, 0, NULL); - return ptid_build (inf->pid, 0, 0); + inferior_ptid = ptid_build (inf->pid, 0, 0); + return inferior_ptid; } else { @@ -1204,17 +1355,14 @@ darwin_interrupt (struct target_ops *self, ptid_t t) kill (inf->pid, SIGINT); } +/* Deallocate threads port and vector. */ + static void -darwin_mourn_inferior (struct target_ops *ops) +darwin_deallocate_threads (struct inferior *inf) { - struct inferior *inf = current_inferior (); - kern_return_t kret; - mach_port_t prev; - int i; - - /* Deallocate threads. */ if (inf->priv->threads) { + kern_return_t kret; int k; darwin_thread_t *t; for (k = 0; @@ -1227,11 +1375,25 @@ darwin_mourn_inferior (struct target_ops *ops) VEC_free (darwin_thread_t, inf->priv->threads); inf->priv->threads = NULL; } +} +static void +darwin_mourn_inferior (struct target_ops *ops) +{ + struct inferior *inf = current_inferior (); + kern_return_t kret; + mach_port_t prev; + int i; + + /* Deallocate threads. */ + darwin_deallocate_threads (inf); + + /* Remove notify_port from darwin_port_set. */ kret = mach_port_move_member (gdb_task, inf->priv->notify_port, MACH_PORT_NULL); MACH_CHECK_ERROR (kret); + /* Remove task port dead_name notification. */ kret = mach_port_request_notification (gdb_task, inf->priv->task, MACH_NOTIFY_DEAD_NAME, 0, MACH_PORT_NULL, @@ -1247,19 +1409,14 @@ darwin_mourn_inferior (struct target_ops *ops) MACH_CHECK_ERROR (kret); } + /* Destroy notify_port. */ kret = mach_port_destroy (gdb_task, inf->priv->notify_port); MACH_CHECK_ERROR (kret); - /* Deallocate saved exception ports. */ - for (i = 0; i < inf->priv->exception_info.count; i++) - { - kret = mach_port_deallocate - (gdb_task, inf->priv->exception_info.ports[i]); - MACH_CHECK_ERROR (kret); - } - inf->priv->exception_info.count = 0; + darwin_deallocate_exception_ports (inf->priv); + /* Deallocate task port. */ kret = mach_port_deallocate (gdb_task, inf->priv->task); MACH_CHECK_ERROR (kret); @@ -1349,6 +1506,48 @@ 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_setup_exceptions (struct inferior *inf) +{ + kern_return_t kret; + int traps_expected; + exception_mask_t mask; + + kret = darwin_save_exception_ports (inf->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 (inf->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); +} + static void darwin_kill_inferior (struct target_ops *ops) { @@ -1385,6 +1584,34 @@ darwin_kill_inferior (struct target_ops *ops) } static void +darwin_setup_request_notification (struct inferior *inf) +{ + kern_return_t kret; + mach_port_t prev_not; + + kret = mach_port_request_notification (gdb_task, inf->priv->task, + MACH_NOTIFY_DEAD_NAME, 0, + inf->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) + { + /* 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; @@ -1463,44 +1690,9 @@ darwin_attach_pid (struct inferior *inf) "returned: %d"), kret); - kret = mach_port_request_notification (gdb_task, inf->priv->task, - MACH_NOTIFY_DEAD_NAME, 0, - inf->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) - { - /* 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.")); - } + darwin_setup_request_notification (inf); - kret = darwin_save_exception_ports (inf->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 (inf->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); + darwin_setup_exceptions (inf); if (!target_is_pushed (darwin_ops)) push_target (darwin_ops); -- 2.9.3 (Apple Git-75)