/* Darwin support for GDB, the GNU debugger.
- Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
Contributed by AdaCore.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "top.h"
#include "regcache.h"
#include "event-top.h"
#include "inf-loop.h"
-#include "gdb_stat.h"
+#include <sys/stat.h>
#include "exceptions.h"
#include "inf-child.h"
#include "value.h"
#include "arch-utils.h"
#include "bfd.h"
+#include "bfd/mach-o.h"
#include <sys/ptrace.h>
#include <sys/signal.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
-#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/proc.h>
#include <libproc.h>
#include <sys/syscall.h>
+#include <spawn.h>
#include <mach/mach_error.h>
#include <mach/mach_vm.h>
#include <mach/port.h>
#include "darwin-nat.h"
+#include "common/filestuff.h"
/* Quick overview.
Darwin kernel is Mach + BSD derived kernel. Note that they share the
static void darwin_stop (ptid_t);
static void darwin_resume_to (struct target_ops *ops, ptid_t ptid, int step,
- enum target_signal signal);
+ enum gdb_signal signal);
static void darwin_resume (ptid_t ptid, int step,
- enum target_signal signal);
+ enum gdb_signal signal);
static ptid_t darwin_wait_to (struct target_ops *ops, ptid_t ptid,
struct target_waitstatus *status, int options);
static void darwin_mourn_inferior (struct target_ops *ops);
-static int darwin_lookup_task (char *args, task_t * ptask, int *ppid);
-
static void darwin_kill_inferior (struct target_ops *ops);
static void darwin_ptrace_me (void);
/* Port set. */
mach_port_t darwin_port_set;
-/* Page size. */
+/* Page size. */
static vm_size_t mach_page_size;
/* If Set, catch all mach exceptions (before they are converted to signals
#define PAGE_ROUND(x) PAGE_TRUNC((x) + mach_page_size - 1)
/* This controls output of inferior debugging. */
-static int darwin_debug_flag = 0;
+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. */
+static const unsigned char info_plist[]
+__attribute__ ((section ("__TEXT,__info_plist"),used)) =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
+ " \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ "<plist version=\"1.0\">\n"
+ "<dict>\n"
+ " <key>CFBundleIdentifier</key>\n"
+ " <string>org.gnu.gdb</string>\n"
+ " <key>CFBundleName</key>\n"
+ " <string>gdb</string>\n"
+ " <key>CFBundleVersion</key>\n"
+ " <string>1.0</string>\n"
+ " <key>SecTaskAccess</key>\n"
+ " <array>\n"
+ " <string>allowed</string>\n"
+ " <string>debug</string>\n"
+ " </array>\n"
+ "</dict>\n"
+ "</plist>\n";
static void
inferior_debug (int level, const char *fmt, ...)
if (func == NULL)
func = _("[UNKNOWN]");
- warning (_("Mach error at \"%s:%u\" in function \"%s\": %s (0x%lx)\n"),
+ warning (_("Mach error at \"%s:%u\" in function \"%s\": %s (0x%lx)"),
file, line, func, mach_error_string (ret), (unsigned long) ret);
}
}
}
+/* Set errno to zero, and then call ptrace with the given arguments.
+ If inferior debugging traces are on, then also print a debug
+ trace.
+
+ The returned value is the same as the value returned by ptrace,
+ except in the case where that value is -1 but errno is zero.
+ This case is documented to be a non-error situation, so we
+ return zero in that case. */
+
static int
darwin_ptrace (const char *name,
int request, int pid, PTRACE_TYPE_ARG3 arg3, int arg4)
{
int ret;
+ errno = 0;
ret = ptrace (request, pid, (caddr_t) 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,
thread_t old_id = old ? old->gdb_port : THREAD_NULL;
inferior_debug
- (12, _(" new_ix:%d/%d, old_ix:%d/%d, new_id:%x old_id:%x\n"),
+ (12, _(" new_ix:%d/%d, old_ix:%d/%d, new_id:0x%x old_id:0x%x\n"),
new_ix, new_nbr, old_ix, old_nbr, new_id, old_id);
if (old_id == new_id)
struct thread_info *tp;
struct private_thread_info *pti;
- pti = XZALLOC (struct private_thread_info);
+ pti = XCNEW (struct private_thread_info);
pti->gdb_port = new_id;
pti->msg_state = DARWIN_RUNNING;
old_ix++;
continue;
}
- gdb_assert (0);
+ gdb_assert_not_reached ("unexpected thread case");
}
if (darwin_inf->threads)
return -1;
*pthread = thread;
+ /* 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. */
- gdb_assert (thread->msg_state == DARWIN_RUNNING);
thread->event.header = *hdr;
thread->event.thread_port = thread_port;
thread->event.task_port = task_port;
thread->signaled = 1;
}
- /* Set single step. */
- inferior_debug (4, _("darwin_set_sstep (thread=%x, enable=%d)\n"),
- thread->gdb_port, step);
- darwin_set_sstep (thread->gdb_port, step);
- thread->single_step = step;
+ /* 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;
+ }
darwin_send_reply (inf, thread);
thread->msg_state = DARWIN_RUNNING;
}
static void
-darwin_resume (ptid_t ptid, int step, enum target_signal signal)
+darwin_resume (ptid_t ptid, int step, enum gdb_signal signal)
{
struct target_waitstatus status;
int pid;
(2, _("darwin_resume: pid=%d, tid=0x%x, step=%d, signal=%d\n"),
ptid_get_pid (ptid), ptid_get_tid (ptid), step, signal);
- if (signal == TARGET_SIGNAL_0)
+ if (signal == GDB_SIGNAL_0)
nsignal = 0;
else
- nsignal = target_signal_to_host (signal);
+ nsignal = gdb_signal_to_host (signal);
/* Don't try to single step all threads. */
if (step)
static void
darwin_resume_to (struct target_ops *ops, ptid_t ptid, int step,
- enum target_signal signal)
+ enum gdb_signal signal)
{
return darwin_resume (ptid, step, signal);
}
if (res < 0)
{
/* Should not happen... */
- printf_unfiltered (_("darwin_wait: ill-formatted message (id=%x)\n"),
- hdr->msgh_id);
+ printf_unfiltered
+ (_("darwin_wait: ill-formatted message (id=0x%x)\n"), hdr->msgh_id);
/* FIXME: send a failure reply? */
status->kind = TARGET_WAITKIND_SPURIOUS;
return minus_one_ptid;
status->kind = TARGET_WAITKIND_STOPPED;
thread->msg_state = DARWIN_MESSAGE;
- inferior_debug (4, _("darwin_wait: thread=%x, got %s\n"),
+ inferior_debug (4, _("darwin_wait: thread=0x%x, got %s\n"),
thread->gdb_port,
unparse_exception_type (thread->event.ex_type));
switch (thread->event.ex_type)
{
case EXC_BAD_ACCESS:
- status->value.sig = TARGET_EXC_BAD_ACCESS;
+ status->value.sig = GDB_EXC_BAD_ACCESS;
break;
case EXC_BAD_INSTRUCTION:
- status->value.sig = TARGET_EXC_BAD_INSTRUCTION;
+ status->value.sig = GDB_EXC_BAD_INSTRUCTION;
break;
case EXC_ARITHMETIC:
- status->value.sig = TARGET_EXC_ARITHMETIC;
+ status->value.sig = GDB_EXC_ARITHMETIC;
break;
case EXC_EMULATION:
- status->value.sig = TARGET_EXC_EMULATION;
+ status->value.sig = GDB_EXC_EMULATION;
break;
case EXC_SOFTWARE:
if (thread->event.ex_data[0] == EXC_SOFT_SIGNAL)
{
status->value.sig =
- target_signal_from_host (thread->event.ex_data[1]);
+ gdb_signal_from_host (thread->event.ex_data[1]);
inferior_debug (5, _(" (signal %d: %s)\n"),
thread->event.ex_data[1],
- target_signal_to_name (status->value.sig));
+ gdb_signal_to_name (status->value.sig));
/* If the thread is stopped because it has received a signal
that gdb has just sent, continue. */
}
}
else
- status->value.sig = TARGET_EXC_SOFTWARE;
+ status->value.sig = GDB_EXC_SOFTWARE;
break;
case EXC_BREAKPOINT:
/* Many internal GDB routines expect breakpoints to be reported
- as TARGET_SIGNAL_TRAP, and will report TARGET_EXC_BREAKPOINT
- as a spurious signal. */
- status->value.sig = TARGET_SIGNAL_TRAP;
+ as GDB_SIGNAL_TRAP, and will report GDB_EXC_BREAKPOINT
+ as a spurious signal. */
+ status->value.sig = GDB_SIGNAL_TRAP;
break;
default:
- status->value.sig = TARGET_SIGNAL_UNKNOWN;
+ status->value.sig = GDB_SIGNAL_UNKNOWN;
break;
}
status->value.sig = WTERMSIG (wstatus);
}
- inferior_debug (4, _("darwin_wait: pid=%d exit, status=%x\n"),
+ inferior_debug (4, _("darwin_wait: pid=%d exit, status=0x%x\n"),
res, wstatus);
/* Looks necessary on Leopard and harmless... */
}
}
- printf_unfiltered (_("Bad local-port: %x\n"), hdr->msgh_local_port);
+ printf_unfiltered (_("Bad local-port: 0x%x\n"), hdr->msgh_local_port);
status->kind = TARGET_WAITKIND_SPURIOUS;
return minus_one_ptid;
}
static int
cancel_breakpoint (ptid_t ptid)
{
- /* Arrange for a breakpoint to be hit again later. We will handle
+ /* Arrange for a breakpoint to be hit again later. We will handle
the current event, eventually we will resume this thread, and this
breakpoint will trap again.
struct gdbarch *gdbarch = get_regcache_arch (regcache);
CORE_ADDR pc;
- pc = regcache_read_pc (regcache) - gdbarch_decr_pc_after_break (gdbarch);
- if (breakpoint_inserted_here_p (pc))
+ pc = regcache_read_pc (regcache) - target_decr_pc_after_break (gdbarch);
+ if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
{
- inferior_debug (4, "cancel_breakpoint for thread %x\n",
+ inferior_debug (4, "cancel_breakpoint for thread 0x%x\n",
ptid_get_tid (ptid));
/* Back up the PC if necessary. */
- if (gdbarch_decr_pc_after_break (gdbarch))
+ if (target_decr_pc_after_break (gdbarch))
regcache_write_pc (regcache, pc);
return 1;
darwin_inf_fake_stop = NULL;
status->kind = TARGET_WAITKIND_STOPPED;
- status->value.sig = TARGET_SIGNAL_TRAP;
+ status->value.sig = GDB_SIGNAL_TRAP;
thread = VEC_index (darwin_thread_t, inf->private->threads, 0);
thread->msg_state = DARWIN_STOPPED;
return ptid_build (inf->pid, 0, thread->gdb_port);
if (kret != MACH_MSG_SUCCESS)
{
- inferior_debug (5, _("mach_msg: ret=%x\n"), kret);
+ inferior_debug (5, _("mach_msg: ret=0x%x\n"), kret);
status->kind = TARGET_WAITKIND_SPURIOUS;
return minus_one_ptid;
}
if (kret != MACH_MSG_SUCCESS)
{
inferior_debug
- (5, _("darwin_wait: mach_msg(pending) ret=%x\n"), kret);
+ (5, _("darwin_wait: mach_msg(pending) ret=0x%x\n"), kret);
break;
}
}
else
inferior_debug
- (3, _("darwin_wait: thread %x hit a non-gdb breakpoint\n"),
+ (3, _("darwin_wait: thread 0x%x hit a non-gdb breakpoint\n"),
thread->gdb_port);
}
else
kret = mach_port_move_member (gdb_task,
inf->private->notify_port, MACH_PORT_NULL);
- gdb_assert (kret == KERN_SUCCESS);
+ MACH_CHECK_ERROR (kret);
kret = mach_port_request_notification (gdb_task, inf->private->task,
MACH_NOTIFY_DEAD_NAME, 0,
MACH_MSG_TYPE_MAKE_SEND_ONCE,
&prev);
/* This can fail if the task is dead. */
- inferior_debug (4, "task=%x, prev=%x, notify_port=%x\n",
+ inferior_debug (4, "task=0x%x, prev=0x%x, notify_port=0x%x\n",
inf->private->task, prev, inf->private->notify_port);
if (kret == KERN_SUCCESS)
res = kill (inf->pid, SIGSTOP);
if (res != 0)
- warning (_("cannot kill: %s\n"), safe_strerror (errno));
+ warning (_("cannot kill: %s"), safe_strerror (errno));
/* Wait until the process is really stopped. */
while (1)
{
ptid = darwin_wait (inferior_ptid, &wstatus);
if (wstatus.kind == TARGET_WAITKIND_STOPPED
- && wstatus.value.sig == TARGET_SIGNAL_STOP)
+ && wstatus.value.sig == GDB_SIGNAL_STOP)
break;
}
}
gdb_assert (inf != NULL);
- if (!inf->private->no_ptrace)
- {
- darwin_stop_inferior (inf);
-
- res = PTRACE (PT_KILL, inf->pid, 0, 0);
- gdb_assert (res == 0);
+ kret = darwin_restore_exception_ports (inf->private);
+ MACH_CHECK_ERROR (kret);
- darwin_reply_to_all_pending_messages (inf);
+ darwin_reply_to_all_pending_messages (inf);
- darwin_resume_inferior (inf);
+ res = kill (inf->pid, 9);
- ptid = darwin_wait (inferior_ptid, &wstatus);
- }
- else
+ if (res == 0)
{
- kret = darwin_restore_exception_ports (inf->private);
- MACH_CHECK_ERROR (kret);
-
- darwin_reply_to_all_pending_messages (inf);
-
darwin_resume_inferior (inf);
-
- res = kill (inf->pid, 9);
-
+
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 ();
}
mach_port_t prev_not;
exception_mask_t mask;
- inf->private = XZALLOC (darwin_inferior);
+ inf->private = XCNEW (darwin_inferior);
kret = task_for_pid (gdb_task, inf->pid, &inf->private->task);
if (kret != KERN_SUCCESS)
}
error (_("Unable to find Mach task port for process-id %d: %s (0x%lx).\n"
- " (please check gdb is setgid procmod)"),
+ " (please check gdb is codesigned - see taskgated(8))"),
inf->pid, mach_error_string (kret), (unsigned long) kret);
}
/* Create a port to get exceptions. */
kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_RECEIVE,
&darwin_ex_port);
- gdb_assert (kret == KERN_SUCCESS);
+ 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);
- gdb_assert (kret == KERN_SUCCESS);
+ 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);
- gdb_assert (kret == KERN_SUCCESS);
+ if (kret != KERN_SUCCESS)
+ error (_("Unable to create port set, mach_port_allocate "
+ "returned: %d"),
+ kret);
kret = mach_port_move_member (gdb_task, darwin_ex_port, darwin_port_set);
- gdb_assert (kret == KERN_SUCCESS);
+ 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,
&inf->private->notify_port);
- gdb_assert (kret == KERN_SUCCESS);
+ 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);
- gdb_assert (kret == KERN_SUCCESS);
+ if (kret != KERN_SUCCESS)
+ error (_("Unable to move notification port into new port set, "
+ "mach_port_move_member\n"
+ "returned: %d"),
+ kret);
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);
- gdb_assert (kret == KERN_SUCCESS);
- gdb_assert (prev_not == MACH_PORT_NULL);
+ 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."));
+ }
kret = darwin_save_exception_ports (inf->private);
- gdb_assert (kret == KERN_SUCCESS);
+ 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_SOFTWARE | EXC_MASK_BREAKPOINT;
kret = task_set_exception_ports (inf->private->task, mask, darwin_ex_port,
EXCEPTION_DEFAULT, THREAD_STATE_NONE);
- gdb_assert (kret == KERN_SUCCESS);
+ if (kret != KERN_SUCCESS)
+ error (_("Unable to set exception ports, task_set_exception_ports"
+ "returned: %d"),
+ kret);
push_target (darwin_ops);
}
/* Wait until gdb is ready. */
res = read (ptrace_fds[0], &c, 1);
- gdb_assert (res == 0);
+ if (res != 0)
+ error (_("unable to read from pipe, read returned: %d"), res);
close (ptrace_fds[0]);
/* Get rid of privileges. */
ptrace_fds[1] = -1;
error (_("unable to create a pipe: %s"), safe_strerror (errno));
}
+
+ mark_fd_no_cloexec (ptrace_fds[0]);
+ mark_fd_no_cloexec (ptrace_fds[1]);
}
static void
close (ptrace_fds[0]);
close (ptrace_fds[1]);
+ unmark_fd_no_cloexec (ptrace_fds[0]);
+ unmark_fd_no_cloexec (ptrace_fds[1]);
+
darwin_init_thread_list (inf);
startup_inferior (START_INFERIOR_TRAPS_EXPECTED);
}
+static void
+darwin_execvp (const char *file, char * const argv[], char * const env[])
+{
+ posix_spawnattr_t attr;
+ short ps_flags = 0;
+ int res;
+
+ res = posix_spawnattr_init (&attr);
+ if (res != 0)
+ {
+ fprintf_unfiltered
+ (gdb_stderr, "Cannot initialize attribute for posix_spawn\n");
+ return;
+ }
+
+ /* Do like execve: replace the image. */
+ ps_flags = POSIX_SPAWN_SETEXEC;
+
+ /* Disable ASLR. The constant doesn't look to be available outside the
+ kernel include files. */
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+#endif
+ ps_flags |= _POSIX_SPAWN_DISABLE_ASLR;
+ res = posix_spawnattr_setflags (&attr, ps_flags);
+ if (res != 0)
+ {
+ fprintf_unfiltered (gdb_stderr, "Cannot set posix_spawn flags\n");
+ return;
+ }
+
+ posix_spawnp (NULL, argv[0], NULL, &attr, argv, env);
+}
+
static void
darwin_create_inferior (struct target_ops *ops, char *exec_file,
char *allargs, char **env, int from_tty)
{
/* Do the hard work. */
fork_inferior (exec_file, allargs, env, darwin_ptrace_me, darwin_ptrace_him,
- darwin_pre_ptrace, NULL);
+ darwin_pre_ptrace, NULL, darwin_execvp);
/* Return now in case of error. */
if (ptid_equal (inferior_ptid, null_ptid))
}
\f
+/* Set things up such that the next call to darwin_wait will immediately
+ return a fake stop event for inferior INF.
+
+ This assumes that the inferior's thread list has been initialized,
+ as it will suspend the inferior's first thread. */
+
+static void
+darwin_setup_fake_stop_event (struct inferior *inf)
+{
+ darwin_thread_t *thread;
+ kern_return_t kret;
+
+ gdb_assert (darwin_inf_fake_stop == NULL);
+ darwin_inf_fake_stop = inf;
+
+ /* When detecting a fake pending stop event, darwin_wait returns
+ an event saying that the first thread is in a DARWIN_STOPPED
+ state. To make that accurate, we need to suspend that thread
+ 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);
+ 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
struct inferior *inf;
kern_return_t kret;
- if (!args)
- error_no_arg (_("process-id to attach"));
+ pid = parse_pid_to_attach (args);
- pid = atoi (args);
-
- if (pid == getpid ()) /* Trying to masturbate? */
+ if (pid == getpid ()) /* Trying to masturbate? */
error (_("I refuse to debug myself!"));
if (from_tty)
pid, safe_strerror (errno), errno);
inferior_ptid = pid_to_ptid (pid);
- inf = add_inferior (pid);
+ inf = current_inferior ();
+ inferior_appeared (inf, pid);
inf->attach_flag = 1;
+
/* Always add a main thread. */
add_thread_silent (inferior_ptid);
darwin_check_osabi (inf->private, ptid_get_tid (inferior_ptid));
- gdb_assert (darwin_inf_fake_stop == NULL);
- darwin_inf_fake_stop = inf;
+ darwin_setup_fake_stop_event (inf);
+
inf->private->no_ptrace = 1;
}
previously attached. It *might* work if the program was
started via fork. */
static void
-darwin_detach (struct target_ops *ops, char *args, int from_tty)
+darwin_detach (struct target_ops *ops, const char *args, int from_tty)
{
pid_t pid = ptid_get_pid (inferior_ptid);
struct inferior *inf = current_inferior ();
darwin_reply_to_all_pending_messages (inf);
- darwin_resume_inferior (inf);
+ /* 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)
+ darwin_resume_inferior (inf);
darwin_mourn_inferior (ops);
}
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 / writen otherwise. */
static int
darwin_read_write_inferior (task_t task, CORE_ADDR addr,
- char *rdaddr, const char *wraddr, int length)
+ gdb_byte *rdaddr, const gdb_byte *wraddr,
+ 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;
- int copy_count;
+ 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=%x, %s, len=%d)\n"),
- task, core_addr_to_string (addr), length);
+ inferior_debug (8, _("darwin_read_write_inferior(task=0x%x, %s, len=%s)\n"),
+ task, core_addr_to_string (addr), pulongest (length));
- /* Get memory from inferior with page aligned addresses */
+ /* Get memory from inferior with page aligned addresses. */
kret = mach_vm_read (task, low_address, aligned_length,
&copied, ©_count);
if (kret != KERN_SUCCESS)
core_addr_to_string (region_start),
core_addr_to_string (region_length));
- /* Check for holes in memory */
+ /* Check for holes in memory. */
if (region_start > region_address)
{
- warning (_("No memory at %s (vs %s+0x%x). Nothing written"),
+ warning (_("No memory at %s (vs %s+0x%x). Nothing written"),
core_addr_to_string (region_address),
core_addr_to_string (region_start),
(unsigned)region_length);
return length;
}
-\f
-/* Return 0 on failure, number of bytes handled otherwise. TARGET
- is ignored. */
-static int
-darwin_xfer_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len, int write,
- struct mem_attrib *attrib, struct target_ops *target)
-{
- struct inferior *inf = current_inferior ();
- task_t task = inf->private->task;
+/* Read LENGTH bytes at offset ADDR of task_dyld_info for TASK, and copy them
+ to RDADDR.
+ Return 0 on failure; number of bytes read / written otherwise. */
- if (task == MACH_PORT_NULL)
- return 0;
+#ifdef TASK_DYLD_INFO_COUNT
+/* This is not available in Darwin 9. */
+static enum target_xfer_status
+darwin_read_dyld_info (task_t task, CORE_ADDR addr, gdb_byte *rdaddr,
+ 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;
- inferior_debug (8, _("darwin_xfer_memory(%s, %d, %c)\n"),
- core_addr_to_string (memaddr), len, write ? 'w' : 'r');
+ if (addr >= sz)
+ return TARGET_XFER_EOF;
- if (write)
- return darwin_read_write_inferior (task, memaddr, NULL, myaddr, len);
- else
- return darwin_read_write_inferior (task, memaddr, myaddr, NULL, len);
+ kret = task_info (task, TASK_DYLD_INFO, (task_info_t) &task_dyld_info, &count);
+ MACH_CHECK_ERROR (kret);
+ if (kret != KERN_SUCCESS)
+ return TARGET_XFER_E_IO;
+ /* Truncate. */
+ if (addr + length > sz)
+ length = sz - addr;
+ memcpy (rdaddr, (char *)&task_dyld_info + addr, length);
+ *xfered_len = (ULONGEST) length;
+ return TARGET_XFER_OK;
}
+#endif
+
+\f
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)
+ ULONGEST offset, ULONGEST len, ULONGEST *xfered_len)
{
struct inferior *inf = current_inferior ();
inferior_debug
- (8, _("darwin_xfer_partial(%s, %d, rbuf=%p, wbuf=%p) pid=%u\n"),
- core_addr_to_string (offset), (int)len, readbuf, writebuf, inf->pid);
+ (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);
- if (object != TARGET_OBJECT_MEMORY)
- return -1;
+ switch (object)
+ {
+ case TARGET_OBJECT_MEMORY:
+ {
+ int l = darwin_read_write_inferior (inf->private->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 TARGET_XFER_E_IO;
+ }
+ return darwin_read_dyld_info (inf->private->task, offset, readbuf, len,
+ xfered_len);
+#endif
+ default:
+ return TARGET_XFER_E_IO;
+ }
- return darwin_read_write_inferior (inf->private->task, offset,
- readbuf, writebuf, len);
}
static void
char *path;
int res;
- path = xmalloc (MAXPATHLEN);
+ path = xmalloc (PATH_MAX);
make_cleanup (xfree, path);
- res = proc_pidinfo (pid, PROC_PIDPATHINFO, 0, path, MAXPATHLEN);
+ res = proc_pidinfo (pid, PROC_PIDPATHINFO, 0, path, PATH_MAX);
if (res >= 0)
return path;
else
return 1;
}
+/* -Wmissing-prototypes */
+extern initialize_file_ftype _initialize_darwin_inferior;
+
void
_initialize_darwin_inferior (void)
{
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->deprecated_xfer_memory = darwin_xfer_memory;
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;
inferior_debug (2, _("GDB task: 0x%lx, pid: %d\n"), mach_task_self (),
getpid ());
- add_setshow_zinteger_cmd ("darwin", class_obscure,
- &darwin_debug_flag, _("\
+ add_setshow_zuinteger_cmd ("darwin", class_obscure,
+ &darwin_debug_flag, _("\
Set if printing inferior communication debugging statements."), _("\
Show if printing inferior communication debugging statements."), NULL,
- NULL, NULL,
- &setdebuglist, &showdebuglist);
+ NULL, NULL,
+ &setdebuglist, &showdebuglist);
add_setshow_boolean_cmd ("mach-exceptions", class_support,
&enable_mach_exceptions, _("\