From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 29037 invoked by alias); 3 Sep 2013 08:03:36 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 29027 invoked by uid 89); 3 Sep 2013 08:03:36 -0000 Received: from mail-la0-f46.google.com (HELO mail-la0-f46.google.com) (209.85.215.46) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Tue, 03 Sep 2013 08:03:36 +0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.6 required=5.0 tests=AWL,BAYES_50,FREEMAIL_FROM,NO_RELAYS autolearn=ham version=3.3.2 X-HELO: mail-la0-f46.google.com Received: by mail-la0-f46.google.com with SMTP id eh20so4250534lab.5 for ; Tue, 03 Sep 2013 01:03:18 -0700 (PDT) X-Received: by 10.112.126.37 with SMTP id mv5mr24477734lbb.20.1378195398047; Tue, 03 Sep 2013 01:03:18 -0700 (PDT) MIME-Version: 1.0 Received: by 10.152.130.194 with HTTP; Tue, 3 Sep 2013 01:03:02 -0700 (PDT) From: Yue Lu Date: Tue, 03 Sep 2013 08:03:00 -0000 Message-ID: Subject: [PATCH 2/2] Port gdbserver to GNU/Hurd To: gdb-patches , bug-hurd@gnu.org Cc: Thomas Schwinge , Luis Machado Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable X-IsSubscribed: yes X-SW-Source: 2013-09/txt/msg00064.txt.bz2 Hi all, 2013-09-03 Yue Lu * 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.d= efs. * 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 =3D @INSTALL_DATA@ RANLIB =3D @RANLIB@ CC =3D @CC@ +MIG =3D @MIG@ +AWK =3D @AWK@ # Dependency tracking information. DEPMODE =3D @CCDEPMODE@ @@ -172,7 +174,7 @@ OBS =3D 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 =3D gdbreplay.o version.o GDBSERVER_LIBS =3D @GDBSERVER_LIBS@ XM_CLIBS =3D @LIBS@ @@ -195,6 +197,10 @@ CLEANDIRS =3D $(SUBDIRS) # The format here is for the `case' shell command. REQUIRED_SUBDIRS =3D $(GNULIB_BUILDDIR) +# Host-dependent makefile fragment comes in here. +@host_makefile_frag@ +# End of host-dependent makefile fragment + FLAGS_TO_PASS =3D \ "prefix=3D$(prefix)" \ "exec_prefix=3D$(exec_prefix)" \ @@ -228,7 +234,7 @@ FLAGS_TO_PASS =3D \ "RUNTESTFLAGS=3D$(RUNTESTFLAGS)" # All generated files which can be included by another file. -generated_files =3D config.h $(GNULIB_H) +generated_files =3D 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=3D +case $host_os in + gnu*) + #Needed for GNU Hurd hosts. + AC_PROG_AWK + AC_CHECK_TOOL(MIG, mig) + if test x"$MIG" =3D x; then + AC_MSG_ERROR([MIG not found but required for $host hosts]) + fi + host_makefile_frag=3D${srcdir}/i386gnu.mh + if test ! -f ${host_makefile_frag}; then + AC_MSG_ERROR("*** Gdb does not support native target ${host}") + fi + frags=3D"$frags $host_makefile_frag" + ;; + *) + host_makefile_frag=3D/dev/null + ;; +esac + +echo "host_makefile_frag=3D$host_makefile_frag" + echo "frags=3D$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=3Dyes ipa_obj=3D"${ipa_i386_linux_regobj} linux-i386-ipa.o" ;; + i[34567]86-*-gnu*) srv_regobj=3D"$srv_i386_regobj" + srv_tgtobj=3D"gnu-low.o gnu-i386-low.o" + srv_xmlfiles=3D"$srv_i386_xmlfiles" + ;; + i[34567]86-*-lynxos*) srv_regobj=3D"i386.o" srv_tgtobj=3D"lynx-low.o lynx-i386-low.o" srv_xmlfiles=3D"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 + +#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 . = */ +#include "server.h" +#include "target.h" + +#include "gnu-low.h" + +#include +#include +#include +#include +#include +#include "gdb_wait.h" +#include + +#include +#include +#include +#include + +#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[] =3D { + 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[] =3D { + CREG_OFFSET (EAX), CREG_OFFSET (ECX), CREG_OFFSET (EDX), CREG_OFFSET (EB= X), + CREG_OFFSET (UESP), CREG_OFFSET (EBP), CREG_OFFSET (ESI), CREG_OFFSET (E= DI), + 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[regn= um]) + + +/* 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 =3D i386_FLOAT_STATE_COUNT; + struct i386_float_state state; + error_t err; + + err =3D 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 =3D 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 =3D=3D -1) + { + thread_state_t state; + + /* This does the dirty work for us. */ + state =3D proc_get_state (thread, 0); + if (!state) + { + warning (_("Couldn't fetch registers from %s"), + proc_string (thread)); + + return; + } + + if (regno =3D=3D -1) + { + int i; + + proc_debug (thread, "fetching all register"); + + for (i =3D 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 =3D ~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 |=3D (1 << regno); + } + } + + if (regno >=3D I386_NUM_GREGS || regno =3D=3D -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 re= gno) +{ + gnu_debug ("store_fpregs() not support now\n"); +#if 0 + mach_msg_type_number_t count =3D i386_FLOAT_STATE_COUNT; + struct i386_float_state state; + error_t err; + + err =3D 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 =3D 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 =3D=3D -1. */ +void +gnu_store_registers_1 (struct target_ops *ops, + struct regcache *regcache, int regno) +{ + struct proc *thread; + /*struct gdbarch *gdbarch =3D get_regcache_arch (regcache); */ + const struct target_desc *gdbarch =3D regcache->tdesc; + + /* Make sure we know about new threads. */ + inf_update_procs (gnu_current_inf); + + thread =3D 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 =3D=3D -1) + { + thread_state_t state; + thread_state_data_t old_state; + int was_aborted =3D thread->aborted; + int was_valid =3D thread->state_valid; + int trace; + + if (!was_aborted && was_valid) + memcpy (&old_state, &thread->state, sizeof (old_state)); + + state =3D 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 =3D ((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 =3D 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 >=3D 0 && regno !=3D 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 =3D=3D -1) + { + int i; + + proc_debug (thread, "storing all registers"); + + for (i =3D 0; i < I386_NUM_GREGS; i++) + /*if (REG_VALID =3D=3D 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 =3D=3D 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 &=3D ~0x100; + ((struct i386_thread_state *) state)->efl |=3D trace; + } + + if (regno >=3D I386_NUM_GREGS || regno =3D=3D -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 . = */ + +#include "server.h" +#include "target.h" + +#include "gnu-low.h" + +#include +#include +#include +#include +#include +#include "gdb_wait.h" +#include + +#include +#include +#include +#include + +#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 =3D 0; + +/* The number of wait requests we've sent, and expect replies from. */ +int proc_waits_pending =3D 0; + +int using_threads =3D 1; + +struct inf *gnu_current_inf =3D NULL; +struct inf *waiting_inf =3D NULL; +static process_t proc_server =3D MACH_PORT_NULL; +static int next_thread_id =3D 1; +ptid_t inferior_ptid; + +/* Evaluate RPC_EXPR in a scope with the variables MSGPORT and REFPORT bou= nd + 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 =3D 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 =3D 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 =3D 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 =3D inf->threads; thread; thread =3D thread->next) + if (thread =3D=3D run_thread) + thread->resume_sc =3D 0; + else if (run_others) + thread->resume_sc =3D thread->run_sc; + else + thread->resume_sc =3D thread->pause_sc; +} + +void +inf_clear_wait (struct inf *inf) +{ + inf_debug (inf, "clearing wait"); + inf->wait.status.kind =3D TARGET_WAITKIND_SPURIOUS; + inf->wait.thread =3D 0; + inf->wait.suppress =3D 0; + if (inf->wait.exc.handler !=3D MACH_PORT_NULL) + { + mach_port_deallocate (mach_task_self (), inf->wait.exc.handler); + inf->wait.exc.handler =3D MACH_PORT_NULL; + } + if (inf->wait.exc.reply !=3D MACH_PORT_NULL) + { + mach_port_deallocate (mach_task_self (), inf->wait.exc.reply); + inf->wait.exc.reply =3D 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 =3D 0; + int delta =3D proc->sc - proc->cur_sc; + + if (delta) + gnu_debug ("sc: %d --> %d", proc->cur_sc, proc->sc); + + if (proc->sc =3D=3D 0 && proc->state_changed) + /* Since PROC may start running, we must write back any state changes.= */ + { + gdb_assert (proc_is_thread (proc)); + err =3D thread_set_state (proc->port, THREAD_STATE_FLAVOR, + (thread_state_t) & proc->state, + THREAD_STATE_SIZE); + if (!err) + proc->state_changed =3D 0; + } + + if (delta > 0) + { + while (delta-- > 0 && !err) + { + if (proc_is_task (proc)) + err =3D task_suspend (proc->port); + else + err =3D thread_suspend (proc->port); + } + } + else + { + while (delta++ < 0 && !err) + { + if (proc_is_task (proc)) + err =3D task_resume (proc->port); + else + err =3D thread_resume (proc->port); + } + } + if (!err) + proc->cur_sc =3D proc->sc; + + /* If we got an error, then the task/thread has disappeared. */ + running =3D !err && proc->sc =3D=3D 0; + + proc_debug (proc, "is %s", + err ? "dead" : running ? "running" : "suspended"); + if (err) + proc_debug (proc, "err =3D %s", safe_strerror (err)); + + if (running) + { + proc->aborted =3D 0; + proc->state_valid =3D proc->state_changed =3D 0; + proc->fetched_regs =3D 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 =3D 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 =3D MACH_PORT_NULL; + if (proc->saved_exc_port) + mach_port_deallocate (mach_task_self (), proc->saved_exc_port); + proc->saved_exc_port =3D MACH_PORT_NULL; + } + + return exc_port; +} + +void +inf_set_traced (struct inf *inf, int on) +{ + if (on =3D=3D inf->traced) + return; + + if (inf->task && !inf->task->dead) + /* Make it take effect immediately. */ + { + sigset_t mask =3D on ? ~(sigset_t) 0 : 0; + error_t err =3D + INF_RESUME_MSGPORT_RPC (inf, msg_set_init_int (msgport, refport, + INIT_TRACEMASK, mask)); + + if (err =3D=3D EIEIO) + { + /*if (on) */ + /*warning (_("Can't modify tracing state for pid %d: %s"), */ + /*inf->pid, "No signal thread"); */ + inf->traced =3D on; + } + else if (err) + ; + /*warning (_("Can't modify tracing state for pid %d: %s"), */ + /*inf->pid, safe_strerror (err)); */ + else + inf->traced =3D on; + } + else + inf->traced =3D 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 =3D 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 =3D (task->sc =3D=3D 0), thread_running =3D 0; + + if (task->sc > task->cur_sc) + /* The task is becoming _more_ suspended; do before any threads. */ + task_running =3D 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 =3D 1; + + /* Do all the thread suspend counts. */ + for (thread =3D inf->threads; thread; thread =3D thread->next) + thread_running |=3D proc_update_sc (thread); + + if (task->sc !=3D task->cur_sc) + /* We didn't do the task first, because we wanted to wait for the + threads; do it now. */ + task_running =3D proc_update_sc (task); + + inf_debug (inf, "%srunning...", + (thread_running && task_running) ? "" : "not "); + + inf->running =3D 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 =3D 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 =3D proc->inf; + int running =3D (proc->cur_sc =3D=3D 0 && inf->task->cur_sc =3D=3D 0= ); + + if (running && force) + { + proc->sc =3D 1; + inf_update_suspends (proc->inf); + running =3D 0; + /*warning (_("Stopped %s."), proc_string (proc)); */ + } + else if (proc =3D=3D inf->wait.thread && inf->wait.exc.reply && !for= ce) + /* An exception is pending on PROC, which don't mess with. */ + running =3D 1; + + if (!running) + /* We only abort the thread if it's not actually running. */ + { + thread_abort (proc->port); + proc_debug (proc, "aborted"); + proc->aborted =3D 1; + } + else + proc_debug (proc, "not aborting"); + } +} + +thread_state_t +proc_get_state (struct proc *proc, int will_modify) +{ + int was_aborted =3D 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 =3D 0; + + if (!proc->state_valid) + { + mach_msg_type_number_t state_size =3D THREAD_STATE_SIZE; + error_t err =3D thread_get_state (proc->port, THREAD_STATE_FLAVOR, + (thread_state_t) & proc->state, + &state_size); + + proc_debug (proc, "getting thread state"); + proc->state_valid =3D !err; + } + + if (proc->state_valid) + { + if (will_modify) + proc->state_changed =3D 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 =3D _proc_get_exc_port (proc); + + if (cur_exc_port) + { + error_t err =3D 0; + + proc_debug (proc, "inserting exception port: %d", exc_port); + + if (cur_exc_port !=3D exc_port) + /* Put in our exception port. */ + err =3D proc_set_exception_port (proc, exc_port); + + if (err || cur_exc_port =3D=3D 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 =3D cur_exc_port; + } + + proc_debug (proc, "saved exception port: %d", proc->saved_exc_port); + + if (!err) + proc->exc_port =3D 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 =3D 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 =3D=3D 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 =3D _proc_get_exc_port (proc); + + if (cur_exc_port) + { + error_t err =3D 0; + + proc_debug (proc, "restoring real exception port"); + + if (proc->exc_port =3D=3D cur_exc_port) + /* Our's is still there. */ + err =3D 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 =3D MACH_PORT_NULL; + + if (!err) + proc->exc_port =3D 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 !=3D thread) + { + if (inf->step_thread && inf->step_thread->port !=3D MACH_PORT_NULL) + if (!proc_trace (inf->step_thread, 0)) + return; + if (thread && proc_trace (thread, 1)) + inf->step_thread =3D thread; + else + inf->step_thread =3D 0; + } +} + +struct proc * +_proc_free (struct proc *proc) +{ + struct inf *inf =3D proc->inf; + struct proc *next =3D proc->next; + + if (proc =3D=3D inf->step_thread) + /* Turn off single stepping. */ + inf_set_step_thread (inf, 0); + if (proc =3D=3D inf->wait.thread) + inf_clear_wait (inf); + if (proc =3D=3D inf->signal_thread) + inf->signal_thread =3D 0; + + if (proc->port !=3D MACH_PORT_NULL) + { + if (proc->exc_port !=3D MACH_PORT_NULL) + /* Restore the original exception port. */ + proc_restore_exc_port (proc); + if (proc->cur_sc !=3D 0) + /* Resume the thread/task. */ + { + proc->sc =3D 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 =3D MACH_PORT_NULL; + struct proc *proc =3D xmalloc (sizeof (struct proc)); + + proc->port =3D port; + proc->tid =3D tid; + proc->inf =3D inf; + proc->next =3D 0; + proc->saved_exc_port =3D MACH_PORT_NULL; + proc->exc_port =3D MACH_PORT_NULL; + + proc->sc =3D 0; + proc->cur_sc =3D 0; + + /* Note that these are all the values for threads; the task simply uses = the + corresponding field in INF directly. */ + proc->run_sc =3D inf->default_thread_run_sc; + proc->pause_sc =3D inf->default_thread_pause_sc; + proc->detach_sc =3D inf->default_thread_detach_sc; + proc->resume_sc =3D proc->run_sc; + + proc->aborted =3D 0; + proc->dead =3D 0; + proc->state_valid =3D 0; + proc->state_changed =3D 0; + + proc_debug (proc, "is new"); + + /* Get notified when things die. */ + err =3D + 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 !=3D 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 =3D 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 =3D inf->threads ? !inf->running : 0; + + if (task) + { + error_t err =3D task_threads (task->port, &threads, &num_threads); + + inf_debug (inf, "fetching threads"); + if (err) + /* TASK must be dead. */ + { + task->dead =3D 1; + task =3D 0; + } + } + + if (!task) + { + num_threads =3D 0; + inf_debug (inf, "no task"); + } + + { + /* Make things normally linear. */ + mach_msg_type_number_t search_start =3D 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 =3D 0; + /* The current thread we're considering. */ + struct proc *thread =3D inf->threads; + + memset (matched, 0, sizeof (matched)); + + while (thread) + { + mach_msg_type_number_t left; + + for (i =3D search_start, left =3D num_threads; left; i++, left--) + { + if (i >=3D num_threads) + i -=3D num_threads; /* I wrapped around. */ + if (thread->port =3D=3D threads[i]) + /* We already know about this thread. */ + { + matched[i] =3D thread; + last =3D thread; + thread =3D thread->next; + search_start++; + break; + } + } + + if (!left) + { + proc_debug (thread, "died!"); + + ptid_t ptid; + ptid =3D gnu_ptid_build (inf->pid, 0, thread->tid); + if (find_thread_ptid (ptid)) + remove_thread (find_thread_ptid (ptid)); + + thread->port =3D MACH_PORT_NULL; + thread =3D _proc_free (thread); /* THREAD is dead. */ + if (last) + last->next =3D thread; + else + inf->threads =3D thread; + } + } + + for (i =3D 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 =3D make_proc (inf, threads[i], next_thread_id++); + if (last) + last->next =3D thread; + else + inf->threads =3D thread; + last =3D thread; + proc_debug (thread, "new thread: %d", threads[i]); + + ptid =3D 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 !=3D 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=3D%d, tid=3D%d\n", inf->pid, + thread->tid); + add_thread (ptid, thread); + inferior_ptid =3D 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 =3D inf->task; + + inf_debug (inf, "setting pid: %d", pid); + + if (pid < 0) + task_port =3D MACH_PORT_NULL; + else + { + error_t err =3D 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 !=3D task_port) + { + inf->task =3D 0; + inf_validate_procs (inf); /* Trash all the threads. */ + _proc_free (task); /* And the task. */ + } + + if (task_port !=3D MACH_PORT_NULL) + { + inf->task =3D make_proc (inf, task_port, PROC_TID_TASK); + inf->threads_up_to_date =3D 0; + } + + if (inf->task) + { + inf->pid =3D pid; + if (inf->pause_sc) + /* Reflect task_suspend above. */ + inf->task->sc =3D inf->task->cur_sc =3D 1; + } + else + inf->pid =3D -1; +} + +void +inf_cleanup (struct inf *inf) +{ + inf_debug (inf, "cleanup"); + + inf_clear_wait (inf); + + inf_set_pid (inf, -1); + inf->pid =3D 0; + inf->running =3D 0; + inf->stopped =3D 0; + inf->nomsg =3D 1; + inf->traced =3D 0; + inf->no_wait =3D 0; + inf->pending_execs =3D 0; + + if (inf->event_port) + { + mach_port_destroy (mach_task_self (), inf->event_port); + inf->event_port =3D MACH_PORT_NULL; + } +} + +void +inf_startup (struct inf *inf, int pid) +{ + error_t err; + + inf_debug (inf, "startup: pid =3D %d", pid); + + inf_cleanup (inf); + + /* Make the port on which we receive all events. */ + err =3D 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 =3D 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 =3D inf->detach_sc; + + for (thread =3D inf->threads; thread; thread =3D thread->next) + { + proc_restore_exc_port (thread); + thread->sc =3D 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 =3D xmalloc (sizeof (struct inf)); + + inf->task =3D 0; + inf->threads =3D 0; + inf->threads_up_to_date =3D 0; + inf->pid =3D 0; + inf->wait.status.kind =3D TARGET_WAITKIND_SPURIOUS; + inf->wait.thread =3D 0; + inf->wait.exc.handler =3D MACH_PORT_NULL; + inf->wait.exc.reply =3D MACH_PORT_NULL; + inf->step_thread =3D 0; + inf->signal_thread =3D 0; + inf->event_port =3D MACH_PORT_NULL; + inf->running =3D 0; + inf->stopped =3D 0; + inf->nomsg =3D 1; + inf->traced =3D 0; + inf->no_wait =3D 0; + inf->pending_execs =3D 0; + inf->pause_sc =3D 1; + inf->detach_sc =3D 0; + inf->default_thread_run_sc =3D 0; + inf->default_thread_pause_sc =3D 0; + inf->default_thread_detach_sc =3D 0; + inf->want_signals =3D 1; /* By default */ + inf->want_exceptions =3D 1; /* By default */ + + return inf; +} + +static struct inf * +cur_inf (void) +{ + if (!gnu_current_inf) + gnu_current_inf =3D make_inf (); + return gnu_current_inf; +} + + +static struct process_info * +gnu_add_process (int pid, int attached) +{ + struct process_info *proc; + + proc =3D add_process (pid, attached); + proc->tdesc =3D gnu_tdesc; + proc->private =3D xcalloc (1, sizeof (*proc->private)); + proc->private->inf =3D cur_inf (); + struct inf *inf =3D gnu_current_inf; + + inf_attach (inf, pid); + inf->pending_execs =3D 2; + inf->nomsg =3D 1; + inf->traced =3D 1; + + inf_resume (inf); + + return proc; +} + +static int +gnu_create_inferior (char *program, char **allargs) +{ + int pid; + pid =3D fork (); + if (pid < 0) + perror_with_name ("fork"); + + if (pid =3D=3D 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 =3D cur_inf (); + /*struct inferior *inferior; */ + + if (pid =3D=3D 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 =3D gnu_ptid_build (pid, 0, inf_pick_first_thread ()); + + inf_validate_procinfo (inf); + inf->signal_thread =3D 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 =3D gnu_current_inf->task; + struct process_info *process; + + process =3D 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 =3D find_process_pid (pid); + if (process =3D=3D NULL) + return -1; + + inf_detach (gnu_current_inf); + + inferior_ptid =3D 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 =3D 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) !=3D 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 =3D TARGET_WAITKIND_SIGNALLED; + inf->wait.status.value.sig =3D GDB_SIGNAL_KILL; +} + +struct proc * +inf_tid_to_thread (struct inf *inf, int tid) +{ + struct proc *thread =3D inf->threads; + + gnu_debug ("[inf_tid_to_thread]:search thread which tid=3D%d\n", tid); + + while (thread) + if (thread->tid =3D=3D tid) + return thread; + else + thread =3D 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 =3D 0; + struct procinfo *pi; + mach_msg_type_number_t pi_len =3D 0; + int info_flags =3D 0; + error_t err =3D proc_getprocinfo (proc_server, inf->pid, &info_flags, + (procinfo_t *) & pi, &pi_len, &noise, + &noise_len); + + if (!err) + { + inf->stopped =3D !!(pi->state & PI_STOPPED); + inf->nomsg =3D !!(pi->state & PI_NOMSG); + if (inf->nomsg) + inf->traced =3D !!(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 =3D 0; + int host_sig =3D gdb_signal_to_host (sig); + +#define NAME gdb_signal_to_name (sig) + + if (host_sig >=3D _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 =3D &inf->wait; + + if (w->status.kind =3D=3D TARGET_WAITKIND_STOPPED + && w->status.value.sig =3D=3D 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 =3D &w->exc; + + inf_debug (inf, "passing through exception:" + " task =3D %d, thread =3D %d, exc =3D %d" + ", code =3D %d, subcode =3D %d", + w->thread->port, inf->task->port, + e->exception, e->code, e->subcode); + err =3D + 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 =3D + 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 =3D 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 =3D + INF_RESUME_MSGPORT_RPC (inf, + msg_sig_post_untraced (msgport, host_sig, + 0, refport)); + } + + if (err =3D=3D 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 =3D proc_pid2proc (proc_server, inf->pid, &proc); + + if (!err) + { + inf_debug (inf, "continuing process"); + + err =3D proc_mark_cont (proc); + if (!err) + { + struct proc *thread; + + for (thread =3D inf->threads; thread; thread =3D thread->next) + thread_resume (thread->port); + + inf->stopped =3D 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 =3D + 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 =3D 0; + int resume_all; + struct inf *inf =3D gnu_current_inf; + + inf_debug (inf, "ptid =3D %s, step =3D %d, sig =3D %d", + target_pid_to_str (ptid), step, sig); + + inf_validate_procinfo (inf); + + if (sig !=3D GDB_SIGNAL_0 || inf->stopped) + { + if (sig =3D=3D GDB_SIGNAL_0 && inf->nomsg) + inf_continue (inf); + else + inf_signal (inf, sig); + } + else if (inf->wait.exc.reply !=3D 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 resumi= ng + 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 =3D 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 =3D %d", + PIDGET (inferior_ptid)); + ptid =3D inferior_ptid; /* What to step. */ + inf_set_threads_resume_sc (inf, 0, 1); + } + else + /* Just allow a single thread to run. */ + { + struct proc *thread =3D 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 =3D 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 !=3D 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 =3D=3D 1. */ + ptid_t ptid =3D resume_info[0].thread; + const int step =3D (resume_info[0].kind =3D=3D resume_step ? 1 : 0); //1 means step, 0 means contiune + const int signal =3D resume_info[0].sig; + if (ptid_equal (ptid, minus_one_ptid)) + ptid =3D thread_to_gdb_id (current_inferior); + + regcache_invalidate (); + + gnu_debug ("in gnu_resume: ptid=3D%d, step=3D%d, signal=3D%d\n", ptid, s= tep, + 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 =3D inf->threads; thread; thread =3D thread->next) + thread->sc =3D thread->pause_sc; + + if (inf->task) + inf->task->sc =3D inf->pause_sc; + + inf_update_suspends (inf); +} + +static ptid_t +gnu_wait_1 (ptid_t ptid, struct target_waitstatus *status, int target_opti= ons) +{ + 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 =3D 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 =3D inf; + + inf_debug (inf, "waiting for: %s", target_pid_to_str (ptid)); + +rewait: + if (proc_wait_pid !=3D 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 =3D + 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 =3D 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 =3D 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 =3D 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 =3D=3D 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 =3D %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 =3D %d."), msg.hdr.msgh_id); + + if (reply.err) + error (_("Handling event, msgid =3D %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 =3D &inf->wait; + enum target_waitkind kind =3D w->status.kind; + + if (kind =3D=3D 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 =3D 1; + inf_debug (inf, "pending_execs =3D %d, ignoring minor event", + inf->pending_execs); + } + else if (kind =3D=3D TARGET_WAITKIND_STOPPED + && w->status.value.sig =3D=3D 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 =3D=3D 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 =3D> %d", + inf->pending_execs); + } + else if (kind =3D=3D 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 =3D inf->wait.thread; + if (thread) + ptid =3D gnu_ptid_build (inf->pid, 0, thread->tid); + else if (ptid_equal (ptid, minus_one_ptid)) + thread =3D inf_tid_to_thread (inf, -1); + else + thread =3D inf_tid_to_thread (inf, gnu_get_tid (ptid)); + + if (!thread || thread->port =3D=3D MACH_PORT_NULL) + { + /* TID is dead; try and find a new thread. */ + if (inf_update_procs (inf) && inf->threads) + ptid =3D gnu_ptid_build (inf->pid, 0, inf->threads->tid); + /* The first + available + thread. */ + else + ptid =3D inferior_ptid; /* let wait_for_inferior handle exit case */ + } + + if (thread + && !ptid_equal (ptid, minus_one_ptid) + && status->kind !=3D TARGET_WAITKIND_SPURIOUS + && inf->pause_sc =3D=3D 0 && thread->pause_sc =3D=3D 0) + /* If something actually happened to THREAD, make sure we + suspend it. */ + { + thread->sc =3D 1; + inf_update_suspends (inf); + } + + inf_debug (inf, "returning ptid =3D %s, status =3D %s (%d)", + target_pid_to_str (ptid), + status->kind =3D=3D TARGET_WAITKIND_EXITED ? "EXITED" + : status->kind =3D=3D TARGET_WAITKIND_STOPPED ? "STOPPED" + : status->kind =3D=3D TARGET_WAITKIND_SIGNALLED ? "SIGNALLED" + : status->kind =3D=3D TARGET_WAITKIND_LOADED ? "LOADED" + : status->kind =3D=3D TARGET_WAITKIND_SPURIOUS ? "SPURIOUS" + : "?", status->value.integer); + + inferior_ptid =3D ptid; + return ptid; +} + +static ptid_t +gnu_wait (ptid_t ptid, struct target_waitstatus *status, int target_option= s) +{ + ptid_t event_ptid; + gnu_debug ("gnu_wait: [%s]", target_pid_to_str (ptid)); + event_ptid =3D gnu_wait_1 (ptid, status, target_options); + gnu_debug (" -> (status->kind =3D %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=3D%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=3D%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 =3D (vm_address_t) trunc_page (addr); + vm_size_t aligned_length =3D + (vm_size_t) round_page (addr + length) - low_address; + pointer_t copied; + int copy_count; + + /* Get memory from inferior with page aligned addresses. */ + err =3D vm_read (task, low_address, aligned_length, &copied, ©_count= ); + if (err) + return 0; + + err =3D hurd_safe_copyin (myaddr, (void *) (addr - low_address + copied), + length); + if (err) + { + warning (_("Read from inferior faulted: %s"), safe_strerror (err)); + length =3D 0; + } + + err =3D 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 !=3D KERN_SUCCESS) { errstr =3D #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 =3D 0; + vm_address_t low_address =3D (vm_address_t) trunc_page (addr); + vm_size_t aligned_length =3D + (vm_size_t) round_page (addr + length) - low_address; + pointer_t copied; + int copy_count; + int deallocate =3D 0; + + char *errstr =3D "Bug in gnu_write_inferior"; + + struct vm_region_list *region_element; + struct vm_region_list *region_head =3D (struct vm_region_list *) NULL; + + /* Get memory from inferior with page aligned addresses. */ + err =3D vm_read (task, low_address, aligned_length, &copied, ©_count= ); + CHK_GOTO_OUT ("gnu_write_inferior vm_read failed", err); + + deallocate++; + + err =3D 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 =3D aligned_length; + vm_address_t region_address =3D 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 =3D remaining_length; + vm_address_t old_address =3D region_address; + + err =3D 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 !=3D region_address) + { + warning (_("No memory at 0x%x. Nothing written"), old_address); + err =3D KERN_SUCCESS; + length =3D 0; + goto out; + } + + if (!(max_protection & VM_PROT_WRITE)) + { + warning (_("Memory at address 0x%x is unwritable. " + "Nothing written"), old_address); + err =3D KERN_SUCCESS; + length =3D 0; + goto out; + } + + /* Chain the regions for later use. */ + /*region_element =3D */ + /*(struct vm_region_list *) */ + /*obstack_alloc (®ion_obstack, sizeof (struct vm_region_list)); */ + region_element =3D + (struct vm_region_list *) malloc (sizeof (struct vm_region_list)); + + region_element->protection =3D protection; + region_element->start =3D region_address; + region_element->length =3D region_length; + + /* Chain the regions along with protections. */ + region_element->next =3D region_head; + region_head =3D region_element; + + region_address +=3D region_length; + remaining_length =3D 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 =3D region_head; scan; scan =3D scan->next) + { + if (!(scan->protection & VM_PROT_WRITE)) + { + err =3D vm_protect (task, + scan->start, + scan->length, + FALSE, scan->protection | VM_PROT_WRITE); + CHK_GOTO_OUT ("vm_protect: enable write failed", err); + } + } + + err =3D 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 =3D region_head; scan; scan =3D scan->next) + { + if (!(scan->protection & VM_PROT_WRITE)) + { + err =3D 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 !=3D 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 =3D 0; + task_t task =3D (gnu_current_inf + ? (gnu_current_inf->task + ? gnu_current_inf->task->port : 0) : 0); + if (task =3D=3D MACH_PORT_NULL) + return 0; + ret =3D gnu_read_inferior (task, addr, myaddr, length); + if (length !=3D ret) + { + gnu_debug ("gnu_read_inferior,length=3D%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 =3D 0; + task_t task =3D (gnu_current_inf + ? (gnu_current_inf->task + ? gnu_current_inf->task->port : 0) : 0); + if (task =3D=3D MACH_PORT_NULL) + return 0; + ret =3D gnu_write_inferior (task, addr, myaddr, length); + if (length !=3D ret) + { + gnu_debug ("gnu_write_inferior,length=3D%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 =3D TARGET_WAITKIND_EXITED; + ourstatus->value.integer =3D WEXITSTATUS (hoststatus); + } + else if (!WIFSTOPPED (hoststatus)) + { + ourstatus->kind =3D TARGET_WAITKIND_SIGNALLED; + ourstatus->value.sig =3D gdb_signal_from_host (WTERMSIG (hoststatus)= ); + } + else + { + ourstatus->kind =3D TARGET_WAITKIND_STOPPED; + ourstatus->value.sig =3D 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 =3D 0; + struct procinfo *pi; + mach_msg_type_number_t pi_len =3D 0; + int info_flags =3D PI_FETCH_TASKINFO; + int suspend_count =3D -1; + error_t err; + +retry: + err =3D proc_getprocinfo (proc_server, inf->pid, &info_flags, + (procinfo_t *) & pi, &pi_len, &noise, &noise_len); + if (err) + { + inf->task->dead =3D 1; /* oh well */ + return; + } + + if (inf->task->cur_sc < pi->taskinfo.suspend_count && suspend_count =3D= =3D -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 =3D pi->taskinfo.suspend_count; + goto retry; + } + + suspend_count =3D 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 =3D !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 =3D suspend_count; + } +} + +void +inf_resume (struct inf *inf) +{ + struct proc *thread; + + inf_update_procs (inf); + + for (thread =3D inf->threads; thread; thread =3D thread->next) + thread->sc =3D 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 =3D 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 =3D inf->threads; + + while (thread) + if (thread->port =3D=3D port) + return thread; + else + thread =3D 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 =3D waiting_inf; + struct proc *thread =3D inf_port_to_thread (inf, thread_port); + + inf_debug (waiting_inf, + "S_exception_raise_request thread =3D %d, task =3D %d, exc =3D %d, code =3D %d, subcode =3D %d", + thread_port, task_port, exception, code, subcode); + + if (!thread) + /* We don't know about thread? */ + { + inf_update_procs (inf); + thread =3D 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 =3D thread; + + inf->wait.status.kind =3D TARGET_WAITKIND_STOPPED; + + if (exception =3D=3D EXC_BREAKPOINT) + /* GDB likes to get SIGTRAP for breakpoints. */ + { + inf->wait.status.value.sig =3D 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 =3D=3D port) + { + inf_debug (waiting_inf, "Handler is thread exception port <%d>", + thread->saved_exc_port); + inf->wait.exc.handler =3D thread->saved_exc_port; + } + else + { + inf_debug (waiting_inf, "Handler is task exception port <%d>", + inf->task->saved_exc_port); + inf->wait.exc.handler =3D inf->task->saved_exc_port; + gdb_assert (inf->task->exc_port =3D=3D port); + } + if (inf->wait.exc.handler !=3D 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 =3D exception; + inf->wait.exc.code =3D code; + inf->wait.exc.subcode =3D subcode; + inf->wait.exc.reply =3D 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 =3D + gdb_signal_from_host (_NSIG + exception); + } + } + else + /* A supppressed exception, which ignore. */ + { + inf->wait.suppress =3D 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 =3D waiting_inf; + + inf_debug (inf, + "S_proc_wait_reply err =3D %s, pid =3D %d, status =3D 0x%x, sigcode = =3D %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 =3D=3D 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 =3D 0; + + inf_debug (inf, "waits pending now: %d", proc_waits_pending); + + if (err) + { + if (err !=3D EINTR) + { + /*warning (_("Can't wait for pid %d: %s"), */ + /*inf->pid, safe_strerror (err)); */ + inf->no_wait =3D 1; + + /* Since we can't see the inferior's signals, don't trap them. */ + inf_set_traced (inf, 0); + } + } + else if (pid =3D=3D inf->pid) + { + store_waitstatus (&inf->wait.status, status); + if (inf->wait.status.kind =3D=3D 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 =3D 1; + } + } + else + inf->wait.suppress =3D 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 =3D waiting_inf; + + if (err =3D=3D EBUSY) + /* EBUSY is what we get when the crash server has grabbed control of t= he + 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 cr= ash + server should like). */ + { + inf->wait.status.kind =3D TARGET_WAITKIND_STOPPED; + inf->wait.status.value.sig =3D 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 whic= h we + thought was stopped, and which we expected to continue after the si= gnal. + Given that the signal has failed for some reason, it's reasonable to + assume it's still stopped. */ + inf->stopped =3D 1; + else + inf->wait.suppress =3D 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 =3D waiting_inf; + + inf_debug (waiting_inf, "port =3D %d", dead_port); + + if (inf->task && inf->task->port =3D=3D dead_port) + { + proc_debug (inf->task, "is dead"); + inf->task->port =3D MACH_PORT_NULL; + if (proc_wait_pid =3D=3D 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 =3D 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 =3D inf_port_to_thread (inf, dead_port); + + if (thread) + { + proc_debug (thread, "is dead"); + thread->port =3D MACH_PORT_NULL; + } + + if (inf->task->dead) + /* Since the task is dead, its threads are dying with it. */ + inf->wait.suppress =3D 1; + } + + mach_port_deallocate (mach_task_self (), dead_port); + inf->threads_up_to_date =3D 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_p= ort) +{ + return ill_rpc ("S_proc_getmsgport_reply"); +} + +static struct target_ops gnu_target_ops =3D { + 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 =3D getproc (); +} + +static void +initialize_low_arch () +{ + init_registers_i386 (); + gnu_tdesc =3D 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 . = */ + +#include "server.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +/* 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 |=3D 0x100 +#define THREAD_STATE_CLEAR_TRACED(state) \ + ((((struct i386_thread_state *) (state))->efl &=3D ~0x100), 1) + +#define proc_is_task(proc) ((proc)->tid =3D=3D PROC_TID_TASK) +#define proc_is_thread(proc) ((proc)->tid !=3D 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 a= ll + 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 =3D (_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 =3D (_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 #include +#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=3D 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 =3D -D'MSG_IMPORTS=3Dwaittime 1000;' + +# ick +MIGCOM =3D $(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=3DS_ -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=3DS_ -x c $< \ + | $(MIGCOM) -sheader $*_S.h -server $*_S.c -user /dev/null -header /dev/n= ull +# 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 =3D 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 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 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 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 diff --git a/gdb/gdbserver/reply_mig_hack.awk b/gdb/gdbserver/reply_mig_hac= k.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 +# +# 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 . +# +# 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 =3D 0; } + +/^}/ { parse_phase =3D 0; } + +parse_phase =3D=3D 0 && /^mig_internal void _X[a-zA-Z0-9_]*_reply/ { + # The start of a mig server routine. Reset everything. Note that we on= ly + # mess with rpcs that have the suffix `_reply'. + num_args =3D 0; + num_checks =3D 0; + parse_phase =3D 1; + print; next; +} + +parse_phase =3D=3D 1 && /^[\t ]*typedef struct/ { + # The first structure in the server routine should describe the arguments + parse_phase =3D 2; + print; next; +} + +parse_phase =3D=3D 2 { + # The message header field in the args structure, which skip. + parse_phase =3D 3; + print; next; +} + +parse_phase =3D=3D 3 && /}/ { + # The args structure is over. + if (num_args > 1) + parse_phase =3D 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 =3D 0; + print; next; +} + +parse_phase =3D=3D 3 { + # The type field for an argument. + arg_type_code_name[num_args] =3D $2; + sub (/;$/, "", arg_type_code_name[num_args]) # Get rid of the semi-colon + parse_phase =3D 4; + print; next; +} + +parse_phase =3D=3D 4 { + # The value field for an argument. + arg_name[num_args] =3D $2; + sub (/;$/, "", arg_name[num_args]) # Get rid of the semi-colon + arg_type[num_args] =3D $1; + num_args++; + parse_phase =3D 3; + print; next; +} + +parse_phase =3D=3D 5 && /^[ \t]*(auto|static) const mach_msg_type_t/ { + # The type check structure for an argument. + arg_check_name[num_checks] =3D $4; + num_checks++; + print; next; +} + +parse_phase =3D=3D 5 && /^[ \t]*mig_external kern_return_t/ { + # The declaration of the user server function for this rpc. + user_function_name =3D $3; + print; next; +} + +parse_phase =3D=3D 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, aft= er + # 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 =3D=3D 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] " !=3D 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 =3D 0; i < num_args; i++) + printf (", %s", arg_type[i]); + printf (") =3D %s;\n", user_function_name); + print "\t OutP->RetCode =3D (*(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 =3D 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 =3D get_cell (); + + xsnprintf (str, CELLSIZE, "0x%s", phex_nz ((unsigned long long) addr, sizeof (addr))); + return str; +} + static char * decimal2str (char *sign, ULONGEST addr) { --=20 Yue Lu (=E9=99=86=E5=B2=B3)