X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fdarwin-nat.c;h=0b7cfb69dc805d3e6b565e2a67a5ac1d7d3d6498;hb=3276427003bd1fc676d1cb913c4d09a9f7b1c74f;hp=8104de53e7f869850504fbb48f0fbfd220ff954e;hpb=d7e15655a40e9133a1cbf53ea071f82cd6745ac8;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/darwin-nat.c b/gdb/darwin-nat.c index 8104de53e7..0b7cfb69dc 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-2018 Free Software Foundation, Inc. + Copyright (C) 2008-2019 Free Software Foundation, Inc. Contributed by AdaCore. @@ -38,6 +38,7 @@ #include "bfd.h" #include "bfd/mach-o.h" +#include #include #include #include @@ -61,7 +62,11 @@ #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. @@ -119,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) @@ -259,6 +268,9 @@ darwin_check_new_threads (struct inferior *inf) 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); MACH_CHECK_ERROR (kret); @@ -374,7 +386,7 @@ find_inferior_task_it (struct inferior *inf, void *port_ptr) { darwin_inferior *priv = get_darwin_inferior (inf); - return priv->task == *(task_t *)port_ptr; + return priv != nullptr && priv->task == *(task_t *)port_ptr; } static int @@ -403,11 +415,12 @@ darwin_find_thread (struct inferior *inf, thread_t thread) { darwin_inferior *priv = get_darwin_inferior (inf); - for (darwin_thread_t *t : priv->threads) - { - if (t->gdb_port == thread) - return t; - } + if (priv != nullptr) + for (darwin_thread_t *t : priv->threads) + { + if (t->gdb_port == thread) + return t; + } return NULL; } @@ -419,7 +432,7 @@ darwin_suspend_inferior (struct inferior *inf) { darwin_inferior *priv = get_darwin_inferior (inf); - if (!priv->suspended) + if (priv != nullptr && !priv->suspended) { kern_return_t kret; @@ -437,7 +450,7 @@ darwin_resume_inferior (struct inferior *inf) { darwin_inferior *priv = get_darwin_inferior (inf); - if (priv->suspended) + if (priv != nullptr && priv->suspended) { kern_return_t kret; @@ -742,11 +755,7 @@ 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) @@ -766,11 +775,13 @@ darwin_decode_notify_message (mach_msg_header_t *hdr, struct inferior **pinf) inf = darwin_find_inferior_by_task (task_port); *pinf = inf; - darwin_inferior *priv = get_darwin_inferior (inf); - /* Check message destination. */ - if (inf != NULL && hdr->msgh_local_port != priv->notify_port) - return -4; + if (inf != NULL) + { + darwin_inferior *priv = get_darwin_inferior (inf); + if (hdr->msgh_local_port != priv->notify_port) + return -4; + } return 0; } @@ -809,13 +820,24 @@ darwin_send_reply (struct inferior *inf, darwin_thread_t *thread) 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); @@ -827,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, - (caddr_t) (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); } @@ -836,7 +858,7 @@ 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; @@ -856,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; @@ -871,8 +893,9 @@ darwin_resume_inferior_threads (struct inferior *inf, int step, int nsignal) { darwin_inferior *priv = get_darwin_inferior (inf); - for (darwin_thread_t *thread : priv->threads) - 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 @@ -921,12 +944,8 @@ 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%lx, step=%d, signal=%d\n"), @@ -1343,7 +1362,6 @@ darwin_nat_target::mourn_inferior () darwin_inferior *priv = get_darwin_inferior (inf); kern_return_t kret; mach_port_t prev; - int i; /* Deallocate threads. */ darwin_deallocate_threads (inf); @@ -1402,8 +1420,6 @@ 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); @@ -1484,7 +1500,6 @@ darwin_setup_exceptions (struct inferior *inf) { darwin_inferior *priv = get_darwin_inferior (inf); kern_return_t kret; - int traps_expected; exception_mask_t mask; kret = darwin_save_exception_ports (priv); @@ -1514,7 +1529,6 @@ darwin_nat_target::kill () struct target_waitstatus wstatus; ptid_t ptid; kern_return_t kret; - int status; int res; if (inferior_ptid == null_ptid) @@ -1531,6 +1545,24 @@ darwin_nat_target::kill () 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); @@ -1575,10 +1607,6 @@ static void darwin_attach_pid (struct inferior *inf) { kern_return_t kret; - mach_port_t prev_port; - int traps_expected; - mach_port_t prev_not; - exception_mask_t mask; darwin_inferior *priv = new darwin_inferior; inf->priv.reset (priv); @@ -1662,8 +1690,7 @@ darwin_attach_pid (struct inferior *inf) } CATCH (ex, RETURN_MASK_ALL) { - inf->pid = 0; - inf->priv.reset (); + exit_inferior (inf); inferior_ptid = null_ptid; throw_exception (ex); @@ -1680,19 +1707,15 @@ darwin_attach_pid (struct inferior *inf) static struct thread_info * thread_info_from_private_thread_info (darwin_thread_info *pti) { - struct thread_info *it; - - ALL_THREADS (it) + 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) - break; + return it; } - gdb_assert (it != NULL); - - return it; + gdb_assert_not_reached ("did not find gdb thread for darwin thread"); } static void @@ -1765,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); @@ -1819,14 +1838,170 @@ 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 +copy_shell_to_cache (const char *shell, const std::string &new_name) +{ + 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 (); +} + +/* 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, NULL, + darwin_ptrace_him, darwin_pre_ptrace, copied_shell, darwin_execvp); } @@ -1864,11 +2039,7 @@ 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); @@ -1885,8 +2056,6 @@ darwin_nat_target::attach (const char *args, int from_tty) else printf_unfiltered (_("Attaching to %s\n"), target_pid_to_str (ptid_t (pid))); - - gdb_flush (gdb_stdout); } if (pid == 0 || ::kill (pid, 0) < 0) @@ -1924,7 +2093,6 @@ darwin_nat_target::attach (const char *args, int from_tty) void darwin_nat_target::detach (inferior *inf, int from_tty) { - pid_t pid = inferior_ptid.pid (); darwin_inferior *priv = get_darwin_inferior (inf); kern_return_t kret; int res; @@ -1993,11 +2161,6 @@ darwin_read_write_inferior (task_t task, CORE_ADDR addr, { kern_return_t kret; mach_vm_size_t res_length = 0; - 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=%s)\n"), task, core_addr_to_string (addr), pulongest (length)); @@ -2151,7 +2314,6 @@ darwin_read_dyld_info (task_t task, CORE_ADDR addr, gdb_byte *rdaddr, { 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 != 0 || length > sizeof (mach_vm_address_t))