/* 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.
#include "arch-utils.h"
#include "bfd.h"
#include "bfd/mach-o.h"
+#include "gdbarch.h"
+#include <copyfile.h>
#include <sys/ptrace.h>
#include <sys/signal.h>
#include <setjmp.h>
#include <mach/port.h>
#include "darwin-nat.h"
-#include "common/filestuff.h"
+#include "filenames.h"
+#include "gdbsupport/filestuff.h"
+#include "gdbsupport/gdb_unlinker.h"
+#include "gdbsupport/pathstuff.h"
+#include "gdbsupport/scoped_fd.h"
#include "nat/fork-inferior.h"
/* Quick overview.
#define PTRACE(CMD, PID, ADDR, SIG) \
darwin_ptrace(#CMD, CMD, (PID), (ADDR), (SIG))
-static void darwin_interrupt (struct target_ops *self, 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,
- const char *exec_file,
- const std::string &allargs,
- char **env, int from_tty);
-
-static void darwin_files_info (struct target_ops *ops);
-
-static const 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);
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;
-
/* Task identifier of gdb. */
static task_t gdb_task;
/* If Set, catch all mach exceptions (before they are converted to signals
by the kernel). */
-static int enable_mach_exceptions;
+static bool 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)
darwin_inferior *darwin_inf = get_darwin_inferior (inf);
std::vector<darwin_thread_t *> 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);
pti->msg_state = DARWIN_RUNNING;
/* Add the new thread. */
- add_thread_with_info (ptid_build (inf->pid, 0, new_id), pti);
+ 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++;
{
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
{
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;
}
{
darwin_inferior *priv = get_darwin_inferior (inf);
- if (!priv->suspended)
+ if (priv != nullptr && !priv->suspended)
{
kern_return_t kret;
{
darwin_inferior *priv = get_darwin_inferior (inf);
- if (priv->suspended)
+ if (priv != nullptr && priv->suspended)
{
kern_return_t kret;
/* 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
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)
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;
}
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);
&& 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);
}
{
/* 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;
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;
{
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
}
}
-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%lx, step=%d, signal=%d\n"),
- ptid_get_pid (ptid), ptid_get_tid (ptid), step, signal);
+ ptid.pid (), ptid.tid (), step, signal);
if (signal == GDB_SIGNAL_0)
nsignal = 0;
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;
else
{
struct inferior *inf = find_inferior_ptid (ptid);
- long tid = ptid_get_tid (ptid);
+ long tid = ptid.tid ();
/* Stop the inferior (should be useless). */
darwin_suspend_inferior (inf);
}
}
-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,
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)
{
if (!priv->no_ptrace)
{
- pid_t res;
+ pid_t res_pid;
int wstatus;
- res = wait4 (inf->pid, &wstatus, 0, NULL);
- if (res < 0 || res != inf->pid)
+ res_pid = wait4 (inf->pid, &wstatus, 0, NULL);
+ if (res_pid < 0 || res_pid != inf->pid)
{
printf_unfiltered (_("wait4: res=%d: %s\n"),
- res, safe_strerror (errno));
+ res_pid, safe_strerror (errno));
status->kind = TARGET_WAITKIND_IGNORE;
return minus_one_ptid;
}
}
inferior_debug (4, _("darwin_wait: pid=%d exit, status=0x%x\n"),
- res, wstatus);
+ res_pid, wstatus);
/* Looks necessary on Leopard and harmless... */
wait4 (inf->pid, &wstatus, 0, NULL);
- inferior_ptid = ptid_build (inf->pid, 0, 0);
+ inferior_ptid = ptid_t (inf->pid, 0, 0);
return inferior_ptid;
}
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);
+ return ptid_t (inf->pid, 0, 0);
}
}
}
if (breakpoint_inserted_here_p (regcache->aspace (), pc))
{
inferior_debug (4, "cancel_breakpoint for thread 0x%lx\n",
- (unsigned long) ptid_get_tid (ptid));
+ (unsigned long) ptid.tid ());
/* Back up the PC if necessary. */
if (gdbarch_decr_pc_after_break (gdbarch))
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)
status->value.sig = GDB_SIGNAL_TRAP;
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
darwin_dump_message (hdr, darwin_debug_flag > 11);
res = darwin_decode_message (hdr, &thread, &inf, status);
- if (ptid_equal (res, minus_one_ptid))
+ if (res == minus_one_ptid)
continue;
/* Early return in case an inferior has exited. */
&& 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);
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_interrupt (struct target_ops *self, 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 (!priv->no_ptrace);
- kill (inf->pid, SIGINT);
+ ::kill (inf->pid, SIGINT);
}
/* Deallocate threads port and vector. */
priv->threads.clear ();
}
-static void
-darwin_mourn_inferior (struct target_ops *ops)
+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;
/* Deallocate threads. */
darwin_deallocate_threads (inf);
inf->priv = NULL;
- inf_child_mourn_inferior (ops);
+ inf_child_target::mourn_inferior ();
}
static void
{
struct target_waitstatus wstatus;
ptid_t ptid;
- kern_return_t kret;
- int status;
int res;
darwin_inferior *priv = get_darwin_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);
kret);
}
-static void
-darwin_kill_inferior (struct target_ops *ops)
+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);
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). */
+ for (darwin_thread_t *thread : priv->threads)
+ thread->signaled = 1;
+
darwin_resume_inferior (inf);
ptid = darwin_wait (inferior_ptid, &wstatus);
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);
- kret = task_for_pid (gdb_task, inf->pid, &priv->task);
- if (kret != KERN_SUCCESS)
+ try
{
- int status;
-
- if (!inf->attach_flag)
+ 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"),
- priv->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,
- &priv->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,
- priv->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);
- darwin_setup_request_notification (inf);
+ darwin_setup_exceptions (inf);
+ }
+ catch (const gdb_exception &ex)
+ {
+ exit_inferior (inf);
+ inferior_ptid = null_ptid;
- darwin_setup_exceptions (inf);
+ throw;
+ }
+ target_ops *darwin_ops = get_native_target ();
if (!target_is_pushed (darwin_ops))
push_target (darwin_ops);
}
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
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);
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,
- const char *exec_file,
- const std::string &allargs,
- char **env, int from_tty)
+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 (const gdb_exception_error &ex)
+ {
+ 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.what ());
+ return false;
+ }
+
+ 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<scoped_restore_tmpl<int>> 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);
}
\f
/* 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, const 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);
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;
darwin_inferior *priv = get_darwin_inferior (inf);
- darwin_check_osabi (priv, ptid_get_tid (inferior_ptid));
+ darwin_check_osabi (priv, inferior_ptid.tid ());
darwin_setup_fake_stop_event (inf);
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, inferior *inf, int from_tty)
+
+void
+darwin_nat_target::detach (inferior *inf, int from_tty)
{
- pid_t pid = ptid_get_pid (inferior_ptid);
darwin_inferior *priv = get_darwin_inferior (inf);
kern_return_t kret;
int res;
if (priv->no_ptrace)
darwin_resume_inferior (inf);
- darwin_mourn_inferior (ops);
-}
-
-static void
-darwin_files_info (struct target_ops *ops)
-{
+ mourn_inferior ();
}
-static const 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
copy it to RDADDR in gdb's address space.
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. */
+ Return 0 on failure; number of bytes read / written otherwise. */
static int
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));
{
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))
\f
-static enum target_xfer_status
-darwin_xfer_partial (struct target_ops *ops,
- enum target_object object, const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, ULONGEST len, ULONGEST *xfered_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);
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);
}
}
-static char *
-darwin_pid_to_exec_file (struct target_ops *self, int pid)
+char *
+darwin_nat_target::pid_to_exec_file (int pid)
{
static char path[PATH_MAX];
int res;
return NULL;
}
-static ptid_t
-darwin_get_ada_task_ptid (struct target_ops *self, long lwp, long thread)
+ptid_t
+darwin_nat_target::get_ada_task_ptid (long lwp, long thread)
{
struct inferior *inf = current_inferior ();
darwin_inferior *priv = get_darwin_inferior (inf);
for (darwin_thread_t *t : priv->threads)
{
if (t->inf_port == lwp)
- return ptid_build (ptid_get_pid (inferior_ptid), 0, t->gdb_port);
+ return ptid_t (inferior_ptid.pid (), 0, t->gdb_port);
}
/* Maybe the port was never extract. Do it now. */
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 (struct target_ops *self)
+bool
+darwin_nat_target::supports_multi_process ()
{
- return 1;
+ return true;
}
void
-_initialize_darwin_inferior (void)
+_initialize_darwin_nat ()
{
kern_return_t kret;
MACH_CHECK_ERROR (kret);
}
- darwin_ops = inf_child_target ();
-
- 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_interrupt = darwin_interrupt;
- 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"),
(unsigned long) mach_task_self (), getpid ());