diff options
author | Stan Shebs <shebs@codesourcery.com> | 1999-04-16 01:35:26 +0000 |
---|---|---|
committer | Stan Shebs <shebs@codesourcery.com> | 1999-04-16 01:35:26 +0000 |
commit | c906108c21474dfb4ed285bcc0ac6fe02cd400cc (patch) | |
tree | a0015aa5cedc19ccbab307251353a41722a3ae13 /gdb/procfs.c | |
parent | cd946cff9ede3f30935803403f06f6ed30cad136 (diff) | |
download | gdb-c906108c21474dfb4ed285bcc0ac6fe02cd400cc.zip gdb-c906108c21474dfb4ed285bcc0ac6fe02cd400cc.tar.gz gdb-c906108c21474dfb4ed285bcc0ac6fe02cd400cc.tar.bz2 |
Initial creation of sourceware repositorygdb-4_18-branchpoint
Diffstat (limited to 'gdb/procfs.c')
-rw-r--r-- | gdb/procfs.c | 5764 |
1 files changed, 5764 insertions, 0 deletions
diff --git a/gdb/procfs.c b/gdb/procfs.c new file mode 100644 index 0000000..ff20240 --- /dev/null +++ b/gdb/procfs.c @@ -0,0 +1,5764 @@ +/* Machine independent support for SVR4 /proc (process file system) for GDB. + Copyright 1991, 1992-98, 1999 Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support. Changes for sysv4.2mp procfs + compatibility by Geoffrey Noer at Cygnus Solutions. + +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 2 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, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + + +/* N O T E S + +For information on the details of using /proc consult section proc(4) +in the UNIX System V Release 4 System Administrator's Reference Manual. + +The general register and floating point register sets are manipulated +separately. This file makes the assumption that if FP0_REGNUM is +defined, then support for the floating point register set is desired, +regardless of whether or not the actual target has floating point hardware. + + */ + + +#include "defs.h" + +#include <sys/types.h> +#include <time.h> +#include <sys/fault.h> +#include <sys/syscall.h> +#include <sys/procfs.h> +#include <fcntl.h> +#include <errno.h> +#include "gdb_string.h" +#include <stropts.h> +#include <poll.h> +#include <unistd.h> +#include "gdb_stat.h" + +#include "inferior.h" +#include "target.h" +#include "command.h" +#include "gdbcore.h" +#include "gdbthread.h" + +#if !defined(SYS_lwp_create) && defined(SYS_lwpcreate) +# define SYS_lwp_create SYS_lwpcreate +#endif + +#if !defined(SYS_lwp_exit) && defined(SYS_lwpexit) +# define SYS_lwp_exit SYS_lwpexit +#endif + +#if !defined(SYS_lwp_wait) && defined(SYS_lwpwait) +# define SYS_lwp_wait SYS_lwpwait +#endif + +#if !defined(SYS_lwp_self) && defined(SYS_lwpself) +# define SYS_lwp_self SYS_lwpself +#endif + +#if !defined(SYS_lwp_info) && defined(SYS_lwpinfo) +# define SYS_lwp_info SYS_lwpinfo +#endif + +#if !defined(SYS_lwp_private) && defined(SYS_lwpprivate) +# define SYS_lwp_private SYS_lwpprivate +#endif + +#if !defined(SYS_lwp_kill) && defined(SYS_lwpkill) +# define SYS_lwp_kill SYS_lwpkill +#endif + +#if !defined(SYS_lwp_suspend) && defined(SYS_lwpsuspend) +# define SYS_lwp_suspend SYS_lwpsuspend +#endif + +#if !defined(SYS_lwp_continue) && defined(SYS_lwpcontinue) +# define SYS_lwp_continue SYS_lwpcontinue +#endif + +/* the name of the proc status struct depends on the implementation */ +/* Wrap Light Weight Process member in THE_PR_LWP macro for clearer code */ +#ifndef HAVE_PSTATUS_T + typedef prstatus_t gdb_prstatus_t; +#define THE_PR_LWP(a) a +#else /* HAVE_PSTATUS_T */ + typedef pstatus_t gdb_prstatus_t; +#define THE_PR_LWP(a) a.pr_lwp +#if !defined(HAVE_PRRUN_T) && defined(HAVE_MULTIPLE_PROC_FDS) + /* Fallback definitions - for using configure information directly */ +#ifndef UNIXWARE +#define UNIXWARE 1 +#endif +#if !defined(PROCFS_USE_READ_WRITE) && !defined(HAVE_PROCFS_PIOCSET) +#define PROCFS_USE_READ_WRITE 1 +#endif +#endif /* !HAVE_PRRUN_T && HAVE_MULTIPLE_PROC_FDS */ +#endif /* HAVE_PSTATUS_T */ + +#define MAX_SYSCALLS 256 /* Maximum number of syscalls for table */ + +/* proc name formats may vary depending on the proc implementation */ +#ifdef HAVE_MULTIPLE_PROC_FDS +# ifndef CTL_PROC_NAME_FMT +# define CTL_PROC_NAME_FMT "/proc/%d/ctl" +# define AS_PROC_NAME_FMT "/proc/%d/as" +# define MAP_PROC_NAME_FMT "/proc/%d/map" +# define STATUS_PROC_NAME_FMT "/proc/%d/status" +# endif +#else /* HAVE_MULTIPLE_PROC_FDS */ +# ifndef CTL_PROC_NAME_FMT +# define CTL_PROC_NAME_FMT "/proc/%05d" +# define AS_PROC_NAME_FMT "/proc/%05d" +# define MAP_PROC_NAME_FMT "/proc/%05d" +# define STATUS_PROC_NAME_FMT "/proc/%05d" +# endif +#endif /* HAVE_MULTIPLE_PROC_FDS */ + + +/* These #ifdefs are for sol2.x in particular. sol2.x has + both a "gregset_t" and a "prgregset_t", which have + similar uses but different layouts. sol2.x gdb tries to + use prgregset_t (and prfpregset_t) everywhere. */ + +#ifdef GDB_GREGSET_TYPE + typedef GDB_GREGSET_TYPE gdb_gregset_t; +#else + typedef gregset_t gdb_gregset_t; +#endif + +#ifdef GDB_FPREGSET_TYPE + typedef GDB_FPREGSET_TYPE gdb_fpregset_t; +#else + typedef fpregset_t gdb_fpregset_t; +#endif + + +#define MAX_PROC_NAME_SIZE sizeof("/proc/1234567890/status") + +struct target_ops procfs_ops; + +int procfs_suppress_run = 0; /* Non-zero if procfs should pretend not to + be a runnable target. Used by targets + that can sit atop procfs, such as solaris + thread support. */ + +#if 1 /* FIXME: Gross and ugly hack to resolve coredep.c global */ +CORE_ADDR kernel_u_addr; +#endif + +#ifdef BROKEN_SIGINFO_H /* Workaround broken SGS <sys/siginfo.h> */ +#undef si_pid +#define si_pid _data._proc.pid +#undef si_uid +#define si_uid _data._proc._pdata._kill.uid +#endif /* BROKEN_SIGINFO_H */ + +/* Define structures for passing commands to /proc/pid/ctl file. Note that + while we create these for the PROCFS_USE_READ_WRITE world, we use them + and ignore the extra cmd int in other proc schemes. +*/ +/* generic ctl msg */ +struct proc_ctl { + int cmd; + long data; +}; + +/* set general registers */ +struct greg_ctl { + int cmd; + gdb_gregset_t gregset; +}; + +/* set fp registers */ +struct fpreg_ctl { + int cmd; + gdb_fpregset_t fpregset; +}; + +/* set signals to be traced */ +struct sig_ctl { + int cmd; + sigset_t sigset; +}; + +/* set faults to be traced */ +struct flt_ctl { + int cmd; + fltset_t fltset; +}; + +/* set system calls to be traced */ +struct sys_ctl { + int cmd; + sysset_t sysset; +}; + +/* set current signal to be traced */ +struct sigi_ctl { + int cmd; + siginfo_t siginfo; +}; + +/* All access to the inferior, either one started by gdb or one that has + been attached to, is controlled by an instance of a procinfo structure, + defined below. Since gdb currently only handles one inferior at a time, + the procinfo structure for the inferior is statically allocated and + only one exists at any given time. There is a separate procinfo + structure for use by the "info proc" command, so that we can print + useful information about any random process without interfering with + the inferior's procinfo information. */ + +struct procinfo { + struct procinfo *next; + int pid; /* Process ID of inferior */ + int ctl_fd; /* File descriptor for /proc ctl file */ + int status_fd; /* File descriptor for /proc status file */ + int as_fd; /* File descriptor for /proc as file */ + int map_fd; /* File descriptor for /proc map file */ + char *pathname; /* Pathname to /proc entry */ + int had_event; /* poll/select says something happened */ + int was_stopped; /* Nonzero if was stopped prior to attach */ + int nopass_next_sigstop; /* Don't pass a sigstop on next resume */ +#ifdef HAVE_PRRUN_T + prrun_t prrun; /* Control state when it is run */ +#endif + gdb_prstatus_t prstatus; /* Current process status info */ + struct greg_ctl gregset; /* General register set */ + struct fpreg_ctl fpregset; /* Floating point register set */ + struct flt_ctl fltset; /* Current traced hardware fault set */ + struct sig_ctl trace; /* Current traced signal set */ + struct sys_ctl exitset; /* Current traced system call exit set */ + struct sys_ctl entryset; /* Current traced system call entry set */ + struct sig_ctl saved_sighold; /* Saved held signal set */ + struct flt_ctl saved_fltset; /* Saved traced hardware fault set */ + struct sig_ctl saved_trace; /* Saved traced signal set */ + struct sys_ctl saved_exitset; /* Saved traced system call exit set */ + struct sys_ctl saved_entryset;/* Saved traced system call entry set */ + int num_syscall_handlers; /* Number of syscall trap handlers + currently installed */ + /* Pointer to list of syscall trap handlers */ + struct procfs_syscall_handler *syscall_handlers; + int saved_rtnval; /* return value and status for wait(), */ + int saved_statval; /* as supplied by a syscall handler. */ + int new_child; /* Non-zero if it's a new thread */ +}; + +/* List of inferior process information */ +static struct procinfo *procinfo_list = NULL; +static struct pollfd *poll_list; /* pollfds used for waiting on /proc */ + +static int num_poll_list = 0; /* Number of entries in poll_list */ + +/* Much of the information used in the /proc interface, particularly for + printing status information, is kept as tables of structures of the + following form. These tables can be used to map numeric values to + their symbolic names and to a string that describes their specific use. */ + +struct trans { + int value; /* The numeric value */ + char *name; /* The equivalent symbolic value */ + char *desc; /* Short description of value */ +}; + +/* Translate bits in the pr_flags member of the prstatus structure, into the + names and desc information. */ + +static struct trans pr_flag_table[] = +{ +#if defined (PR_STOPPED) + { PR_STOPPED, "PR_STOPPED", "Process is stopped" }, +#endif +#if defined (PR_ISTOP) + { PR_ISTOP, "PR_ISTOP", "Stopped on an event of interest" }, +#endif +#if defined (PR_DSTOP) + { PR_DSTOP, "PR_DSTOP", "A stop directive is in effect" }, +#endif +#if defined (PR_ASLEEP) + { PR_ASLEEP, "PR_ASLEEP", "Sleeping in an interruptible system call" }, +#endif +#if defined (PR_FORK) + { PR_FORK, "PR_FORK", "Inherit-on-fork is in effect" }, +#endif +#if defined (PR_RLC) + { PR_RLC, "PR_RLC", "Run-on-last-close is in effect" }, +#endif +#if defined (PR_PTRACE) + { PR_PTRACE, "PR_PTRACE", "Process is being controlled by ptrace" }, +#endif +#if defined (PR_PCINVAL) + { PR_PCINVAL, "PR_PCINVAL", "PC refers to an invalid virtual address" }, +#endif +#if defined (PR_ISSYS) + { PR_ISSYS, "PR_ISSYS", "Is a system process" }, +#endif +#if defined (PR_STEP) + { PR_STEP, "PR_STEP", "Process has single step pending" }, +#endif +#if defined (PR_KLC) + { PR_KLC, "PR_KLC", "Kill-on-last-close is in effect" }, +#endif +#if defined (PR_ASYNC) + { PR_ASYNC, "PR_ASYNC", "Asynchronous stop is in effect" }, +#endif +#if defined (PR_PCOMPAT) + { PR_PCOMPAT, "PR_PCOMPAT", "Ptrace compatibility mode in effect" }, +#endif +#if defined (PR_MSACCT) + { PR_MSACCT, "PR_MSACCT", "Microstate accounting enabled" }, +#endif +#if defined (PR_BPTADJ) + { PR_BPTADJ, "PR_BPTADJ", "Breakpoint PC adjustment in effect" }, +#endif +#if defined (PR_ASLWP) + { PR_ASLWP, "PR_ASLWP", "Asynchronus signal LWP" }, +#endif + { 0, NULL, NULL } +}; + +/* Translate values in the pr_why field of the prstatus struct. */ + +static struct trans pr_why_table[] = +{ +#if defined (PR_REQUESTED) + { PR_REQUESTED, "PR_REQUESTED", "Directed to stop via PIOCSTOP/PIOCWSTOP" }, +#endif +#if defined (PR_SIGNALLED) + { PR_SIGNALLED, "PR_SIGNALLED", "Receipt of a traced signal" }, +#endif +#if defined (PR_SYSENTRY) + { PR_SYSENTRY, "PR_SYSENTRY", "Entry to a traced system call" }, +#endif +#if defined (PR_SYSEXIT) + { PR_SYSEXIT, "PR_SYSEXIT", "Exit from a traced system call" }, +#endif +#if defined (PR_JOBCONTROL) + { PR_JOBCONTROL, "PR_JOBCONTROL", "Default job control stop signal action" }, +#endif +#if defined (PR_FAULTED) + { PR_FAULTED, "PR_FAULTED", "Incurred a traced hardware fault" }, +#endif +#if defined (PR_SUSPENDED) + { PR_SUSPENDED, "PR_SUSPENDED", "Process suspended" }, +#endif +#if defined (PR_CHECKPOINT) + { PR_CHECKPOINT, "PR_CHECKPOINT", "(???)" }, +#endif + { 0, NULL, NULL } +}; + +/* Hardware fault translation table. */ + +static struct trans faults_table[] = +{ +#if defined (FLTILL) + { FLTILL, "FLTILL", "Illegal instruction" }, +#endif +#if defined (FLTPRIV) + { FLTPRIV, "FLTPRIV", "Privileged instruction" }, +#endif +#if defined (FLTBPT) + { FLTBPT, "FLTBPT", "Breakpoint trap" }, +#endif +#if defined (FLTTRACE) + { FLTTRACE, "FLTTRACE", "Trace trap" }, +#endif +#if defined (FLTACCESS) + { FLTACCESS, "FLTACCESS", "Memory access fault" }, +#endif +#if defined (FLTBOUNDS) + { FLTBOUNDS, "FLTBOUNDS", "Memory bounds violation" }, +#endif +#if defined (FLTIOVF) + { FLTIOVF, "FLTIOVF", "Integer overflow" }, +#endif +#if defined (FLTIZDIV) + { FLTIZDIV, "FLTIZDIV", "Integer zero divide" }, +#endif +#if defined (FLTFPE) + { FLTFPE, "FLTFPE", "Floating-point exception" }, +#endif +#if defined (FLTSTACK) + { FLTSTACK, "FLTSTACK", "Unrecoverable stack fault" }, +#endif +#if defined (FLTPAGE) + { FLTPAGE, "FLTPAGE", "Recoverable page fault" }, +#endif + { 0, NULL, NULL } +}; + +/* Translation table for signal generation information. See UNIX System + V Release 4 Programmer's Reference Manual, siginfo(5). */ + +static struct sigcode { + int signo; + int code; + char *codename; + char *desc; +} siginfo_table[] = { +#if defined (SIGILL) && defined (ILL_ILLOPC) + { SIGILL, ILL_ILLOPC, "ILL_ILLOPC", "Illegal opcode" }, +#endif +#if defined (SIGILL) && defined (ILL_ILLOPN) + { SIGILL, ILL_ILLOPN, "ILL_ILLOPN", "Illegal operand", }, +#endif +#if defined (SIGILL) && defined (ILL_ILLADR) + { SIGILL, ILL_ILLADR, "ILL_ILLADR", "Illegal addressing mode" }, +#endif +#if defined (SIGILL) && defined (ILL_ILLTRP) + { SIGILL, ILL_ILLTRP, "ILL_ILLTRP", "Illegal trap" }, +#endif +#if defined (SIGILL) && defined (ILL_PRVOPC) + { SIGILL, ILL_PRVOPC, "ILL_PRVOPC", "Privileged opcode" }, +#endif +#if defined (SIGILL) && defined (ILL_PRVREG) + { SIGILL, ILL_PRVREG, "ILL_PRVREG", "Privileged register" }, +#endif +#if defined (SIGILL) && defined (ILL_COPROC) + { SIGILL, ILL_COPROC, "ILL_COPROC", "Coprocessor error" }, +#endif +#if defined (SIGILL) && defined (ILL_BADSTK) + { SIGILL, ILL_BADSTK, "ILL_BADSTK", "Internal stack error" }, +#endif +#if defined (SIGFPE) && defined (FPE_INTDIV) + { SIGFPE, FPE_INTDIV, "FPE_INTDIV", "Integer divide by zero" }, +#endif +#if defined (SIGFPE) && defined (FPE_INTOVF) + { SIGFPE, FPE_INTOVF, "FPE_INTOVF", "Integer overflow" }, +#endif +#if defined (SIGFPE) && defined (FPE_FLTDIV) + { SIGFPE, FPE_FLTDIV, "FPE_FLTDIV", "Floating point divide by zero" }, +#endif +#if defined (SIGFPE) && defined (FPE_FLTOVF) + { SIGFPE, FPE_FLTOVF, "FPE_FLTOVF", "Floating point overflow" }, +#endif +#if defined (SIGFPE) && defined (FPE_FLTUND) + { SIGFPE, FPE_FLTUND, "FPE_FLTUND", "Floating point underflow" }, +#endif +#if defined (SIGFPE) && defined (FPE_FLTRES) + { SIGFPE, FPE_FLTRES, "FPE_FLTRES", "Floating point inexact result" }, +#endif +#if defined (SIGFPE) && defined (FPE_FLTINV) + { SIGFPE, FPE_FLTINV, "FPE_FLTINV", "Invalid floating point operation" }, +#endif +#if defined (SIGFPE) && defined (FPE_FLTSUB) + { SIGFPE, FPE_FLTSUB, "FPE_FLTSUB", "Subscript out of range" }, +#endif +#if defined (SIGSEGV) && defined (SEGV_MAPERR) + { SIGSEGV, SEGV_MAPERR, "SEGV_MAPERR", "Address not mapped to object" }, +#endif +#if defined (SIGSEGV) && defined (SEGV_ACCERR) + { SIGSEGV, SEGV_ACCERR, "SEGV_ACCERR", "Invalid permissions for object" }, +#endif +#if defined (SIGBUS) && defined (BUS_ADRALN) + { SIGBUS, BUS_ADRALN, "BUS_ADRALN", "Invalid address alignment" }, +#endif +#if defined (SIGBUS) && defined (BUS_ADRERR) + { SIGBUS, BUS_ADRERR, "BUS_ADRERR", "Non-existent physical address" }, +#endif +#if defined (SIGBUS) && defined (BUS_OBJERR) + { SIGBUS, BUS_OBJERR, "BUS_OBJERR", "Object specific hardware error" }, +#endif +#if defined (SIGTRAP) && defined (TRAP_BRKPT) + { SIGTRAP, TRAP_BRKPT, "TRAP_BRKPT", "Process breakpoint" }, +#endif +#if defined (SIGTRAP) && defined (TRAP_TRACE) + { SIGTRAP, TRAP_TRACE, "TRAP_TRACE", "Process trace trap" }, +#endif +#if defined (SIGCLD) && defined (CLD_EXITED) + { SIGCLD, CLD_EXITED, "CLD_EXITED", "Child has exited" }, +#endif +#if defined (SIGCLD) && defined (CLD_KILLED) + { SIGCLD, CLD_KILLED, "CLD_KILLED", "Child was killed" }, +#endif +#if defined (SIGCLD) && defined (CLD_DUMPED) + { SIGCLD, CLD_DUMPED, "CLD_DUMPED", "Child has terminated abnormally" }, +#endif +#if defined (SIGCLD) && defined (CLD_TRAPPED) + { SIGCLD, CLD_TRAPPED, "CLD_TRAPPED", "Traced child has trapped" }, +#endif +#if defined (SIGCLD) && defined (CLD_STOPPED) + { SIGCLD, CLD_STOPPED, "CLD_STOPPED", "Child has stopped" }, +#endif +#if defined (SIGCLD) && defined (CLD_CONTINUED) + { SIGCLD, CLD_CONTINUED, "CLD_CONTINUED", "Stopped child had continued" }, +#endif +#if defined (SIGPOLL) && defined (POLL_IN) + { SIGPOLL, POLL_IN, "POLL_IN", "Input input available" }, +#endif +#if defined (SIGPOLL) && defined (POLL_OUT) + { SIGPOLL, POLL_OUT, "POLL_OUT", "Output buffers available" }, +#endif +#if defined (SIGPOLL) && defined (POLL_MSG) + { SIGPOLL, POLL_MSG, "POLL_MSG", "Input message available" }, +#endif +#if defined (SIGPOLL) && defined (POLL_ERR) + { SIGPOLL, POLL_ERR, "POLL_ERR", "I/O error" }, +#endif +#if defined (SIGPOLL) && defined (POLL_PRI) + { SIGPOLL, POLL_PRI, "POLL_PRI", "High priority input available" }, +#endif +#if defined (SIGPOLL) && defined (POLL_HUP) + { SIGPOLL, POLL_HUP, "POLL_HUP", "Device disconnected" }, +#endif + { 0, 0, NULL, NULL } +}; + +static char *syscall_table[MAX_SYSCALLS]; + +/* Prototypes for local functions */ + +static void procfs_stop PARAMS ((void)); + +static int procfs_thread_alive PARAMS ((int)); + +static int procfs_can_run PARAMS ((void)); + +static void procfs_mourn_inferior PARAMS ((void)); + +static void procfs_fetch_registers PARAMS ((int)); + +static int procfs_wait PARAMS ((int, struct target_waitstatus *)); + +static void procfs_open PARAMS ((char *, int)); + +static void procfs_files_info PARAMS ((struct target_ops *)); + +static void procfs_prepare_to_store PARAMS ((void)); + +static void procfs_detach PARAMS ((char *, int)); + +static void procfs_attach PARAMS ((char *, int)); + +static void proc_set_exec_trap PARAMS ((void)); + +static void procfs_init_inferior PARAMS ((int)); + +static struct procinfo *create_procinfo PARAMS ((int)); + +static void procfs_store_registers PARAMS ((int)); + +static int procfs_xfer_memory PARAMS ((CORE_ADDR, char *, int, int, struct target_ops *)); + +static void procfs_kill_inferior PARAMS ((void)); + +static char *sigcodedesc PARAMS ((siginfo_t *)); + +static char *sigcodename PARAMS ((siginfo_t *)); + +static struct procinfo *wait_fd PARAMS ((void)); + +static void remove_fd PARAMS ((struct procinfo *)); + +static void add_fd PARAMS ((struct procinfo *)); + +static void set_proc_siginfo PARAMS ((struct procinfo *, int)); + +static void init_syscall_table PARAMS ((void)); + +static char *syscallname PARAMS ((int)); + +static char *signalname PARAMS ((int)); + +static char *errnoname PARAMS ((int)); + +static int proc_address_to_fd PARAMS ((struct procinfo *, CORE_ADDR, int)); + +static int open_proc_file PARAMS ((int, struct procinfo *, int, int)); + +static void close_proc_file PARAMS ((struct procinfo *)); + +static void unconditionally_kill_inferior PARAMS ((struct procinfo *)); + +static NORETURN void proc_init_failed PARAMS ((struct procinfo *, char *, int)) ATTR_NORETURN; + +static void info_proc PARAMS ((char *, int)); + +static void info_proc_flags PARAMS ((struct procinfo *, int)); + +static void info_proc_stop PARAMS ((struct procinfo *, int)); + +static void info_proc_siginfo PARAMS ((struct procinfo *, int)); + +static void info_proc_syscalls PARAMS ((struct procinfo *, int)); + +static void info_proc_mappings PARAMS ((struct procinfo *, int)); + +static void info_proc_signals PARAMS ((struct procinfo *, int)); + +static void info_proc_faults PARAMS ((struct procinfo *, int)); + +static char *mappingflags PARAMS ((long)); + +static char *lookupname PARAMS ((struct trans *, unsigned int, char *)); + +static char *lookupdesc PARAMS ((struct trans *, unsigned int)); + +static int do_attach PARAMS ((int pid)); + +static void do_detach PARAMS ((int siggnal)); + +static void procfs_create_inferior PARAMS ((char *, char *, char **)); + +static void procfs_notice_signals PARAMS ((int pid)); + +static void notice_signals PARAMS ((struct procinfo *, struct sig_ctl *)); + +static struct procinfo *find_procinfo PARAMS ((pid_t pid, int okfail)); + +static int procfs_write_pcwstop PARAMS ((struct procinfo *)); +static int procfs_read_status PARAMS ((struct procinfo *)); +static void procfs_write_pckill PARAMS ((struct procinfo *)); + +typedef int syscall_func_t PARAMS ((struct procinfo *pi, int syscall_num, + int why, int *rtnval, int *statval)); + +static void procfs_set_syscall_trap PARAMS ((struct procinfo *pi, + int syscall_num, int flags, + syscall_func_t *func)); + +static void procfs_clear_syscall_trap PARAMS ((struct procinfo *pi, + int syscall_num, int errok)); + +#define PROCFS_SYSCALL_ENTRY 0x1 /* Trap on entry to sys call */ +#define PROCFS_SYSCALL_EXIT 0x2 /* Trap on exit from sys call */ + +static syscall_func_t procfs_exit_handler; + +static syscall_func_t procfs_exec_handler; + +#ifdef SYS_sproc +static syscall_func_t procfs_sproc_handler; +static syscall_func_t procfs_fork_handler; +#endif + +#ifdef SYS_lwp_create +static syscall_func_t procfs_lwp_creation_handler; +#endif + +static void modify_inherit_on_fork_flag PARAMS ((int fd, int flag)); +static void modify_run_on_last_close_flag PARAMS ((int fd, int flag)); + +/* */ + +struct procfs_syscall_handler +{ + int syscall_num; /* The number of the system call being handled */ + /* The function to be called */ + syscall_func_t *func; +}; + +static void procfs_resume PARAMS ((int pid, int step, + enum target_signal signo)); + +static void init_procfs_ops PARAMS ((void)); + +/* External function prototypes that can't be easily included in any + header file because the args are typedefs in system include files. */ + +extern void supply_gregset PARAMS ((gdb_gregset_t *)); + +extern void fill_gregset PARAMS ((gdb_gregset_t *, int)); + +#ifdef FP0_REGNUM +extern void supply_fpregset PARAMS ((gdb_fpregset_t *)); + +extern void fill_fpregset PARAMS ((gdb_fpregset_t *, int)); +#endif + +/* + +LOCAL FUNCTION + + find_procinfo -- convert a process id to a struct procinfo + +SYNOPSIS + + static struct procinfo * find_procinfo (pid_t pid, int okfail); + +DESCRIPTION + + Given a process id, look it up in the procinfo chain. Returns + a struct procinfo *. If can't find pid, then call error(), + unless okfail is set, in which case, return NULL; + */ + +static struct procinfo * +find_procinfo (pid, okfail) + pid_t pid; + int okfail; +{ + struct procinfo *procinfo; + + for (procinfo = procinfo_list; procinfo; procinfo = procinfo->next) + if (procinfo->pid == pid) + return procinfo; + + if (okfail) + return NULL; + + error ("procfs (find_procinfo): Couldn't locate pid %d", pid); +} + +/* + +LOCAL MACRO + + current_procinfo -- convert inferior_pid to a struct procinfo + +SYNOPSIS + + static struct procinfo * current_procinfo; + +DESCRIPTION + + Looks up inferior_pid in the procinfo chain. Always returns a + struct procinfo *. If process can't be found, we error() out. + */ + +#define current_procinfo find_procinfo (inferior_pid, 0) + +/* + +LOCAL FUNCTION + + add_fd -- Add the fd to the poll/select list + +SYNOPSIS + + static void add_fd (struct procinfo *); + +DESCRIPTION + + Add the fd of the supplied procinfo to the list of fds used for + poll/select operations. + */ + +static void +add_fd (pi) + struct procinfo *pi; +{ + if (num_poll_list <= 0) + poll_list = (struct pollfd *) xmalloc (sizeof (struct pollfd)); + else + poll_list = (struct pollfd *) xrealloc (poll_list, + (num_poll_list + 1) + * sizeof (struct pollfd)); + poll_list[num_poll_list].fd = pi->ctl_fd; +#ifdef UNIXWARE + poll_list[num_poll_list].events = POLLWRNORM; +#else + poll_list[num_poll_list].events = POLLPRI; +#endif + + num_poll_list++; +} + +/* + +LOCAL FUNCTION + + remove_fd -- Remove the fd from the poll/select list + +SYNOPSIS + + static void remove_fd (struct procinfo *); + +DESCRIPTION + + Remove the fd of the supplied procinfo from the list of fds used + for poll/select operations. + */ + +static void +remove_fd (pi) + struct procinfo *pi; +{ + int i; + + for (i = 0; i < num_poll_list; i++) + { + if (poll_list[i].fd == pi->ctl_fd) + { + if (i != num_poll_list - 1) + memcpy (poll_list + i, poll_list + i + 1, + (num_poll_list - i - 1) * sizeof (struct pollfd)); + + num_poll_list--; + + if (num_poll_list == 0) + free (poll_list); + else + poll_list = (struct pollfd *) xrealloc (poll_list, + num_poll_list + * sizeof (struct pollfd)); + return; + } + } +} + +/* + +LOCAL FUNCTION + + procfs_read_status - get procfs fd status + +SYNOPSIS + + static int procfs_read_status (pi) struct procinfo *pi; + +DESCRIPTION + + Given a pointer to a procinfo struct, get the status of + the status_fd in the appropriate way. Returns 0 on failure, + 1 on success. + */ + +static int +procfs_read_status (pi) + struct procinfo *pi; +{ +#ifdef PROCFS_USE_READ_WRITE + if ((lseek (pi->status_fd, 0, SEEK_SET) < 0) || + (read (pi->status_fd, (char *) &pi->prstatus, + sizeof (gdb_prstatus_t)) != sizeof (gdb_prstatus_t))) +#else + if (ioctl (pi->status_fd, PIOCSTATUS, &pi->prstatus) < 0) +#endif + return 0; + else + return 1; +} + +/* + +LOCAL FUNCTION + + procfs_write_pcwstop - send a PCWSTOP to procfs fd + +SYNOPSIS + + static int procfs_write_pcwstop (pi) struct procinfo *pi; + +DESCRIPTION + + Given a pointer to a procinfo struct, send a PCWSTOP to + the ctl_fd in the appropriate way. Returns 0 on failure, + 1 on success. + */ + +static int +procfs_write_pcwstop (pi) + struct procinfo *pi; +{ +#ifdef PROCFS_USE_READ_WRITE + long cmd = PCWSTOP; + if (write (pi->ctl_fd, (char *) &cmd, sizeof (long)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCWSTOP, &pi->prstatus) < 0) +#endif + return 0; + else + return 1; +} + +/* + +LOCAL FUNCTION + + procfs_write_pckill - send a kill to procfs fd + +SYNOPSIS + + static void procfs_write_pckill (pi) struct procinfo *pi; + +DESCRIPTION + + Given a pointer to a procinfo struct, send a kill to + the ctl_fd in the appropriate way. Returns 0 on failure, + 1 on success. + */ + +static void +procfs_write_pckill (pi) + struct procinfo *pi; +{ +#ifdef PROCFS_USE_READ_WRITE + struct proc_ctl pctl; + pctl.cmd = PCKILL; + pctl.data = SIGKILL; + write (pi->ctl_fd, &pctl, sizeof (struct proc_ctl)); +#else + int signo = SIGKILL; + ioctl (pi->ctl_fd, PIOCKILL, &signo); +#endif +} + +static struct procinfo * +wait_fd () +{ + struct procinfo *pi, *next_pi; +#ifndef LOSING_POLL + int num_fds; + int i; +#endif + + set_sigint_trap (); /* Causes SIGINT to be passed on to the + attached process. */ + set_sigio_trap (); + + wait_again: +#ifndef LOSING_POLL + while (1) + { + num_fds = poll (poll_list, num_poll_list, -1); + if (num_fds > 0) + break; + if (num_fds < 0 && errno == EINTR) + continue; + print_sys_errmsg ("poll failed", errno); + error ("Poll failed, returned %d", num_fds); + } +#else /* LOSING_POLL */ + pi = current_procinfo; + + while (!procfs_write_pcwstop (pi)) + { + if (errno == ENOENT) + { + /* Process exited. */ + pi->prstatus.pr_flags = 0; + break; + } + else if (errno != EINTR) + { + print_sys_errmsg (pi->pathname, errno); + error ("procfs_write_pcwstop failed"); + } + } + pi->had_event = 1; +#endif /* LOSING_POLL */ + + clear_sigint_trap (); + clear_sigio_trap (); + +#ifndef LOSING_POLL + + for (i = 0; i < num_poll_list && num_fds > 0; i++) + { + if (0 == (poll_list[i].revents & + (POLLWRNORM | POLLPRI | POLLERR | POLLHUP | POLLNVAL))) + continue; + for (pi = procinfo_list; pi; pi = next_pi) + { + next_pi = pi->next; + if (poll_list[i].fd == pi->ctl_fd) + { + num_fds--; + if ((poll_list[i].revents & POLLHUP) != 0 || + !procfs_read_status(pi)) + { /* The LWP has apparently terminated. */ + if (num_poll_list <= 1) + { + pi->prstatus.pr_flags = 0; + pi->had_event = 1; + break; + } + if (info_verbose) + printf_filtered ("LWP %d exited.\n", + (pi->pid >> 16) & 0xffff); + close_proc_file (pi); + i--; /* don't skip deleted entry */ + if (num_fds != 0) + break; /* already another event to process */ + else + goto wait_again; /* wait for another event */ + } + pi->had_event = 1; + break; + } + } + if (!pi) + error ("wait_fd: Couldn't find procinfo for fd %d\n", + poll_list[i].fd); + } +#endif /* LOSING_POLL */ + + return pi; +} + +/* + +LOCAL FUNCTION + + lookupdesc -- translate a value to a summary desc string + +SYNOPSIS + + static char *lookupdesc (struct trans *transp, unsigned int val); + +DESCRIPTION + + Given a pointer to a translation table and a value to be translated, + lookup the desc string and return it. + */ + +static char * +lookupdesc (transp, val) + struct trans *transp; + unsigned int val; +{ + char *desc; + + for (desc = NULL; transp -> name != NULL; transp++) + { + if (transp -> value == val) + { + desc = transp -> desc; + break; + } + } + + /* Didn't find a translation for the specified value, set a default one. */ + + if (desc == NULL) + { + desc = "Unknown"; + } + return (desc); +} + +/* + +LOCAL FUNCTION + + lookupname -- translate a value to symbolic name + +SYNOPSIS + + static char *lookupname (struct trans *transp, unsigned int val, + char *prefix); + +DESCRIPTION + + Given a pointer to a translation table, a value to be translated, + and a default prefix to return if the value can't be translated, + match the value with one of the translation table entries and + return a pointer to the symbolic name. + + If no match is found it just returns the value as a printable string, + with the given prefix. The previous such value, if any, is freed + at this time. + */ + +static char * +lookupname (transp, val, prefix) + struct trans *transp; + unsigned int val; + char *prefix; +{ + static char *locbuf; + char *name; + + for (name = NULL; transp -> name != NULL; transp++) + { + if (transp -> value == val) + { + name = transp -> name; + break; + } + } + + /* Didn't find a translation for the specified value, build a default + one using the specified prefix and return it. The lifetime of + the value is only until the next one is needed. */ + + if (name == NULL) + { + if (locbuf != NULL) + { + free (locbuf); + } + locbuf = xmalloc (strlen (prefix) + 16); + sprintf (locbuf, "%s %u", prefix, val); + name = locbuf; + } + return (name); +} + +static char * +sigcodename (sip) + siginfo_t *sip; +{ + struct sigcode *scp; + char *name = NULL; + static char locbuf[32]; + + for (scp = siginfo_table; scp -> codename != NULL; scp++) + { + if ((scp -> signo == sip -> si_signo) && + (scp -> code == sip -> si_code)) + { + name = scp -> codename; + break; + } + } + if (name == NULL) + { + sprintf (locbuf, "sigcode %u", sip -> si_signo); + name = locbuf; + } + return (name); +} + +static char * +sigcodedesc (sip) + siginfo_t *sip; +{ + struct sigcode *scp; + char *desc = NULL; + + for (scp = siginfo_table; scp -> codename != NULL; scp++) + { + if ((scp -> signo == sip -> si_signo) && + (scp -> code == sip -> si_code)) + { + desc = scp -> desc; + break; + } + } + if (desc == NULL) + { + desc = "Unrecognized signal or trap use"; + } + return (desc); +} + +/* + +LOCAL FUNCTION + + syscallname - translate a system call number into a system call name + +SYNOPSIS + + char *syscallname (int syscallnum) + +DESCRIPTION + + Given a system call number, translate it into the printable name + of a system call, or into "syscall <num>" if it is an unknown + number. + */ + +static char * +syscallname (syscallnum) + int syscallnum; +{ + static char locbuf[32]; + + if (syscallnum >= 0 && syscallnum < MAX_SYSCALLS + && syscall_table[syscallnum] != NULL) + return syscall_table[syscallnum]; + else + { + sprintf (locbuf, "syscall %u", syscallnum); + return locbuf; + } +} + +/* + +LOCAL FUNCTION + + init_syscall_table - initialize syscall translation table + +SYNOPSIS + + void init_syscall_table (void) + +DESCRIPTION + + Dynamically initialize the translation table to convert system + call numbers into printable system call names. Done once per + gdb run, on initialization. + +NOTES + + This is awfully ugly, but preprocessor tricks to make it prettier + tend to be nonportable. + */ + +static void +init_syscall_table () +{ +#if defined (SYS_exit) + syscall_table[SYS_exit] = "exit"; +#endif +#if defined (SYS_fork) + syscall_table[SYS_fork] = "fork"; +#endif +#if defined (SYS_read) + syscall_table[SYS_read] = "read"; +#endif +#if defined (SYS_write) + syscall_table[SYS_write] = "write"; +#endif +#if defined (SYS_open) + syscall_table[SYS_open] = "open"; +#endif +#if defined (SYS_close) + syscall_table[SYS_close] = "close"; +#endif +#if defined (SYS_wait) + syscall_table[SYS_wait] = "wait"; +#endif +#if defined (SYS_creat) + syscall_table[SYS_creat] = "creat"; +#endif +#if defined (SYS_link) + syscall_table[SYS_link] = "link"; +#endif +#if defined (SYS_unlink) + syscall_table[SYS_unlink] = "unlink"; +#endif +#if defined (SYS_exec) + syscall_table[SYS_exec] = "exec"; +#endif +#if defined (SYS_execv) + syscall_table[SYS_execv] = "execv"; +#endif +#if defined (SYS_execve) + syscall_table[SYS_execve] = "execve"; +#endif +#if defined (SYS_chdir) + syscall_table[SYS_chdir] = "chdir"; +#endif +#if defined (SYS_time) + syscall_table[SYS_time] = "time"; +#endif +#if defined (SYS_mknod) + syscall_table[SYS_mknod] = "mknod"; +#endif +#if defined (SYS_chmod) + syscall_table[SYS_chmod] = "chmod"; +#endif +#if defined (SYS_chown) + syscall_table[SYS_chown] = "chown"; +#endif +#if defined (SYS_brk) + syscall_table[SYS_brk] = "brk"; +#endif +#if defined (SYS_stat) + syscall_table[SYS_stat] = "stat"; +#endif +#if defined (SYS_lseek) + syscall_table[SYS_lseek] = "lseek"; +#endif +#if defined (SYS_getpid) + syscall_table[SYS_getpid] = "getpid"; +#endif +#if defined (SYS_mount) + syscall_table[SYS_mount] = "mount"; +#endif +#if defined (SYS_umount) + syscall_table[SYS_umount] = "umount"; +#endif +#if defined (SYS_setuid) + syscall_table[SYS_setuid] = "setuid"; +#endif +#if defined (SYS_getuid) + syscall_table[SYS_getuid] = "getuid"; +#endif +#if defined (SYS_stime) + syscall_table[SYS_stime] = "stime"; +#endif +#if defined (SYS_ptrace) + syscall_table[SYS_ptrace] = "ptrace"; +#endif +#if defined (SYS_alarm) + syscall_table[SYS_alarm] = "alarm"; +#endif +#if defined (SYS_fstat) + syscall_table[SYS_fstat] = "fstat"; +#endif +#if defined (SYS_pause) + syscall_table[SYS_pause] = "pause"; +#endif +#if defined (SYS_utime) + syscall_table[SYS_utime] = "utime"; +#endif +#if defined (SYS_stty) + syscall_table[SYS_stty] = "stty"; +#endif +#if defined (SYS_gtty) + syscall_table[SYS_gtty] = "gtty"; +#endif +#if defined (SYS_access) + syscall_table[SYS_access] = "access"; +#endif +#if defined (SYS_nice) + syscall_table[SYS_nice] = "nice"; +#endif +#if defined (SYS_statfs) + syscall_table[SYS_statfs] = "statfs"; +#endif +#if defined (SYS_sync) + syscall_table[SYS_sync] = "sync"; +#endif +#if defined (SYS_kill) + syscall_table[SYS_kill] = "kill"; +#endif +#if defined (SYS_fstatfs) + syscall_table[SYS_fstatfs] = "fstatfs"; +#endif +#if defined (SYS_pgrpsys) + syscall_table[SYS_pgrpsys] = "pgrpsys"; +#endif +#if defined (SYS_xenix) + syscall_table[SYS_xenix] = "xenix"; +#endif +#if defined (SYS_dup) + syscall_table[SYS_dup] = "dup"; +#endif +#if defined (SYS_pipe) + syscall_table[SYS_pipe] = "pipe"; +#endif +#if defined (SYS_times) + syscall_table[SYS_times] = "times"; +#endif +#if defined (SYS_profil) + syscall_table[SYS_profil] = "profil"; +#endif +#if defined (SYS_plock) + syscall_table[SYS_plock] = "plock"; +#endif +#if defined (SYS_setgid) + syscall_table[SYS_setgid] = "setgid"; +#endif +#if defined (SYS_getgid) + syscall_table[SYS_getgid] = "getgid"; +#endif +#if defined (SYS_signal) + syscall_table[SYS_signal] = "signal"; +#endif +#if defined (SYS_msgsys) + syscall_table[SYS_msgsys] = "msgsys"; +#endif +#if defined (SYS_sys3b) + syscall_table[SYS_sys3b] = "sys3b"; +#endif +#if defined (SYS_sysi86) + syscall_table[SYS_sysi86] = "sysi86"; +#endif +#if defined (SYS_acct) + syscall_table[SYS_acct] = "acct"; +#endif +#if defined (SYS_shmsys) + syscall_table[SYS_shmsys] = "shmsys"; +#endif +#if defined (SYS_semsys) + syscall_table[SYS_semsys] = "semsys"; +#endif +#if defined (SYS_ioctl) + syscall_table[SYS_ioctl] = "ioctl"; +#endif +#if defined (SYS_uadmin) + syscall_table[SYS_uadmin] = "uadmin"; +#endif +#if defined (SYS_utssys) + syscall_table[SYS_utssys] = "utssys"; +#endif +#if defined (SYS_fsync) + syscall_table[SYS_fsync] = "fsync"; +#endif +#if defined (SYS_umask) + syscall_table[SYS_umask] = "umask"; +#endif +#if defined (SYS_chroot) + syscall_table[SYS_chroot] = "chroot"; +#endif +#if defined (SYS_fcntl) + syscall_table[SYS_fcntl] = "fcntl"; +#endif +#if defined (SYS_ulimit) + syscall_table[SYS_ulimit] = "ulimit"; +#endif +#if defined (SYS_rfsys) + syscall_table[SYS_rfsys] = "rfsys"; +#endif +#if defined (SYS_rmdir) + syscall_table[SYS_rmdir] = "rmdir"; +#endif +#if defined (SYS_mkdir) + syscall_table[SYS_mkdir] = "mkdir"; +#endif +#if defined (SYS_getdents) + syscall_table[SYS_getdents] = "getdents"; +#endif +#if defined (SYS_sysfs) + syscall_table[SYS_sysfs] = "sysfs"; +#endif +#if defined (SYS_getmsg) + syscall_table[SYS_getmsg] = "getmsg"; +#endif +#if defined (SYS_putmsg) + syscall_table[SYS_putmsg] = "putmsg"; +#endif +#if defined (SYS_poll) + syscall_table[SYS_poll] = "poll"; +#endif +#if defined (SYS_lstat) + syscall_table[SYS_lstat] = "lstat"; +#endif +#if defined (SYS_symlink) + syscall_table[SYS_symlink] = "symlink"; +#endif +#if defined (SYS_readlink) + syscall_table[SYS_readlink] = "readlink"; +#endif +#if defined (SYS_setgroups) + syscall_table[SYS_setgroups] = "setgroups"; +#endif +#if defined (SYS_getgroups) + syscall_table[SYS_getgroups] = "getgroups"; +#endif +#if defined (SYS_fchmod) + syscall_table[SYS_fchmod] = "fchmod"; +#endif +#if defined (SYS_fchown) + syscall_table[SYS_fchown] = "fchown"; +#endif +#if defined (SYS_sigprocmask) + syscall_table[SYS_sigprocmask] = "sigprocmask"; +#endif +#if defined (SYS_sigsuspend) + syscall_table[SYS_sigsuspend] = "sigsuspend"; +#endif +#if defined (SYS_sigaltstack) + syscall_table[SYS_sigaltstack] = "sigaltstack"; +#endif +#if defined (SYS_sigaction) + syscall_table[SYS_sigaction] = "sigaction"; +#endif +#if defined (SYS_sigpending) + syscall_table[SYS_sigpending] = "sigpending"; +#endif +#if defined (SYS_context) + syscall_table[SYS_context] = "context"; +#endif +#if defined (SYS_evsys) + syscall_table[SYS_evsys] = "evsys"; +#endif +#if defined (SYS_evtrapret) + syscall_table[SYS_evtrapret] = "evtrapret"; +#endif +#if defined (SYS_statvfs) + syscall_table[SYS_statvfs] = "statvfs"; +#endif +#if defined (SYS_fstatvfs) + syscall_table[SYS_fstatvfs] = "fstatvfs"; +#endif +#if defined (SYS_nfssys) + syscall_table[SYS_nfssys] = "nfssys"; +#endif +#if defined (SYS_waitsys) + syscall_table[SYS_waitsys] = "waitsys"; +#endif +#if defined (SYS_sigsendsys) + syscall_table[SYS_sigsendsys] = "sigsendsys"; +#endif +#if defined (SYS_hrtsys) + syscall_table[SYS_hrtsys] = "hrtsys"; +#endif +#if defined (SYS_acancel) + syscall_table[SYS_acancel] = "acancel"; +#endif +#if defined (SYS_async) + syscall_table[SYS_async] = "async"; +#endif +#if defined (SYS_priocntlsys) + syscall_table[SYS_priocntlsys] = "priocntlsys"; +#endif +#if defined (SYS_pathconf) + syscall_table[SYS_pathconf] = "pathconf"; +#endif +#if defined (SYS_mincore) + syscall_table[SYS_mincore] = "mincore"; +#endif +#if defined (SYS_mmap) + syscall_table[SYS_mmap] = "mmap"; +#endif +#if defined (SYS_mprotect) + syscall_table[SYS_mprotect] = "mprotect"; +#endif +#if defined (SYS_munmap) + syscall_table[SYS_munmap] = "munmap"; +#endif +#if defined (SYS_fpathconf) + syscall_table[SYS_fpathconf] = "fpathconf"; +#endif +#if defined (SYS_vfork) + syscall_table[SYS_vfork] = "vfork"; +#endif +#if defined (SYS_fchdir) + syscall_table[SYS_fchdir] = "fchdir"; +#endif +#if defined (SYS_readv) + syscall_table[SYS_readv] = "readv"; +#endif +#if defined (SYS_writev) + syscall_table[SYS_writev] = "writev"; +#endif +#if defined (SYS_xstat) + syscall_table[SYS_xstat] = "xstat"; +#endif +#if defined (SYS_lxstat) + syscall_table[SYS_lxstat] = "lxstat"; +#endif +#if defined (SYS_fxstat) + syscall_table[SYS_fxstat] = "fxstat"; +#endif +#if defined (SYS_xmknod) + syscall_table[SYS_xmknod] = "xmknod"; +#endif +#if defined (SYS_clocal) + syscall_table[SYS_clocal] = "clocal"; +#endif +#if defined (SYS_setrlimit) + syscall_table[SYS_setrlimit] = "setrlimit"; +#endif +#if defined (SYS_getrlimit) + syscall_table[SYS_getrlimit] = "getrlimit"; +#endif +#if defined (SYS_lchown) + syscall_table[SYS_lchown] = "lchown"; +#endif +#if defined (SYS_memcntl) + syscall_table[SYS_memcntl] = "memcntl"; +#endif +#if defined (SYS_getpmsg) + syscall_table[SYS_getpmsg] = "getpmsg"; +#endif +#if defined (SYS_putpmsg) + syscall_table[SYS_putpmsg] = "putpmsg"; +#endif +#if defined (SYS_rename) + syscall_table[SYS_rename] = "rename"; +#endif +#if defined (SYS_uname) + syscall_table[SYS_uname] = "uname"; +#endif +#if defined (SYS_setegid) + syscall_table[SYS_setegid] = "setegid"; +#endif +#if defined (SYS_sysconfig) + syscall_table[SYS_sysconfig] = "sysconfig"; +#endif +#if defined (SYS_adjtime) + syscall_table[SYS_adjtime] = "adjtime"; +#endif +#if defined (SYS_systeminfo) + syscall_table[SYS_systeminfo] = "systeminfo"; +#endif +#if defined (SYS_seteuid) + syscall_table[SYS_seteuid] = "seteuid"; +#endif +#if defined (SYS_sproc) + syscall_table[SYS_sproc] = "sproc"; +#endif +#if defined (SYS_keyctl) + syscall_table[SYS_keyctl] = "keyctl"; +#endif +#if defined (SYS_secsys) + syscall_table[SYS_secsys] = "secsys"; +#endif +#if defined (SYS_filepriv) + syscall_table[SYS_filepriv] = "filepriv"; +#endif +#if defined (SYS_procpriv) + syscall_table[SYS_procpriv] = "procpriv"; +#endif +#if defined (SYS_devstat) + syscall_table[SYS_devstat] = "devstat"; +#endif +#if defined (SYS_aclipc) + syscall_table[SYS_aclipc] = "aclipc"; +#endif +#if defined (SYS_fdevstat) + syscall_table[SYS_fdevstat] = "fdevstat"; +#endif +#if defined (SYS_flvlfile) + syscall_table[SYS_flvlfile] = "flvlfile"; +#endif +#if defined (SYS_lvlfile) + syscall_table[SYS_lvlfile] = "lvlfile"; +#endif +#if defined (SYS_lvlequal) + syscall_table[SYS_lvlequal] = "lvlequal"; +#endif +#if defined (SYS_lvlproc) + syscall_table[SYS_lvlproc] = "lvlproc"; +#endif +#if defined (SYS_lvlipc) + syscall_table[SYS_lvlipc] = "lvlipc"; +#endif +#if defined (SYS_acl) + syscall_table[SYS_acl] = "acl"; +#endif +#if defined (SYS_auditevt) + syscall_table[SYS_auditevt] = "auditevt"; +#endif +#if defined (SYS_auditctl) + syscall_table[SYS_auditctl] = "auditctl"; +#endif +#if defined (SYS_auditdmp) + syscall_table[SYS_auditdmp] = "auditdmp"; +#endif +#if defined (SYS_auditlog) + syscall_table[SYS_auditlog] = "auditlog"; +#endif +#if defined (SYS_auditbuf) + syscall_table[SYS_auditbuf] = "auditbuf"; +#endif +#if defined (SYS_lvldom) + syscall_table[SYS_lvldom] = "lvldom"; +#endif +#if defined (SYS_lvlvfs) + syscall_table[SYS_lvlvfs] = "lvlvfs"; +#endif +#if defined (SYS_mkmld) + syscall_table[SYS_mkmld] = "mkmld"; +#endif +#if defined (SYS_mldmode) + syscall_table[SYS_mldmode] = "mldmode"; +#endif +#if defined (SYS_secadvise) + syscall_table[SYS_secadvise] = "secadvise"; +#endif +#if defined (SYS_online) + syscall_table[SYS_online] = "online"; +#endif +#if defined (SYS_setitimer) + syscall_table[SYS_setitimer] = "setitimer"; +#endif +#if defined (SYS_getitimer) + syscall_table[SYS_getitimer] = "getitimer"; +#endif +#if defined (SYS_gettimeofday) + syscall_table[SYS_gettimeofday] = "gettimeofday"; +#endif +#if defined (SYS_settimeofday) + syscall_table[SYS_settimeofday] = "settimeofday"; +#endif +#if defined (SYS_lwp_create) + syscall_table[SYS_lwp_create] = "_lwp_create"; +#endif +#if defined (SYS_lwp_exit) + syscall_table[SYS_lwp_exit] = "_lwp_exit"; +#endif +#if defined (SYS_lwp_wait) + syscall_table[SYS_lwp_wait] = "_lwp_wait"; +#endif +#if defined (SYS_lwp_self) + syscall_table[SYS_lwp_self] = "_lwp_self"; +#endif +#if defined (SYS_lwp_info) + syscall_table[SYS_lwp_info] = "_lwp_info"; +#endif +#if defined (SYS_lwp_private) + syscall_table[SYS_lwp_private] = "_lwp_private"; +#endif +#if defined (SYS_processor_bind) + syscall_table[SYS_processor_bind] = "processor_bind"; +#endif +#if defined (SYS_processor_exbind) + syscall_table[SYS_processor_exbind] = "processor_exbind"; +#endif +#if defined (SYS_prepblock) + syscall_table[SYS_prepblock] = "prepblock"; +#endif +#if defined (SYS_block) + syscall_table[SYS_block] = "block"; +#endif +#if defined (SYS_rdblock) + syscall_table[SYS_rdblock] = "rdblock"; +#endif +#if defined (SYS_unblock) + syscall_table[SYS_unblock] = "unblock"; +#endif +#if defined (SYS_cancelblock) + syscall_table[SYS_cancelblock] = "cancelblock"; +#endif +#if defined (SYS_pread) + syscall_table[SYS_pread] = "pread"; +#endif +#if defined (SYS_pwrite) + syscall_table[SYS_pwrite] = "pwrite"; +#endif +#if defined (SYS_truncate) + syscall_table[SYS_truncate] = "truncate"; +#endif +#if defined (SYS_ftruncate) + syscall_table[SYS_ftruncate] = "ftruncate"; +#endif +#if defined (SYS_lwp_kill) + syscall_table[SYS_lwp_kill] = "_lwp_kill"; +#endif +#if defined (SYS_sigwait) + syscall_table[SYS_sigwait] = "sigwait"; +#endif +#if defined (SYS_fork1) + syscall_table[SYS_fork1] = "fork1"; +#endif +#if defined (SYS_forkall) + syscall_table[SYS_forkall] = "forkall"; +#endif +#if defined (SYS_modload) + syscall_table[SYS_modload] = "modload"; +#endif +#if defined (SYS_moduload) + syscall_table[SYS_moduload] = "moduload"; +#endif +#if defined (SYS_modpath) + syscall_table[SYS_modpath] = "modpath"; +#endif +#if defined (SYS_modstat) + syscall_table[SYS_modstat] = "modstat"; +#endif +#if defined (SYS_modadm) + syscall_table[SYS_modadm] = "modadm"; +#endif +#if defined (SYS_getksym) + syscall_table[SYS_getksym] = "getksym"; +#endif +#if defined (SYS_lwp_suspend) + syscall_table[SYS_lwp_suspend] = "_lwp_suspend"; +#endif +#if defined (SYS_lwp_continue) + syscall_table[SYS_lwp_continue] = "_lwp_continue"; +#endif +#if defined (SYS_priocntllst) + syscall_table[SYS_priocntllst] = "priocntllst"; +#endif +#if defined (SYS_sleep) + syscall_table[SYS_sleep] = "sleep"; +#endif +#if defined (SYS_lwp_sema_wait) + syscall_table[SYS_lwp_sema_wait] = "_lwp_sema_wait"; +#endif +#if defined (SYS_lwp_sema_post) + syscall_table[SYS_lwp_sema_post] = "_lwp_sema_post"; +#endif +#if defined (SYS_lwp_sema_trywait) + syscall_table[SYS_lwp_sema_trywait] = "lwp_sema_trywait"; +#endif +#if defined(SYS_fstatvfs64) + syscall_table[SYS_fstatvfs64] = "fstatvfs64"; +#endif +#if defined(SYS_statvfs64) + syscall_table[SYS_statvfs64] = "statvfs64"; +#endif +#if defined(SYS_ftruncate64) + syscall_table[SYS_ftruncate64] = "ftruncate64"; +#endif +#if defined(SYS_truncate64) + syscall_table[SYS_truncate64] = "truncate64"; +#endif +#if defined(SYS_getrlimit64) + syscall_table[SYS_getrlimit64] = "getrlimit64"; +#endif +#if defined(SYS_setrlimit64) + syscall_table[SYS_setrlimit64] = "setrlimit64"; +#endif +#if defined(SYS_lseek64) + syscall_table[SYS_lseek64] = "lseek64"; +#endif +#if defined(SYS_mmap64) + syscall_table[SYS_mmap64] = "mmap64"; +#endif +#if defined(SYS_pread64) + syscall_table[SYS_pread64] = "pread64"; +#endif +#if defined(SYS_creat64) + syscall_table[SYS_creat64] = "creat64"; +#endif +#if defined(SYS_dshmsys) + syscall_table[SYS_dshmsys] = "dshmsys"; +#endif +#if defined(SYS_invlpg) + syscall_table[SYS_invlpg] = "invlpg"; +#endif +#if defined(SYS_cg_ids) + syscall_table[SYS_cg_ids] = "cg_ids"; +#endif +#if defined(SYS_cg_processors) + syscall_table[SYS_cg_processors] = "cg_processors"; +#endif +#if defined(SYS_cg_info) + syscall_table[SYS_cg_info] = "cg_info"; +#endif +#if defined(SYS_cg_bind) + syscall_table[SYS_cg_bind] = "cg_bind"; +#endif +#if defined(SYS_cg_current) + syscall_table[SYS_cg_current] = "cg_current"; +#endif +#if defined(SYS_cg_memloc) + syscall_table[SYS_cg_memloc] = "cg_memloc"; +#endif +} + +/* + +LOCAL FUNCTION + + procfs_kill_inferior - kill any currently inferior + +SYNOPSIS + + void procfs_kill_inferior (void) + +DESCRIPTION + + Kill any current inferior. + +NOTES + + Kills even attached inferiors. Presumably the user has already + been prompted that the inferior is an attached one rather than + one started by gdb. (FIXME?) + +*/ + +static void +procfs_kill_inferior () +{ + target_mourn_inferior (); +} + +/* + +LOCAL FUNCTION + + unconditionally_kill_inferior - terminate the inferior + +SYNOPSIS + + static void unconditionally_kill_inferior (struct procinfo *) + +DESCRIPTION + + Kill the specified inferior. + +NOTE + + A possibly useful enhancement would be to first try sending + the inferior a terminate signal, politely asking it to commit + suicide, before we murder it (we could call that + politely_kill_inferior()). + +*/ + +static void +unconditionally_kill_inferior (pi) + struct procinfo *pi; +{ + int ppid; + struct proc_ctl pctl; + + ppid = pi->prstatus.pr_ppid; + +#ifdef PROCFS_NEED_CLEAR_CURSIG_FOR_KILL + /* Alpha OSF/1-3.x procfs needs a clear of the current signal + before the PIOCKILL, otherwise it might generate a corrupted core + file for the inferior. */ + ioctl (pi->ctl_fd, PIOCSSIG, NULL); +#endif +#ifdef PROCFS_NEED_PIOCSSIG_FOR_KILL + /* Alpha OSF/1-2.x procfs needs a PIOCSSIG call with a SIGKILL signal + to kill the inferior, otherwise it might remain stopped with a + pending SIGKILL. + We do not check the result of the PIOCSSIG, the inferior might have + died already. */ + { + struct siginfo newsiginfo; + + memset ((char *) &newsiginfo, 0, sizeof (newsiginfo)); + newsiginfo.si_signo = SIGKILL; + newsiginfo.si_code = 0; + newsiginfo.si_errno = 0; + newsiginfo.si_pid = getpid (); + newsiginfo.si_uid = getuid (); + ioctl (pi->ctl_fd, PIOCSSIG, &newsiginfo); + } +#else /* PROCFS_NEED_PIOCSSIG_FOR_KILL */ + procfs_write_pckill (pi); +#endif /* PROCFS_NEED_PIOCSSIG_FOR_KILL */ + + close_proc_file (pi); + +/* Only wait() for our direct children. Our grandchildren zombies are killed + by the death of their parents. */ + + if (ppid == getpid()) + wait ((int *) 0); +} + +/* + +LOCAL FUNCTION + + procfs_xfer_memory -- copy data to or from inferior memory space + +SYNOPSIS + + int procfs_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, + int dowrite, struct target_ops target) + +DESCRIPTION + + Copy LEN bytes to/from inferior's memory starting at MEMADDR + from/to debugger memory starting at MYADDR. Copy from inferior + if DOWRITE is zero or to inferior if DOWRITE is nonzero. + + Returns the length copied, which is either the LEN argument or + zero. This xfer function does not do partial moves, since procfs_ops + doesn't allow memory operations to cross below us in the target stack + anyway. + +NOTES + + The /proc interface makes this an almost trivial task. + */ + +static int +procfs_xfer_memory (memaddr, myaddr, len, dowrite, target) + CORE_ADDR memaddr; + char *myaddr; + int len; + int dowrite; + struct target_ops *target; /* ignored */ +{ + int nbytes = 0; + struct procinfo *pi; + + pi = current_procinfo; + + if (lseek(pi->as_fd, (off_t) memaddr, SEEK_SET) == (off_t) memaddr) + { + if (dowrite) + { + nbytes = write (pi->as_fd, myaddr, len); + } + else + { + nbytes = read (pi->as_fd, myaddr, len); + } + if (nbytes < 0) + { + nbytes = 0; + } + } + return (nbytes); +} + +/* + +LOCAL FUNCTION + + procfs_store_registers -- copy register values back to inferior + +SYNOPSIS + + void procfs_store_registers (int regno) + +DESCRIPTION + + Store our current register values back into the inferior. If + REGNO is -1 then store all the register, otherwise store just + the value specified by REGNO. + +NOTES + + If we are storing only a single register, we first have to get all + the current values from the process, overwrite the desired register + in the gregset with the one we want from gdb's registers, and then + send the whole set back to the process. For writing all the + registers, all we have to do is generate the gregset and send it to + the process. + + Also note that the process has to be stopped on an event of interest + for this to work, which basically means that it has to have been + run under the control of one of the other /proc ioctl calls and not + ptrace. Since we don't use ptrace anyway, we don't worry about this + fine point, but it is worth noting for future reference. + + Gdb is confused about what this function is supposed to return. + Some versions return a value, others return nothing. Some are + declared to return a value and actually return nothing. Gdb ignores + anything returned. (FIXME) + + */ + +static void +procfs_store_registers (regno) + int regno; +{ + struct procinfo *pi; +#ifdef PROCFS_USE_READ_WRITE + struct greg_ctl greg; + struct fpreg_ctl fpreg; +#endif + + pi = current_procinfo; + +#ifdef PROCFS_USE_READ_WRITE + if (regno != -1) + { + procfs_read_status (pi); + memcpy ((char *) &greg.gregset, + (char *) &pi->prstatus.pr_lwp.pr_context.uc_mcontext.gregs, + sizeof (gdb_gregset_t)); + } + fill_gregset (&greg.gregset, regno); + greg.cmd = PCSREG; + write (pi->ctl_fd, &greg, sizeof (greg)); +#else /* PROCFS_USE_READ_WRITE */ + if (regno != -1) + { + ioctl (pi->ctl_fd, PIOCGREG, &pi->gregset.gregset); + } + fill_gregset (&pi->gregset.gregset, regno); + ioctl (pi->ctl_fd, PIOCSREG, &pi->gregset.gregset); +#endif /* PROCFS_USE_READ_WRITE */ + +#if defined (FP0_REGNUM) + + /* Now repeat everything using the floating point register set, if the + target has floating point hardware. Since we ignore the returned value, + we'll never know whether it worked or not anyway. */ + +#ifdef PROCFS_USE_READ_WRITE + if (regno != -1) + { + procfs_read_status (pi); + memcpy ((char *) &fpreg.fpregset, + (char *) &pi->prstatus.pr_lwp.pr_context.uc_mcontext.fpregs, + sizeof (gdb_fpregset_t)); + } + fill_fpregset (&fpreg.fpregset, regno); + fpreg.cmd = PCSFPREG; + write (pi->ctl_fd, &fpreg, sizeof (fpreg)); +#else /* PROCFS_USE_READ_WRITE */ + if (regno != -1) + { + ioctl (pi->ctl_fd, PIOCGFPREG, &pi->fpregset.fpregset); + } + fill_fpregset (&pi->fpregset.fpregset, regno); + ioctl (pi->ctl_fd, PIOCSFPREG, &pi->fpregset.fpregset); +#endif /* PROCFS_USE_READ_WRITE */ + +#endif /* FP0_REGNUM */ + +} + +/* + +LOCAL FUNCTION + + init_procinfo - setup a procinfo struct and connect it to a process + +SYNOPSIS + + struct procinfo * init_procinfo (int pid) + +DESCRIPTION + + Allocate a procinfo structure, open the /proc file and then set up the + set of signals and faults that are to be traced. Returns a pointer to + the new procinfo structure. + +NOTES + + If proc_init_failed ever gets called, control returns to the command + processing loop via the standard error handling code. + + */ + +static struct procinfo * +init_procinfo (pid, kill) + int pid; + int kill; +{ + struct procinfo *pi = (struct procinfo *) + xmalloc (sizeof (struct procinfo)); + struct sig_ctl sctl; + struct flt_ctl fctl; + + memset ((char *) pi, 0, sizeof (*pi)); + if (!open_proc_file (pid, pi, O_RDWR, 1)) + proc_init_failed (pi, "can't open process file", kill); + + /* open_proc_file may modify pid. */ + + pid = pi -> pid; + + /* Add new process to process info list */ + + pi->next = procinfo_list; + procinfo_list = pi; + + add_fd (pi); /* Add to list for poll/select */ + + /* Remember some things about the inferior that we will, or might, change + so that we can restore them when we detach. */ +#ifdef UNIXWARE + memcpy ((char *) &pi->saved_trace.sigset, + (char *) &pi->prstatus.pr_sigtrace, sizeof (sigset_t)); + memcpy ((char *) &pi->saved_fltset.fltset, + (char *) &pi->prstatus.pr_flttrace, sizeof (fltset_t)); + memcpy ((char *) &pi->saved_entryset.sysset, + (char *) &pi->prstatus.pr_sysentry, sizeof (sysset_t)); + memcpy ((char *) &pi->saved_exitset.sysset, + (char *) &pi->prstatus.pr_sysexit, sizeof (sysset_t)); + + /* Set up trace and fault sets, as gdb expects them. */ + + prfillset (&sctl.sigset); + notice_signals (pi, &sctl); + prfillset (&fctl.fltset); + prdelset (&fctl.fltset, FLTPAGE); + +#else /* ! UNIXWARE */ + ioctl (pi->ctl_fd, PIOCGTRACE, &pi->saved_trace.sigset); + ioctl (pi->ctl_fd, PIOCGHOLD, &pi->saved_sighold.sigset); + ioctl (pi->ctl_fd, PIOCGFAULT, &pi->saved_fltset.fltset); + ioctl (pi->ctl_fd, PIOCGENTRY, &pi->saved_entryset.sysset); + ioctl (pi->ctl_fd, PIOCGEXIT, &pi->saved_exitset.sysset); + + /* Set up trace and fault sets, as gdb expects them. */ + + memset ((char *) &pi->prrun, 0, sizeof (pi->prrun)); + prfillset (&pi->prrun.pr_trace); + procfs_notice_signals (pid); + prfillset (&pi->prrun.pr_fault); + prdelset (&pi->prrun.pr_fault, FLTPAGE); +#ifdef PROCFS_DONT_TRACE_FAULTS + premptyset (&pi->prrun.pr_fault); +#endif +#endif /* UNIXWARE */ + + if (!procfs_read_status (pi)) + proc_init_failed (pi, "procfs_read_status failed", kill); + + return pi; +} + +/* + +LOCAL FUNCTION + + create_procinfo - initialize access to a /proc entry + +SYNOPSIS + + struct procinfo * create_procinfo (int pid) + +DESCRIPTION + + Allocate a procinfo structure, open the /proc file and then set up the + set of signals and faults that are to be traced. Returns a pointer to + the new procinfo structure. + +NOTES + + If proc_init_failed ever gets called, control returns to the command + processing loop via the standard error handling code. + + */ + +static struct procinfo * +create_procinfo (pid) + int pid; +{ + struct procinfo *pi; + struct sig_ctl sctl; + struct flt_ctl fctl; + + pi = find_procinfo (pid, 1); + if (pi != NULL) + return pi; /* All done! It already exists */ + + pi = init_procinfo (pid, 1); + +#ifndef UNIXWARE +/* A bug in Solaris (2.5 at least) causes PIOCWSTOP to hang on LWPs that are + already stopped, even if they all have PR_ASYNC set. */ + if (!(pi->prstatus.pr_flags & PR_STOPPED)) +#endif + if (!procfs_write_pcwstop (pi)) + proc_init_failed (pi, "procfs_write_pcwstop failed", 1); + +#ifdef PROCFS_USE_READ_WRITE + fctl.cmd = PCSFAULT; + if (write (pi->ctl_fd, (char *) &fctl, sizeof (struct flt_ctl)) < 0) + proc_init_failed (pi, "PCSFAULT failed", 1); +#else + if (ioctl (pi->ctl_fd, PIOCSFAULT, &pi->prrun.pr_fault) < 0) + proc_init_failed (pi, "PIOCSFAULT failed", 1); +#endif + + return pi; +} + +/* + +LOCAL FUNCTION + + procfs_exit_handler - handle entry into the _exit syscall + +SYNOPSIS + + int procfs_exit_handler (pi, syscall_num, why, rtnvalp, statvalp) + +DESCRIPTION + + This routine is called when an inferior process enters the _exit() + system call. It continues the process, and then collects the exit + status and pid which are returned in *statvalp and *rtnvalp. After + that it returns non-zero to indicate that procfs_wait should wake up. + +NOTES + There is probably a better way to do this. + + */ + +static int +procfs_exit_handler (pi, syscall_num, why, rtnvalp, statvalp) + struct procinfo *pi; + int syscall_num; + int why; + int *rtnvalp; + int *statvalp; +{ + struct procinfo *temp_pi, *next_pi; + struct proc_ctl pctl; + +#ifdef UNIXWARE + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; +#else + pi->prrun.pr_flags = PRCFAULT; +#endif + +#ifdef PROCFS_USE_READ_WRITE + if (write (pi->ctl_fd, (char *)&pctl, sizeof (struct proc_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) +#endif + perror_with_name (pi->pathname); + + if (attach_flag) + { + /* Claim it exited (don't call wait). */ + if (info_verbose) + printf_filtered ("(attached process has exited)\n"); + *statvalp = 0; + *rtnvalp = inferior_pid; + } + else + { + *rtnvalp = wait (statvalp); + if (*rtnvalp >= 0) + *rtnvalp = pi->pid; + } + + /* Close ALL open proc file handles, + except the one that called SYS_exit. */ + for (temp_pi = procinfo_list; temp_pi; temp_pi = next_pi) + { + next_pi = temp_pi->next; + if (temp_pi == pi) + continue; /* Handled below */ + close_proc_file (temp_pi); + } + return 1; +} + +/* + +LOCAL FUNCTION + + procfs_exec_handler - handle exit from the exec family of syscalls + +SYNOPSIS + + int procfs_exec_handler (pi, syscall_num, why, rtnvalp, statvalp) + +DESCRIPTION + + This routine is called when an inferior process is about to finish any + of the exec() family of system calls. It pretends that we got a + SIGTRAP (for compatibility with ptrace behavior), and returns non-zero + to tell procfs_wait to wake up. + +NOTES + This need for compatibility with ptrace is questionable. In the + future, it shouldn't be necessary. + + */ + +static int +procfs_exec_handler (pi, syscall_num, why, rtnvalp, statvalp) + struct procinfo *pi; + int syscall_num; + int why; + int *rtnvalp; + int *statvalp; +{ + *statvalp = (SIGTRAP << 8) | 0177; + + return 1; +} + +#if defined(SYS_sproc) && !defined(UNIXWARE) +/* IRIX lwp creation system call */ + +/* + +LOCAL FUNCTION + + procfs_sproc_handler - handle exit from the sproc syscall + +SYNOPSIS + + int procfs_sproc_handler (pi, syscall_num, why, rtnvalp, statvalp) + +DESCRIPTION + + This routine is called when an inferior process is about to finish an + sproc() system call. This is the system call that IRIX uses to create + a lightweight process. When the target process gets this event, we can + look at rval1 to find the new child processes ID, and create a new + procinfo struct from that. + + After that, it pretends that we got a SIGTRAP, and returns non-zero + to tell procfs_wait to wake up. Subsequently, wait_for_inferior gets + woken up, sees the new process and continues it. + +NOTES + We actually never see the child exiting from sproc because we will + shortly stop the child with PIOCSTOP, which is then registered as the + event of interest. + */ + +static int +procfs_sproc_handler (pi, syscall_num, why, rtnvalp, statvalp) + struct procinfo *pi; + int syscall_num; + int why; + int *rtnvalp; + int *statvalp; +{ +/* We've just detected the completion of an sproc system call. Now we need to + setup a procinfo struct for this thread, and notify the thread system of the + new arrival. */ + +/* If sproc failed, then nothing interesting happened. Continue the process + and go back to sleep. */ + + if (pi->prstatus.pr_errno != 0) + { + pi->prrun.pr_flags &= PRSTEP; + pi->prrun.pr_flags |= PRCFAULT; + + if (ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) + perror_with_name (pi->pathname); + + return 0; + } + + /* At this point, the new thread is stopped at it's first instruction, and + the parent is stopped at the exit from sproc. */ + + /* Notify the caller of the arrival of a new thread. */ + create_procinfo (pi->prstatus.pr_rval1); + + *rtnvalp = pi->prstatus.pr_rval1; + *statvalp = (SIGTRAP << 8) | 0177; + + return 1; +} + +/* + +LOCAL FUNCTION + + procfs_fork_handler - handle exit from the fork syscall + +SYNOPSIS + + int procfs_fork_handler (pi, syscall_num, why, rtnvalp, statvalp) + +DESCRIPTION + + This routine is called when an inferior process is about to finish a + fork() system call. We will open up the new process, and then close + it, which releases it from the clutches of the debugger. + + After that, we continue the target process as though nothing had + happened. + +NOTES + This is necessary for IRIX because we have to set PR_FORK in order + to catch the creation of lwps (via sproc()). When an actual fork + occurs, it becomes necessary to reset the forks debugger flags and + continue it because we can't hack multiple processes yet. + */ + +static int +procfs_fork_handler (pi, syscall_num, why, rtnvalp, statvalp) + struct procinfo *pi; + int syscall_num; + int why; + int *rtnvalp; + int *statvalp; +{ + struct procinfo *pitemp; + +/* At this point, we've detected the completion of a fork (or vfork) call in + our child. The grandchild is also stopped because we set inherit-on-fork + earlier. (Note that nobody has the grandchilds' /proc file open at this + point.) We will release the grandchild from the debugger by opening it's + /proc file and then closing it. Since run-on-last-close is set, the + grandchild continues on its' merry way. */ + + + pitemp = create_procinfo (pi->prstatus.pr_rval1); + if (pitemp) + close_proc_file (pitemp); + + if (ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) + perror_with_name (pi->pathname); + + return 0; +} +#endif /* SYS_sproc && !UNIXWARE */ + +/* + +LOCAL FUNCTION + + procfs_set_inferior_syscall_traps - setup the syscall traps + +SYNOPSIS + + void procfs_set_inferior_syscall_traps (struct procinfo *pip) + +DESCRIPTION + + Called for each "procinfo" (process, thread, or LWP) in the + inferior, to register for notification of and handlers for + syscall traps in the inferior. + + */ + +static void +procfs_set_inferior_syscall_traps (pip) + struct procinfo *pip; +{ + procfs_set_syscall_trap (pip, SYS_exit, PROCFS_SYSCALL_ENTRY, + procfs_exit_handler); + +#ifndef PRFS_STOPEXEC +#ifdef SYS_exec + procfs_set_syscall_trap (pip, SYS_exec, PROCFS_SYSCALL_EXIT, + procfs_exec_handler); +#endif +#ifdef SYS_execv + procfs_set_syscall_trap (pip, SYS_execv, PROCFS_SYSCALL_EXIT, + procfs_exec_handler); +#endif +#ifdef SYS_execve + procfs_set_syscall_trap (pip, SYS_execve, PROCFS_SYSCALL_EXIT, + procfs_exec_handler); +#endif +#endif /* PRFS_STOPEXEC */ + + /* Setup traps on exit from sproc() */ + +#ifdef SYS_sproc + procfs_set_syscall_trap (pip, SYS_sproc, PROCFS_SYSCALL_EXIT, + procfs_sproc_handler); + procfs_set_syscall_trap (pip, SYS_fork, PROCFS_SYSCALL_EXIT, + procfs_fork_handler); +#ifdef SYS_vfork + procfs_set_syscall_trap (pip, SYS_vfork, PROCFS_SYSCALL_EXIT, + procfs_fork_handler); +#endif +/* Turn on inherit-on-fork flag so that all children of the target process + start with tracing flags set. This allows us to trap lwp creation. Note + that we also have to trap on fork and vfork in order to disable all tracing + in the targets child processes. */ + + modify_inherit_on_fork_flag (pip->ctl_fd, 1); +#endif + +#ifdef SYS_lwp_create + procfs_set_syscall_trap (pip, SYS_lwp_create, PROCFS_SYSCALL_EXIT, + procfs_lwp_creation_handler); +#endif +} + +/* + +LOCAL FUNCTION + + procfs_init_inferior - initialize target vector and access to a + /proc entry + +SYNOPSIS + + void procfs_init_inferior (int pid) + +DESCRIPTION + + When gdb starts an inferior, this function is called in the parent + process immediately after the fork. It waits for the child to stop + on the return from the exec system call (the child itself takes care + of ensuring that this is set up), then sets up the set of signals + and faults that are to be traced. Returns the pid, which may have had + the thread-id added to it. + +NOTES + + If proc_init_failed ever gets called, control returns to the command + processing loop via the standard error handling code. + + */ + +static void +procfs_init_inferior (pid) + int pid; +{ + struct procinfo *pip; + + push_target (&procfs_ops); + + pip = create_procinfo (pid); + + procfs_set_inferior_syscall_traps (pip); + + /* create_procinfo may change the pid, so we have to update inferior_pid + here before calling other gdb routines that need the right pid. */ + + pid = pip -> pid; + inferior_pid = pid; + + add_thread (pip -> pid); /* Setup initial thread */ + +#ifdef START_INFERIOR_TRAPS_EXPECTED + startup_inferior (START_INFERIOR_TRAPS_EXPECTED); +#else + /* One trap to exec the shell, one to exec the program being debugged. */ + startup_inferior (2); +#endif +} + +/* + +GLOBAL FUNCTION + + procfs_notice_signals + +SYNOPSIS + + static void procfs_notice_signals (int pid); + +DESCRIPTION + + When the user changes the state of gdb's signal handling via the + "handle" command, this function gets called to see if any change + in the /proc interface is required. It is also called internally + by other /proc interface functions to initialize the state of + the traced signal set. + + One thing it does is that signals for which the state is "nostop", + "noprint", and "pass", have their trace bits reset in the pr_trace + field, so that they are no longer traced. This allows them to be + delivered directly to the inferior without the debugger ever being + involved. + */ + +static void +procfs_notice_signals (pid) + int pid; +{ + struct procinfo *pi; + struct sig_ctl sctl; + + pi = find_procinfo (pid, 0); + +#ifndef HAVE_PRRUN_T + premptyset (&sctl.sigset); +#else + sctl.sigset = pi->prrun.pr_trace; +#endif + + notice_signals (pi, &sctl); + +#ifdef HAVE_PRRUN_T + pi->prrun.pr_trace = sctl.sigset; +#endif +} + +static void +notice_signals (pi, sctl) + struct procinfo *pi; + struct sig_ctl *sctl; +{ + int signo; + + for (signo = 0; signo < NSIG; signo++) + { + if (signal_stop_state (target_signal_from_host (signo)) == 0 && + signal_print_state (target_signal_from_host (signo)) == 0 && + signal_pass_state (target_signal_from_host (signo)) == 1) + { + prdelset (&sctl->sigset, signo); + } + else + { + praddset (&sctl->sigset, signo); + } + } +#ifdef PROCFS_USE_READ_WRITE + sctl->cmd = PCSTRACE; + if (write (pi->ctl_fd, (char *) sctl, sizeof (struct sig_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSTRACE, &sctl->sigset)) +#endif + { + print_sys_errmsg ("PIOCSTRACE failed", errno); + } +} + +/* + +LOCAL FUNCTION + + proc_set_exec_trap -- arrange for exec'd child to halt at startup + +SYNOPSIS + + void proc_set_exec_trap (void) + +DESCRIPTION + + This function is called in the child process when starting up + an inferior, prior to doing the exec of the actual inferior. + It sets the child process's exitset to make exit from the exec + system call an event of interest to stop on, and then simply + returns. The child does the exec, the system call returns, and + the child stops at the first instruction, ready for the gdb + parent process to take control of it. + +NOTE + + We need to use all local variables since the child may be sharing + it's data space with the parent, if vfork was used rather than + fork. + + Also note that we want to turn off the inherit-on-fork flag in + the child process so that any grand-children start with all + tracing flags cleared. + */ + +static void +proc_set_exec_trap () +{ + struct sys_ctl exitset; + struct sys_ctl entryset; + char procname[MAX_PROC_NAME_SIZE]; + int fd; + + sprintf (procname, CTL_PROC_NAME_FMT, getpid ()); +#ifdef UNIXWARE + if ((fd = open (procname, O_WRONLY)) < 0) +#else + if ((fd = open (procname, O_RDWR)) < 0) +#endif + { + perror (procname); + gdb_flush (gdb_stderr); + _exit (127); + } + premptyset (&exitset.sysset); + premptyset (&entryset.sysset); + +#ifdef PRFS_STOPEXEC + /* Under Alpha OSF/1 we have to use a PIOCSSPCACT ioctl to trace + exits from exec system calls because of the user level loader. */ + { + int prfs_flags; + + if (ioctl (fd, PIOCGSPCACT, &prfs_flags) < 0) + { + perror (procname); + gdb_flush (gdb_stderr); + _exit (127); + } + prfs_flags |= PRFS_STOPEXEC; + if (ioctl (fd, PIOCSSPCACT, &prfs_flags) < 0) + { + perror (procname); + gdb_flush (gdb_stderr); + _exit (127); + } + } +#else /* PRFS_STOPEXEC */ + /* GW: Rationale... + Not all systems with /proc have all the exec* syscalls with the same + names. On the SGI, for example, there is no SYS_exec, but there + *is* a SYS_execv. So, we try to account for that. */ + +#ifdef SYS_exec + praddset (&exitset.sysset, SYS_exec); +#endif +#ifdef SYS_execve + praddset (&exitset.sysset, SYS_execve); +#endif +#ifdef SYS_execv + praddset (&exitset.sysset, SYS_execv); +#endif + +#ifdef PROCFS_USE_READ_WRITE + exitset.cmd = PCSEXIT; + if (write (fd, (char *) &exitset, sizeof (struct sys_ctl)) < 0) +#else + if (ioctl (fd, PIOCSEXIT, &exitset.sysset) < 0) +#endif + { + perror (procname); + gdb_flush (gdb_stderr); + _exit (127); + } +#endif /* PRFS_STOPEXEC */ + + praddset (&entryset.sysset, SYS_exit); + +#ifdef PROCFS_USE_READ_WRITE + entryset.cmd = PCSENTRY; + if (write (fd, (char *) &entryset, sizeof (struct sys_ctl)) < 0) +#else + if (ioctl (fd, PIOCSENTRY, &entryset.sysset) < 0) +#endif + { + perror (procname); + gdb_flush (gdb_stderr); + _exit (126); + } + + /* Turn off inherit-on-fork flag so that all grand-children of gdb + start with tracing flags cleared. */ + + modify_inherit_on_fork_flag (fd, 0); + + /* Turn on run-on-last-close flag so that this process will not hang + if GDB goes away for some reason. */ + + modify_run_on_last_close_flag (fd, 1); + +#ifndef UNIXWARE /* since this is a solaris-ism, we don't want it */ + /* NOTE: revisit when doing thread support for UW */ +#ifdef PR_ASYNC + { + long pr_flags; + struct proc_ctl pctl; + +/* Solaris needs this to make procfs treat all threads seperately. Without + this, all threads halt whenever something happens to any thread. Since + GDB wants to control all this itself, it needs to set PR_ASYNC. */ + + pr_flags = PR_ASYNC; +#ifdef PROCFS_USE_READ_WRITE + pctl.cmd = PCSET; + pctl.data = PR_FORK|PR_ASYNC; + write (fd, (char *) &pctl, sizeof (struct proc_ctl)); +#else + ioctl (fd, PIOCSET, &pr_flags); +#endif + } +#endif /* PR_ASYNC */ +#endif /* !UNIXWARE */ +} + +/* + +GLOBAL FUNCTION + + proc_iterate_over_mappings -- call function for every mapped space + +SYNOPSIS + + int proc_iterate_over_mappings (int (*func)()) + +DESCRIPTION + + Given a pointer to a function, call that function for every + mapped address space, passing it an open file descriptor for + the file corresponding to that mapped address space (if any) + and the base address of the mapped space. Quit when we hit + the end of the mappings or the function returns nonzero. + */ + +#ifdef UNIXWARE +int +proc_iterate_over_mappings (func) + int (*func) PARAMS ((int, CORE_ADDR)); +{ + int nmap; + int fd; + int funcstat = 0; + prmap_t *prmaps; + prmap_t *prmap; + struct procinfo *pi; + struct stat sbuf; + + pi = current_procinfo; + + if (fstat (pi->map_fd, &sbuf) < 0) + return 0; + + nmap = sbuf.st_size / sizeof (prmap_t); + prmaps = (prmap_t *) alloca (nmap * sizeof(prmap_t)); + if ((lseek (pi->map_fd, 0, SEEK_SET) == 0) && + (read (pi->map_fd, (char *) prmaps, nmap * sizeof (prmap_t)) == + (nmap * sizeof (prmap_t)))) + { + int i = 0; + for (prmap = prmaps; i < nmap && funcstat == 0; ++prmap, ++i) + { + char name[sizeof ("/proc/1234567890/object") + + sizeof (prmap->pr_mapname)]; + sprintf (name, "/proc/%d/object/%s", pi->pid, prmap->pr_mapname); + if ((fd = open (name, O_RDONLY)) == -1) + { + funcstat = 1; + break; + } + funcstat = (*func) (fd, (CORE_ADDR) prmap->pr_vaddr); + close (fd); + } + } + return (funcstat); +} +#else /* UNIXWARE */ +int +proc_iterate_over_mappings (func) + int (*func) PARAMS ((int, CORE_ADDR)); +{ + int nmap; + int fd; + int funcstat = 0; + struct prmap *prmaps; + struct prmap *prmap; + struct procinfo *pi; + + pi = current_procinfo; + + if (ioctl (pi->map_fd, PIOCNMAP, &nmap) == 0) + { + prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); + if (ioctl (pi->map_fd, PIOCMAP, prmaps) == 0) + { + for (prmap = prmaps; prmap -> pr_size && funcstat == 0; ++prmap) + { + fd = proc_address_to_fd (pi, (CORE_ADDR) prmap -> pr_vaddr, 0); + funcstat = (*func) (fd, (CORE_ADDR) prmap -> pr_vaddr); + close (fd); + } + } + } + return (funcstat); +} +#endif /* UNIXWARE */ + +#if 0 /* Currently unused */ +/* + +GLOBAL FUNCTION + + proc_base_address -- find base address for segment containing address + +SYNOPSIS + + CORE_ADDR proc_base_address (CORE_ADDR addr) + +DESCRIPTION + + Given an address of a location in the inferior, find and return + the base address of the mapped segment containing that address. + + This is used for example, by the shared library support code, + where we have the pc value for some location in the shared library + where we are stopped, and need to know the base address of the + segment containing that address. +*/ + +CORE_ADDR +proc_base_address (addr) + CORE_ADDR addr; +{ + int nmap; + struct prmap *prmaps; + struct prmap *prmap; + CORE_ADDR baseaddr = 0; + struct procinfo *pi; + + pi = current_procinfo; + + if (ioctl (pi->map_fd, PIOCNMAP, &nmap) == 0) + { + prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); + if (ioctl (pi->map_fd, PIOCMAP, prmaps) == 0) + { + for (prmap = prmaps; prmap -> pr_size; ++prmap) + { + if ((prmap -> pr_vaddr <= (caddr_t) addr) && + (prmap -> pr_vaddr + prmap -> pr_size > (caddr_t) addr)) + { + baseaddr = (CORE_ADDR) prmap -> pr_vaddr; + break; + } + } + } + } + return (baseaddr); +} + +#endif /* 0 */ + +#ifndef UNIXWARE +/* + +LOCAL FUNCTION + + proc_address_to_fd -- return open fd for file mapped to address + +SYNOPSIS + + int proc_address_to_fd (struct procinfo *pi, CORE_ADDR addr, complain) + +DESCRIPTION + + Given an address in the current inferior's address space, use the + /proc interface to find an open file descriptor for the file that + this address was mapped in from. Return -1 if there is no current + inferior. Print a warning message if there is an inferior but + the address corresponds to no file (IE a bogus address). + +*/ + +static int +proc_address_to_fd (pi, addr, complain) + struct procinfo *pi; + CORE_ADDR addr; + int complain; +{ + int fd = -1; + + if ((fd = ioctl (pi->ctl_fd, PIOCOPENM, (caddr_t *) &addr)) < 0) + { + if (complain) + { + print_sys_errmsg (pi->pathname, errno); + warning ("can't find mapped file for address 0x%x", addr); + } + } + return (fd); +} +#endif /* !UNIXWARE */ + +/* Attach to process PID, then initialize for debugging it + and wait for the trace-trap that results from attaching. */ + +static void +procfs_attach (args, from_tty) + char *args; + int from_tty; +{ + char *exec_file; + int pid; + + if (!args) + error_no_arg ("process-id to attach"); + + pid = atoi (args); + + if (pid == getpid()) /* Trying to masturbate? */ + error ("I refuse to debug myself!"); + + if (from_tty) + { + exec_file = (char *) get_exec_file (0); + + if (exec_file) + printf_unfiltered ("Attaching to program `%s', %s\n", exec_file, target_pid_to_str (pid)); + else + printf_unfiltered ("Attaching to %s\n", target_pid_to_str (pid)); + + gdb_flush (gdb_stdout); + } + + inferior_pid = pid = do_attach (pid); + push_target (&procfs_ops); +} + + +/* Take a program previously attached to and detaches it. + The program resumes execution and will no longer stop + on signals, etc. We'd better not have left any breakpoints + in the program or it'll die when it hits one. For this + to work, it may be necessary for the process to have been + previously attached. It *might* work if the program was + started via the normal ptrace (PTRACE_TRACEME). */ + +static void +procfs_detach (args, from_tty) + char *args; + int from_tty; +{ + int siggnal = 0; + + if (from_tty) + { + char *exec_file = get_exec_file (0); + if (exec_file == 0) + exec_file = ""; + printf_unfiltered ("Detaching from program: %s %s\n", + exec_file, target_pid_to_str (inferior_pid)); + gdb_flush (gdb_stdout); + } + if (args) + siggnal = atoi (args); + + do_detach (siggnal); + inferior_pid = 0; + unpush_target (&procfs_ops); /* Pop out of handling an inferior */ +} + +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On machines + which store all the registers in one fell swoop, this makes sure + that registers contains all the registers from the program being + debugged. */ + +static void +procfs_prepare_to_store () +{ +#ifdef CHILD_PREPARE_TO_STORE + CHILD_PREPARE_TO_STORE (); +#endif +} + +/* Print status information about what we're accessing. */ + +static void +procfs_files_info (ignore) + struct target_ops *ignore; +{ + printf_unfiltered ("\tUsing the running image of %s %s via /proc.\n", + attach_flag? "attached": "child", target_pid_to_str (inferior_pid)); +} + +/* ARGSUSED */ +static void +procfs_open (arg, from_tty) + char *arg; + int from_tty; +{ + error ("Use the \"run\" command to start a Unix child process."); +} + +/* + +LOCAL FUNCTION + + do_attach -- attach to an already existing process + +SYNOPSIS + + int do_attach (int pid) + +DESCRIPTION + + Attach to an already existing process with the specified process + id. If the process is not already stopped, query whether to + stop it or not. + +NOTES + + The option of stopping at attach time is specific to the /proc + versions of gdb. Versions using ptrace force the attachee + to stop. (I have changed this version to do so, too. All you + have to do is "continue" to make it go on. -- gnu@cygnus.com) + +*/ + +static int +do_attach (pid) + int pid; +{ + struct procinfo *pi; + struct sig_ctl sctl; + struct flt_ctl fctl; + int nlwp, *lwps; + + pi = init_procinfo (pid, 0); + +#ifdef PIOCLWPIDS + nlwp = pi->prstatus.pr_nlwp; + lwps = alloca ((2 * nlwp + 2) * sizeof (id_t)); + + if (ioctl (pi->ctl_fd, PIOCLWPIDS, lwps)) + { + print_sys_errmsg (pi -> pathname, errno); + error ("PIOCLWPIDS failed"); + } +#else /* PIOCLWPIDS */ + nlwp = 1; + lwps = alloca ((2 * nlwp + 2) * sizeof *lwps); + lwps[0] = 0; +#endif + for (; nlwp > 0; nlwp--, lwps++) + { + /* First one has already been created above. */ + if ((pi = find_procinfo ((*lwps << 16) | pid, 1)) == 0) + pi = init_procinfo ((*lwps << 16) | pid, 0); + + if (THE_PR_LWP(pi->prstatus).pr_flags & (PR_STOPPED | PR_ISTOP)) + { + pi->was_stopped = 1; + } + else + { + pi->was_stopped = 0; + if (1 || query ("Process is currently running, stop it? ")) + { + long cmd; + /* Make it run again when we close it. */ + modify_run_on_last_close_flag (pi->ctl_fd, 1); +#ifdef PROCFS_USE_READ_WRITE + cmd = PCSTOP; + if (write (pi->ctl_fd, (char *) &cmd, sizeof (long)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSTOP, &pi->prstatus) < 0) +#endif + { + print_sys_errmsg (pi->pathname, errno); + close_proc_file (pi); + error ("PIOCSTOP failed"); + } +#ifdef UNIXWARE + if (!procfs_read_status (pi)) + { + print_sys_errmsg (pi->pathname, errno); + close_proc_file (pi); + error ("procfs_read_status failed"); + } +#endif + pi->nopass_next_sigstop = 1; + } + else + { + printf_unfiltered ("Ok, gdb will wait for %s to stop.\n", + target_pid_to_str (pi->pid)); + } + } + +#ifdef PROCFS_USE_READ_WRITE + fctl.cmd = PCSFAULT; + if (write (pi->ctl_fd, (char *) &fctl, sizeof (struct flt_ctl)) < 0) + print_sys_errmsg ("PCSFAULT failed", errno); +#else /* PROCFS_USE_READ_WRITE */ + if (ioctl (pi->ctl_fd, PIOCSFAULT, &pi->prrun.pr_fault)) + { + print_sys_errmsg ("PIOCSFAULT failed", errno); + } + if (ioctl (pi->ctl_fd, PIOCSTRACE, &pi->prrun.pr_trace)) + { + print_sys_errmsg ("PIOCSTRACE failed", errno); + } + add_thread (pi->pid); + procfs_set_inferior_syscall_traps (pi); +#endif /* PROCFS_USE_READ_WRITE */ + } + attach_flag = 1; + return (pi->pid); +} + +/* + +LOCAL FUNCTION + + do_detach -- detach from an attached-to process + +SYNOPSIS + + void do_detach (int signal) + +DESCRIPTION + + Detach from the current attachee. + + If signal is non-zero, the attachee is started running again and sent + the specified signal. + + If signal is zero and the attachee was not already stopped when we + attached to it, then we make it runnable again when we detach. + + Otherwise, we query whether or not to make the attachee runnable + again, since we may simply want to leave it in the state it was in + when we attached. + + We report any problems, but do not consider them errors, since we + MUST detach even if some things don't seem to go right. This may not + be the ideal situation. (FIXME). + */ + +static void +do_detach (signal) + int signal; +{ + struct procinfo *pi; + + for (pi = procinfo_list; pi; pi = pi->next) + { + if (signal) + { + set_proc_siginfo (pi, signal); + } +#ifdef PROCFS_USE_READ_WRITE + pi->saved_exitset.cmd = PCSEXIT; + if (write (pi->ctl_fd, (char *) &pi->saved_exitset, + sizeof (struct sys_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSEXIT, &pi->saved_exitset.sysset) < 0) +#endif + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOCSEXIT failed.\n"); + } +#ifdef PROCFS_USE_READ_WRITE + pi->saved_entryset.cmd = PCSENTRY; + if (write (pi->ctl_fd, (char *) &pi->saved_entryset, + sizeof (struct sys_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSENTRY, &pi->saved_entryset.sysset) < 0) +#endif + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOCSENTRY failed.\n"); + } +#ifdef PROCFS_USE_READ_WRITE + pi->saved_trace.cmd = PCSTRACE; + if (write (pi->ctl_fd, (char *) &pi->saved_trace, + sizeof (struct sig_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSTRACE, &pi->saved_trace.sigset) < 0) +#endif + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOCSTRACE failed.\n"); + } +#ifndef UNIXWARE + if (ioctl (pi->ctl_fd, PIOCSHOLD, &pi->saved_sighold.sigset) < 0) + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOSCHOLD failed.\n"); + } +#endif +#ifdef PROCFS_USE_READ_WRITE + pi->saved_fltset.cmd = PCSFAULT; + if (write (pi->ctl_fd, (char *) &pi->saved_fltset, + sizeof (struct flt_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSFAULT, &pi->saved_fltset.fltset) < 0) +#endif + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOCSFAULT failed.\n"); + } + if (!procfs_read_status (pi)) + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("procfs_read_status failed.\n"); + } + else + { + if (signal + || (THE_PR_LWP(pi->prstatus).pr_flags & (PR_STOPPED | PR_ISTOP))) + { + long cmd; + struct proc_ctl pctl; + + if (signal || !pi->was_stopped || + query ("Was stopped when attached, make it runnable again? ")) + { + /* Clear any pending signal if we want to detach without + a signal. */ + if (signal == 0) + set_proc_siginfo (pi, signal); + + /* Clear any fault that might have stopped it. */ +#ifdef PROCFS_USE_READ_WRITE + cmd = PCCFAULT; + if (write (pi->ctl_fd, (char *) &cmd, sizeof (long)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCCFAULT, 0)) +#endif + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOCCFAULT failed.\n"); + } + + /* Make it run again when we close it. */ + + modify_run_on_last_close_flag (pi->ctl_fd, 1); + } + } + } + close_proc_file (pi); + } + attach_flag = 0; +} + +/* emulate wait() as much as possible. + Wait for child to do something. Return pid of child, or -1 in case + of error; store status in *OURSTATUS. + + Not sure why we can't + just use wait(), but it seems to have problems when applied to a + process being controlled with the /proc interface. + + We have a race problem here with no obvious solution. We need to let + the inferior run until it stops on an event of interest, which means + that we need to use the PIOCWSTOP ioctl. However, we cannot use this + ioctl if the process is already stopped on something that is not an + event of interest, or the call will hang indefinitely. Thus we first + use PIOCSTATUS to see if the process is not stopped. If not, then we + use PIOCWSTOP. But during the window between the two, if the process + stops for any reason that is not an event of interest (such as a job + control signal) then gdb will hang. One possible workaround is to set + an alarm to wake up every minute of so and check to see if the process + is still running, and if so, then reissue the PIOCWSTOP. But this is + a real kludge, so has not been implemented. FIXME: investigate + alternatives. + + FIXME: Investigate why wait() seems to have problems with programs + being control by /proc routines. */ +static int +procfs_wait (pid, ourstatus) + int pid; + struct target_waitstatus *ourstatus; +{ + short what; + short why; + int statval = 0; + int checkerr = 0; + int rtnval = -1; + struct procinfo *pi; + struct proc_ctl pctl; + +scan_again: + + /* handle all syscall events first, otherwise we might not + notice a thread was created until too late. */ + + for (pi = procinfo_list; pi; pi = pi->next) + { + if (!pi->had_event) + continue; + + if (! (THE_PR_LWP(pi->prstatus).pr_flags & (PR_STOPPED | PR_ISTOP)) ) + continue; + + why = THE_PR_LWP(pi->prstatus).pr_why; + what = THE_PR_LWP(pi->prstatus).pr_what; + if (why == PR_SYSENTRY || why == PR_SYSEXIT) + { + int i; + int found_handler = 0; + + for (i = 0; i < pi->num_syscall_handlers; i++) + if (pi->syscall_handlers[i].syscall_num == what) + { + found_handler = 1; + pi->saved_rtnval = pi->pid; + pi->saved_statval = 0; + if (!pi->syscall_handlers[i].func + (pi, what, why, &pi->saved_rtnval, &pi->saved_statval)) + pi->had_event = 0; + break; + } + + if (!found_handler) + { + if (why == PR_SYSENTRY) + error ("PR_SYSENTRY, unhandled system call %d", what); + else + error ("PR_SYSEXIT, unhandled system call %d", what); + } + } + } + + /* find a relevant process with an event */ + + for (pi = procinfo_list; pi; pi = pi->next) + if (pi->had_event && (pid == -1 || pi->pid == pid)) + break; + + if (!pi) + { + wait_fd (); + goto scan_again; + } + + if (!checkerr + && !(THE_PR_LWP(pi->prstatus).pr_flags & (PR_STOPPED | PR_ISTOP))) + { + if (!procfs_write_pcwstop (pi)) + { + checkerr++; + } + } + if (checkerr) + { + if (errno == ENOENT) + { + /* XXX Fixme -- what to do if attached? Can't call wait... */ + rtnval = wait (&statval); + if ((rtnval) != (PIDGET (inferior_pid))) + { + print_sys_errmsg (pi->pathname, errno); + error ("procfs_wait: wait failed, returned %d", rtnval); + /* NOTREACHED */ + } + } + else + { + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSTATUS or PIOCWSTOP failed."); + /* NOTREACHED */ + } + } + else if (THE_PR_LWP(pi->prstatus).pr_flags & (PR_STOPPED | PR_ISTOP)) + { +#ifdef UNIXWARE + rtnval = pi->prstatus.pr_pid; +#else + rtnval = pi->pid; +#endif + why = THE_PR_LWP(pi->prstatus).pr_why; + what = THE_PR_LWP(pi->prstatus).pr_what; + + switch (why) + { + case PR_SIGNALLED: + statval = (what << 8) | 0177; + break; + case PR_SYSENTRY: + case PR_SYSEXIT: + rtnval = pi->saved_rtnval; + statval = pi->saved_statval; + break; + case PR_REQUESTED: + statval = (SIGSTOP << 8) | 0177; + break; + case PR_JOBCONTROL: + statval = (what << 8) | 0177; + break; + case PR_FAULTED: + switch (what) + { +#ifdef FLTWATCH + case FLTWATCH: + statval = (SIGTRAP << 8) | 0177; + break; +#endif +#ifdef FLTKWATCH + case FLTKWATCH: + statval = (SIGTRAP << 8) | 0177; + break; +#endif +#ifndef FAULTED_USE_SIGINFO + /* Irix, contrary to the documentation, fills in 0 for si_signo. + Solaris fills in si_signo. I'm not sure about others. */ + case FLTPRIV: + case FLTILL: + statval = (SIGILL << 8) | 0177; + break; + case FLTBPT: + case FLTTRACE: + statval = (SIGTRAP << 8) | 0177; + break; + case FLTSTACK: + case FLTACCESS: + case FLTBOUNDS: + statval = (SIGSEGV << 8) | 0177; + break; + case FLTIOVF: + case FLTIZDIV: + case FLTFPE: + statval = (SIGFPE << 8) | 0177; + break; + case FLTPAGE: /* Recoverable page fault */ +#endif /* not FAULTED_USE_SIGINFO */ + default: + /* Use the signal which the kernel assigns. This is better than + trying to second-guess it from the fault. In fact, I suspect + that FLTACCESS can be either SIGSEGV or SIGBUS. */ + statval = + ((THE_PR_LWP(pi->prstatus).pr_info.si_signo) << 8) | 0177; + break; + } + break; + default: + error ("PIOCWSTOP, unknown why %d, what %d", why, what); + } + /* Stop all the other threads when any of them stops. */ + + { + struct procinfo *procinfo, *next_pi; + + for (procinfo = procinfo_list; procinfo; procinfo = next_pi) + { + next_pi = procinfo->next; + if (!procinfo->had_event) + { +#ifdef PROCFS_USE_READ_WRITE + long cmd = PCSTOP; + if (write (pi->ctl_fd, (char *) &cmd, sizeof (long)) < 0) + { + print_sys_errmsg (procinfo->pathname, errno); + error ("PCSTOP failed"); + } +#else + /* A bug in Solaris (2.5) causes us to hang when trying to + stop a stopped process. So, we have to check first in + order to avoid the hang. */ + if (!procfs_read_status (procinfo)) + { + /* The LWP has apparently terminated. */ + if (info_verbose) + printf_filtered ("LWP %d doesn't respond.\n", + (procinfo->pid >> 16) & 0xffff); + close_proc_file (procinfo); + continue; + } + + if (!(procinfo->prstatus.pr_flags & PR_STOPPED)) + if (ioctl (procinfo->ctl_fd, PIOCSTOP, &procinfo->prstatus) + < 0) + { + print_sys_errmsg (procinfo->pathname, errno); + warning ("PIOCSTOP failed"); + } +#endif + } + } + } + } + else + { + error ("PIOCWSTOP, stopped for unknown/unhandled reason, flags %#x", + THE_PR_LWP(pi->prstatus).pr_flags); + } + + store_waitstatus (ourstatus, statval); + + if (rtnval == -1) /* No more children to wait for */ + { + warning ("Child process unexpectedly missing"); + /* Claim it exited with unknown signal. */ + ourstatus->kind = TARGET_WAITKIND_SIGNALLED; + ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; + return rtnval; + } + + pi->had_event = 0; /* Indicate that we've seen this one */ + return (rtnval); +} + +/* + +LOCAL FUNCTION + + set_proc_siginfo - set a process's current signal info + +SYNOPSIS + + void set_proc_siginfo (struct procinfo *pip, int signo); + +DESCRIPTION + + Given a pointer to a process info struct in PIP and a signal number + in SIGNO, set the process's current signal and its associated signal + information. The signal will be delivered to the process immediately + after execution is resumed, even if it is being held. In addition, + this particular delivery will not cause another PR_SIGNALLED stop + even if the signal is being traced. + + If we are not delivering the same signal that the prstatus siginfo + struct contains information about, then synthesize a siginfo struct + to match the signal we are doing to deliver, make it of the type + "generated by a user process", and send this synthesized copy. When + used to set the inferior's signal state, this will be required if we + are not currently stopped because of a traced signal, or if we decide + to continue with a different signal. + + Note that when continuing the inferior from a stop due to receipt + of a traced signal, we either have set PRCSIG to clear the existing + signal, or we have to call this function to do a PIOCSSIG with either + the existing siginfo struct from pr_info, or one we have synthesized + appropriately for the signal we want to deliver. Otherwise if the + signal is still being traced, the inferior will immediately stop + again. + + See siginfo(5) for more details. +*/ + +static void +set_proc_siginfo (pip, signo) + struct procinfo *pip; + int signo; +{ + struct siginfo newsiginfo; + struct siginfo *sip; + struct sigi_ctl sictl; + +#ifdef PROCFS_DONT_PIOCSSIG_CURSIG + /* With Alpha OSF/1 procfs, the kernel gets really confused if it + receives a PIOCSSIG with a signal identical to the current signal, + it messes up the current signal. Work around the kernel bug. */ + if (signo == THE_PR_LWP(pip->prstatus).pr_cursig) + return; +#endif + +#ifdef UNIXWARE + if (signo == THE_PR_LWP(pip->prstatus).pr_info.si_signo) + { + memcpy ((char *) &sictl.siginfo, (char *) &pip->prstatus.pr_lwp.pr_info, + sizeof (siginfo_t)); + } +#else + if (signo == THE_PR_LWP(pip->prstatus).pr_info.si_signo) + { + sip = &pip -> prstatus.pr_info; + } +#endif + else + { +#ifdef UNIXWARE + siginfo_t *sip = &sictl.siginfo; + memset ((char *) sip, 0, sizeof (siginfo_t)); +#else + memset ((char *) &newsiginfo, 0, sizeof (newsiginfo)); + sip = &newsiginfo; +#endif + sip -> si_signo = signo; + sip -> si_code = 0; + sip -> si_errno = 0; + sip -> si_pid = getpid (); + sip -> si_uid = getuid (); + } +#ifdef PROCFS_USE_READ_WRITE + sictl.cmd = PCSSIG; + if (write (pip->ctl_fd, (char *) &sictl, sizeof (struct sigi_ctl)) < 0) +#else + if (ioctl (pip->ctl_fd, PIOCSSIG, sip) < 0) +#endif + { + print_sys_errmsg (pip -> pathname, errno); + warning ("PIOCSSIG failed"); + } +} + +/* Resume execution of process PID. If STEP is nozero, then + just single step it. If SIGNAL is nonzero, restart it with that + signal activated. */ + +static void +procfs_resume (pid, step, signo) + int pid; + int step; + enum target_signal signo; +{ + int signal_to_pass; + struct procinfo *pi, *procinfo, *next_pi; + struct proc_ctl pctl; + + pi = find_procinfo (pid == -1 ? inferior_pid : pid, 0); + + errno = 0; +#ifdef UNIXWARE + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; +#else + pi->prrun.pr_flags = PRSTRACE | PRSFAULT | PRCFAULT; +#endif + +#if 0 + /* It should not be necessary. If the user explicitly changes the value, + value_assign calls write_register_bytes, which writes it. */ +/* It may not be absolutely necessary to specify the PC value for + restarting, but to be safe we use the value that gdb considers + to be current. One case where this might be necessary is if the + user explicitly changes the PC value that gdb considers to be + current. FIXME: Investigate if this is necessary or not. */ + +#ifdef PRSVADDR_BROKEN +/* Can't do this under Solaris running on a Sparc, as there seems to be no + place to put nPC. In fact, if you use this, nPC seems to be set to some + random garbage. We have to rely on the fact that PC and nPC have been + written previously via PIOCSREG during a register flush. */ + + pi->prrun.pr_vaddr = (caddr_t) *(int *) ®isters[REGISTER_BYTE (PC_REGNUM)]; + pi->prrun.pr_flags != PRSVADDR; +#endif +#endif + + if (signo == TARGET_SIGNAL_STOP && pi->nopass_next_sigstop) + /* When attaching to a child process, if we forced it to stop with + a PIOCSTOP, then we will have set the nopass_next_sigstop flag. + Upon resuming the first time after such a stop, we explicitly + inhibit sending it another SIGSTOP, which would be the normal + result of default signal handling. One potential drawback to + this is that we will also ignore any attempt to by the user + to explicitly continue after the attach with a SIGSTOP. Ultimately + this problem should be dealt with by making the routines that + deal with the inferior a little smarter, and possibly even allow + an inferior to continue running at the same time as gdb. (FIXME?) */ + signal_to_pass = 0; + else if (signo == TARGET_SIGNAL_TSTP + && THE_PR_LWP(pi->prstatus).pr_cursig == SIGTSTP + && THE_PR_LWP(pi->prstatus).pr_action.sa_handler == SIG_DFL + ) + + /* We are about to pass the inferior a SIGTSTP whose action is + SIG_DFL. The SIG_DFL action for a SIGTSTP is to stop + (notifying the parent via wait()), and then keep going from the + same place when the parent is ready for you to keep going. So + under the debugger, it should do nothing (as if the program had + been stopped and then later resumed. Under ptrace, this + happens for us, but under /proc, the system obligingly stops + the process, and wait_for_inferior would have no way of + distinguishing that type of stop (which indicates that we + should just start it again), with a stop due to the pr_trace + field of the prrun_t struct. + + Note that if the SIGTSTP is being caught, we *do* need to pass it, + because the handler needs to get executed. */ + signal_to_pass = 0; + else + signal_to_pass = target_signal_to_host (signo); + + if (signal_to_pass) + { + set_proc_siginfo (pi, signal_to_pass); + } + else + { +#ifdef UNIXWARE + pctl.data |= PRCSIG; +#else + pi->prrun.pr_flags |= PRCSIG; +#endif + } + pi->nopass_next_sigstop = 0; + if (step) + { +#ifdef UNIXWARE + pctl.data |= PRSTEP; +#else + pi->prrun.pr_flags |= PRSTEP; +#endif + } + pi->had_event = 0; + /* Don't try to start a process unless it's stopped on an + `event of interest'. Doing so will cause errors. */ + + if (!procfs_read_status (pi)) + { + /* The LWP has apparently terminated. */ + if (info_verbose) + printf_filtered ("LWP %d doesn't respond.\n", + (pi->pid >> 16) & 0xffff); + close_proc_file (pi); + } + else + { +#ifdef PROCFS_USE_READ_WRITE + if (write (pi->ctl_fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) +#else + if ((pi->prstatus.pr_flags & PR_ISTOP) + && ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) +#endif + { + /* The LWP has apparently terminated. */ + if (info_verbose) + printf_filtered ("LWP %d doesn't respond.\n", + (pi->pid >> 16) & 0xffff); + close_proc_file (pi); + } + } + + /* Continue all the other threads that haven't had an event of interest. + Also continue them if they have NOPASS_NEXT_SIGSTOP set; this is only + set by do_attach, and means this is the first resume after an attach. + All threads were CSTOP'd by do_attach, and should be resumed now. */ + + if (pid == -1) + for (procinfo = procinfo_list; procinfo; procinfo = next_pi) + { + next_pi = procinfo->next; + if (pi != procinfo) + if (!procinfo->had_event || + (procinfo->nopass_next_sigstop && signo == TARGET_SIGNAL_STOP)) + { + procinfo->had_event = procinfo->nopass_next_sigstop = 0; +#ifdef PROCFS_USE_READ_WRITE + pctl.data = PRCFAULT | PRCSIG; + if (write (procinfo->ctl_fd, (char *) &pctl, + sizeof (struct proc_ctl)) < 0) + { + if (!procfs_read_status (procinfo)) + fprintf_unfiltered(gdb_stderr, + "procfs_read_status failed, errno=%d\n", + errno); + print_sys_errmsg (procinfo->pathname, errno); + error ("PCRUN failed"); + } +#else + procinfo->prrun.pr_flags &= PRSTEP; + procinfo->prrun.pr_flags |= PRCFAULT | PRCSIG; + if (!procfs_read_status (procinfo)) + { + /* The LWP has apparently terminated. */ + if (info_verbose) + printf_filtered ("LWP %d doesn't respond.\n", + (procinfo->pid >> 16) & 0xffff); + close_proc_file (procinfo); + continue; + } + + /* Don't try to start a process unless it's stopped on an + `event of interest'. Doing so will cause errors. */ + + if ((procinfo->prstatus.pr_flags & PR_ISTOP) + && ioctl (procinfo->ctl_fd, PIOCRUN, &procinfo->prrun) < 0) + { + if (!procfs_read_status (procinfo)) + fprintf_unfiltered(gdb_stderr, + "procfs_read_status failed, errno=%d\n", + errno); + print_sys_errmsg (procinfo->pathname, errno); + warning ("PIOCRUN failed"); + } +#endif + } + procfs_read_status (procinfo); + } +} + +/* + +LOCAL FUNCTION + + procfs_fetch_registers -- fetch current registers from inferior + +SYNOPSIS + + void procfs_fetch_registers (int regno) + +DESCRIPTION + + Read the current values of the inferior's registers, both the + general register set and floating point registers (if supported) + and update gdb's idea of their current values. + +*/ + +static void +procfs_fetch_registers (regno) + int regno; +{ + struct procinfo *pi; + + pi = current_procinfo; + +#ifdef UNIXWARE + if (procfs_read_status (pi)) + { + supply_gregset (&pi->prstatus.pr_lwp.pr_context.uc_mcontext.gregs); +#if defined (FP0_REGNUM) + supply_fpregset (&pi->prstatus.pr_lwp.pr_context.uc_mcontext.fpregs); +#endif + } +#else /* UNIXWARE */ + if (ioctl (pi->ctl_fd, PIOCGREG, &pi->gregset.gregset) != -1) + { + supply_gregset (&pi->gregset.gregset); + } +#if defined (FP0_REGNUM) + if (ioctl (pi->ctl_fd, PIOCGFPREG, &pi->fpregset.fpregset) != -1) + { + supply_fpregset (&pi->fpregset.fpregset); + } +#endif +#endif /* UNIXWARE */ +} + +/* + +LOCAL FUNCTION + + proc_init_failed - called when /proc access initialization fails +fails + +SYNOPSIS + + static void proc_init_failed (struct procinfo *pi, + char *why, int kill_p) + +DESCRIPTION + + This function is called whenever initialization of access to a /proc + entry fails. It prints a suitable error message, does some cleanup, + and then invokes the standard error processing routine which dumps + us back into the command loop. If KILL_P is true, sends SIGKILL. + */ + +static void +proc_init_failed (pi, why, kill_p) + struct procinfo *pi; + char *why; + int kill_p; +{ + print_sys_errmsg (pi->pathname, errno); + if (kill_p) + kill (pi->pid, SIGKILL); + close_proc_file (pi); + error (why); + /* NOTREACHED */ +} + +/* + +LOCAL FUNCTION + + close_proc_file - close any currently open /proc entry + +SYNOPSIS + + static void close_proc_file (struct procinfo *pip) + +DESCRIPTION + + Close any currently open /proc entry and mark the process information + entry as invalid. In order to ensure that we don't try to reuse any + stale information, the pid, fd, and pathnames are explicitly + invalidated, which may be overkill. + + */ + +static void +close_proc_file (pip) + struct procinfo *pip; +{ + struct procinfo *procinfo; + + delete_thread (pip->pid); /* remove thread from GDB's thread list */ + remove_fd (pip); /* Remove fd from poll/select list */ + + close (pip->ctl_fd); +#ifdef HAVE_MULTIPLE_PROC_FDS + close (pip->as_fd); + close (pip->status_fd); + close (pip->map_fd); +#endif + + free (pip -> pathname); + + /* Unlink pip from the procinfo chain. Note pip might not be on the list. */ + + if (procinfo_list == pip) + procinfo_list = pip->next; + else + { + for (procinfo = procinfo_list; procinfo; procinfo = procinfo->next) + { + if (procinfo->next == pip) + { + procinfo->next = pip->next; + break; + } + } + free (pip); + } +} + +/* + +LOCAL FUNCTION + + open_proc_file - open a /proc entry for a given process id + +SYNOPSIS + + static int open_proc_file (int pid, struct procinfo *pip, int mode) + +DESCRIPTION + + Given a process id and a mode, close the existing open /proc + entry (if any) and open one for the new process id, in the + specified mode. Once it is open, then mark the local process + information structure as valid, which guarantees that the pid, + fd, and pathname fields match an open /proc entry. Returns + zero if the open fails, nonzero otherwise. + + Note that the pathname is left intact, even when the open fails, + so that callers can use it to construct meaningful error messages + rather than just "file open failed". + + Note that for Solaris, the process-id also includes an LWP-id, so we + actually attempt to open that. If we are handed a pid with a 0 LWP-id, + then we will ask the kernel what it is and add it to the pid. Hence, + the pid can be changed by us. + */ + +static int +open_proc_file (pid, pip, mode, control) + int pid; + struct procinfo *pip; + int mode; + int control; +{ + int tmp, tmpfd; + + pip -> next = NULL; + pip -> had_event = 0; + pip -> pathname = xmalloc (MAX_PROC_NAME_SIZE); + pip -> pid = pid; + +#ifndef PIOCOPENLWP + tmp = pid; +#else + tmp = pid & 0xffff; +#endif + +#ifdef HAVE_MULTIPLE_PROC_FDS + sprintf (pip->pathname, STATUS_PROC_NAME_FMT, tmp); + if ((pip->status_fd = open (pip->pathname, O_RDONLY)) < 0) + { + return 0; + } + + sprintf (pip->pathname, AS_PROC_NAME_FMT, tmp); + if ((pip->as_fd = open (pip->pathname, O_RDWR)) < 0) + { + close (pip->status_fd); + return 0; + } + + sprintf (pip->pathname, MAP_PROC_NAME_FMT, tmp); + if ((pip->map_fd = open (pip->pathname, O_RDONLY)) < 0) + { + close (pip->status_fd); + close (pip->as_fd); + return 0; + } + + if (control) + { + sprintf (pip->pathname, CTL_PROC_NAME_FMT, tmp); + if ((pip->ctl_fd = open (pip->pathname, O_WRONLY)) < 0) + { + close (pip->status_fd); + close (pip->as_fd); + close (pip->map_fd); + return 0; + } + } + +#else /* HAVE_MULTIPLE_PROC_FDS */ + sprintf (pip -> pathname, CTL_PROC_NAME_FMT, tmp); + + if ((tmpfd = open (pip -> pathname, mode)) < 0) + return 0; + +#ifndef PIOCOPENLWP + pip -> ctl_fd = tmpfd; + pip -> as_fd = tmpfd; + pip -> map_fd = tmpfd; + pip -> status_fd = tmpfd; +#else + tmp = (pid >> 16) & 0xffff; /* Extract thread id */ + + if (tmp == 0) + { /* Don't know thread id yet */ + if (ioctl (tmpfd, PIOCSTATUS, &pip -> prstatus) < 0) + { + print_sys_errmsg (pip -> pathname, errno); + close (tmpfd); + error ("open_proc_file: PIOCSTATUS failed"); + } + + tmp = pip -> prstatus.pr_who; /* Get thread id from prstatus_t */ + pip -> pid = (tmp << 16) | pid; /* Update pip */ + } + + if ((pip -> ctl_fd = ioctl (tmpfd, PIOCOPENLWP, &tmp)) < 0) + { + close (tmpfd); + return 0; + } + +#ifdef PIOCSET /* New method */ + { + long pr_flags; + pr_flags = PR_ASYNC; + ioctl (pip -> ctl_fd, PIOCSET, &pr_flags); + } +#endif + + /* keep extra fds in sync */ + pip->as_fd = pip->ctl_fd; + pip->map_fd = pip->ctl_fd; + pip->status_fd = pip->ctl_fd; + + close (tmpfd); /* All done with main pid */ +#endif /* PIOCOPENLWP */ + +#endif /* HAVE_MULTIPLE_PROC_FDS */ + + return 1; +} + +static char * +mappingflags (flags) + long flags; +{ + static char asciiflags[8]; + + strcpy (asciiflags, "-------"); +#if defined (MA_PHYS) + if (flags & MA_PHYS) asciiflags[0] = 'd'; +#endif + if (flags & MA_STACK) asciiflags[1] = 's'; + if (flags & MA_BREAK) asciiflags[2] = 'b'; + if (flags & MA_SHARED) asciiflags[3] = 's'; + if (flags & MA_READ) asciiflags[4] = 'r'; + if (flags & MA_WRITE) asciiflags[5] = 'w'; + if (flags & MA_EXEC) asciiflags[6] = 'x'; + return (asciiflags); +} + +static void +info_proc_flags (pip, summary) + struct procinfo *pip; + int summary; +{ + struct trans *transp; +#ifdef UNIXWARE + long flags = pip->prstatus.pr_flags | pip->prstatus.pr_lwp.pr_flags; +#else + long flags = pip->prstatus.pr_flags; +#endif + + printf_filtered ("%-32s", "Process status flags:"); + if (!summary) + { + printf_filtered ("\n\n"); + } + for (transp = pr_flag_table; transp -> name != NULL; transp++) + { + if (flags & transp -> value) + { + if (summary) + { + printf_filtered ("%s ", transp -> name); + } + else + { + printf_filtered ("\t%-16s %s.\n", transp -> name, transp -> desc); + } + } + } + printf_filtered ("\n"); +} + +static void +info_proc_stop (pip, summary) + struct procinfo *pip; + int summary; +{ + struct trans *transp; + int why; + int what; + + why = THE_PR_LWP(pip->prstatus).pr_why; + what = THE_PR_LWP(pip->prstatus).pr_what; + + if (THE_PR_LWP(pip->prstatus).pr_flags & PR_STOPPED) + { + printf_filtered ("%-32s", "Reason for stopping:"); + if (!summary) + { + printf_filtered ("\n\n"); + } + for (transp = pr_why_table; transp -> name != NULL; transp++) + { + if (why == transp -> value) + { + if (summary) + { + printf_filtered ("%s ", transp -> name); + } + else + { + printf_filtered ("\t%-16s %s.\n", + transp -> name, transp -> desc); + } + break; + } + } + + /* Use the pr_why field to determine what the pr_what field means, and + print more information. */ + + switch (why) + { + case PR_REQUESTED: + /* pr_what is unused for this case */ + break; + case PR_JOBCONTROL: + case PR_SIGNALLED: + if (summary) + { + printf_filtered ("%s ", signalname (what)); + } + else + { + printf_filtered ("\t%-16s %s.\n", signalname (what), + safe_strsignal (what)); + } + break; + case PR_SYSENTRY: + if (summary) + { + printf_filtered ("%s ", syscallname (what)); + } + else + { + printf_filtered ("\t%-16s %s.\n", syscallname (what), + "Entered this system call"); + } + break; + case PR_SYSEXIT: + if (summary) + { + printf_filtered ("%s ", syscallname (what)); + } + else + { + printf_filtered ("\t%-16s %s.\n", syscallname (what), + "Returned from this system call"); + } + break; + case PR_FAULTED: + if (summary) + { + printf_filtered ("%s ", + lookupname (faults_table, what, "fault")); + } + else + { + printf_filtered ("\t%-16s %s.\n", + lookupname (faults_table, what, "fault"), + lookupdesc (faults_table, what)); + } + break; + } + printf_filtered ("\n"); + } +} + +static void +info_proc_siginfo (pip, summary) + struct procinfo *pip; + int summary; +{ + struct siginfo *sip; + + if ((THE_PR_LWP(pip->prstatus).pr_flags & PR_STOPPED) && + (THE_PR_LWP(pip->prstatus).pr_why == PR_SIGNALLED || + THE_PR_LWP(pip->prstatus).pr_why == PR_FAULTED)) + { + printf_filtered ("%-32s", "Additional signal/fault info:"); + sip = &(THE_PR_LWP(pip->prstatus).pr_info); + if (summary) + { + printf_filtered ("%s ", signalname (sip -> si_signo)); + if (sip -> si_errno > 0) + { + printf_filtered ("%s ", errnoname (sip -> si_errno)); + } + if (sip -> si_code <= 0) + { + printf_filtered ("sent by %s, uid %d ", + target_pid_to_str (sip -> si_pid), + sip -> si_uid); + } + else + { + printf_filtered ("%s ", sigcodename (sip)); + if ((sip -> si_signo == SIGILL) || + (sip -> si_signo == SIGFPE) || + (sip -> si_signo == SIGSEGV) || + (sip -> si_signo == SIGBUS)) + { + printf_filtered ("addr=%#lx ", + (unsigned long) sip -> si_addr); + } + else if ((sip -> si_signo == SIGCHLD)) + { + printf_filtered ("child %s, status %u ", + target_pid_to_str (sip -> si_pid), + sip -> si_status); + } + else if ((sip -> si_signo == SIGPOLL)) + { + printf_filtered ("band %u ", sip -> si_band); + } + } + } + else + { + printf_filtered ("\n\n"); + printf_filtered ("\t%-16s %s.\n", signalname (sip -> si_signo), + safe_strsignal (sip -> si_signo)); + if (sip -> si_errno > 0) + { + printf_filtered ("\t%-16s %s.\n", + errnoname (sip -> si_errno), + safe_strerror (sip -> si_errno)); + } + if (sip -> si_code <= 0) + { + printf_filtered ("\t%-16u %s\n", sip -> si_pid, /* XXX need target_pid_to_str() */ + "PID of process sending signal"); + printf_filtered ("\t%-16u %s\n", sip -> si_uid, + "UID of process sending signal"); + } + else + { + printf_filtered ("\t%-16s %s.\n", sigcodename (sip), + sigcodedesc (sip)); + if ((sip -> si_signo == SIGILL) || + (sip -> si_signo == SIGFPE)) + { + printf_filtered ("\t%#-16lx %s.\n", + (unsigned long) sip -> si_addr, + "Address of faulting instruction"); + } + else if ((sip -> si_signo == SIGSEGV) || + (sip -> si_signo == SIGBUS)) + { + printf_filtered ("\t%#-16lx %s.\n", + (unsigned long) sip -> si_addr, + "Address of faulting memory reference"); + } + else if ((sip -> si_signo == SIGCHLD)) + { + printf_filtered ("\t%-16u %s.\n", sip -> si_pid, /* XXX need target_pid_to_str() */ + "Child process ID"); + printf_filtered ("\t%-16u %s.\n", sip -> si_status, + "Child process exit value or signal"); + } + else if ((sip -> si_signo == SIGPOLL)) + { + printf_filtered ("\t%-16u %s.\n", sip -> si_band, + "Band event for POLL_{IN,OUT,MSG}"); + } + } + } + printf_filtered ("\n"); + } +} + +static void +info_proc_syscalls (pip, summary) + struct procinfo *pip; + int summary; +{ + int syscallnum; + + if (!summary) + { + +#if 0 /* FIXME: Needs to use gdb-wide configured info about system calls. */ + if (pip -> prstatus.pr_flags & PR_ASLEEP) + { + int syscallnum = pip -> prstatus.pr_reg[R_D0]; + if (summary) + { + printf_filtered ("%-32s", "Sleeping in system call:"); + printf_filtered ("%s", syscallname (syscallnum)); + } + else + { + printf_filtered ("Sleeping in system call '%s'.\n", + syscallname (syscallnum)); + } + } +#endif + +#ifndef UNIXWARE + if (ioctl (pip -> ctl_fd, PIOCGENTRY, &pip -> entryset) < 0) + { + print_sys_errmsg (pip -> pathname, errno); + error ("PIOCGENTRY failed"); + } + + if (ioctl (pip -> ctl_fd, PIOCGEXIT, &pip -> exitset) < 0) + { + print_sys_errmsg (pip -> pathname, errno); + error ("PIOCGEXIT failed"); + } +#endif + + printf_filtered ("System call tracing information:\n\n"); + + printf_filtered ("\t%-12s %-8s %-8s\n", + "System call", + "Entry", + "Exit"); + for (syscallnum = 0; syscallnum < MAX_SYSCALLS; syscallnum++) + { + QUIT; + if (syscall_table[syscallnum] != NULL) + printf_filtered ("\t%-12s ", syscall_table[syscallnum]); + else + printf_filtered ("\t%-12d ", syscallnum); + +#ifdef UNIXWARE + printf_filtered ("%-8s ", + prismember (&pip->prstatus.pr_sysentry, syscallnum) + ? "on" : "off"); + printf_filtered ("%-8s ", + prismember (&pip->prstatus.pr_sysexit, syscallnum) + ? "on" : "off"); +#else + printf_filtered ("%-8s ", + prismember (&pip -> entryset, syscallnum) + ? "on" : "off"); + printf_filtered ("%-8s ", + prismember (&pip -> exitset, syscallnum) + ? "on" : "off"); +#endif + printf_filtered ("\n"); + } + printf_filtered ("\n"); + } +} + +static char * +signalname (signo) + int signo; +{ + const char *name; + static char locbuf[32]; + + name = strsigno (signo); + if (name == NULL) + { + sprintf (locbuf, "Signal %d", signo); + } + else + { + sprintf (locbuf, "%s (%d)", name, signo); + } + return (locbuf); +} + +static char * +errnoname (errnum) + int errnum; +{ + const char *name; + static char locbuf[32]; + + name = strerrno (errnum); + if (name == NULL) + { + sprintf (locbuf, "Errno %d", errnum); + } + else + { + sprintf (locbuf, "%s (%d)", name, errnum); + } + return (locbuf); +} + +static void +info_proc_signals (pip, summary) + struct procinfo *pip; + int summary; +{ + int signo; + + if (!summary) + { +#ifndef PROCFS_USE_READ_WRITE + if (ioctl (pip -> ctl_fd, PIOCGTRACE, &pip -> trace) < 0) + { + print_sys_errmsg (pip -> pathname, errno); + error ("PIOCGTRACE failed"); + } +#endif + + printf_filtered ("Disposition of signals:\n\n"); + printf_filtered ("\t%-15s %-8s %-8s %-8s %s\n\n", + "Signal", "Trace", "Hold", "Pending", "Description"); + for (signo = 0; signo < NSIG; signo++) + { + QUIT; + printf_filtered ("\t%-15s ", signalname (signo)); +#ifdef UNIXWARE + printf_filtered ("%-8s ", + prismember (&pip -> prstatus.pr_sigtrace, signo) + ? "on" : "off"); + printf_filtered ("%-8s ", + prismember (&pip -> prstatus.pr_lwp.pr_context.uc_sigmask, signo) + ? "on" : "off"); +#else + printf_filtered ("%-8s ", + prismember (&pip -> trace, signo) + ? "on" : "off"); + printf_filtered ("%-8s ", + prismember (&pip -> prstatus.pr_sighold, signo) + ? "on" : "off"); +#endif + +#ifdef UNIXWARE + if (prismember (&pip->prstatus.pr_sigpend, signo) || + prismember (&pip->prstatus.pr_lwp.pr_lwppend, signo)) + printf_filtered("%-8s ", "yes"); + else + printf_filtered("%-8s ", "no"); +#else /* UNIXWARE */ +#ifdef PROCFS_SIGPEND_OFFSET + /* Alpha OSF/1 numbers the pending signals from 1. */ + printf_filtered ("%-8s ", + (signo ? prismember (&pip -> prstatus.pr_sigpend, + signo - 1) + : 0) + ? "yes" : "no"); +#else + printf_filtered ("%-8s ", + prismember (&pip -> prstatus.pr_sigpend, signo) + ? "yes" : "no"); +#endif +#endif /* UNIXWARE */ + printf_filtered (" %s\n", safe_strsignal (signo)); + } + printf_filtered ("\n"); + } +} + +static void +info_proc_faults (pip, summary) + struct procinfo *pip; + int summary; +{ + struct trans *transp; + + if (!summary) + { +#ifndef UNIXWARE + if (ioctl (pip -> ctl_fd, PIOCGFAULT, &pip->fltset.fltset) < 0) + { + print_sys_errmsg (pip -> pathname, errno); + error ("PIOCGFAULT failed"); + } +#endif + + printf_filtered ("Current traced hardware fault set:\n\n"); + printf_filtered ("\t%-12s %-8s\n", "Fault", "Trace"); + + for (transp = faults_table; transp -> name != NULL; transp++) + { + QUIT; + printf_filtered ("\t%-12s ", transp -> name); +#ifdef UNIXWARE + printf_filtered ("%-8s", prismember (&pip->prstatus.pr_flttrace, transp -> value) + ? "on" : "off"); +#else + printf_filtered ("%-8s", prismember (&pip->fltset.fltset, transp -> value) + ? "on" : "off"); +#endif + printf_filtered ("\n"); + } + printf_filtered ("\n"); + } +} + +static void +info_proc_mappings (pip, summary) + struct procinfo *pip; + int summary; +{ + int nmap; + struct prmap *prmaps; + struct prmap *prmap; + struct stat sbuf; + + if (!summary) + { + printf_filtered ("Mapped address spaces:\n\n"); +#ifdef BFD_HOST_64_BIT + printf_filtered (" %18s %18s %10s %10s %7s\n", +#else + printf_filtered ("\t%10s %10s %10s %10s %7s\n", +#endif + "Start Addr", + " End Addr", + " Size", + " Offset", + "Flags"); +#ifdef PROCFS_USE_READ_WRITE + if (fstat (pip->map_fd, &sbuf) == 0) + { + nmap = sbuf.st_size / sizeof (prmap_t); + prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); + if ((lseek (pip->map_fd, 0, SEEK_SET) == 0) && + (read (pip->map_fd, (char *) prmaps, + nmap * sizeof (*prmaps)) == (nmap * sizeof (*prmaps)))) + { + int i = 0; + for (prmap = prmaps; i < nmap; ++prmap, ++i) +#else + if (ioctl (pip -> ctl_fd, PIOCNMAP, &nmap) == 0) + { + prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); + if (ioctl (pip -> ctl_fd, PIOCMAP, prmaps) == 0) + { + for (prmap = prmaps; prmap -> pr_size; ++prmap) +#endif /* PROCFS_USE_READ_WRITE */ + { +#ifdef BFD_HOST_64_BIT + printf_filtered (" %#18lx %#18lx %#10x %#10x %7s\n", +#else + printf_filtered ("\t%#10lx %#10lx %#10x %#10x %7s\n", +#endif + (unsigned long)prmap -> pr_vaddr, + (unsigned long)prmap -> pr_vaddr + + prmap -> pr_size - 1, + prmap -> pr_size, + prmap -> pr_off, + mappingflags (prmap -> pr_mflags)); + } + } + } + printf_filtered ("\n"); + } +} + +/* + +LOCAL FUNCTION + + info_proc -- implement the "info proc" command + +SYNOPSIS + + void info_proc (char *args, int from_tty) + +DESCRIPTION + + Implement gdb's "info proc" command by using the /proc interface + to print status information about any currently running process. + + Examples of the use of "info proc" are: + + info proc (prints summary info for current inferior) + info proc 123 (prints summary info for process with pid 123) + info proc mappings (prints address mappings) + info proc times (prints process/children times) + info proc id (prints pid, ppid, gid, sid, etc) + FIXME: i proc id not implemented. + info proc status (prints general process state info) + FIXME: i proc status not implemented. + info proc signals (prints info about signal handling) + info proc all (prints all info) + + */ + +static void +info_proc (args, from_tty) + char *args; + int from_tty; +{ + int pid; + struct procinfo *pip; + struct cleanup *old_chain; + char **argv; + int argsize; + int summary = 1; + int flags = 0; + int syscalls = 0; + int signals = 0; + int faults = 0; + int mappings = 0; + int times = 0; + int id = 0; + int status = 0; + int all = 0; + int nlwp; + int *lwps; + + old_chain = make_cleanup (null_cleanup, 0); + + /* Default to using the current inferior if no pid specified. Note + that inferior_pid may be 0, hence we set okerr. */ + + pid = inferior_pid & 0x7fffffff; /* strip off sol-thread bit */ + if (!(pip = find_procinfo (pid, 1))) /* inferior_pid no good? */ + pip = procinfo_list; /* take first available */ + pid = pid & 0xffff; /* extract "real" pid */ + + if (args != NULL) + { + if ((argv = buildargv (args)) == NULL) + { + nomem (0); + } + make_cleanup (freeargv, (char *) argv); + + while (*argv != NULL) + { + argsize = strlen (*argv); + if (argsize >= 1 && strncmp (*argv, "all", argsize) == 0) + { + summary = 0; + all = 1; + } + else if (argsize >= 2 && strncmp (*argv, "faults", argsize) == 0) + { + summary = 0; + faults = 1; + } + else if (argsize >= 2 && strncmp (*argv, "flags", argsize) == 0) + { + summary = 0; + flags = 1; + } + else if (argsize >= 1 && strncmp (*argv, "id", argsize) == 0) + { + summary = 0; + id = 1; + } + else if (argsize >= 1 && strncmp (*argv, "mappings", argsize) == 0) + { + summary = 0; + mappings = 1; + } + else if (argsize >= 2 && strncmp (*argv, "signals", argsize) == 0) + { + summary = 0; + signals = 1; + } + else if (argsize >= 2 && strncmp (*argv, "status", argsize) == 0) + { + summary = 0; + status = 1; + } + else if (argsize >= 2 && strncmp (*argv, "syscalls", argsize) == 0) + { + summary = 0; + syscalls = 1; + } + else if (argsize >= 1 && strncmp (*argv, "times", argsize) == 0) + { + summary = 0; + times = 1; + } + else if ((pid = atoi (*argv)) > 0) + { + pip = (struct procinfo *) xmalloc (sizeof (struct procinfo)); + memset (pip, 0, sizeof (*pip)); + + pip->pid = pid; + if (!open_proc_file (pid, pip, O_RDONLY, 0)) + { + perror_with_name (pip -> pathname); + /* NOTREACHED */ + } + pid = pip->pid; + make_cleanup (close_proc_file, pip); + } + else if (**argv != '\000') + { + error ("Unrecognized or ambiguous keyword `%s'.", *argv); + } + argv++; + } + } + + /* If we don't have a valid open process at this point, then we have no + inferior or didn't specify a specific pid. */ + + if (!pip) + { + error ("\ +No process. Start debugging a program or specify an explicit process ID."); + } + + if (!procfs_read_status (pip)) + { + print_sys_errmsg (pip -> pathname, errno); + error ("procfs_read_status failed"); + } + +#ifndef PROCFS_USE_READ_WRITE +#ifdef PIOCLWPIDS + nlwp = pip->prstatus.pr_nlwp; + lwps = alloca ((2 * nlwp + 2) * sizeof (*lwps)); + + if (ioctl (pip->ctl_fd, PIOCLWPIDS, lwps)) + { + print_sys_errmsg (pip -> pathname, errno); + error ("PIOCLWPIDS failed"); + } +#else /* PIOCLWPIDS */ + nlwp = 1; + lwps = alloca ((2 * nlwp + 2) * sizeof *lwps); + lwps[0] = 0; +#endif /* PIOCLWPIDS */ + + for (; nlwp > 0; nlwp--, lwps++) + { + pip = find_procinfo ((*lwps << 16) | pid, 1); + + if (!pip) + { + pip = (struct procinfo *) xmalloc (sizeof (struct procinfo)); + memset (pip, 0, sizeof (*pip)); + if (!open_proc_file ((*lwps << 16) | pid, pip, O_RDONLY, 0)) + continue; + + make_cleanup (close_proc_file, pip); + + if (!procfs_read_status (pip)) + { + print_sys_errmsg (pip -> pathname, errno); + error ("procfs_read_status failed"); + } + } + +#endif /* PROCFS_USE_READ_WRITE */ + + /* Print verbose information of the requested type(s), or just a summary + of the information for all types. */ + + printf_filtered ("\nInformation for %s.%d:\n\n", pip -> pathname, *lwps); + if (summary || all || flags) + { + info_proc_flags (pip, summary); + } + if (summary || all) + { + info_proc_stop (pip, summary); +#ifdef UNIXWARE + supply_gregset (&pip->prstatus.pr_lwp.pr_context.uc_mcontext.gregs); +#else + supply_gregset (&pip->prstatus.pr_reg); +#endif + printf_filtered ("PC: "); + print_address (read_pc (), gdb_stdout); + printf_filtered ("\n"); + } + if (summary || all || signals || faults) + { + info_proc_siginfo (pip, summary); + } + if (summary || all || syscalls) + { + info_proc_syscalls (pip, summary); + } + if (summary || all || mappings) + { + info_proc_mappings (pip, summary); + } + if (summary || all || signals) + { + info_proc_signals (pip, summary); + } + if (summary || all || faults) + { + info_proc_faults (pip, summary); + } + printf_filtered ("\n"); + + /* All done, deal with closing any temporary process info structure, + freeing temporary memory , etc. */ + + do_cleanups (old_chain); +#ifndef PROCFS_USE_READ_WRITE + } +#endif +} + +/* + +LOCAL FUNCTION + + modify_inherit_on_fork_flag - Change the inherit-on-fork flag + +SYNOPSIS + + void modify_inherit_on_fork_flag (fd, flag) + +DESCRIPTION + + Call this routine to modify the inherit-on-fork flag. This routine is + just a nice wrapper to hide the #ifdefs needed by various systems to + control this flag. + + */ + +static void +modify_inherit_on_fork_flag (fd, flag) + int fd; + int flag; +{ +#if defined (PIOCSET) || defined (PCSET) + long pr_flags; +#endif + int retval = 0; + struct proc_ctl pctl; + +#if defined (PIOCSET) || defined (PCSET) /* New method */ + pr_flags = PR_FORK; + if (flag) + { +#ifdef PROCFS_USE_READ_WRITE + pctl.cmd = PCSET; + pctl.data = PR_FORK; + if (write (fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) + retval = -1; +#else + retval = ioctl (fd, PIOCSET, &pr_flags); +#endif + } + else + { +#ifdef PROCFS_USE_READ_WRITE + pctl.cmd = PCRESET; + pctl.data = PR_FORK; + if (write (fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) + retval = -1; +#else + retval = ioctl (fd, PIOCRESET, &pr_flags); +#endif + } + +#else +#ifdef PIOCSFORK /* Original method */ + if (flag) + { + retval = ioctl (fd, PIOCSFORK, NULL); + } + else + { + retval = ioctl (fd, PIOCRFORK, NULL); + } +#else + Neither PR_FORK nor PIOCSFORK exist!!! +#endif +#endif + + if (!retval) + return; + + print_sys_errmsg ("modify_inherit_on_fork_flag", errno); + error ("PIOCSFORK or PR_FORK modification failed"); +} + +/* + +LOCAL FUNCTION + + modify_run_on_last_close_flag - Change the run-on-last-close flag + +SYNOPSIS + + void modify_run_on_last_close_flag (fd, flag) + +DESCRIPTION + + Call this routine to modify the run-on-last-close flag. This routine + is just a nice wrapper to hide the #ifdefs needed by various systems to + control this flag. + + */ + +static void +modify_run_on_last_close_flag (fd, flag) + int fd; + int flag; +{ +#if defined (PIOCSET) || defined (PCSET) + long pr_flags; +#endif + int retval = 0; + struct proc_ctl pctl; + +#if defined (PIOCSET) || defined (PCSET) /* New method */ + pr_flags = PR_RLC; + if (flag) + { +#ifdef PROCFS_USE_READ_WRITE + pctl.cmd = PCSET; + pctl.data = PR_RLC; + if (write (fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) + retval = -1; +#else + retval = ioctl (fd, PIOCSET, &pr_flags); +#endif + } + else + { +#ifdef PROCFS_USE_READ_WRITE + pctl.cmd = PCRESET; + pctl.data = PR_RLC; + if (write (fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) + retval = -1; +#else + retval = ioctl (fd, PIOCRESET, &pr_flags); +#endif + } + +#else +#ifdef PIOCSRLC /* Original method */ + if (flag) + retval = ioctl (fd, PIOCSRLC, NULL); + else + retval = ioctl (fd, PIOCRRLC, NULL); +#else + Neither PR_RLC nor PIOCSRLC exist!!! +#endif +#endif + + if (!retval) + return; + + print_sys_errmsg ("modify_run_on_last_close_flag", errno); + error ("PIOCSRLC or PR_RLC modification failed"); +} + +/* + +LOCAL FUNCTION + + procfs_clear_syscall_trap -- Deletes the trap for the specified system call. + +SYNOPSIS + + void procfs_clear_syscall_trap (struct procinfo *, int syscall_num, int errok) + +DESCRIPTION + + This function function disables traps for the specified system call. + errok is non-zero if errors should be ignored. + */ + +static void +procfs_clear_syscall_trap (pi, syscall_num, errok) + struct procinfo *pi; + int syscall_num; + int errok; +{ + sysset_t sysset; + int goterr, i; + +#ifndef UNIXWARE + goterr = ioctl (pi->ctl_fd, PIOCGENTRY, &sysset) < 0; + + if (goterr && !errok) + { + print_sys_errmsg (pi->pathname, errno); + error ("PIOCGENTRY failed"); + } + + if (!goterr) + { + prdelset (&sysset, syscall_num); + + if ((ioctl (pi->ctl_fd, PIOCSENTRY, &sysset) < 0) && !errok) + { + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSENTRY failed"); + } + } + + goterr = ioctl (pi->ctl_fd, PIOCGEXIT, &sysset) < 0; + + if (goterr && !errok) + { + procfs_clear_syscall_trap (pi, syscall_num, 1); + print_sys_errmsg (pi->pathname, errno); + error ("PIOCGEXIT failed"); + } + + if (!goterr) + { + praddset (&sysset, syscall_num); + + if ((ioctl (pi->ctl_fd, PIOCSEXIT, &sysset) < 0) && !errok) + { + procfs_clear_syscall_trap (pi, syscall_num, 1); + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSEXIT failed"); + } + } +#endif + + if (!pi->syscall_handlers) + { + if (!errok) + error ("procfs_clear_syscall_trap: syscall_handlers is empty"); + return; + } + + /* Remove handler func from the handler list */ + + for (i = 0; i < pi->num_syscall_handlers; i++) + if (pi->syscall_handlers[i].syscall_num == syscall_num) + { + if (i + 1 != pi->num_syscall_handlers) + { /* Not the last entry. + Move subsequent entries fwd. */ + memcpy (&pi->syscall_handlers[i], &pi->syscall_handlers[i + 1], + (pi->num_syscall_handlers - i - 1) + * sizeof (struct procfs_syscall_handler)); + } + + pi->syscall_handlers = xrealloc (pi->syscall_handlers, + (pi->num_syscall_handlers - 1) + * sizeof (struct procfs_syscall_handler)); + pi->num_syscall_handlers--; + return; + } + + if (!errok) + error ("procfs_clear_syscall_trap: Couldn't find handler for sys call %d", + syscall_num); +} + +/* + +LOCAL FUNCTION + + procfs_set_syscall_trap -- arrange for a function to be called when the + child executes the specified system call. + +SYNOPSIS + + void procfs_set_syscall_trap (struct procinfo *, int syscall_num, int flags, + syscall_func_t *function) + +DESCRIPTION + + This function sets up an entry and/or exit trap for the specified system + call. When the child executes the specified system call, your function + will be called with the call #, a flag that indicates entry or exit, and + pointers to rtnval and statval (which are used by procfs_wait). The + function should return non-zero if something interesting happened, zero + otherwise. + */ + +static void +procfs_set_syscall_trap (pi, syscall_num, flags, func) + struct procinfo *pi; + int syscall_num; + int flags; + syscall_func_t *func; +{ + sysset_t sysset; + +#ifndef UNIXWARE + if (flags & PROCFS_SYSCALL_ENTRY) + { + if (ioctl (pi->ctl_fd, PIOCGENTRY, &sysset) < 0) + { + print_sys_errmsg (pi->pathname, errno); + error ("PIOCGENTRY failed"); + } + + praddset (&sysset, syscall_num); + + if (ioctl (pi->ctl_fd, PIOCSENTRY, &sysset) < 0) + { + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSENTRY failed"); + } + } + + if (flags & PROCFS_SYSCALL_EXIT) + { + if (ioctl (pi->ctl_fd, PIOCGEXIT, &sysset) < 0) + { + procfs_clear_syscall_trap (pi, syscall_num, 1); + print_sys_errmsg (pi->pathname, errno); + error ("PIOCGEXIT failed"); + } + + praddset (&sysset, syscall_num); + + if (ioctl (pi->ctl_fd, PIOCSEXIT, &sysset) < 0) + { + procfs_clear_syscall_trap (pi, syscall_num, 1); + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSEXIT failed"); + } + } +#endif + + if (!pi->syscall_handlers) + { + pi->syscall_handlers = xmalloc (sizeof (struct procfs_syscall_handler)); + pi->syscall_handlers[0].syscall_num = syscall_num; + pi->syscall_handlers[0].func = func; + pi->num_syscall_handlers = 1; + } + else + { + int i; + + for (i = 0; i < pi->num_syscall_handlers; i++) + if (pi->syscall_handlers[i].syscall_num == syscall_num) + { + pi->syscall_handlers[i].func = func; + return; + } + + pi->syscall_handlers = xrealloc (pi->syscall_handlers, (i + 1) + * sizeof (struct procfs_syscall_handler)); + pi->syscall_handlers[i].syscall_num = syscall_num; + pi->syscall_handlers[i].func = func; + pi->num_syscall_handlers++; + } +} + +#ifdef SYS_lwp_create + +/* + +LOCAL FUNCTION + + procfs_lwp_creation_handler - handle exit from the _lwp_create syscall + +SYNOPSIS + + int procfs_lwp_creation_handler (pi, syscall_num, why, rtnvalp, statvalp) + +DESCRIPTION + + This routine is called both when an inferior process and it's new lwp + are about to finish a _lwp_create() system call. This is the system + call that Solaris uses to create a lightweight process. When the + target process gets this event, we can look at sysarg[2] to find the + new childs lwp ID, and create a procinfo struct from that. After that, + we pretend that we got a SIGTRAP, and return non-zero to tell + procfs_wait to wake up. Subsequently, wait_for_inferior gets woken up, + sees the new process and continues it. + + When we see the child exiting from lwp_create, we just contine it, + since everything was handled when the parent trapped. + +NOTES + In effect, we are only paying attention to the parent's completion of + the lwp_create syscall. If we only paid attention to the child + instead, then we wouldn't detect the creation of a suspended thread. + */ + +static int +procfs_lwp_creation_handler (pi, syscall_num, why, rtnvalp, statvalp) + struct procinfo *pi; + int syscall_num; + int why; + int *rtnvalp; + int *statvalp; +{ + int lwp_id; + struct procinfo *childpi; + struct proc_ctl pctl; + + /* We've just detected the completion of an lwp_create system call. Now we + need to setup a procinfo struct for this thread, and notify the thread + system of the new arrival. */ + + /* If lwp_create failed, then nothing interesting happened. Continue the + process and go back to sleep. */ + +#ifdef UNIXWARE + /* Joel ... can you check this logic out please? JKJ */ + if (pi->prstatus.pr_lwp.pr_context.uc_mcontext.gregs[R_EFL] & 1) + { /* _lwp_create failed */ + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; + + if (write (pi->ctl_fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) + perror_with_name (pi->pathname); + + return 0; + } +#else /* UNIXWARE */ + if (PROCFS_GET_CARRY (pi->prstatus.pr_reg)) + { /* _lwp_create failed */ + pi->prrun.pr_flags &= PRSTEP; + pi->prrun.pr_flags |= PRCFAULT; + + if (ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) + perror_with_name (pi->pathname); + + return 0; + } +#endif + + /* At this point, the new thread is stopped at it's first instruction, and + the parent is stopped at the exit from lwp_create. */ + + if (pi->new_child) /* Child? */ + { /* Yes, just continue it */ +#ifdef UNIXWARE + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; + + if (write(pi->ctl_fd, (char *)&pctl, sizeof (struct proc_ctl)) < 0) +#else /* !UNIXWARE */ + pi->prrun.pr_flags &= PRSTEP; + pi->prrun.pr_flags |= PRCFAULT; + + if ((pi->prstatus.pr_flags & PR_ISTOP) + && ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) +#endif /* !UNIXWARE */ + perror_with_name (pi->pathname); + + pi->new_child = 0; /* No longer new */ + + return 0; + } + + /* We're the proud parent of a new thread. Setup an exit trap for lwp_create + in the child and continue the parent. */ + + /* Third arg is pointer to new thread id. */ + lwp_id = read_memory_integer ( + THE_PR_LWP(pi->prstatus).pr_sysarg[2], sizeof (int)); + + lwp_id = (lwp_id << 16) | PIDGET (pi->pid); + + childpi = create_procinfo (lwp_id); + + /* The new process has actually inherited the lwp_create syscall trap from + it's parent, but we still have to call this to register handlers for + that child. */ + + procfs_set_inferior_syscall_traps (childpi); + add_thread (lwp_id); + printf_filtered ("[New %s]\n", target_pid_to_str (lwp_id)); + + /* Continue the parent */ +#ifdef UNIXWARE + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; + + if (write(pi->ctl_fd, (char *)&pctl, sizeof (struct proc_ctl)) < 0) +#else + pi->prrun.pr_flags &= PRSTEP; + pi->prrun.pr_flags |= PRCFAULT; + if (ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) +#endif + perror_with_name (pi->pathname); + + /* The new child may have been created in one of two states: + SUSPENDED or RUNNABLE. If runnable, we will simply signal it to run. + If suspended, we flag it to be continued later, when it has an event. */ + + if (THE_PR_LWP(childpi->prstatus).pr_why == PR_SUSPENDED) + childpi->new_child = 1; /* Flag this as an unseen child process */ + else + { + /* Continue the child */ +#ifdef UNIXWARE + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; + + if (write(pi->ctl_fd, (char *)&pctl, sizeof (struct proc_ctl)) < 0) +#else + childpi->prrun.pr_flags &= PRSTEP; + childpi->prrun.pr_flags |= PRCFAULT; + + if (ioctl (childpi->ctl_fd, PIOCRUN, &childpi->prrun) != 0) +#endif + perror_with_name (childpi->pathname); + } + return 0; +} +#endif /* SYS_lwp_create */ + +/* Fork an inferior process, and start debugging it with /proc. */ + +static void +procfs_create_inferior (exec_file, allargs, env) + char *exec_file; + char *allargs; + char **env; +{ + char *shell_file = getenv ("SHELL"); + char *tryname; + if (shell_file != NULL && strchr (shell_file, '/') == NULL) + { + + /* We will be looking down the PATH to find shell_file. If we + just do this the normal way (via execlp, which operates by + attempting an exec for each element of the PATH until it + finds one which succeeds), then there will be an exec for + each failed attempt, each of which will cause a PR_SYSEXIT + stop, and we won't know how to distinguish the PR_SYSEXIT's + for these failed execs with the ones for successful execs + (whether the exec has succeeded is stored at that time in the + carry bit or some such architecture-specific and + non-ABI-specified place). + + So I can't think of anything better than to search the PATH + now. This has several disadvantages: (1) There is a race + condition; if we find a file now and it is deleted before we + exec it, we lose, even if the deletion leaves a valid file + further down in the PATH, (2) there is no way to know exactly + what an executable (in the sense of "capable of being + exec'd") file is. Using access() loses because it may lose + if the caller is the superuser; failing to use it loses if + there are ACLs or some such. */ + + char *p; + char *p1; + /* FIXME-maybe: might want "set path" command so user can change what + path is used from within GDB. */ + char *path = getenv ("PATH"); + int len; + struct stat statbuf; + + if (path == NULL) + path = "/bin:/usr/bin"; + + tryname = alloca (strlen (path) + strlen (shell_file) + 2); + for (p = path; p != NULL; p = p1 ? p1 + 1: NULL) + { + p1 = strchr (p, ':'); + if (p1 != NULL) + len = p1 - p; + else + len = strlen (p); + strncpy (tryname, p, len); + tryname[len] = '\0'; + strcat (tryname, "/"); + strcat (tryname, shell_file); + if (access (tryname, X_OK) < 0) + continue; + if (stat (tryname, &statbuf) < 0) + continue; + if (!S_ISREG (statbuf.st_mode)) + /* We certainly need to reject directories. I'm not quite + as sure about FIFOs, sockets, etc., but I kind of doubt + that people want to exec() these things. */ + continue; + break; + } + if (p == NULL) + /* Not found. This must be an error rather than merely passing + the file to execlp(), because execlp() would try all the + exec()s, causing GDB to get confused. */ + error ("Can't find shell %s in PATH", shell_file); + + shell_file = tryname; + } + + fork_inferior (exec_file, allargs, env, + proc_set_exec_trap, procfs_init_inferior, NULL, shell_file); + + /* We are at the first instruction we care about. */ + /* Pedal to the metal... */ + + proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0); +} + +/* Clean up after the inferior dies. */ + +static void +procfs_mourn_inferior () +{ + struct procinfo *pi; + struct procinfo *next_pi; + + for (pi = procinfo_list; pi; pi = next_pi) + { + next_pi = pi->next; + unconditionally_kill_inferior (pi); + } + + unpush_target (&procfs_ops); + generic_mourn_inferior (); +} + + +/* Mark our target-struct as eligible for stray "run" and "attach" commands. */ +static int +procfs_can_run () +{ + /* This variable is controlled by modules that sit atop procfs that may layer + their own process structure atop that provided here. sol-thread.c does + this because of the Solaris two-level thread model. */ + + return !procfs_suppress_run; +} +#ifdef TARGET_HAS_HARDWARE_WATCHPOINTS +#ifndef UNIXWARE + +/* Insert a watchpoint */ +int +procfs_set_watchpoint(pid, addr, len, rw) + int pid; + CORE_ADDR addr; + int len; + int rw; +{ + struct procinfo *pi; + prwatch_t wpt; + + pi = find_procinfo (pid == -1 ? inferior_pid : pid, 0); + wpt.pr_vaddr = (caddr_t)addr; + wpt.pr_size = len; + wpt.pr_wflags = ((rw & 1) ? MA_READ : 0) | ((rw & 2) ? MA_WRITE : 0); + if (ioctl (pi->ctl_fd, PIOCSWATCH, &wpt) < 0) + { + if (errno == E2BIG) + return -1; + /* Currently it sometimes happens that the same watchpoint gets + deleted twice - don't die in this case (FIXME please) */ + if (errno == ESRCH && len == 0) + return 0; + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSWATCH failed"); + } + return 0; +} + +int +procfs_stopped_by_watchpoint(pid) + int pid; +{ + struct procinfo *pi; + short what; + short why; + + pi = find_procinfo (pid == -1 ? inferior_pid : pid, 0); + if (pi->prstatus.pr_flags & (PR_STOPPED | PR_ISTOP)) + { + why = pi->prstatus.pr_why; + what = pi->prstatus.pr_what; + if (why == PR_FAULTED +#if defined (FLTWATCH) && defined (FLTKWATCH) + && (what == FLTWATCH || what == FLTKWATCH) +#else +#ifdef FLTWATCH + && (what == FLTWATCH) +#endif +#ifdef FLTKWATCH + && (what == FLTKWATCH) +#endif +#endif + ) + return what; + } + return 0; +} +#endif /* !UNIXWARE */ +#endif /* TARGET_HAS_HARDWARE_WATCHPOINTS */ + +/* Why is this necessary? Shouldn't dead threads just be removed from the + thread database? */ + +static int +procfs_thread_alive (pid) + int pid; +{ + struct procinfo *pi, *next_pi; + + for (pi = procinfo_list; pi; pi = next_pi) + { + next_pi = pi->next; + if (pi -> pid == pid) + if (procfs_read_status (pi)) /* alive */ + return 1; + else /* defunct (exited) */ + { + close_proc_file (pi); + return 0; + } + } + return 0; +} + +int +procfs_first_available () +{ + struct procinfo *pi; + + for (pi = procinfo_list; pi; pi = pi->next) + { + if (procfs_read_status (pi)) + return pi->pid; + } + return -1; +} + +int +procfs_get_pid_fd (pid) + int pid; +{ + struct procinfo *pi = find_procinfo (pid, 1); + + if (pi == NULL) + return -1; + + return pi->ctl_fd; +} + +/* Send a SIGINT to the process group. This acts just like the user typed a + ^C on the controlling terminal. + + XXX - This may not be correct for all systems. Some may want to use + killpg() instead of kill (-pgrp). */ + +static void +procfs_stop () +{ + extern pid_t inferior_process_group; + + kill (-inferior_process_group, SIGINT); +} + +/* Convert a pid to printable form. */ + +#ifdef TIDGET +char * +procfs_pid_to_str (pid) + int pid; +{ + static char buf[100]; + + sprintf (buf, "Kernel thread %d", TIDGET (pid)); + + return buf; +} +#endif /* TIDGET */ + + +static void +init_procfs_ops () +{ + procfs_ops.to_shortname = "procfs"; + procfs_ops.to_longname = "Unix /proc child process"; + procfs_ops.to_doc = "Unix /proc child process (started by the \"run\" command)."; + procfs_ops.to_open = procfs_open; + procfs_ops.to_attach = procfs_attach; + procfs_ops.to_detach = procfs_detach; + procfs_ops.to_resume = procfs_resume; + procfs_ops.to_wait = procfs_wait; + procfs_ops.to_fetch_registers = procfs_fetch_registers; + procfs_ops.to_store_registers = procfs_store_registers; + procfs_ops.to_prepare_to_store = procfs_prepare_to_store; + procfs_ops.to_xfer_memory = procfs_xfer_memory; + procfs_ops.to_files_info = procfs_files_info; + procfs_ops.to_insert_breakpoint = memory_insert_breakpoint; + procfs_ops.to_remove_breakpoint = memory_remove_breakpoint; + procfs_ops.to_terminal_init = terminal_init_inferior; + procfs_ops.to_terminal_inferior = terminal_inferior; + procfs_ops.to_terminal_ours_for_output = terminal_ours_for_output; + procfs_ops.to_terminal_ours = terminal_ours; + procfs_ops.to_terminal_info = child_terminal_info; + procfs_ops.to_kill = procfs_kill_inferior; + procfs_ops.to_create_inferior = procfs_create_inferior; + procfs_ops.to_mourn_inferior = procfs_mourn_inferior; + procfs_ops.to_can_run = procfs_can_run; + procfs_ops.to_notice_signals = procfs_notice_signals; + procfs_ops.to_thread_alive = procfs_thread_alive; + procfs_ops.to_stop = procfs_stop; + procfs_ops.to_stratum = process_stratum; + procfs_ops.to_has_all_memory = 1; + procfs_ops.to_has_memory = 1; + procfs_ops.to_has_stack = 1; + procfs_ops.to_has_registers = 1; + procfs_ops.to_has_execution = 1; + procfs_ops.to_magic = OPS_MAGIC; +} + +void +_initialize_procfs () +{ +#ifdef HAVE_OPTIONAL_PROC_FS + char procname[MAX_PROC_NAME_SIZE]; + int fd; + + /* If we have an optional /proc filesystem (e.g. under OSF/1), + don't add procfs support if we cannot access the running + GDB via /proc. */ + sprintf (procname, STATUS_PROC_NAME_FMT, getpid ()); + if ((fd = open (procname, O_RDONLY)) < 0) + return; + close (fd); +#endif + + init_procfs_ops (); + add_target (&procfs_ops); + + add_info ("processes", info_proc, +"Show process status information using /proc entry.\n\ +Specify process id or use current inferior by default.\n\ +Specify keywords for detailed information; default is summary.\n\ +Keywords are: `all', `faults', `flags', `id', `mappings', `signals',\n\ +`status', `syscalls', and `times'.\n\ +Unambiguous abbreviations may be used."); + + init_syscall_table (); +} |