[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 2/2] Port gdbserver to GNU/Hurd
From: |
Yue Lu |
Subject: |
[PATCH 2/2] Port gdbserver to GNU/Hurd |
Date: |
Tue, 3 Sep 2013 16:03:02 +0800 |
Hi all,
2013-09-03 Yue Lu <hacklu.newborn@gmail.com>
* configure.ac (host_makefile_frag): New rule for GNU/Hurd to load
i386gnu.mh.
* configure.srv (srv_tgtobj): Add gnu-low.o gnu-i386-low.o for
GNU/Hurd.
(srv_regobj): Add $(srv_i386_regobj) for GNU/Hurd.
(srv_xmlfiles): Add $(srv_i386_xmlfiles) for GNU/Hurd.
* configure: Regenerate.
* Makefile.in (OBS): Add $(NATDEPFILES).
(generated_files): Add $(NAT_GENERATED_FILES).
(@host_makefile_frag@): New rule.
(MIG): New tools.
(AWK): New tools.
* exc_request.defs: New file. copy from [gdb]/gdb/exc_request.defs.
* gnu-i386-low.c: New file. Modified from [gdb]/gdb/i386gnu-nat.c.
* gnu-low.c: New file. The main part for gdbserver on GNU/Hurd. Most
of code are borrowed from [gdb]/gdb/gnu-nat.c.
* gnu-low.h: New file. Most of code are borrowed from
[gdb]/gdb/gnu-nat.h.
* hostio.c: Add macro define PATH_MAX 512.
* i386gnu.mh: New file. copy from
[gdb]/gdb/config/i386/i386gnu.mh and delete
unused part.
* msg.defs: New file. copy from [gdb]/gdb/msg.defs.
* msg_reply.defs: New file. copy from [gdb]/gdb/msg_reply.defs.
* notify.defs: New file. copy from [gdb]/gdb/notify.defs.
* process_reply.defs: New file. copy from [gdb]/gdb/process_reply.defs.
* reply_mid_hack.awk: New file. copy from [gdb]/gdb/reply_mid_hack.awk.
* server.h: Add typedef long CORE_ADDR;
* utils.c (host_address_to_string): New functions, copy from
[gdb]/gdb/utils.c.
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index e8470a8..3250b5a 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -50,6 +50,8 @@ INSTALL_DATA = @INSTALL_DATA@
RANLIB = @RANLIB@
CC = @CC@
+MIG = @MIG@
+AWK = @AWK@
# Dependency tracking information.
DEPMODE = @CCDEPMODE@
@@ -172,7 +174,7 @@ OBS = agent.o ax.o inferiors.o regcache.o
remote-utils.o server.o signals.o targ
xml-utils.o common-utils.o ptid.o buffer.o format.o filestuff.o \
dll.o notif.o tdesc.o \
$(XML_BUILTIN) \
- $(DEPFILES) $(LIBOBJS)
+ $(DEPFILES) $(LIBOBJS) $(NATDEPFILES)
GDBREPLAY_OBS = gdbreplay.o version.o
GDBSERVER_LIBS = @GDBSERVER_LIBS@
XM_CLIBS = @LIBS@
@@ -195,6 +197,10 @@ CLEANDIRS = $(SUBDIRS)
# The format here is for the `case' shell command.
REQUIRED_SUBDIRS = $(GNULIB_BUILDDIR)
+# Host-dependent makefile fragment comes in here.
+@host_makefile_frag@
+# End of host-dependent makefile fragment
+
FLAGS_TO_PASS = \
"prefix=$(prefix)" \
"exec_prefix=$(exec_prefix)" \
@@ -228,7 +234,7 @@ FLAGS_TO_PASS = \
"RUNTESTFLAGS=$(RUNTESTFLAGS)"
# All generated files which can be included by another file.
-generated_files = config.h $(GNULIB_H)
+generated_files = config.h $(GNULIB_H) $(NAT_GENERATED_FILES)
.c.o:
$(COMPILE) $<
diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac
index b9928d7..6c0ed20 100644
--- a/gdb/gdbserver/configure.ac
+++ b/gdb/gdbserver/configure.ac
@@ -456,6 +456,31 @@ if $want_ipa ; then
fi
fi
+frags=
+case $host_os in
+ gnu*)
+ #Needed for GNU Hurd hosts.
+ AC_PROG_AWK
+ AC_CHECK_TOOL(MIG, mig)
+ if test x"$MIG" = x; then
+ AC_MSG_ERROR([MIG not found but required for $host hosts])
+ fi
+ host_makefile_frag=${srcdir}/i386gnu.mh
+ if test ! -f ${host_makefile_frag}; then
+ AC_MSG_ERROR("*** Gdb does not support native target ${host}")
+ fi
+ frags="$frags $host_makefile_frag"
+ ;;
+ *)
+ host_makefile_frag=/dev/null
+ ;;
+esac
+
+echo "host_makefile_frag=$host_makefile_frag"
+ echo "frags=$frags"
+AC_SUBST_FILE(host_makefile_frag)
+AC_SUBST(frags)
+
AC_SUBST(GDBSERVER_DEPFILES)
AC_SUBST(GDBSERVER_LIBS)
AC_SUBST(srv_xmlbuiltin)
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index 879d0de..e2a6a08 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -118,6 +118,11 @@ case "${target}" in
srv_linux_btrace=yes
ipa_obj="${ipa_i386_linux_regobj} linux-i386-ipa.o"
;;
+ i[34567]86-*-gnu*) srv_regobj="$srv_i386_regobj"
+ srv_tgtobj="gnu-low.o gnu-i386-low.o"
+ srv_xmlfiles="$srv_i386_xmlfiles"
+ ;;
+
i[34567]86-*-lynxos*) srv_regobj="i386.o"
srv_tgtobj="lynx-low.o lynx-i386-low.o"
srv_xmlfiles="i386/i386.xml"
diff --git a/gdb/gdbserver/exc_request.defs b/gdb/gdbserver/exc_request.defs
new file mode 100644
index 0000000..9b5ed2e
--- /dev/null
+++ b/gdb/gdbserver/exc_request.defs
@@ -0,0 +1,51 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1991,1990,1989,1988,1987 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/*
+ * Abstract:
+ * MiG definitions file for Mach exception interface (request half).
+ */
+
+subsystem exc 2400;
+
+#include <mach/std_types.defs>
+
+#ifdef USERPREFIX
+userprefix USERPREFIX;
+#endif
+
+#ifdef SERVERPREFIX
+serverprefix SERVERPREFIX;
+#endif
+
+simpleroutine exception_raise_request (
+ exception_port : mach_port_t;
+ replyport reply : mach_port_send_once_t;
+ thread : mach_port_t;
+ task : mach_port_t;
+ exception : integer_t;
+ code : integer_t;
+ subcode : integer_t);
diff --git a/gdb/gdbserver/gnu-i386-low.c b/gdb/gdbserver/gnu-i386-low.c
new file mode 100644
index 0000000..643b0e9
--- /dev/null
+++ b/gdb/gdbserver/gnu-i386-low.c
@@ -0,0 +1,296 @@
+/* Low level interface to i386 running the GNU Hurd.
+
+ Copyright (C) 1992-2013 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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/>. */
+#include "server.h"
+#include "target.h"
+
+#include "gnu-low.h"
+
+#include <limits.h>
+#include <sys/ptrace.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include "gdb_wait.h"
+#include <signal.h>
+
+#include <mach.h>
+#include <mach_error.h>
+#include <mach/message.h>
+#include <mach/exception.h>
+
+#define I386_NUM_GREGS 16
+
+/* Offset to the thread_state_t location where REG is stored. */
+#define REG_OFFSET(reg) offsetof (struct i386_thread_state, reg)
+
+/* At REG_OFFSET[N] is the offset to the thread_state_t location where
+ the GDB register N is stored. */
+static int reg_offset[] = {
+ REG_OFFSET (eax), REG_OFFSET (ecx), REG_OFFSET (edx), REG_OFFSET (ebx),
+ REG_OFFSET (uesp), REG_OFFSET (ebp), REG_OFFSET (esi), REG_OFFSET (edi),
+ REG_OFFSET (eip), REG_OFFSET (efl), REG_OFFSET (cs), REG_OFFSET (ss),
+ REG_OFFSET (ds), REG_OFFSET (es), REG_OFFSET (fs), REG_OFFSET (gs)
+};
+
+/* Offset to the greg_t location where REG is stored. */
+#define CREG_OFFSET(reg) (REG_##reg * 4)
+
+/* At CREG_OFFSET[N] is the offset to the greg_t location where
+ the GDB register N is stored. */
+static int creg_offset[] = {
+ CREG_OFFSET (EAX), CREG_OFFSET (ECX), CREG_OFFSET (EDX), CREG_OFFSET (EBX),
+ CREG_OFFSET (UESP), CREG_OFFSET (EBP), CREG_OFFSET (ESI), CREG_OFFSET (EDI),
+ CREG_OFFSET (EIP), CREG_OFFSET (EFL), CREG_OFFSET (CS), CREG_OFFSET (SS),
+ CREG_OFFSET (DS), CREG_OFFSET (ES), CREG_OFFSET (FS), CREG_OFFSET (GS)
+};
+
+#define REG_ADDR(state, regnum) ((char *)(state) + reg_offset[regnum])
+#define CREG_ADDR(state, regnum) ((const char *)(state) + creg_offset[regnum])
+
+
+/* Get the whole floating-point state of THREAD and record the values
+ of the corresponding (pseudo) registers. */
+
+static void
+fetch_fpregs (struct regcache *regcache, struct proc *thread)
+{
+ gnu_debug ("fetch_fpregs() not support now\n");
+#if 0
+ mach_msg_type_number_t count = i386_FLOAT_STATE_COUNT;
+ struct i386_float_state state;
+ error_t err;
+
+ err = thread_get_state (thread->port, i386_FLOAT_STATE,
+ (thread_state_t) & state, &count);
+ if (err)
+ {
+ warning (_("Couldn't fetch floating-point state from %s"),
+ proc_string (thread));
+ return;
+ }
+
+ if (!state.initialized)
+ {
+ /* The floating-point state isn't initialized. */
+ i387_supply_fsave (regcache, -1, NULL);
+ }
+ else
+ {
+ /* Supply the floating-point registers. */
+ i387_supply_fsave (regcache, -1, state.hw_state);
+ }
+#endif
+}
+
+extern struct inf *gnu_current_inf;
+extern ptid_t inferior_ptid;
+/* Fetch register REGNO, or all regs if REGNO is -1. */
+void
+gnu_fetch_registers_1 (struct target_ops *ops,
+ struct regcache *regcache, int regno)
+{
+ struct proc *thread;
+
+ /* Make sure we know about new threads. */
+ inf_update_procs (gnu_current_inf);
+
+ thread = inf_tid_to_thread (gnu_current_inf, TIDGET (inferior_ptid));
+ if (!thread)
+ error (_
+ ("[gnu_fetch_registers_1]Can't fetch registers from thread %s: No
such thread"),
+ target_pid_to_str (inferior_ptid));
+
+ if (regno < I386_NUM_GREGS || regno == -1)
+ {
+ thread_state_t state;
+
+ /* This does the dirty work for us. */
+ state = proc_get_state (thread, 0);
+ if (!state)
+ {
+ warning (_("Couldn't fetch registers from %s"),
+ proc_string (thread));
+
+ return;
+ }
+
+ if (regno == -1)
+ {
+ int i;
+
+ proc_debug (thread, "fetching all register");
+
+ for (i = 0; i < I386_NUM_GREGS; i++)
+ /*regcache_raw_supply (regcache, i, REG_ADDR (state, i)); */
+ supply_register (regcache, i, REG_ADDR (state, i));
+ thread->fetched_regs = ~0;
+ }
+ else
+ {
+ /*proc_debug (thread, "fetching register %s", */
+ /*gdbarch_register_name (get_regcache_arch (regcache), */
+ /*regno)); */
+
+ /*regcache_raw_supply (regcache, regno,REG_ADDR (state, regno)); */
+ supply_register (regcache, regno, REG_ADDR (state, regno));
+ thread->fetched_regs |= (1 << regno);
+ }
+ }
+
+ if (regno >= I386_NUM_GREGS || regno == -1)
+ {
+ proc_debug (thread, "fetching floating-point registers");
+
+ fetch_fpregs (regcache, thread);
+ }
+}
+
+
+/* Store the whole floating-point state into THREAD using information
+ from the corresponding (pseudo) registers. */
+void
+store_fpregs (const struct regcache *regcache, struct proc *thread, int regno)
+{
+ gnu_debug ("store_fpregs() not support now\n");
+#if 0
+ mach_msg_type_number_t count = i386_FLOAT_STATE_COUNT;
+ struct i386_float_state state;
+ error_t err;
+
+ err = thread_get_state (thread->port, i386_FLOAT_STATE,
+ (thread_state_t) & state, &count);
+ if (err)
+ {
+ warning (_("Couldn't fetch floating-point state from %s"),
+ proc_string (thread));
+ return;
+ }
+
+ /* FIXME: kettenis/2001-07-15: Is this right? Should we somehow
+ take into account DEPRECATED_REGISTER_VALID like the old code did? */
+ i387_collect_fsave (regcache, regno, state.hw_state);
+
+ err = thread_set_state (thread->port, i386_FLOAT_STATE,
+ (thread_state_t) & state, i386_FLOAT_STATE_COUNT);
+ if (err)
+ {
+ warning (_("Couldn't store floating-point state into %s"),
+ proc_string (thread));
+ return;
+ }
+#endif
+}
+
+/* Store at least register REGNO, or all regs if REGNO == -1. */
+void
+gnu_store_registers_1 (struct target_ops *ops,
+ struct regcache *regcache, int regno)
+{
+ struct proc *thread;
+ /*struct gdbarch *gdbarch = get_regcache_arch (regcache); */
+ const struct target_desc *gdbarch = regcache->tdesc;
+
+ /* Make sure we know about new threads. */
+ inf_update_procs (gnu_current_inf);
+
+ thread = inf_tid_to_thread (gnu_current_inf, TIDGET (inferior_ptid));
+ if (!thread)
+ error (_("Couldn't store registers into thread %s: No such thread"),
+ target_pid_to_str (inferior_ptid));
+
+ if (regno < I386_NUM_GREGS || regno == -1)
+ {
+ thread_state_t state;
+ thread_state_data_t old_state;
+ int was_aborted = thread->aborted;
+ int was_valid = thread->state_valid;
+ int trace;
+
+ if (!was_aborted && was_valid)
+ memcpy (&old_state, &thread->state, sizeof (old_state));
+
+ state = proc_get_state (thread, 1);
+ if (!state)
+ {
+ warning (_("Couldn't store registers into %s"),
+ proc_string (thread));
+ return;
+ }
+
+ /* Save the T bit. We might try to restore the %eflags register
+ below, but changing the T bit would seriously confuse GDB. */
+ trace = ((struct i386_thread_state *) state)->efl & 0x100;
+
+ if (!was_aborted && was_valid)
+ /* See which registers have changed after aborting the thread. */
+ {
+ int check_regno;
+
+ for (check_regno = 0; check_regno < I386_NUM_GREGS; check_regno++)
+ if ((thread->fetched_regs & (1 << check_regno))
+ && memcpy (REG_ADDR (&old_state, check_regno),
+ REG_ADDR (state, check_regno),
+ register_size (gdbarch, check_regno)))
+ /* Register CHECK_REGNO has changed! Ack! */
+ {
+ /*warning (_("Register %s changed after the thread was aborted"), */
+ /*gdbarch_register_name (gdbarch, check_regno)); */
+ if (regno >= 0 && regno != check_regno)
+ /* Update GDB's copy of the register. */
+ /*regcache_raw_supply (regcache, check_regno,REG_ADDR (state,
check_regno)); */
+ supply_register (regcache, check_regno,
+ REG_ADDR (state, check_regno));
+ else
+ warning (_("... also writing this register! "
+ "Suspicious..."));
+ }
+ }
+
+ if (regno == -1)
+ {
+ int i;
+
+ proc_debug (thread, "storing all registers");
+
+ for (i = 0; i < I386_NUM_GREGS; i++)
+ /*if (REG_VALID == regcache_register_status (regcache, i)) */
+ /*regcache_raw_collect (regcache, i, REG_ADDR (state, i)); */
+ collect_register (regcache, i, REG_ADDR (state, i));
+ }
+ else
+ {
+ /*proc_debug (thread, "storing register %s",gdbarch_register_name
(gdbarch, regno)); */
+
+ /*gdb_assert (REG_VALID == regcache_register_status (regcache, regno)); */
+ /*regcache_raw_collect (regcache, regno, REG_ADDR (state, regno)); */
+ collect_register (regcache, regno, REG_ADDR (state, regno));
+ }
+
+ /* Restore the T bit. */
+ ((struct i386_thread_state *) state)->efl &= ~0x100;
+ ((struct i386_thread_state *) state)->efl |= trace;
+ }
+
+ if (regno >= I386_NUM_GREGS || regno == -1)
+ {
+ proc_debug (thread, "storing floating-point registers");
+
+ store_fpregs (regcache, thread, regno);
+ }
+}
diff --git a/gdb/gdbserver/gnu-low.c b/gdb/gdbserver/gnu-low.c
new file mode 100644
index 0000000..3fd1f04
--- /dev/null
+++ b/gdb/gdbserver/gnu-low.c
@@ -0,0 +1,2327 @@
+/* Copyright (C) 2009-2013 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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/>. */
+
+#include "server.h"
+#include "target.h"
+
+#include "gnu-low.h"
+
+#include <limits.h>
+#include <sys/ptrace.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include "gdb_wait.h"
+#include <signal.h>
+
+#include <mach.h>
+#include <mach_error.h>
+#include <mach/message.h>
+#include <mach/exception.h>
+
+#include "msg_reply_S.h"
+#include "exc_request_S.h"
+#include "process_reply_S.h"
+#include "notify_S.h"
+
+/* this should move into gnu-i386-low.c ?*/
+/* Defined in auto-generated file i386.c. */
+extern void init_registers_i386 (void);
+extern const struct target_desc *tdesc_i386;
+
+const struct target_desc *gnu_tdesc;
+/* If we've sent a proc_wait_request to the proc server, the pid of the
+ process we asked about. We can only ever have one outstanding. */
+int proc_wait_pid = 0;
+
+/* The number of wait requests we've sent, and expect replies from. */
+int proc_waits_pending = 0;
+
+int using_threads = 1;
+
+struct inf *gnu_current_inf = NULL;
+struct inf *waiting_inf = NULL;
+static process_t proc_server = MACH_PORT_NULL;
+static int next_thread_id = 1;
+ptid_t inferior_ptid;
+
+/* Evaluate RPC_EXPR in a scope with the variables MSGPORT and REFPORT bound
+ to INF's msg port and task port respectively. If it has no msg port,
+ EIEIO is returned. INF must refer to a running process! */
+#define INF_MSGPORT_RPC(inf, rpc_expr) \
+ HURD_MSGPORT_RPC (proc_getmsgport (proc_server, inf->pid, &msgport), \
+ (refport = inf->task->port, 0), 0, \
+ msgport ? (rpc_expr) : EIEIO)
+
+/* Like INF_MSGPORT_RPC, but will also resume the signal thread to ensure
+ there's someone around to deal with the RPC (and resuspend things
+ afterwards). This effects INF's threads' resume_sc count. */
+#define INF_RESUME_MSGPORT_RPC(inf, rpc_expr) \
+ (inf_set_threads_resume_sc_for_signal_thread (inf) \
+ ? ({ error_t __e; \
+ inf_resume (inf); \
+ __e = INF_MSGPORT_RPC (inf, rpc_expr); \
+ inf_suspend (inf); \
+ __e; }) \
+ : EIEIO)
+static struct target_ops gnu_target_ops;
+
+struct process_info_private
+{
+ struct inf *inf;
+};
+
+int debug_flags = 0;
+
+void
+gnu_debug (char *string, ...)
+{
+ va_list args;
+
+ if (!debug_flags)
+ return;
+ va_start (args, string);
+ fprintf (stderr, "DEBUG(gnu): ");
+ vfprintf (stderr, string, args);
+ fprintf (stderr, "\n");
+ va_end (args);
+}
+
+/* Set up the thread resume_sc's so that only the signal thread is running
+ (plus whatever other thread are set to always run). Returns true if we
+ did so, or false if we can't find a signal thread. */
+int
+inf_set_threads_resume_sc_for_signal_thread (struct inf *inf)
+{
+ if (inf->signal_thread)
+ {
+ inf_set_threads_resume_sc (inf, inf->signal_thread, 0);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Sets the resume_sc of each thread in inf. That of RUN_THREAD is set to 0,
+ and others are set to their run_sc if RUN_OTHERS is true, and otherwise
+ their pause_sc. */
+void
+inf_set_threads_resume_sc (struct inf *inf,
+ struct proc *run_thread, int run_others)
+{
+ struct proc *thread;
+
+ inf_update_procs (inf);
+ for (thread = inf->threads; thread; thread = thread->next)
+ if (thread == run_thread)
+ thread->resume_sc = 0;
+ else if (run_others)
+ thread->resume_sc = thread->run_sc;
+ else
+ thread->resume_sc = thread->pause_sc;
+}
+
+void
+inf_clear_wait (struct inf *inf)
+{
+ inf_debug (inf, "clearing wait");
+ inf->wait.status.kind = TARGET_WAITKIND_SPURIOUS;
+ inf->wait.thread = 0;
+ inf->wait.suppress = 0;
+ if (inf->wait.exc.handler != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), inf->wait.exc.handler);
+ inf->wait.exc.handler = MACH_PORT_NULL;
+ }
+ if (inf->wait.exc.reply != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), inf->wait.exc.reply);
+ inf->wait.exc.reply = MACH_PORT_NULL;
+ }
+}
+
+int
+__proc_pid (struct proc *proc)
+{
+ return proc->inf->pid;
+}
+
+static ptid_t
+gnu_ptid_build (int pid, long lwp, long tid)
+{
+ return ptid_build (pid, tid, 0);
+}
+
+static long
+gnu_get_tid (ptid_t ptid)
+{
+ return ptid_get_lwp (ptid);
+}
+
+int
+proc_update_sc (struct proc *proc)
+{
+ int running;
+ int err = 0;
+ int delta = proc->sc - proc->cur_sc;
+
+ if (delta)
+ gnu_debug ("sc: %d --> %d", proc->cur_sc, proc->sc);
+
+ if (proc->sc == 0 && proc->state_changed)
+ /* Since PROC may start running, we must write back any state changes. */
+ {
+ gdb_assert (proc_is_thread (proc));
+ err = thread_set_state (proc->port, THREAD_STATE_FLAVOR,
+ (thread_state_t) & proc->state,
+ THREAD_STATE_SIZE);
+ if (!err)
+ proc->state_changed = 0;
+ }
+
+ if (delta > 0)
+ {
+ while (delta-- > 0 && !err)
+ {
+ if (proc_is_task (proc))
+ err = task_suspend (proc->port);
+ else
+ err = thread_suspend (proc->port);
+ }
+ }
+ else
+ {
+ while (delta++ < 0 && !err)
+ {
+ if (proc_is_task (proc))
+ err = task_resume (proc->port);
+ else
+ err = thread_resume (proc->port);
+ }
+ }
+ if (!err)
+ proc->cur_sc = proc->sc;
+
+ /* If we got an error, then the task/thread has disappeared. */
+ running = !err && proc->sc == 0;
+
+ proc_debug (proc, "is %s",
+ err ? "dead" : running ? "running" : "suspended");
+ if (err)
+ proc_debug (proc, "err = %s", safe_strerror (err));
+
+ if (running)
+ {
+ proc->aborted = 0;
+ proc->state_valid = proc->state_changed = 0;
+ proc->fetched_regs = 0;
+ }
+
+ return running;
+}
+
+error_t
+proc_get_exception_port (struct proc * proc, mach_port_t * port)
+{
+ if (proc_is_task (proc))
+ return task_get_exception_port (proc->port, port);
+ else
+ return thread_get_exception_port (proc->port, port);
+}
+
+static mach_port_t
+_proc_get_exc_port (struct proc *proc)
+{
+ mach_port_t exc_port;
+ error_t err = proc_get_exception_port (proc, &exc_port);
+
+ if (err)
+ /* PROC must be dead. */
+ {
+ if (proc->exc_port)
+ mach_port_deallocate (mach_task_self (), proc->exc_port);
+ proc->exc_port = MACH_PORT_NULL;
+ if (proc->saved_exc_port)
+ mach_port_deallocate (mach_task_self (), proc->saved_exc_port);
+ proc->saved_exc_port = MACH_PORT_NULL;
+ }
+
+ return exc_port;
+}
+
+void
+inf_set_traced (struct inf *inf, int on)
+{
+ if (on == inf->traced)
+ return;
+
+ if (inf->task && !inf->task->dead)
+ /* Make it take effect immediately. */
+ {
+ sigset_t mask = on ? ~(sigset_t) 0 : 0;
+ error_t err =
+ INF_RESUME_MSGPORT_RPC (inf, msg_set_init_int (msgport, refport,
+ INIT_TRACEMASK, mask));
+
+ if (err == EIEIO)
+ {
+ /*if (on) */
+ /*warning (_("Can't modify tracing state for pid %d: %s"), */
+ /*inf->pid, "No signal thread"); */
+ inf->traced = on;
+ }
+ else if (err)
+ ;
+ /*warning (_("Can't modify tracing state for pid %d: %s"), */
+ /*inf->pid, safe_strerror (err)); */
+ else
+ inf->traced = on;
+ }
+ else
+ inf->traced = on;
+}
+
+/* Makes all the real suspend count deltas of all the procs in INF
+ match the desired values. Careful to always do thread/task suspend
+ counts in the safe order. Returns true if at least one thread is
+ thought to be running. */
+int
+inf_update_suspends (struct inf *inf)
+{
+ struct proc *task = inf->task;
+
+ /* We don't have to update INF->threads even though we're iterating over it
+ because we'll change a thread only if it already has an existing proc
+ entry. */
+ inf_debug (inf, "updating suspend counts");
+
+ if (task)
+ {
+ struct proc *thread;
+ int task_running = (task->sc == 0), thread_running = 0;
+
+ if (task->sc > task->cur_sc)
+ /* The task is becoming _more_ suspended; do before any threads. */
+ task_running = proc_update_sc (task);
+
+ if (inf->pending_execs)
+ /* When we're waiting for an exec, things may be happening behind our
+ back, so be conservative. */
+ thread_running = 1;
+
+ /* Do all the thread suspend counts. */
+ for (thread = inf->threads; thread; thread = thread->next)
+ thread_running |= proc_update_sc (thread);
+
+ if (task->sc != task->cur_sc)
+ /* We didn't do the task first, because we wanted to wait for the
+ threads; do it now. */
+ task_running = proc_update_sc (task);
+
+ inf_debug (inf, "%srunning...",
+ (thread_running && task_running) ? "" : "not ");
+
+ inf->running = thread_running && task_running;
+
+ /* Once any thread has executed some code, we can't depend on the
+ threads list any more. */
+ if (inf->running)
+ inf->threads_up_to_date = 0;
+
+ return inf->running;
+ }
+
+ return 0;
+}
+
+void
+proc_abort (struct proc *proc, int force)
+{
+ gdb_assert (proc_is_thread (proc));
+
+ if (!proc->aborted)
+ {
+ struct inf *inf = proc->inf;
+ int running = (proc->cur_sc == 0 && inf->task->cur_sc == 0);
+
+ if (running && force)
+ {
+ proc->sc = 1;
+ inf_update_suspends (proc->inf);
+ running = 0;
+ /*warning (_("Stopped %s."), proc_string (proc)); */
+ }
+ else if (proc == inf->wait.thread && inf->wait.exc.reply && !force)
+ /* An exception is pending on PROC, which don't mess with. */
+ running = 1;
+
+ if (!running)
+ /* We only abort the thread if it's not actually running. */
+ {
+ thread_abort (proc->port);
+ proc_debug (proc, "aborted");
+ proc->aborted = 1;
+ }
+ else
+ proc_debug (proc, "not aborting");
+ }
+}
+
+thread_state_t
+proc_get_state (struct proc *proc, int will_modify)
+{
+ int was_aborted = proc->aborted;
+
+ proc_debug (proc, "updating state info%s",
+ will_modify ? " (with intention to modify)" : "");
+
+ proc_abort (proc, will_modify);
+
+ if (!was_aborted && proc->aborted)
+ /* PROC's state may have changed since we last fetched it. */
+ proc->state_valid = 0;
+
+ if (!proc->state_valid)
+ {
+ mach_msg_type_number_t state_size = THREAD_STATE_SIZE;
+ error_t err = thread_get_state (proc->port, THREAD_STATE_FLAVOR,
+ (thread_state_t) & proc->state,
+ &state_size);
+
+ proc_debug (proc, "getting thread state");
+ proc->state_valid = !err;
+ }
+
+ if (proc->state_valid)
+ {
+ if (will_modify)
+ proc->state_changed = 1;
+ return (thread_state_t) & proc->state;
+ }
+ else
+ return 0;
+}
+
+void
+proc_steal_exc_port (struct proc *proc, mach_port_t exc_port)
+{
+ mach_port_t cur_exc_port = _proc_get_exc_port (proc);
+
+ if (cur_exc_port)
+ {
+ error_t err = 0;
+
+ proc_debug (proc, "inserting exception port: %d", exc_port);
+
+ if (cur_exc_port != exc_port)
+ /* Put in our exception port. */
+ err = proc_set_exception_port (proc, exc_port);
+
+ if (err || cur_exc_port == proc->exc_port)
+ /* We previously set the exception port, and it's still set. So we
+ just keep the old saved port which is what the proc set. */
+ {
+ if (cur_exc_port)
+ mach_port_deallocate (mach_task_self (), cur_exc_port);
+ }
+ else
+ /* Keep a copy of PROC's old exception port so it can be restored. */
+ {
+ if (proc->saved_exc_port)
+ mach_port_deallocate (mach_task_self (), proc->saved_exc_port);
+ proc->saved_exc_port = cur_exc_port;
+ }
+
+ proc_debug (proc, "saved exception port: %d", proc->saved_exc_port);
+
+ if (!err)
+ proc->exc_port = exc_port;
+ /*else */
+ /*warning (_("Error setting exception port for %s: %s"), */
+ /*proc_string (proc), safe_strerror (err)); */
+ }
+}
+
+int
+proc_trace (struct proc *proc, int set)
+{
+ thread_state_t state = proc_get_state (proc, 1);
+
+ if (!state)
+ return 0; /* The thread must be dead. */
+
+ proc_debug (proc, "tracing %s", set ? "on" : "off");
+
+ if (set)
+ {
+ /* XXX We don't get the exception unless the thread has its own
+ exception port???? */
+ if (proc->exc_port == MACH_PORT_NULL)
+ proc_steal_exc_port (proc, proc->inf->event_port);
+ THREAD_STATE_SET_TRACED (state);
+ }
+ else
+ THREAD_STATE_CLEAR_TRACED (state);
+
+ return 1;
+}
+
+error_t
+proc_set_exception_port (struct proc * proc, mach_port_t port)
+{
+ proc_debug (proc, "setting exception port: %d", port);
+ if (proc_is_task (proc))
+ return task_set_exception_port (proc->port, port);
+ else
+ return thread_set_exception_port (proc->port, port);
+}
+
+void
+proc_restore_exc_port (struct proc *proc)
+{
+ mach_port_t cur_exc_port = _proc_get_exc_port (proc);
+
+ if (cur_exc_port)
+ {
+ error_t err = 0;
+
+ proc_debug (proc, "restoring real exception port");
+
+ if (proc->exc_port == cur_exc_port)
+ /* Our's is still there. */
+ err = proc_set_exception_port (proc, proc->saved_exc_port);
+
+ if (proc->saved_exc_port)
+ mach_port_deallocate (mach_task_self (), proc->saved_exc_port);
+ proc->saved_exc_port = MACH_PORT_NULL;
+
+ if (!err)
+ proc->exc_port = MACH_PORT_NULL;
+ else
+ gnu_debug ("Error setting exception port\n");
+ }
+}
+
+void
+inf_set_step_thread (struct inf *inf, struct proc *thread)
+{
+ gdb_assert (!thread || proc_is_thread (thread));
+
+ /*if (thread) */
+ /*inf_debug (inf, "setting step thread: %d/%d", inf->pid, thread->tid); */
+ /*else */
+ /*inf_debug (inf, "clearing step thread"); */
+
+ if (inf->step_thread != thread)
+ {
+ if (inf->step_thread && inf->step_thread->port != MACH_PORT_NULL)
+ if (!proc_trace (inf->step_thread, 0))
+ return;
+ if (thread && proc_trace (thread, 1))
+ inf->step_thread = thread;
+ else
+ inf->step_thread = 0;
+ }
+}
+
+struct proc *
+_proc_free (struct proc *proc)
+{
+ struct inf *inf = proc->inf;
+ struct proc *next = proc->next;
+
+ if (proc == inf->step_thread)
+ /* Turn off single stepping. */
+ inf_set_step_thread (inf, 0);
+ if (proc == inf->wait.thread)
+ inf_clear_wait (inf);
+ if (proc == inf->signal_thread)
+ inf->signal_thread = 0;
+
+ if (proc->port != MACH_PORT_NULL)
+ {
+ if (proc->exc_port != MACH_PORT_NULL)
+ /* Restore the original exception port. */
+ proc_restore_exc_port (proc);
+ if (proc->cur_sc != 0)
+ /* Resume the thread/task. */
+ {
+ proc->sc = 0;
+ proc_update_sc (proc);
+ }
+ mach_port_deallocate (mach_task_self (), proc->port);
+ }
+
+ xfree (proc);
+ return next;
+}
+
+struct proc *
+make_proc (struct inf *inf, mach_port_t port, int tid)
+{
+ error_t err;
+ mach_port_t prev_port = MACH_PORT_NULL;
+ struct proc *proc = xmalloc (sizeof (struct proc));
+
+ proc->port = port;
+ proc->tid = tid;
+ proc->inf = inf;
+ proc->next = 0;
+ proc->saved_exc_port = MACH_PORT_NULL;
+ proc->exc_port = MACH_PORT_NULL;
+
+ proc->sc = 0;
+ proc->cur_sc = 0;
+
+ /* Note that these are all the values for threads; the task simply uses the
+ corresponding field in INF directly. */
+ proc->run_sc = inf->default_thread_run_sc;
+ proc->pause_sc = inf->default_thread_pause_sc;
+ proc->detach_sc = inf->default_thread_detach_sc;
+ proc->resume_sc = proc->run_sc;
+
+ proc->aborted = 0;
+ proc->dead = 0;
+ proc->state_valid = 0;
+ proc->state_changed = 0;
+
+ proc_debug (proc, "is new");
+
+ /* Get notified when things die. */
+ err =
+ mach_port_request_notification (mach_task_self (), port,
+ MACH_NOTIFY_DEAD_NAME, 1,
+ inf->event_port,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev_port);
+ if (err)
+ warning (_("Couldn't request notification for port %d: %s"),
+ port, safe_strerror (err));
+ else
+ {
+ proc_debug (proc, "notifications to: %d", inf->event_port);
+ if (prev_port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), prev_port);
+ }
+
+ if (inf->want_exceptions)
+ {
+ if (proc_is_task (proc))
+ /* Make the task exception port point to us. */
+ proc_steal_exc_port (proc, inf->event_port);
+ else
+ /* Just clear thread exception ports -- they default to the
+ task one. */
+ proc_steal_exc_port (proc, MACH_PORT_NULL);
+ }
+
+ return proc;
+}
+
+void
+inf_validate_procs (struct inf *inf)
+{
+ thread_array_t threads;
+ mach_msg_type_number_t num_threads, i;
+ struct proc *task = inf->task;
+
+ /* If no threads are currently running, this function will guarantee that
+ things are up to date. The exception is if there are zero threads --
+ then it is almost certainly in an odd state, and probably some outside
+ agent will create threads. */
+ inf->threads_up_to_date = inf->threads ? !inf->running : 0;
+
+ if (task)
+ {
+ error_t err = task_threads (task->port, &threads, &num_threads);
+
+ inf_debug (inf, "fetching threads");
+ if (err)
+ /* TASK must be dead. */
+ {
+ task->dead = 1;
+ task = 0;
+ }
+ }
+
+ if (!task)
+ {
+ num_threads = 0;
+ inf_debug (inf, "no task");
+ }
+
+ {
+ /* Make things normally linear. */
+ mach_msg_type_number_t search_start = 0;
+ /* Which thread in PROCS corresponds to each task thread, & the task. */
+ struct proc *matched[num_threads + 1];
+ /* The last thread in INF->threads, so we can add to the end. */
+ struct proc *last = 0;
+ /* The current thread we're considering. */
+ struct proc *thread = inf->threads;
+
+ memset (matched, 0, sizeof (matched));
+
+ while (thread)
+ {
+ mach_msg_type_number_t left;
+
+ for (i = search_start, left = num_threads; left; i++, left--)
+ {
+ if (i >= num_threads)
+ i -= num_threads; /* I wrapped around. */
+ if (thread->port == threads[i])
+ /* We already know about this thread. */
+ {
+ matched[i] = thread;
+ last = thread;
+ thread = thread->next;
+ search_start++;
+ break;
+ }
+ }
+
+ if (!left)
+ {
+ proc_debug (thread, "died!");
+
+ ptid_t ptid;
+ ptid = gnu_ptid_build (inf->pid, 0, thread->tid);
+ if (find_thread_ptid (ptid))
+ remove_thread (find_thread_ptid (ptid));
+
+ thread->port = MACH_PORT_NULL;
+ thread = _proc_free (thread); /* THREAD is dead. */
+ if (last)
+ last->next = thread;
+ else
+ inf->threads = thread;
+ }
+ }
+
+ for (i = 0; i < num_threads; i++)
+ {
+ if (matched[i])
+ /* Throw away the duplicate send right. */
+ mach_port_deallocate (mach_task_self (), threads[i]);
+ else
+ /* THREADS[I] is a thread we don't know about yet! */
+ {
+ ptid_t ptid;
+
+ thread = make_proc (inf, threads[i], next_thread_id++);
+ if (last)
+ last->next = thread;
+ else
+ inf->threads = thread;
+ last = thread;
+ proc_debug (thread, "new thread: %d", threads[i]);
+
+ ptid = gnu_ptid_build (inf->pid, 0, thread->tid);
+
+ /* Tell GDB's generic thread code. */
+
+#if 0
+ if (ptid_equal (inferior_ptid, pid_to_ptid (inf->pid)))
+ /* This is the first time we're hearing about thread
+ ids, after a fork-child. */
+ thread_change_ptid (inferior_ptid, ptid);
+ else if (inf->pending_execs != 0)
+ /* This is a shell thread. */
+ add_thread_silent (ptid);
+ else
+ add_thread (ptid);
+#endif
+ if (!find_thread_ptid (ptid))
+ {
+ gnu_debug ("New thread, pid=%d, tid=%d\n", inf->pid,
+ thread->tid);
+ add_thread (ptid, thread);
+ inferior_ptid = ptid; // need fix!!!!!!!!!!!!!
+ }
+ }
+ }
+
+ vm_deallocate (mach_task_self (),
+ (vm_address_t) threads, (num_threads * sizeof (thread_t)));
+ }
+}
+
+int
+inf_update_procs (struct inf *inf)
+{
+ if (!inf->task)
+ return 0;
+ if (!inf->threads_up_to_date)
+ inf_validate_procs (inf);
+ return !!inf->task;
+}
+
+void
+inf_set_pid (struct inf *inf, pid_t pid)
+{
+ task_t task_port;
+ struct proc *task = inf->task;
+
+ inf_debug (inf, "setting pid: %d", pid);
+
+ if (pid < 0)
+ task_port = MACH_PORT_NULL;
+ else
+ {
+ error_t err = proc_pid2task (proc_server, pid, &task_port);
+
+ if (err)
+ {
+ error (_("Error getting task for pid %d: %s"), pid, "XXXX");
+ /*pid, safe_strerror (err)); */
+ }
+ }
+
+ inf_debug (inf, "setting task: %d", task_port);
+
+ if (inf->pause_sc)
+ task_suspend (task_port);
+
+ if (task && task->port != task_port)
+ {
+ inf->task = 0;
+ inf_validate_procs (inf); /* Trash all the threads. */
+ _proc_free (task); /* And the task. */
+ }
+
+ if (task_port != MACH_PORT_NULL)
+ {
+ inf->task = make_proc (inf, task_port, PROC_TID_TASK);
+ inf->threads_up_to_date = 0;
+ }
+
+ if (inf->task)
+ {
+ inf->pid = pid;
+ if (inf->pause_sc)
+ /* Reflect task_suspend above. */
+ inf->task->sc = inf->task->cur_sc = 1;
+ }
+ else
+ inf->pid = -1;
+}
+
+void
+inf_cleanup (struct inf *inf)
+{
+ inf_debug (inf, "cleanup");
+
+ inf_clear_wait (inf);
+
+ inf_set_pid (inf, -1);
+ inf->pid = 0;
+ inf->running = 0;
+ inf->stopped = 0;
+ inf->nomsg = 1;
+ inf->traced = 0;
+ inf->no_wait = 0;
+ inf->pending_execs = 0;
+
+ if (inf->event_port)
+ {
+ mach_port_destroy (mach_task_self (), inf->event_port);
+ inf->event_port = MACH_PORT_NULL;
+ }
+}
+
+void
+inf_startup (struct inf *inf, int pid)
+{
+ error_t err;
+
+ inf_debug (inf, "startup: pid = %d", pid);
+
+ inf_cleanup (inf);
+
+ /* Make the port on which we receive all events. */
+ err = mach_port_allocate (mach_task_self (),
+ MACH_PORT_RIGHT_RECEIVE, &inf->event_port);
+ /*if (err) */
+ /*error (_("Error allocating event port: %s"), safe_strerror (err)); */
+
+ /* Make a send right for it, so we can easily copy it for other people. */
+ mach_port_insert_right (mach_task_self (), inf->event_port,
+ inf->event_port, MACH_MSG_TYPE_MAKE_SEND);
+ inf_set_pid (inf, pid);
+}
+
+/* Detachs from INF's inferior task, letting it run once again... */
+void
+inf_detach (struct inf *inf)
+{
+ struct proc *task = inf->task;
+
+ inf_debug (inf, "detaching...");
+
+ inf_clear_wait (inf);
+ inf_set_step_thread (inf, 0);
+
+ if (task)
+ {
+ struct proc *thread;
+
+ inf_validate_procinfo (inf);
+
+ inf_set_traced (inf, 0);
+ if (inf->stopped)
+ {
+ if (inf->nomsg)
+ inf_continue (inf);
+ else
+ inf_signal (inf, GDB_SIGNAL_0);
+ }
+
+ proc_restore_exc_port (task);
+ task->sc = inf->detach_sc;
+
+ for (thread = inf->threads; thread; thread = thread->next)
+ {
+ proc_restore_exc_port (thread);
+ thread->sc = thread->detach_sc;
+ }
+
+ inf_update_suspends (inf);
+ }
+
+ inf_cleanup (inf);
+}
+
+void
+inf_attach (struct inf *inf, int pid)
+{
+ inf_debug (inf, "attaching: %d", pid);
+
+ if (inf->pid)
+ {
+ inf_detach (inf);
+ }
+
+ inf_startup (inf, pid);
+}
+
+struct inf *
+make_inf (void)
+{
+ struct inf *inf = xmalloc (sizeof (struct inf));
+
+ inf->task = 0;
+ inf->threads = 0;
+ inf->threads_up_to_date = 0;
+ inf->pid = 0;
+ inf->wait.status.kind = TARGET_WAITKIND_SPURIOUS;
+ inf->wait.thread = 0;
+ inf->wait.exc.handler = MACH_PORT_NULL;
+ inf->wait.exc.reply = MACH_PORT_NULL;
+ inf->step_thread = 0;
+ inf->signal_thread = 0;
+ inf->event_port = MACH_PORT_NULL;
+ inf->running = 0;
+ inf->stopped = 0;
+ inf->nomsg = 1;
+ inf->traced = 0;
+ inf->no_wait = 0;
+ inf->pending_execs = 0;
+ inf->pause_sc = 1;
+ inf->detach_sc = 0;
+ inf->default_thread_run_sc = 0;
+ inf->default_thread_pause_sc = 0;
+ inf->default_thread_detach_sc = 0;
+ inf->want_signals = 1; /* By default */
+ inf->want_exceptions = 1; /* By default */
+
+ return inf;
+}
+
+static struct inf *
+cur_inf (void)
+{
+ if (!gnu_current_inf)
+ gnu_current_inf = make_inf ();
+ return gnu_current_inf;
+}
+
+
+static struct process_info *
+gnu_add_process (int pid, int attached)
+{
+ struct process_info *proc;
+
+ proc = add_process (pid, attached);
+ proc->tdesc = gnu_tdesc;
+ proc->private = xcalloc (1, sizeof (*proc->private));
+ proc->private->inf = cur_inf ();
+ struct inf *inf = gnu_current_inf;
+
+ inf_attach (inf, pid);
+ inf->pending_execs = 2;
+ inf->nomsg = 1;
+ inf->traced = 1;
+
+ inf_resume (inf);
+
+ return proc;
+}
+
+static int
+gnu_create_inferior (char *program, char **allargs)
+{
+ int pid;
+ pid = fork ();
+ if (pid < 0)
+ perror_with_name ("fork");
+
+ if (pid == 0)
+ {
+ ptrace (PTRACE_TRACEME);
+ setpgid (0, 0);
+ execv (program, allargs);
+
+ fprintf (stderr, "Cannot exec %s: %s.\n", program, strerror (errno));
+ fflush (stderr);
+ _exit (0177);
+ }
+
+ gnu_add_process (pid, 0);
+ return pid;
+}
+
+/* Fork an inferior process, and start debugging it. */
+
+/* Set INFERIOR_PID to the first thread available in the child, if any. */
+static int
+inf_pick_first_thread (void)
+{
+ if (gnu_current_inf->task && gnu_current_inf->threads)
+ /* The first thread. */
+ return gnu_current_inf->threads->tid;
+ else
+ /* What may be the next thread. */
+ return next_thread_id;
+}
+
+static int
+gnu_attach (unsigned long pid)
+{
+ return -1; //not support now
+ struct inf *inf = cur_inf ();
+ /*struct inferior *inferior; */
+
+ if (pid == getpid ()) /* Trying to masturbate? */
+ error (_("I refuse to debug myself!"));
+
+ inf_debug (inf, "attaching to pid: %d", pid);
+
+ inf_attach (inf, pid);
+
+ inf_update_procs (inf);
+
+ inferior_ptid = gnu_ptid_build (pid, 0, inf_pick_first_thread ());
+
+ inf_validate_procinfo (inf);
+ inf->signal_thread = inf->threads ? inf->threads->next : 0;
+ inf_set_traced (inf, inf->want_signals);
+
+ gnu_add_process (pid, 1);
+ add_thread (inferior_ptid, NULL);
+ return 0;
+}
+
+static int
+gnu_kill (int pid)
+{
+ struct proc *task = gnu_current_inf->task;
+ struct process_info *process;
+
+ process = find_process_pid (pid);
+
+ if (task)
+ {
+ proc_debug (task, "terminating...");
+ task_terminate (task->port);
+ inf_set_pid (gnu_current_inf, -1);
+ }
+ the_target->mourn (process);
+ return 0;
+}
+
+static int
+gnu_detach (int pid)
+{
+ struct process_info *process;
+
+ process = find_process_pid (pid);
+ if (process == NULL)
+ return -1;
+
+ inf_detach (gnu_current_inf);
+
+ inferior_ptid = null_ptid;
+ the_target->mourn (process);
+ return 0;
+}
+
+
+static void
+gnu_mourn (struct process_info *process)
+{
+ /* Free our private data. */
+ free (process->private);
+ process->private = NULL;
+
+ clear_inferiors ();
+}
+
+static void
+gnu_join (int pid)
+{
+ /* doesn't need */
+}
+
+static int
+gnu_thread_alive (ptid_t ptid)
+{
+ /* this function is copyed from lynx-low.c */
+ return (find_thread_ptid (ptid) != NULL);
+}
+
+
+/* Fill in INF's wait field after a task has died without giving us more
+ detailed information. */
+void
+inf_task_died_status (struct inf *inf)
+{
+ printf ("Pid %d died with unknown exit status, using SIGKILL.", inf->pid);
+ inf->wait.status.kind = TARGET_WAITKIND_SIGNALLED;
+ inf->wait.status.value.sig = GDB_SIGNAL_KILL;
+}
+
+struct proc *
+inf_tid_to_thread (struct inf *inf, int tid)
+{
+ struct proc *thread = inf->threads;
+
+ gnu_debug ("[inf_tid_to_thread]:search thread which tid=%d\n", tid);
+
+ while (thread)
+ if (thread->tid == tid)
+ return thread;
+ else
+ thread = thread->next;
+ return 0;
+}
+
+/* Validates INF's stopped, nomsg and traced field from the actual
+ proc server state. Note that the traced field is only updated from
+ the proc server state if we do not have a message port. If we do
+ have a message port we'd better look at the tracemask itself. */
+static void
+inf_validate_procinfo (struct inf *inf)
+{
+ char *noise;
+ mach_msg_type_number_t noise_len = 0;
+ struct procinfo *pi;
+ mach_msg_type_number_t pi_len = 0;
+ int info_flags = 0;
+ error_t err = proc_getprocinfo (proc_server, inf->pid, &info_flags,
+ (procinfo_t *) & pi, &pi_len, &noise,
+ &noise_len);
+
+ if (!err)
+ {
+ inf->stopped = !!(pi->state & PI_STOPPED);
+ inf->nomsg = !!(pi->state & PI_NOMSG);
+ if (inf->nomsg)
+ inf->traced = !!(pi->state & PI_TRACED);
+ vm_deallocate (mach_task_self (), (vm_address_t) pi, pi_len);
+ if (noise_len > 0)
+ vm_deallocate (mach_task_self (), (vm_address_t) noise, noise_len);
+ }
+}
+
+/* Deliver signal SIG to INF. If INF is stopped, delivering a signal, even
+ signal 0, will continue it. INF is assumed to be in a paused state, and
+ the resume_sc's of INF's threads may be affected. */
+void
+inf_signal (struct inf *inf, enum gdb_signal sig)
+{
+ error_t err = 0;
+ int host_sig = gdb_signal_to_host (sig);
+
+#define NAME gdb_signal_to_name (sig)
+
+ if (host_sig >= _NSIG)
+ /* A mach exception. Exceptions are encoded in the signal space by
+ putting them after _NSIG; this assumes they're positive (and not
+ extremely large)! */
+ {
+ struct inf_wait *w = &inf->wait;
+
+ if (w->status.kind == TARGET_WAITKIND_STOPPED
+ && w->status.value.sig == sig && w->thread && !w->thread->aborted)
+ /* We're passing through the last exception we received. This is
+ kind of bogus, because exceptions are per-thread whereas gdb
+ treats signals as per-process. We just forward the exception to
+ the correct handler, even it's not for the same thread as TID --
+ i.e., we pretend it's global. */
+ {
+ struct exc_state *e = &w->exc;
+
+ inf_debug (inf, "passing through exception:"
+ " task = %d, thread = %d, exc = %d"
+ ", code = %d, subcode = %d",
+ w->thread->port, inf->task->port,
+ e->exception, e->code, e->subcode);
+ err =
+ exception_raise_request (e->handler,
+ e->reply, MACH_MSG_TYPE_MOVE_SEND_ONCE,
+ w->thread->port, inf->task->port,
+ e->exception, e->code, e->subcode);
+ }
+ else
+ error (_("Can't forward spontaneous exception (%s)."), NAME);
+ }
+ else
+ /* A Unix signal. */
+ if (inf->stopped)
+ /* The process is stopped and expecting a signal. Just send off a
+ request and let it get handled when we resume everything. */
+ {
+ inf_debug (inf, "sending %s to stopped process", NAME);
+ err =
+ INF_MSGPORT_RPC (inf,
+ msg_sig_post_untraced_request (msgport,
+ inf->event_port,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ host_sig, 0,
+ refport));
+ if (!err)
+ /* Posting an untraced signal automatically continues it.
+ We clear this here rather than when we get the reply
+ because we'd rather assume it's not stopped when it
+ actually is, than the reverse. */
+ inf->stopped = 0;
+ }
+ else
+ /* It's not expecting it. We have to let just the signal thread
+ run, and wait for it to get into a reasonable state before we
+ can continue the rest of the process. When we finally resume the
+ process the signal we request will be the very first thing that
+ happens. */
+ {
+ inf_debug (inf, "sending %s to unstopped process"
+ " (so resuming signal thread)", NAME);
+ err =
+ INF_RESUME_MSGPORT_RPC (inf,
+ msg_sig_post_untraced (msgport, host_sig,
+ 0, refport));
+ }
+
+ if (err == EIEIO)
+ /* Can't do too much... */
+ warning (_("Can't deliver signal %s: No signal thread."), NAME);
+ else if (err)
+ warning (_("Delivering signal %s: %s"), NAME, safe_strerror (err));
+
+#undef NAME
+}
+
+/* Continue INF without delivering a signal. This is meant to be used
+ when INF does not have a message port. */
+void
+inf_continue (struct inf *inf)
+{
+ process_t proc;
+ error_t err = proc_pid2proc (proc_server, inf->pid, &proc);
+
+ if (!err)
+ {
+ inf_debug (inf, "continuing process");
+
+ err = proc_mark_cont (proc);
+ if (!err)
+ {
+ struct proc *thread;
+
+ for (thread = inf->threads; thread; thread = thread->next)
+ thread_resume (thread->port);
+
+ inf->stopped = 0;
+ }
+ }
+
+ if (err)
+ warning (_("Can't continue process: %s"), safe_strerror (err));
+}
+
+/* Returns the number of messages queued for the receive right PORT. */
+static mach_port_msgcount_t
+port_msgs_queued (mach_port_t port)
+{
+ struct mach_port_status status;
+ error_t err =
+ mach_port_get_receive_status (mach_task_self (), port, &status);
+
+ if (err)
+ return 0;
+ else
+ return status.mps_msgcount;
+}
+
+static void
+gnu_resume_1 (struct target_ops *ops,
+ ptid_t ptid, int step, enum gdb_signal sig)
+{
+ struct proc *step_thread = 0;
+ int resume_all;
+ struct inf *inf = gnu_current_inf;
+
+ inf_debug (inf, "ptid = %s, step = %d, sig = %d",
+ target_pid_to_str (ptid), step, sig);
+
+ inf_validate_procinfo (inf);
+
+ if (sig != GDB_SIGNAL_0 || inf->stopped)
+ {
+ if (sig == GDB_SIGNAL_0 && inf->nomsg)
+ inf_continue (inf);
+ else
+ inf_signal (inf, sig);
+ }
+ else if (inf->wait.exc.reply != MACH_PORT_NULL)
+ /* We received an exception to which we have chosen not to forward, so
+ abort the faulting thread, which will perhaps retake it. */
+ {
+ proc_abort (inf->wait.thread, 1);
+ /*warning (_("Aborting %s with unforwarded exception %s."), */
+ /*proc_string (inf->wait.thread), */
+ /*gdb_signal_to_name (inf->wait.status.value.sig)); */
+ }
+
+ if (port_msgs_queued (inf->event_port))
+ /* If there are still messages in our event queue, don't bother resuming
+ the process, as we're just going to stop it right away anyway. */
+ return;
+
+ inf_update_procs (inf);
+
+ /* A specific PTID means `step only this process id'. */
+ resume_all = ptid_equal (ptid, minus_one_ptid);
+
+ if (resume_all)
+ /* Allow all threads to run, except perhaps single-stepping one. */
+ {
+ inf_debug (inf, "running all threads; tid = %d",
+ PIDGET (inferior_ptid));
+ ptid = inferior_ptid; /* What to step. */
+ inf_set_threads_resume_sc (inf, 0, 1);
+ }
+ else
+ /* Just allow a single thread to run. */
+ {
+ struct proc *thread = inf_tid_to_thread (inf, gnu_get_tid (ptid));
+
+ if (!thread)
+ error (_("Can't run single thread id %s: no such thread!"),
+ target_pid_to_str (ptid));
+ inf_debug (inf, "running one thread: %s", target_pid_to_str (ptid));
+ inf_set_threads_resume_sc (inf, thread, 0);
+ }
+
+ if (step)
+ {
+ step_thread = inf_tid_to_thread (inf, gnu_get_tid (ptid));
+ if (!step_thread)
+ warning (_("Can't step thread id %s: no such thread."),
+ target_pid_to_str (ptid));
+ else
+ inf_debug (inf, "stepping thread: %s", target_pid_to_str (ptid));
+ }
+ if (step_thread != inf->step_thread)
+ inf_set_step_thread (inf, step_thread);
+
+ inf_debug (inf, "here we go...");
+ inf_resume (inf);
+}
+
+static void
+gnu_resume (struct thread_resume *resume_info, size_t n)
+{
+ /* FIXME: Assume for now that n == 1. */
+ ptid_t ptid = resume_info[0].thread;
+ const int step = (resume_info[0].kind == resume_step ? 1 : 0); //1
means step, 0 means contiune
+ const int signal = resume_info[0].sig;
+ if (ptid_equal (ptid, minus_one_ptid))
+ ptid = thread_to_gdb_id (current_inferior);
+
+ regcache_invalidate ();
+
+ gnu_debug ("in gnu_resume: ptid=%d, step=%d, signal=%d\n", ptid, step,
+ signal);
+
+ /*my_resume(); */
+ /*static void gnu_resume_1 (struct target_ops *ops,ptid_t ptid, int
step, enum gdb_signal sig) */
+ gnu_resume_1 (NULL, ptid, step, signal);
+
+}
+
+void
+inf_suspend (struct inf *inf)
+{
+ struct proc *thread;
+
+ inf_update_procs (inf);
+
+ for (thread = inf->threads; thread; thread = thread->next)
+ thread->sc = thread->pause_sc;
+
+ if (inf->task)
+ inf->task->sc = inf->pause_sc;
+
+ inf_update_suspends (inf);
+}
+
+static ptid_t
+gnu_wait_1 (ptid_t ptid, struct target_waitstatus *status, int target_options)
+{
+ struct msg
+ {
+ mach_msg_header_t hdr;
+ mach_msg_type_t type;
+ int data[8000];
+ } msg;
+ error_t err;
+ struct proc *thread;
+ struct inf *inf = gnu_current_inf;
+
+ extern int exc_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int msg_reply_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int notify_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int process_reply_server (mach_msg_header_t *, mach_msg_header_t *);
+
+ gdb_assert (inf->task);
+
+ if (!inf->threads && !inf->pending_execs)
+ /* No threads! Assume that maybe some outside agency is frobbing our
+ task, and really look for new threads. If we can't find any, just tell
+ the user to try again later. */
+ {
+ inf_validate_procs (inf);
+ if (!inf->threads && !inf->task->dead)
+ error (_("There are no threads; try again later."));
+ }
+
+ waiting_inf = inf;
+
+ inf_debug (inf, "waiting for: %s", target_pid_to_str (ptid));
+
+rewait:
+ if (proc_wait_pid != inf->pid && !inf->no_wait)
+ /* Always get information on events from the proc server. */
+ {
+ inf_debug (inf, "requesting wait on pid %d", inf->pid);
+
+ if (proc_wait_pid)
+ /* The proc server is single-threaded, and only allows a single
+ outstanding wait request, so we have to cancel the previous one. */
+ {
+ inf_debug (inf, "cancelling previous wait on pid %d",
+ proc_wait_pid);
+ interrupt_operation (proc_server, 0);
+ }
+
+ err =
+ proc_wait_request (proc_server, inf->event_port, inf->pid, WUNTRACED);
+ if (err)
+ warning (_("wait request failed: %s"), safe_strerror (err));
+ else
+ {
+ inf_debug (inf, "waits pending: %d", proc_waits_pending);
+ proc_wait_pid = inf->pid;
+ /* Even if proc_waits_pending was > 0 before, we still won't
+ get any other replies, because it was either from a
+ different INF, or a different process attached to INF --
+ and the event port, which is the wait reply port, changes
+ when you switch processes. */
+ proc_waits_pending = 1;
+ }
+ }
+
+ inf_clear_wait (inf);
+
+ /* What can happen? (1) Dead name notification; (2) Exceptions arrive;
+ (3) wait reply from the proc server. */
+
+ inf_debug (inf, "waiting for an event...");
+ err = mach_msg (&msg.hdr, MACH_RCV_MSG | MACH_RCV_INTERRUPT,
+ 0, sizeof (struct msg), inf->event_port,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+ /* Re-suspend the task. */
+ inf_suspend (inf);
+
+ if (!inf->task && inf->pending_execs)
+ /* When doing an exec, it's possible that the old task wasn't reused
+ (e.g., setuid execs). So if the task seems to have disappeared,
+ attempt to refetch it, as the pid should still be the same. */
+ inf_set_pid (inf, inf->pid);
+
+ if (err == EMACH_RCV_INTERRUPTED)
+ printf ("interrupted\n");
+ /*inf_debug (inf, "interrupted"); */
+ else if (err)
+ printf ("Couldn't wait for an event:\n");
+ /*error (_("Couldn't wait for an event: %s"), safe_strerror (err)); */
+ else
+ {
+ struct
+ {
+ mach_msg_header_t hdr;
+ mach_msg_type_t err_type;
+ kern_return_t err;
+ char noise[200];
+ }
+ reply;
+
+ inf_debug (inf, "event: msgid = %d", msg.hdr.msgh_id);
+
+ /* Handle what we got. */
+ if (!notify_server (&msg.hdr, &reply.hdr)
+ && !exc_server (&msg.hdr, &reply.hdr)
+ && !process_reply_server (&msg.hdr, &reply.hdr)
+ && !msg_reply_server (&msg.hdr, &reply.hdr))
+ /* Whatever it is, it's something strange. */
+ error (_("Got a strange event, msg id = %d."), msg.hdr.msgh_id);
+
+ if (reply.err)
+ error (_("Handling event, msgid = %d: %s"),
+ msg.hdr.msgh_id, safe_strerror (reply.err));
+ }
+
+ if (inf->pending_execs)
+ /* We're waiting for the inferior to finish execing. */
+ {
+ struct inf_wait *w = &inf->wait;
+ enum target_waitkind kind = w->status.kind;
+
+ if (kind == TARGET_WAITKIND_SPURIOUS)
+ /* Since gdb is actually counting the number of times the inferior
+ stops, expecting one stop per exec, we only return major events
+ while execing. */
+ {
+ w->suppress = 1;
+ inf_debug (inf, "pending_execs = %d, ignoring minor event",
+ inf->pending_execs);
+ }
+ else if (kind == TARGET_WAITKIND_STOPPED
+ && w->status.value.sig == GDB_SIGNAL_TRAP)
+ /* Ah hah! A SIGTRAP from the inferior while starting up probably
+ means we've succesfully completed an exec! */
+ {
+ if (--inf->pending_execs == 0)
+ /* We're done! */
+ {
+#if 0 /* do we need this? */
+ prune_threads (1); /* Get rid of the old shell
+ threads. */
+ renumber_threads (0); /* Give our threads reasonable
+ names. */
+#endif
+ }
+ inf_debug (inf, "pending exec completed, pending_execs => %d",
+ inf->pending_execs);
+ }
+ else if (kind == TARGET_WAITKIND_STOPPED)
+ /* It's possible that this signal is because of a crashed process
+ being handled by the hurd crash server; in this case, the process
+ will have an extra task suspend, which we need to know about.
+ Since the code in inf_resume that normally checks for this is
+ disabled while INF->pending_execs, we do the check here instead. */
+ inf_validate_task_sc (inf);
+ }
+
+ if (inf->wait.suppress)
+ /* Some totally spurious event happened that we don't consider
+ worth returning to gdb. Just keep waiting. */
+ {
+ inf_debug (inf, "suppressing return, rewaiting...");
+ inf_resume (inf);
+ goto rewait;
+ }
+
+ /* Pass back out our results. */
+ memcpy (status, &inf->wait.status, sizeof (*status));
+
+ thread = inf->wait.thread;
+ if (thread)
+ ptid = gnu_ptid_build (inf->pid, 0, thread->tid);
+ else if (ptid_equal (ptid, minus_one_ptid))
+ thread = inf_tid_to_thread (inf, -1);
+ else
+ thread = inf_tid_to_thread (inf, gnu_get_tid (ptid));
+
+ if (!thread || thread->port == MACH_PORT_NULL)
+ {
+ /* TID is dead; try and find a new thread. */
+ if (inf_update_procs (inf) && inf->threads)
+ ptid = gnu_ptid_build (inf->pid, 0, inf->threads->tid);
+ /* The first
+ available
+ thread. */
+ else
+ ptid = inferior_ptid; /* let wait_for_inferior handle exit case */
+ }
+
+ if (thread
+ && !ptid_equal (ptid, minus_one_ptid)
+ && status->kind != TARGET_WAITKIND_SPURIOUS
+ && inf->pause_sc == 0 && thread->pause_sc == 0)
+ /* If something actually happened to THREAD, make sure we
+ suspend it. */
+ {
+ thread->sc = 1;
+ inf_update_suspends (inf);
+ }
+
+ inf_debug (inf, "returning ptid = %s, status = %s (%d)",
+ target_pid_to_str (ptid),
+ status->kind == TARGET_WAITKIND_EXITED ? "EXITED"
+ : status->kind == TARGET_WAITKIND_STOPPED ? "STOPPED"
+ : status->kind == TARGET_WAITKIND_SIGNALLED ? "SIGNALLED"
+ : status->kind == TARGET_WAITKIND_LOADED ? "LOADED"
+ : status->kind == TARGET_WAITKIND_SPURIOUS ? "SPURIOUS"
+ : "?", status->value.integer);
+
+ inferior_ptid = ptid;
+ return ptid;
+}
+
+static ptid_t
+gnu_wait (ptid_t ptid, struct target_waitstatus *status, int target_options)
+{
+ ptid_t event_ptid;
+ gnu_debug ("gnu_wait: [%s]", target_pid_to_str (ptid));
+ event_ptid = gnu_wait_1 (ptid, status, target_options);
+ gnu_debug (" -> (status->kind = %d)\n", status->kind);
+ return event_ptid;
+}
+
+/* Return printable description of proc. */
+char *
+proc_string (struct proc *proc)
+{
+ static char tid_str[80];
+
+ if (proc_is_task (proc))
+ xsnprintf (tid_str, sizeof (tid_str), "process %d", proc->inf->pid);
+ else
+ xsnprintf (tid_str, sizeof (tid_str), "Thread %d.%d",
+ proc->inf->pid, proc->tid);
+ return tid_str;
+}
+
+void
+gnu_fetch_registers (struct regcache *regcache, int regno)
+{
+ gnu_debug ("gnu_fetch_registers() regno=%d\n", regno);
+ return gnu_fetch_registers_1 (NULL, regcache, regno);
+}
+
+void
+gnu_store_registers (struct regcache *regcache, int regno)
+{
+ gnu_debug ("gnu_store_registers() regno=%d\n", regno);
+ return gnu_store_registers_1 (NULL, regcache, regno);
+}
+
+/* Read inferior task's LEN bytes from ADDR and copy it to MYADDR in
+ gdb's address space. Return 0 on failure; number of bytes read
+ otherwise. */
+int
+gnu_read_inferior (task_t task, CORE_ADDR addr, unsigned char *myaddr,
+ int length)
+{
+ error_t err;
+ vm_address_t low_address = (vm_address_t) trunc_page (addr);
+ vm_size_t aligned_length =
+ (vm_size_t) round_page (addr + length) - low_address;
+ pointer_t copied;
+ int copy_count;
+
+ /* Get memory from inferior with page aligned addresses. */
+ err = vm_read (task, low_address, aligned_length, &copied, ©_count);
+ if (err)
+ return 0;
+
+ err = hurd_safe_copyin (myaddr, (void *) (addr - low_address + copied),
+ length);
+ if (err)
+ {
+ warning (_("Read from inferior faulted: %s"), safe_strerror (err));
+ length = 0;
+ }
+
+ err = vm_deallocate (mach_task_self (), copied, copy_count);
+ if (err)
+ warning (_("gnu_read_inferior vm_deallocate failed: %s"),
+ safe_strerror (err));
+
+ return length;
+}
+
+#define CHK_GOTO_OUT(str,ret) \
+ do if (ret != KERN_SUCCESS) { errstr = #str; goto out; } while(0)
+
+struct vm_region_list
+{
+ struct vm_region_list *next;
+ vm_prot_t protection;
+ vm_address_t start;
+ vm_size_t length;
+};
+/*struct obstack region_obstack;*/
+
+/* Write gdb's LEN bytes from MYADDR and copy it to ADDR in inferior
+ task's address space. */
+int
+gnu_write_inferior (task_t task, CORE_ADDR addr, const char *myaddr,
+ int length)
+{
+ error_t err = 0;
+ vm_address_t low_address = (vm_address_t) trunc_page (addr);
+ vm_size_t aligned_length =
+ (vm_size_t) round_page (addr + length) - low_address;
+ pointer_t copied;
+ int copy_count;
+ int deallocate = 0;
+
+ char *errstr = "Bug in gnu_write_inferior";
+
+ struct vm_region_list *region_element;
+ struct vm_region_list *region_head = (struct vm_region_list *) NULL;
+
+ /* Get memory from inferior with page aligned addresses. */
+ err = vm_read (task, low_address, aligned_length, &copied, ©_count);
+ CHK_GOTO_OUT ("gnu_write_inferior vm_read failed", err);
+
+ deallocate++;
+
+ err = hurd_safe_copyout ((void *) (addr - low_address + copied),
+ myaddr, length);
+ CHK_GOTO_OUT ("Write to inferior faulted", err);
+
+ /*obstack_init (®ion_obstack); */
+
+ /* Do writes atomically.
+ First check for holes and unwritable memory. */
+ {
+ vm_size_t remaining_length = aligned_length;
+ vm_address_t region_address = low_address;
+
+ struct vm_region_list *scan;
+
+ while (region_address < low_address + aligned_length)
+ {
+ vm_prot_t protection;
+ vm_prot_t max_protection;
+ vm_inherit_t inheritance;
+ boolean_t shared;
+ mach_port_t object_name;
+ vm_offset_t offset;
+ vm_size_t region_length = remaining_length;
+ vm_address_t old_address = region_address;
+
+ err = vm_region (task,
+ ®ion_address,
+ ®ion_length,
+ &protection,
+ &max_protection,
+ &inheritance, &shared, &object_name, &offset);
+ CHK_GOTO_OUT ("vm_region failed", err);
+
+ /* Check for holes in memory. */
+ if (old_address != region_address)
+ {
+ warning (_("No memory at 0x%x. Nothing written"), old_address);
+ err = KERN_SUCCESS;
+ length = 0;
+ goto out;
+ }
+
+ if (!(max_protection & VM_PROT_WRITE))
+ {
+ warning (_("Memory at address 0x%x is unwritable. "
+ "Nothing written"), old_address);
+ err = KERN_SUCCESS;
+ length = 0;
+ goto out;
+ }
+
+ /* Chain the regions for later use. */
+ /*region_element = */
+ /*(struct vm_region_list *) */
+ /*obstack_alloc (®ion_obstack, sizeof (struct vm_region_list)); */
+ region_element =
+ (struct vm_region_list *) malloc (sizeof (struct vm_region_list));
+
+ region_element->protection = protection;
+ region_element->start = region_address;
+ region_element->length = region_length;
+
+ /* Chain the regions along with protections. */
+ region_element->next = region_head;
+ region_head = region_element;
+
+ region_address += region_length;
+ remaining_length = remaining_length - region_length;
+ }
+
+ /* If things fail after this, we give up.
+ Somebody is messing up inferior_task's mappings. */
+
+ /* Enable writes to the chained vm regions. */
+ for (scan = region_head; scan; scan = scan->next)
+ {
+ if (!(scan->protection & VM_PROT_WRITE))
+ {
+ err = vm_protect (task,
+ scan->start,
+ scan->length,
+ FALSE, scan->protection | VM_PROT_WRITE);
+ CHK_GOTO_OUT ("vm_protect: enable write failed", err);
+ }
+ }
+
+ err = vm_write (task, low_address, copied, aligned_length);
+ CHK_GOTO_OUT ("vm_write failed", err);
+
+ /* Set up the original region protections, if they were changed. */
+ for (scan = region_head; scan; scan = scan->next)
+ {
+ if (!(scan->protection & VM_PROT_WRITE))
+ {
+ err = vm_protect (task,
+ scan->start,
+ scan->length, FALSE, scan->protection);
+ CHK_GOTO_OUT ("vm_protect: enable write failed", err);
+ }
+ }
+ }
+
+out:
+ if (deallocate)
+ {
+ /*obstack_free (®ion_obstack, 0); */
+
+ (void) vm_deallocate (mach_task_self (), copied, copy_count);
+ }
+
+ if (err != KERN_SUCCESS)
+ {
+ warning (_("%s: %s"), errstr, mach_error_string (err));
+ return 0;
+ }
+
+ return length;
+}
+
+static int
+gnu_read_memory (CORE_ADDR addr, unsigned char *myaddr, int length)
+{
+ int ret = 0;
+ task_t task = (gnu_current_inf
+ ? (gnu_current_inf->task
+ ? gnu_current_inf->task->port : 0) : 0);
+ if (task == MACH_PORT_NULL)
+ return 0;
+ ret = gnu_read_inferior (task, addr, myaddr, length);
+ if (length != ret)
+ {
+ gnu_debug ("gnu_read_inferior,length=%d, but return %d\n", length, ret);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+gnu_write_memory (CORE_ADDR addr, const unsigned char *myaddr, int length)
+{
+ int ret = 0;
+ task_t task = (gnu_current_inf
+ ? (gnu_current_inf->task
+ ? gnu_current_inf->task->port : 0) : 0);
+ if (task == MACH_PORT_NULL)
+ return 0;
+ ret = gnu_write_inferior (task, addr, myaddr, length);
+ if (length != ret)
+ {
+ gnu_debug ("gnu_write_inferior,length=%d, but return %d\n", length,
+ ret);
+ return -1;
+ }
+ return 0;
+}
+
+static void
+gnu_request_interrupt (void)
+{
+ printf ("gnu_request_interrupt not support!\n");
+ exit (-1);
+}
+
+/* Helper function for child_wait and the derivatives of child_wait.
+ HOSTSTATUS is the waitstatus from wait() or the equivalent; store our
+ translation of that in OURSTATUS. */
+void
+store_waitstatus (struct target_waitstatus *ourstatus, int hoststatus)
+{
+ if (WIFEXITED (hoststatus))
+ {
+ ourstatus->kind = TARGET_WAITKIND_EXITED;
+ ourstatus->value.integer = WEXITSTATUS (hoststatus);
+ }
+ else if (!WIFSTOPPED (hoststatus))
+ {
+ ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ ourstatus->value.sig = gdb_signal_from_host (WTERMSIG (hoststatus));
+ }
+ else
+ {
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+ ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (hoststatus));
+ }
+}
+
+/* Validates INF's task suspend count. If it's higher than we expect,
+ verify with the user before `stealing' the extra count. */
+static void
+inf_validate_task_sc (struct inf *inf)
+{
+ char *noise;
+ mach_msg_type_number_t noise_len = 0;
+ struct procinfo *pi;
+ mach_msg_type_number_t pi_len = 0;
+ int info_flags = PI_FETCH_TASKINFO;
+ int suspend_count = -1;
+ error_t err;
+
+retry:
+ err = proc_getprocinfo (proc_server, inf->pid, &info_flags,
+ (procinfo_t *) & pi, &pi_len, &noise, &noise_len);
+ if (err)
+ {
+ inf->task->dead = 1; /* oh well */
+ return;
+ }
+
+ if (inf->task->cur_sc < pi->taskinfo.suspend_count && suspend_count == -1)
+ {
+ /* The proc server might have suspended the task while stopping
+ it. This happens when the task is handling a traced signal.
+ Refetch the suspend count. The proc server should be
+ finished stopping the task by now. */
+ suspend_count = pi->taskinfo.suspend_count;
+ goto retry;
+ }
+
+ suspend_count = pi->taskinfo.suspend_count;
+
+ vm_deallocate (mach_task_self (), (vm_address_t) pi, pi_len);
+ if (noise_len > 0)
+ vm_deallocate (mach_task_self (), (vm_address_t) pi, pi_len);
+
+ if (inf->task->cur_sc < suspend_count)
+ {
+#if 0
+ int abort;
+
+ target_terminal_ours (); /* Allow I/O. */
+ abort = !query (_("Pid %d has an additional task suspend count of %d;"
+ " clear it? "), inf->pid,
+ suspend_count - inf->task->cur_sc);
+ target_terminal_inferior (); /* Give it back to the child. */
+
+ if (abort)
+ error (_("Additional task suspend count left untouched."));
+#endif
+
+ //need fix!
+ inf->task->cur_sc = suspend_count;
+ }
+}
+
+void
+inf_resume (struct inf *inf)
+{
+ struct proc *thread;
+
+ inf_update_procs (inf);
+
+ for (thread = inf->threads; thread; thread = thread->next)
+ thread->sc = thread->resume_sc;
+
+ if (inf->task)
+ {
+ if (!inf->pending_execs)
+ /* Try to make sure our task count is correct -- in the case where
+ we're waiting for an exec though, things are too volatile, so just
+ assume things will be reasonable (which they usually will be). */
+ inf_validate_task_sc (inf);
+ inf->task->sc = 0;
+ }
+
+ inf_update_suspends (inf);
+}
+
+/* Converts a thread port to a struct proc. */
+struct proc *
+inf_port_to_thread (struct inf *inf, mach_port_t port)
+{
+ struct proc *thread = inf->threads;
+
+ while (thread)
+ if (thread->port == port)
+ return thread;
+ else
+ thread = thread->next;
+ return 0;
+}
+
+error_t
+S_exception_raise_request (mach_port_t port, mach_port_t reply_port,
+ thread_t thread_port, task_t task_port,
+ int exception, int code, int subcode)
+{
+ struct inf *inf = waiting_inf;
+ struct proc *thread = inf_port_to_thread (inf, thread_port);
+
+ inf_debug (waiting_inf,
+ "S_exception_raise_request thread = %d, task = %d, exc = %d,
code = %d, subcode = %d",
+ thread_port, task_port, exception, code, subcode);
+
+ if (!thread)
+ /* We don't know about thread? */
+ {
+ inf_update_procs (inf);
+ thread = inf_port_to_thread (inf, thread_port);
+ if (!thread)
+ /* Give up, the generating thread is gone. */
+ return 0;
+ }
+
+ mach_port_deallocate (mach_task_self (), thread_port);
+ mach_port_deallocate (mach_task_self (), task_port);
+
+ if (!thread->aborted)
+ /* THREAD hasn't been aborted since this exception happened (abortion
+ clears any exception state), so it must be real. */
+ {
+ /* Store away the details; this will destroy any previous info. */
+ inf->wait.thread = thread;
+
+ inf->wait.status.kind = TARGET_WAITKIND_STOPPED;
+
+ if (exception == EXC_BREAKPOINT)
+ /* GDB likes to get SIGTRAP for breakpoints. */
+ {
+ inf->wait.status.value.sig = GDB_SIGNAL_TRAP;
+ mach_port_deallocate (mach_task_self (), reply_port);
+ }
+ else
+ /* Record the exception so that we can forward it later. */
+ {
+ if (thread->exc_port == port)
+ {
+ inf_debug (waiting_inf, "Handler is thread exception port <%d>",
+ thread->saved_exc_port);
+ inf->wait.exc.handler = thread->saved_exc_port;
+ }
+ else
+ {
+ inf_debug (waiting_inf, "Handler is task exception port <%d>",
+ inf->task->saved_exc_port);
+ inf->wait.exc.handler = inf->task->saved_exc_port;
+ gdb_assert (inf->task->exc_port == port);
+ }
+ if (inf->wait.exc.handler != MACH_PORT_NULL)
+ /* Add a reference to the exception handler. */
+ mach_port_mod_refs (mach_task_self (),
+ inf->wait.exc.handler, MACH_PORT_RIGHT_SEND,
+ 1);
+
+ inf->wait.exc.exception = exception;
+ inf->wait.exc.code = code;
+ inf->wait.exc.subcode = subcode;
+ inf->wait.exc.reply = reply_port;
+
+ /* Exceptions are encoded in the signal space by putting
+ them after _NSIG; this assumes they're positive (and not
+ extremely large)! */
+ inf->wait.status.value.sig =
+ gdb_signal_from_host (_NSIG + exception);
+ }
+ }
+ else
+ /* A supppressed exception, which ignore. */
+ {
+ inf->wait.suppress = 1;
+ mach_port_deallocate (mach_task_self (), reply_port);
+ }
+ return 0;
+}
+
+error_t
+S_proc_wait_reply (mach_port_t reply, error_t err,
+ int status, int sigcode, rusage_t rusage, pid_t pid)
+{
+ struct inf *inf = waiting_inf;
+
+ inf_debug (inf,
+ "S_proc_wait_reply err = %s, pid = %d, status = 0x%x, sigcode = %d",
+ err ? safe_strerror (err) : "0", pid, status, sigcode);
+
+ if (err && proc_wait_pid && (!inf->task || !inf->task->port))
+ /* Ack. The task has died, but the task-died notification code didn't
+ tell anyone because it thought a more detailed reply from the
+ procserver was forthcoming. However, we now learn that won't
+ happen... So we have to act like the task just died, and this time,
+ tell the world. */
+ inf_task_died_status (inf);
+
+ if (--proc_waits_pending == 0)
+ /* PROC_WAIT_PID represents the most recent wait. We will always get
+ replies in order because the proc server is single threaded. */
+ proc_wait_pid = 0;
+
+ inf_debug (inf, "waits pending now: %d", proc_waits_pending);
+
+ if (err)
+ {
+ if (err != EINTR)
+ {
+ /*warning (_("Can't wait for pid %d: %s"), */
+ /*inf->pid, safe_strerror (err)); */
+ inf->no_wait = 1;
+
+ /* Since we can't see the inferior's signals, don't trap them. */
+ inf_set_traced (inf, 0);
+ }
+ }
+ else if (pid == inf->pid)
+ {
+ store_waitstatus (&inf->wait.status, status);
+ if (inf->wait.status.kind == TARGET_WAITKIND_STOPPED)
+ /* The process has sent us a signal, and stopped itself in a sane
+ state pending our actions. */
+ {
+ inf_debug (inf, "process has stopped itself");
+ inf->stopped = 1;
+ }
+ }
+ else
+ inf->wait.suppress = 1; /* Something odd happened. Ignore. */
+
+ return 0;
+}
+
+error_t
+S_msg_sig_post_untraced_reply (mach_port_t reply, error_t err)
+{
+ struct inf *inf = waiting_inf;
+
+ if (err == EBUSY)
+ /* EBUSY is what we get when the crash server has grabbed control of the
+ process and doesn't like what signal we tried to send it. Just act
+ like the process stopped (using a signal of 0 should mean that the
+ *next* time the user continues, it will pass signal 0, which the crash
+ server should like). */
+ {
+ inf->wait.status.kind = TARGET_WAITKIND_STOPPED;
+ inf->wait.status.value.sig = GDB_SIGNAL_0;
+ }
+ else if (err)
+ warning (_("Signal delivery failed: %s"), safe_strerror (err));
+
+ if (err)
+ /* We only get this reply when we've posted a signal to a process which we
+ thought was stopped, and which we expected to continue after the signal.
+ Given that the signal has failed for some reason, it's reasonable to
+ assume it's still stopped. */
+ inf->stopped = 1;
+ else
+ inf->wait.suppress = 1;
+ return 0;
+}
+
+error_t
+S_msg_sig_post_reply (mach_port_t reply, error_t err)
+{
+ printf ("bug in S_msg_sig_post_reply!!\n");
+ exit (-238);
+}
+
+error_t
+do_mach_notify_dead_name (mach_port_t notify, mach_port_t dead_port)
+{
+ struct inf *inf = waiting_inf;
+
+ inf_debug (waiting_inf, "port = %d", dead_port);
+
+ if (inf->task && inf->task->port == dead_port)
+ {
+ proc_debug (inf->task, "is dead");
+ inf->task->port = MACH_PORT_NULL;
+ if (proc_wait_pid == inf->pid)
+ /* We have a wait outstanding on the process, which will return more
+ detailed information, so delay until we get that. */
+ inf->wait.suppress = 1;
+ else
+ /* We never waited for the process (maybe it wasn't a child), so just
+ pretend it got a SIGKILL. */
+ inf_task_died_status (inf);
+ }
+ else
+ {
+ struct proc *thread = inf_port_to_thread (inf, dead_port);
+
+ if (thread)
+ {
+ proc_debug (thread, "is dead");
+ thread->port = MACH_PORT_NULL;
+ }
+
+ if (inf->task->dead)
+ /* Since the task is dead, its threads are dying with it. */
+ inf->wait.suppress = 1;
+ }
+
+ mach_port_deallocate (mach_task_self (), dead_port);
+ inf->threads_up_to_date = 0; /* Just in case. */
+
+ return 0;
+}
+
+static error_t
+ill_rpc (char *fun)
+{
+ warning (_("illegal rpc: %s"), fun);
+ return 0;
+}
+
+error_t
+do_mach_notify_no_senders (mach_port_t notify, mach_port_mscount_t count)
+{
+ return ill_rpc ("do_mach_notify_no_senders");
+}
+
+error_t
+do_mach_notify_port_deleted (mach_port_t notify, mach_port_t name)
+{
+ return ill_rpc ("do_mach_notify_port_deleted");
+}
+
+error_t
+do_mach_notify_msg_accepted (mach_port_t notify, mach_port_t name)
+{
+ return ill_rpc ("do_mach_notify_msg_accepted");
+}
+
+error_t
+do_mach_notify_port_destroyed (mach_port_t notify, mach_port_t name)
+{
+ return ill_rpc ("do_mach_notify_port_destroyed");
+}
+
+error_t
+do_mach_notify_send_once (mach_port_t notify)
+{
+ return ill_rpc ("do_mach_notify_send_once");
+}
+
+error_t
+S_proc_setmsgport_reply (mach_port_t reply, error_t err,
+ mach_port_t old_msg_port)
+{
+ return ill_rpc ("S_proc_setmsgport_reply");
+}
+
+error_t
+S_proc_getmsgport_reply (mach_port_t reply, error_t err, mach_port_t msg_port)
+{
+ return ill_rpc ("S_proc_getmsgport_reply");
+}
+
+static struct target_ops gnu_target_ops = {
+ gnu_create_inferior,
+ gnu_attach,
+ gnu_kill,
+ gnu_detach,
+ gnu_mourn,
+ gnu_join,
+ gnu_thread_alive,
+ gnu_resume,
+ gnu_wait,
+ gnu_fetch_registers,
+ gnu_store_registers,
+ NULL, /* prepare_to_access_memory */
+ NULL, /* done_accessing_memory */
+ gnu_read_memory,
+ gnu_write_memory,
+ NULL, /* look_up_symbols */
+ gnu_request_interrupt,
+ NULL, /* read_auxv */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* read_offsets */
+ NULL, /* get_tls_address */
+ NULL, /* qxfer_spu */
+ NULL, /* hostio_last_error */
+ NULL, /* qxfer_osdata */
+ NULL, /* qxfer_siginfo */
+ NULL, /* supports_non_stop */
+ NULL, /* async */
+ NULL, /* start_non_stop */
+ NULL, /* supports_multi_process */
+ NULL, /* handle_monitor_command */
+};
+
+void
+_initialize_gnu_nat (void)
+{
+ proc_server = getproc ();
+}
+
+static void
+initialize_low_arch ()
+{
+ init_registers_i386 ();
+ gnu_tdesc = tdesc_i386;
+}
+
+void
+initialize_low (void)
+{
+ set_target_ops (&gnu_target_ops);
+ initialize_low_arch ();
+ _initialize_gnu_nat ();
+}
diff --git a/gdb/gdbserver/gnu-low.h b/gdb/gdbserver/gnu-low.h
new file mode 100644
index 0000000..fa211d2
--- /dev/null
+++ b/gdb/gdbserver/gnu-low.h
@@ -0,0 +1,270 @@
+/* Copyright (C) 2010-2013 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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/>. */
+
+#include "server.h"
+
+#include <mach.h>
+#include <mach_error.h>
+#include <mach/exception.h>
+#include <mach/message.h>
+#include <mach/notify.h>
+#include <mach/vm_attributes.h>
+
+#include <hurd.h>
+#include <hurd/interrupt.h>
+#include <hurd/msg.h>
+#include <hurd/msg_request.h>
+#include <hurd/process.h>
+/* All info needed to access an architecture/mode's registers. */
+
+struct regs_info
+{
+ /* Regset support bitmap: 1 for registers that are transferred as a part
+ of a regset, 0 for ones that need to be handled individually. This
+ can be NULL if all registers are transferred with regsets or regsets
+ are not supported. */
+ unsigned char *regset_bitmap;
+
+ /* Info used when accessing registers with PTRACE_PEEKUSER /
+ PTRACE_POKEUSER. This can be NULL if all registers are
+ transferred with regsets .*/
+ struct usrregs_info *usrregs;
+
+#ifdef HAVE_gnu_REGSETS
+ /* Info used when accessing registers with regsets. */
+ struct regsets_info *regsets_info;
+#endif
+};
+
+#define ptid_of(proc) ((proc)->head.id)
+#define pid_of(proc) ptid_get_pid ((proc)->head.id)
+#define lwpid_of(proc) ptid_get_lwp ((proc)->head.id)
+
+#define get_lwp(inf) ((struct lwp_info *)(inf))
+#define get_thread_lwp(thr) (get_lwp (inferior_target_data (thr)))
+#define get_lwp_thread(proc) ((struct thread_info *) \
+ find_inferior_id (&all_threads, \
+ get_lwp (proc)->head.id))
+
+#define PROC_TID_TASK (-1)
+#define THREAD_STATE_FLAVOR i386_REGS_SEGS_STATE
+#define THREAD_STATE_SIZE i386_THREAD_STATE_COUNT
+#define THREAD_STATE_SET_TRACED(state) \
+ ((struct i386_thread_state *) (state))->efl |= 0x100
+#define THREAD_STATE_CLEAR_TRACED(state) \
+ ((((struct i386_thread_state *) (state))->efl &= ~0x100), 1)
+
+#define proc_is_task(proc) ((proc)->tid == PROC_TID_TASK)
+#define proc_is_thread(proc) ((proc)->tid != PROC_TID_TASK)
+
+#ifndef PIDGET
+#define PIDGET(PTID) (ptid_get_pid (PTID))
+#define TIDGET(PTID) (ptid_get_lwp (PTID))
+#define MERGEPID(PID, TID) ptid_build (PID, TID, 0)
+#endif
+
+struct exc_state
+ {
+ int exception; /* The exception code. */
+ int code, subcode;
+ mach_port_t handler; /* The real exception port to handle this. */
+ mach_port_t reply; /* The reply port from the exception call. */
+ };
+
+/* The results of the last wait an inf did. */
+struct inf_wait
+ {
+ struct target_waitstatus status; /* The status returned to gdb. */
+ struct exc_state exc; /* The exception that caused us to return. */
+ struct proc *thread; /* The thread in question. */
+ int suppress; /* Something trivial happened. */
+ };
+
+struct proc
+{
+ thread_t port; /* The task or thread port. */
+ int tid; /* The GDB pid (actually a thread id). */
+ int num; /* An id number for threads, to print. */
+
+ mach_port_t saved_exc_port; /* The task/thread's real exception port. */
+ mach_port_t exc_port; /* Our replacement, which for. */
+
+ int sc; /* Desired suspend count. */
+ int cur_sc; /* Implemented suspend count. */
+ int run_sc; /* Default sc when the program is running. */
+ int pause_sc; /* Default sc when gdb has control. */
+ int resume_sc; /* Sc resulting from the last resume. */
+ int detach_sc; /* SC to leave around when detaching
+ from program. */
+
+ thread_state_data_t state; /* Registers, &c. */
+ int state_valid:1; /* True if STATE is up to date. */
+ int state_changed:1;
+
+ int aborted:1; /* True if thread_abort has been called. */
+ int dead:1; /* We happen to know it's actually dead. */
+
+ /* Bit mask of registers fetched by gdb. This is used when we re-fetch
+ STATE after aborting the thread, to detect that gdb may have out-of-date
+ information. */
+ unsigned long fetched_regs;
+
+ struct inf *inf; /* Where we come from. */
+
+ struct proc *next;
+};
+
+struct inf
+{
+ /* Fields describing the current inferior. */
+
+ struct proc *task; /* The mach task. */
+ struct proc *threads; /* A linked list of all threads in TASK. */
+
+ /* True if THREADS needn't be validated by querying the task. We
+ assume that we and the task in question are the only ones
+ frobbing the thread list, so as long as we don't let any code
+ run, we don't have to worry about THREADS changing. */
+ int threads_up_to_date;
+
+ pid_t pid; /* The real system PID. */
+
+ struct inf_wait wait; /* What to return from target_wait. */
+
+ /* One thread proc in INF may be in `single-stepping mode'. This
+ is it. */
+ struct proc *step_thread;
+
+ /* The thread we think is the signal thread. */
+ struct proc *signal_thread;
+
+ mach_port_t event_port; /* Where we receive various msgs. */
+
+ /* True if we think at least one thread in the inferior could currently be
+ running. */
+ unsigned int running:1;
+
+ /* True if the process has stopped (in the proc server sense). Note that
+ since a proc server `stop' leaves the signal thread running, the inf can
+ be RUNNING && STOPPED... */
+ unsigned int stopped:1;
+
+ /* True if the inferior has no message port. */
+ unsigned int nomsg:1;
+
+ /* True if the inferior is traced. */
+ unsigned int traced:1;
+
+ /* True if we shouldn't try waiting for the inferior, usually because we
+ can't for some reason. */
+ unsigned int no_wait:1;
+
+ /* When starting a new inferior, we don't try to validate threads until all
+ the proper execs have been done. This is a count of how many execs we
+ expect to happen. */
+ unsigned pending_execs;
+
+ /* Fields describing global state. */
+
+ /* The task suspend count used when gdb has control. This is normally 1 to
+ make things easier for us, but sometimes (like when attaching to vital
+ system servers) it may be desirable to let the task continue to run
+ (pausing individual threads as necessary). */
+ int pause_sc;
+
+ /* The task suspend count left when detaching from a task. */
+ int detach_sc;
+
+ /* The initial values used for the run_sc and pause_sc of newly discovered
+ threads -- see the definition of those fields in struct proc. */
+ int default_thread_run_sc;
+ int default_thread_pause_sc;
+ int default_thread_detach_sc;
+
+ /* True if the process should be traced when started/attached. Newly
+ started processes *must* be traced at first to exec them properly, but
+ if this is false, tracing is turned off as soon it has done so. */
+ int want_signals;
+
+ /* True if exceptions from the inferior process should be trapped. This
+ must be on to use breakpoints. */
+ int want_exceptions;
+};
+
+/* Forward decls */
+struct inf *make_inf ();
+void inf_clear_wait (struct inf *inf);
+void inf_cleanup (struct inf *inf);
+void inf_startup (struct inf *inf, int pid);
+int inf_update_suspends (struct inf *inf);
+void inf_set_pid (struct inf *inf, pid_t pid);
+void inf_validate_procs (struct inf *inf);
+void inf_steal_exc_ports (struct inf *inf);
+void inf_restore_exc_ports (struct inf *inf);
+struct proc *inf_tid_to_proc (struct inf *inf, int tid);
+void inf_set_threads_resume_sc (struct inf *inf,
+ struct proc *run_thread,
+ int run_others);
+int inf_set_threads_resume_sc_for_signal_thread (struct inf *inf);
+void inf_suspend (struct inf *inf);
+void inf_resume (struct inf *inf);
+void inf_set_step_thread (struct inf *inf, struct proc *proc);
+void inf_detach (struct inf *inf);
+void inf_attach (struct inf *inf, int pid);
+void inf_signal (struct inf *inf, enum gdb_signal sig);
+void inf_continue (struct inf *inf);
+int inf_update_procs (struct inf *inf);
+
+void proc_abort (struct proc *proc, int force);
+struct proc *make_proc (struct inf *inf, mach_port_t port, int tid);
+struct proc *_proc_free (struct proc *proc);
+int proc_update_sc (struct proc *proc);
+error_t proc_get_exception_port (struct proc *proc, mach_port_t * port);
+error_t proc_set_exception_port (struct proc *proc, mach_port_t port);
+static mach_port_t _proc_get_exc_port (struct proc *proc);
+void proc_steal_exc_port (struct proc *proc, mach_port_t exc_port);
+void proc_restore_exc_port (struct proc *proc);
+int proc_trace (struct proc *proc, int set);
+static void inf_validate_task_sc (struct inf *inf);
+static void inf_validate_procinfo (struct inf *inf);
+
+//gdbserver use ptid_t not the same as gdb does!
+static ptid_t gnu_ptid_build(int pid,long lwp,long tid);
+
+//add for erase warning
+extern const char * host_address_to_string (const void *addr);
+
+extern int debug_flags;
+#define inf_debug(_inf, msg, args...) \
+ do { struct inf *__inf = (_inf); \
+ debug ("{inf %d %s}: " msg, __inf->pid, \
+ host_address_to_string (__inf) , ##args); } while (0)
+
+#define proc_debug(_proc, msg, args...) \
+ do { struct proc *__proc = (_proc); \
+ debug ("{proc %d/%d %s}: " msg, \
+ __proc_pid (__proc), __proc->tid, \
+ host_address_to_string (__proc) , ##args); } while (0)
+
+#define debug(msg, args...) \
+ do { if (debug_flags) \
+ printf ("%s:%d: " msg "\r\n", \
+ __FILE__ , __LINE__ , ##args); } while (0)
+#ifndef safe_strerror
+#define safe_strerror(err) \
+ "XXXX"
+#endif
diff --git a/gdb/gdbserver/hostio.c b/gdb/gdbserver/hostio.c
index df94d31..f3af499 100644
--- a/gdb/gdbserver/hostio.c
+++ b/gdb/gdbserver/hostio.c
@@ -25,6 +25,10 @@
#include <limits.h>
#include <unistd.h>
+#ifndef PATH_MAX
+#define PATH_MAX 512
+#endif
+
extern int remote_debug;
struct fd_list
diff --git a/gdb/gdbserver/i386gnu.mh b/gdb/gdbserver/i386gnu.mh
new file mode 100644
index 0000000..3bdffe7
--- /dev/null
+++ b/gdb/gdbserver/i386gnu.mh
@@ -0,0 +1,30 @@
+# Host: Intel 386 running the GNU Hurd
+NATDEPFILES= notify_S.o process_reply_S.o msg_reply_S.o \
+ exc_request_U.o exc_request_S.o
+
+# Use our own user stubs for the msg rpcs, so we can make them time out, in
+# case the program is fucked, or we guess the wrong signal thread.
+msg-MIGUFLAGS = -D'MSG_IMPORTS=waittime 1000;'
+
+# ick
+MIGCOM = $(MIG) -cc cat - /dev/null
+
+# Reply servers need special massaging of the code mig generates, to make
+# them work correctly for error returns in some cases.
+%_reply_S.h %_reply_S.c: %_reply.defs
+ $(CPP) $(CPPFLAGS) -DSERVERPREFIX=S_ -x c $< \
+ | $(MIGCOM) -sheader $*_reply_S.h -server $*_reply_S.raw -user
/dev/null -header /dev/null \
+ && $(AWK) -f $(srcdir)/reply_mig_hack.awk < $*_reply_S.raw > $*_reply_S.c
+# Normal servers
+%_S.h %_S.c: %.defs
+ $(CPP) $(CPPFLAGS) -DSERVERPREFIX=S_ -x c $< \
+ | $(MIGCOM) -sheader $*_S.h -server $*_S.c -user /dev/null -header /dev/null
+# User rpc stubs
+%_U.h %_U.c: %.defs
+ $(CPP) $(CPPFLAGS) $($*-MIGUFLAGS) -x c $< \
+ | $(MIGCOM) -sheader /dev/null -server /dev/null -user $*_U.c -header $*_U.h
+
+NAT_GENERATED_FILES = notify_S.h notify_S.c \
+ process_reply_S.h process_reply_S.c \
+ msg_reply_S.h msg_reply_S.c \
+ exc_request_U.h exc_request_U.c exc_request_S.h exc_request_S.c
diff --git a/gdb/gdbserver/msg.defs b/gdb/gdbserver/msg.defs
new file mode 100644
index 0000000..7c9fcd1
--- /dev/null
+++ b/gdb/gdbserver/msg.defs
@@ -0,0 +1 @@
+#include <hurd/msg.defs>
diff --git a/gdb/gdbserver/msg_reply.defs b/gdb/gdbserver/msg_reply.defs
new file mode 100644
index 0000000..049bfa8
--- /dev/null
+++ b/gdb/gdbserver/msg_reply.defs
@@ -0,0 +1 @@
+#include <hurd/msg_reply.defs>
diff --git a/gdb/gdbserver/notify.defs b/gdb/gdbserver/notify.defs
new file mode 100644
index 0000000..2014be5
--- /dev/null
+++ b/gdb/gdbserver/notify.defs
@@ -0,0 +1 @@
+#include <mach/notify.defs>
diff --git a/gdb/gdbserver/process_reply.defs b/gdb/gdbserver/process_reply.defs
new file mode 100644
index 0000000..824b5c6
--- /dev/null
+++ b/gdb/gdbserver/process_reply.defs
@@ -0,0 +1 @@
+#include <hurd/process_reply.defs>
diff --git a/gdb/gdbserver/reply_mig_hack.awk b/gdb/gdbserver/reply_mig_hack.awk
new file mode 100644
index 0000000..97e080f
--- /dev/null
+++ b/gdb/gdbserver/reply_mig_hack.awk
@@ -0,0 +1,122 @@
+# Reply server mig-output massager
+#
+# Copyright (C) 1995-2013 Free Software Foundation, Inc.
+#
+# Written by Miles Bader <miles@gnu.ai.mit.edu>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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/>.
+#
+# This awk script hacks the output of mig-generated reply server code
+# so that it allows replies with just the error-code in them (as this is
+# how mig returns errors).
+#
+# It is highly, highly, dependent on the exact format of mig output. Ick.
+#
+
+BEGIN { parse_phase = 0; }
+
+/^}/ { parse_phase = 0; }
+
+parse_phase == 0 && /^mig_internal void _X[a-zA-Z0-9_]*_reply/ {
+ # The start of a mig server routine. Reset everything. Note that we only
+ # mess with rpcs that have the suffix `_reply'.
+ num_args = 0;
+ num_checks = 0;
+ parse_phase = 1;
+ print; next;
+}
+
+parse_phase == 1 && /^[\t ]*typedef struct/ {
+ # The first structure in the server routine should describe the arguments
+ parse_phase = 2;
+ print; next;
+}
+
+parse_phase == 2 {
+ # The message header field in the args structure, which skip.
+ parse_phase = 3;
+ print; next;
+}
+
+parse_phase == 3 && /}/ {
+ # The args structure is over.
+ if (num_args > 1)
+ parse_phase = 5;
+ else
+ # There's no extra args that could screw up the normal mechanism for
+ # error returns, so we don't have to insert any new code.
+ parse_phase = 0;
+ print; next;
+}
+
+parse_phase == 3 {
+ # The type field for an argument.
+ arg_type_code_name[num_args] = $2;
+ sub (/;$/, "", arg_type_code_name[num_args]) # Get rid of the semi-colon
+ parse_phase = 4;
+ print; next;
+}
+
+parse_phase == 4 {
+ # The value field for an argument.
+ arg_name[num_args] = $2;
+ sub (/;$/, "", arg_name[num_args]) # Get rid of the semi-colon
+ arg_type[num_args] = $1;
+ num_args++;
+ parse_phase = 3;
+ print; next;
+}
+
+parse_phase == 5 && /^[ \t]*(auto|static) const mach_msg_type_t/ {
+ # The type check structure for an argument.
+ arg_check_name[num_checks] = $4;
+ num_checks++;
+ print; next;
+}
+
+parse_phase == 5 && /^[ \t]*mig_external kern_return_t/ {
+ # The declaration of the user server function for this rpc.
+ user_function_name = $3;
+ print; next;
+}
+
+parse_phase == 5 && /^#if[ \t]TypeCheck/ {
+ # The first args type checking statement; we need to insert our chunk of
+ # code that bypasses all the type checks if this is an error return, after
+ # which we're done until we get to the next function. Handily, the size
+ # of mig's Reply structure is also the size of the alternate Request
+ # structure that we want to check for.
+ print "\tif (In0P->Head.msgh_size == sizeof (Reply)";
+ print "\t && ! (In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)";
+ print "\t && ! BAD_TYPECHECK(&In0P->" arg_type_code_name[0] ",
&" arg_check_name[0] ")";
+ print "\t && In0P->" arg_name[0] " != 0)";
+ print "\t /* Error return, only the error code argument is passed. */";
+ print "\t {";
+ # Force the function into a type that only takes the first two args, via
+ # the temp variable SFUN (is there another way to correctly do this cast?).
+ # This is possibly bogus, but easier than supplying bogus values for all
+ # the other args (we can't just pass 0 for them, as they might not
be scalar).
+ printf ("\t kern_return_t (*sfun)(mach_port_t");
+ for (i = 0; i < num_args; i++)
+ printf (", %s", arg_type[i]);
+ printf (") = %s;\n", user_function_name);
+ print "\t OutP->RetCode = (*(kern_return_t (*)(mach_port_t,
kern_return_t))sfun) (In0P->Head.msgh_request_port, In0P->"
arg_name[0] ");";
+ print "\t return;";
+ print "\t }";
+ print "";
+ parse_phase = 0;
+ print; next;
+}
+
+{ print; }
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 18d060c..20e88bf 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -91,7 +91,8 @@ typedef unsigned char gdb_byte;
/* FIXME: This should probably be autoconf'd for. It's an integer type at
least the size of a (void *). */
-typedef long long CORE_ADDR;
+//typedef long long CORE_ADDR;
+typedef long CORE_ADDR;
typedef long long LONGEST;
typedef unsigned long long ULONGEST;
diff --git a/gdb/gdbserver/utils.c b/gdb/gdbserver/utils.c
index 9706d74..d6dd4f9 100644
--- a/gdb/gdbserver/utils.c
+++ b/gdb/gdbserver/utils.c
@@ -170,7 +170,6 @@ internal_error (const char *file, int line, const
char *fmt, ...)
#define CELLSIZE 50
/* Return the next entry in the circular buffer. */
-
static char *
get_cell (void)
{
@@ -181,6 +180,15 @@ get_cell (void)
return buf[cell];
}
+const char *
+host_address_to_string (const void *addr)
+{
+ char *str = get_cell ();
+
+ xsnprintf (str, CELLSIZE, "0x%s", phex_nz ((unsigned long long)
addr, sizeof (addr)));
+ return str;
+}
+
static char *
decimal2str (char *sign, ULONGEST addr)
{
--
Yue Lu (陆岳)
- [PATCH 2/2] Port gdbserver to GNU/Hurd,
Yue Lu <=
- Re: [PATCH 1/2] Port gdbserver to GNU/Hurd, Thomas Schwinge, 2013/09/03
- Re: [PATCH 1/2] Port gdbserver to GNU/Hurd, Pedro Alves, 2013/09/03
- Re: [PATCH 1/2] Port gdbserver to GNU/Hurd, Yue Lu, 2013/09/03
- Re: [PATCH 1/2] Port gdbserver to GNU/Hurd, Yue Lu, 2013/09/05
- Re: [PATCH 1/2] Port gdbserver to GNU/Hurd, Pedro Alves, 2013/09/05
- Re: [PATCH 1/2] Port gdbserver to GNU/Hurd, Joel Brobecker, 2013/09/05
- Re: [PATCH 1/2] Port gdbserver to GNU/Hurd, Thomas Schwinge, 2013/09/05
- Re: [PATCH 1/2] Port gdbserver to GNU/Hurd, Yue Lu, 2013/09/08