| /* Machine independent support for SVR4 /proc (process file system) for GDB. |
| |
| Copyright (C) 1999-2003, 2006-2012 Free Software Foundation, Inc. |
| |
| Written by Michael Snyder at Cygnus Solutions. |
| Based on work by Fred Fish, Stu Grossman, Geoff Noer, and others. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #include "defs.h" |
| #include "inferior.h" |
| #include "target.h" |
| #include "gdbcore.h" |
| #include "elf-bfd.h" /* for elfcore_write_* */ |
| #include "gdbcmd.h" |
| #include "gdbthread.h" |
| #include "regcache.h" |
| #include "inf-child.h" |
| |
| #if defined (NEW_PROC_API) |
| #define _STRUCTURED_PROC 1 /* Should be done by configure script. */ |
| #endif |
| |
| #include <sys/procfs.h> |
| #ifdef HAVE_SYS_FAULT_H |
| #include <sys/fault.h> |
| #endif |
| #ifdef HAVE_SYS_SYSCALL_H |
| #include <sys/syscall.h> |
| #endif |
| #include <sys/errno.h> |
| #include "gdb_wait.h" |
| #include <signal.h> |
| #include <ctype.h> |
| #include "gdb_string.h" |
| #include "gdb_assert.h" |
| #include "inflow.h" |
| #include "auxv.h" |
| #include "procfs.h" |
| #include "observer.h" |
| |
| /* This module provides the interface between GDB and the |
| /proc file system, which is used on many versions of Unix |
| as a means for debuggers to control other processes. |
| |
| Examples of the systems that use this interface are: |
| |
| Irix |
| Solaris |
| OSF |
| Unixware |
| AIX5 |
| |
| /proc works by imitating a file system: you open a simulated file |
| that represents the process you wish to interact with, and perform |
| operations on that "file" in order to examine or change the state |
| of the other process. |
| |
| The most important thing to know about /proc and this module is |
| that there are two very different interfaces to /proc: |
| |
| One that uses the ioctl system call, and another that uses read |
| and write system calls. |
| |
| This module has to support both /proc interfaces. This means that |
| there are two different ways of doing every basic operation. |
| |
| In order to keep most of the code simple and clean, I have defined |
| an interface "layer" which hides all these system calls. An ifdef |
| (NEW_PROC_API) determines which interface we are using, and most or |
| all occurrances of this ifdef should be confined to this interface |
| layer. */ |
| |
| /* Determine which /proc API we are using: The ioctl API defines |
| PIOCSTATUS, while the read/write (multiple fd) API never does. */ |
| |
| #ifdef NEW_PROC_API |
| #include <sys/types.h> |
| #include "gdb_dirent.h" /* opendir/readdir, for listing the LWP's */ |
| #endif |
| |
| #include <fcntl.h> /* for O_RDONLY */ |
| #include <unistd.h> /* for "X_OK" */ |
| #include "gdb_stat.h" /* for struct stat */ |
| |
| /* Note: procfs-utils.h must be included after the above system header |
| files, because it redefines various system calls using macros. |
| This may be incompatible with the prototype declarations. */ |
| |
| #include "proc-utils.h" |
| |
| /* Prototypes for supply_gregset etc. */ |
| #include "gregset.h" |
| |
| /* =================== TARGET_OPS "MODULE" =================== */ |
| |
| /* This module defines the GDB target vector and its methods. */ |
| |
| static void procfs_attach (struct target_ops *, char *, int); |
| static void procfs_detach (struct target_ops *, char *, int); |
| static void procfs_resume (struct target_ops *, |
| ptid_t, int, enum gdb_signal); |
| static void procfs_stop (ptid_t); |
| static void procfs_files_info (struct target_ops *); |
| static void procfs_fetch_registers (struct target_ops *, |
| struct regcache *, int); |
| static void procfs_store_registers (struct target_ops *, |
| struct regcache *, int); |
| static void procfs_pass_signals (int, unsigned char *); |
| static void procfs_kill_inferior (struct target_ops *ops); |
| static void procfs_mourn_inferior (struct target_ops *ops); |
| static void procfs_create_inferior (struct target_ops *, char *, |
| char *, char **, int); |
| static ptid_t procfs_wait (struct target_ops *, |
| ptid_t, struct target_waitstatus *, int); |
| static int procfs_xfer_memory (CORE_ADDR, gdb_byte *, int, int, |
| struct mem_attrib *attrib, |
| struct target_ops *); |
| static LONGEST procfs_xfer_partial (struct target_ops *ops, |
| enum target_object object, |
| const char *annex, |
| gdb_byte *readbuf, |
| const gdb_byte *writebuf, |
| ULONGEST offset, LONGEST len); |
| |
| static int procfs_thread_alive (struct target_ops *ops, ptid_t); |
| |
| static void procfs_find_new_threads (struct target_ops *ops); |
| static char *procfs_pid_to_str (struct target_ops *, ptid_t); |
| |
| static int proc_find_memory_regions (int (*) (CORE_ADDR, |
| unsigned long, |
| int, int, int, |
| void *), |
| void *); |
| |
| static char * procfs_make_note_section (bfd *, int *); |
| |
| static int procfs_can_use_hw_breakpoint (int, int, int); |
| |
| static void procfs_info_proc (struct target_ops *, char *, |
| enum info_proc_what); |
| |
| #if defined (PR_MODEL_NATIVE) && (PR_MODEL_NATIVE == PR_MODEL_LP64) |
| /* When GDB is built as 64-bit application on Solaris, the auxv data |
| is presented in 64-bit format. We need to provide a custom parser |
| to handle that. */ |
| static int |
| procfs_auxv_parse (struct target_ops *ops, gdb_byte **readptr, |
| gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch); |
| gdb_byte *ptr = *readptr; |
| |
| if (endptr == ptr) |
| return 0; |
| |
| if (endptr - ptr < 8 * 2) |
| return -1; |
| |
| *typep = extract_unsigned_integer (ptr, 4, byte_order); |
| ptr += 8; |
| /* The size of data is always 64-bit. If the application is 32-bit, |
| it will be zero extended, as expected. */ |
| *valp = extract_unsigned_integer (ptr, 8, byte_order); |
| ptr += 8; |
| |
| *readptr = ptr; |
| return 1; |
| } |
| #endif |
| |
| struct target_ops * |
| procfs_target (void) |
| { |
| struct target_ops *t = inf_child_target (); |
| |
| t->to_shortname = "procfs"; |
| t->to_longname = "Unix /proc child process"; |
| t->to_doc = |
| "Unix /proc child process (started by the \"run\" command)."; |
| t->to_create_inferior = procfs_create_inferior; |
| t->to_kill = procfs_kill_inferior; |
| t->to_mourn_inferior = procfs_mourn_inferior; |
| t->to_attach = procfs_attach; |
| t->to_detach = procfs_detach; |
| t->to_wait = procfs_wait; |
| t->to_resume = procfs_resume; |
| t->to_fetch_registers = procfs_fetch_registers; |
| t->to_store_registers = procfs_store_registers; |
| t->to_xfer_partial = procfs_xfer_partial; |
| t->deprecated_xfer_memory = procfs_xfer_memory; |
| t->to_pass_signals = procfs_pass_signals; |
| t->to_files_info = procfs_files_info; |
| t->to_stop = procfs_stop; |
| |
| t->to_find_new_threads = procfs_find_new_threads; |
| t->to_thread_alive = procfs_thread_alive; |
| t->to_pid_to_str = procfs_pid_to_str; |
| |
| t->to_has_thread_control = tc_schedlock; |
| t->to_find_memory_regions = proc_find_memory_regions; |
| t->to_make_corefile_notes = procfs_make_note_section; |
| t->to_info_proc = procfs_info_proc; |
| |
| #if defined(PR_MODEL_NATIVE) && (PR_MODEL_NATIVE == PR_MODEL_LP64) |
| t->to_auxv_parse = procfs_auxv_parse; |
| #endif |
| |
| t->to_magic = OPS_MAGIC; |
| |
| return t; |
| } |
| |
| /* =================== END, TARGET_OPS "MODULE" =================== */ |
| |
| /* World Unification: |
| |
| Put any typedefs, defines etc. here that are required for the |
| unification of code that handles different versions of /proc. */ |
| |
| #ifdef NEW_PROC_API /* Solaris 7 && 8 method for watchpoints */ |
| #ifdef WA_READ |
| enum { READ_WATCHFLAG = WA_READ, |
| WRITE_WATCHFLAG = WA_WRITE, |
| EXEC_WATCHFLAG = WA_EXEC, |
| AFTER_WATCHFLAG = WA_TRAPAFTER |
| }; |
| #endif |
| #else /* Irix method for watchpoints */ |
| enum { READ_WATCHFLAG = MA_READ, |
| WRITE_WATCHFLAG = MA_WRITE, |
| EXEC_WATCHFLAG = MA_EXEC, |
| AFTER_WATCHFLAG = 0 /* trapafter not implemented */ |
| }; |
| #endif |
| |
| /* gdb_sigset_t */ |
| #ifdef HAVE_PR_SIGSET_T |
| typedef pr_sigset_t gdb_sigset_t; |
| #else |
| typedef sigset_t gdb_sigset_t; |
| #endif |
| |
| /* sigaction */ |
| #ifdef HAVE_PR_SIGACTION64_T |
| typedef pr_sigaction64_t gdb_sigaction_t; |
| #else |
| typedef struct sigaction gdb_sigaction_t; |
| #endif |
| |
| /* siginfo */ |
| #ifdef HAVE_PR_SIGINFO64_T |
| typedef pr_siginfo64_t gdb_siginfo_t; |
| #else |
| typedef siginfo_t gdb_siginfo_t; |
| #endif |
| |
| /* On mips-irix, praddset and prdelset are defined in such a way that |
| they return a value, which causes GCC to emit a -Wunused error |
| because the returned value is not used. Prevent this warning |
| by casting the return value to void. On sparc-solaris, this issue |
| does not exist because the definition of these macros already include |
| that cast to void. */ |
| #define gdb_praddset(sp, flag) ((void) praddset (sp, flag)) |
| #define gdb_prdelset(sp, flag) ((void) prdelset (sp, flag)) |
| |
| /* gdb_premptysysset */ |
| #ifdef premptysysset |
| #define gdb_premptysysset premptysysset |
| #else |
| #define gdb_premptysysset premptyset |
| #endif |
| |
| /* praddsysset */ |
| #ifdef praddsysset |
| #define gdb_praddsysset praddsysset |
| #else |
| #define gdb_praddsysset gdb_praddset |
| #endif |
| |
| /* prdelsysset */ |
| #ifdef prdelsysset |
| #define gdb_prdelsysset prdelsysset |
| #else |
| #define gdb_prdelsysset gdb_prdelset |
| #endif |
| |
| /* prissyssetmember */ |
| #ifdef prissyssetmember |
| #define gdb_pr_issyssetmember prissyssetmember |
| #else |
| #define gdb_pr_issyssetmember prismember |
| #endif |
| |
| /* As a feature test, saying ``#if HAVE_PRSYSENT_T'' everywhere isn't |
| as intuitively descriptive as it could be, so we'll define |
| DYNAMIC_SYSCALLS to mean the same thing. Anyway, at the time of |
| this writing, this feature is only found on AIX5 systems and |
| basically means that the set of syscalls is not fixed. I.e, |
| there's no nice table that one can #include to get all of the |
| syscall numbers. Instead, they're stored in /proc/PID/sysent |
| for each process. We are at least guaranteed that they won't |
| change over the lifetime of the process. But each process could |
| (in theory) have different syscall numbers. */ |
| #ifdef HAVE_PRSYSENT_T |
| #define DYNAMIC_SYSCALLS |
| #endif |
| |
| |
| |
| /* =================== STRUCT PROCINFO "MODULE" =================== */ |
| |
| /* FIXME: this comment will soon be out of date W.R.T. threads. */ |
| |
| /* The procinfo struct is a wrapper to hold all the state information |
| concerning a /proc process. There should be exactly one procinfo |
| for each process, and since GDB currently can debug only one |
| process at a time, that means there should be only one procinfo. |
| All of the LWP's of a process can be accessed indirectly thru the |
| single process procinfo. |
| |
| However, against the day when GDB may debug more than one process, |
| this data structure is kept in a list (which for now will hold no |
| more than one member), and many functions will have a pointer to a |
| procinfo as an argument. |
| |
| There will be a separate procinfo structure for use by the (not yet |
| implemented) "info proc" command, so that we can print useful |
| information about any random process without interfering with the |
| inferior's procinfo information. */ |
| |
| #ifdef NEW_PROC_API |
| /* format strings for /proc paths */ |
| # ifndef CTL_PROC_NAME_FMT |
| # define MAIN_PROC_NAME_FMT "/proc/%d" |
| # 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" |
| # define MAX_PROC_NAME_SIZE sizeof("/proc/99999/lwp/8096/lstatus") |
| # endif |
| /* the name of the proc status struct depends on the implementation */ |
| typedef pstatus_t gdb_prstatus_t; |
| typedef lwpstatus_t gdb_lwpstatus_t; |
| #else /* ! NEW_PROC_API */ |
| /* format strings for /proc paths */ |
| # ifndef CTL_PROC_NAME_FMT |
| # define MAIN_PROC_NAME_FMT "/proc/%05d" |
| # 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" |
| # define MAX_PROC_NAME_SIZE sizeof("/proc/ttttppppp") |
| # endif |
| /* The name of the proc status struct depends on the implementation. */ |
| typedef prstatus_t gdb_prstatus_t; |
| typedef prstatus_t gdb_lwpstatus_t; |
| #endif /* NEW_PROC_API */ |
| |
| typedef struct procinfo { |
| struct procinfo *next; |
| int pid; /* Process ID */ |
| int tid; /* Thread/LWP id */ |
| |
| /* process state */ |
| int was_stopped; |
| int ignore_next_sigstop; |
| |
| /* The following four fd fields may be identical, or may contain |
| several different fd's, depending on the version of /proc |
| (old ioctl or new read/write). */ |
| |
| int ctl_fd; /* File descriptor for /proc control file */ |
| |
| /* The next three file descriptors are actually only needed in the |
| read/write, multiple-file-descriptor implemenation |
| (NEW_PROC_API). However, to avoid a bunch of #ifdefs in the |
| code, we will use them uniformly by (in the case of the ioctl |
| single-file-descriptor implementation) filling them with copies |
| of the control fd. */ |
| int status_fd; /* File descriptor for /proc status file */ |
| int as_fd; /* File descriptor for /proc as file */ |
| |
| char pathname[MAX_PROC_NAME_SIZE]; /* Pathname to /proc entry */ |
| |
| fltset_t saved_fltset; /* Saved traced hardware fault set */ |
| gdb_sigset_t saved_sigset; /* Saved traced signal set */ |
| gdb_sigset_t saved_sighold; /* Saved held signal set */ |
| sysset_t *saved_exitset; /* Saved traced system call exit set */ |
| sysset_t *saved_entryset; /* Saved traced system call entry set */ |
| |
| gdb_prstatus_t prstatus; /* Current process status info */ |
| |
| #ifndef NEW_PROC_API |
| gdb_fpregset_t fpregset; /* Current floating point registers */ |
| #endif |
| |
| #ifdef DYNAMIC_SYSCALLS |
| int num_syscalls; /* Total number of syscalls */ |
| char **syscall_names; /* Syscall number to name map */ |
| #endif |
| |
| struct procinfo *thread_list; |
| |
| int status_valid : 1; |
| int gregs_valid : 1; |
| int fpregs_valid : 1; |
| int threads_valid: 1; |
| } procinfo; |
| |
| static char errmsg[128]; /* shared error msg buffer */ |
| |
| /* Function prototypes for procinfo module: */ |
| |
| static procinfo *find_procinfo_or_die (int pid, int tid); |
| static procinfo *find_procinfo (int pid, int tid); |
| static procinfo *create_procinfo (int pid, int tid); |
| static void destroy_procinfo (procinfo * p); |
| static void do_destroy_procinfo_cleanup (void *); |
| static void dead_procinfo (procinfo * p, char *msg, int killp); |
| static int open_procinfo_files (procinfo * p, int which); |
| static void close_procinfo_files (procinfo * p); |
| static int sysset_t_size (procinfo *p); |
| static sysset_t *sysset_t_alloc (procinfo * pi); |
| #ifdef DYNAMIC_SYSCALLS |
| static void load_syscalls (procinfo *pi); |
| static void free_syscalls (procinfo *pi); |
| static int find_syscall (procinfo *pi, char *name); |
| #endif /* DYNAMIC_SYSCALLS */ |
| |
| static int iterate_over_mappings |
| (procinfo *pi, find_memory_region_ftype child_func, void *data, |
| int (*func) (struct prmap *map, find_memory_region_ftype child_func, |
| void *data)); |
| |
| /* The head of the procinfo list: */ |
| static procinfo * procinfo_list; |
| |
| /* Search the procinfo list. Return a pointer to procinfo, or NULL if |
| not found. */ |
| |
| static procinfo * |
| find_procinfo (int pid, int tid) |
| { |
| procinfo *pi; |
| |
| for (pi = procinfo_list; pi; pi = pi->next) |
| if (pi->pid == pid) |
| break; |
| |
| if (pi) |
| if (tid) |
| { |
| /* Don't check threads_valid. If we're updating the |
| thread_list, we want to find whatever threads are already |
| here. This means that in general it is the caller's |
| responsibility to check threads_valid and update before |
| calling find_procinfo, if the caller wants to find a new |
| thread. */ |
| |
| for (pi = pi->thread_list; pi; pi = pi->next) |
| if (pi->tid == tid) |
| break; |
| } |
| |
| return pi; |
| } |
| |
| /* Calls find_procinfo, but errors on failure. */ |
| |
| static procinfo * |
| find_procinfo_or_die (int pid, int tid) |
| { |
| procinfo *pi = find_procinfo (pid, tid); |
| |
| if (pi == NULL) |
| { |
| if (tid) |
| error (_("procfs: couldn't find pid %d " |
| "(kernel thread %d) in procinfo list."), |
| pid, tid); |
| else |
| error (_("procfs: couldn't find pid %d in procinfo list."), pid); |
| } |
| return pi; |
| } |
| |
| /* Wrapper for `open'. The appropriate open call is attempted; if |
| unsuccessful, it will be retried as many times as needed for the |
| EAGAIN and EINTR conditions. |
| |
| For other conditions, retry the open a limited number of times. In |
| addition, a short sleep is imposed prior to retrying the open. The |
| reason for this sleep is to give the kernel a chance to catch up |
| and create the file in question in the event that GDB "wins" the |
| race to open a file before the kernel has created it. */ |
| |
| static int |
| open_with_retry (const char *pathname, int flags) |
| { |
| int retries_remaining, status; |
| |
| retries_remaining = 2; |
| |
| while (1) |
| { |
| status = open (pathname, flags); |
| |
| if (status >= 0 || retries_remaining == 0) |
| break; |
| else if (errno != EINTR && errno != EAGAIN) |
| { |
| retries_remaining--; |
| sleep (1); |
| } |
| } |
| |
| return status; |
| } |
| |
| /* Open the file descriptor for the process or LWP. If NEW_PROC_API |
| is defined, we only open the control file descriptor; the others |
| are opened lazily as needed. Otherwise (if not NEW_PROC_API), |
| there is only one real file descriptor, but we keep multiple copies |
| of it so that the code that uses them does not have to be #ifdef'd. |
| Returns the file descriptor, or zero for failure. */ |
| |
| enum { FD_CTL, FD_STATUS, FD_AS }; |
| |
| static int |
| open_procinfo_files (procinfo *pi, int which) |
| { |
| #ifdef NEW_PROC_API |
| char tmp[MAX_PROC_NAME_SIZE]; |
| #endif |
| int fd; |
| |
| /* This function is getting ALMOST long enough to break up into |
| several. Here is some rationale: |
| |
| NEW_PROC_API (Solaris 2.6, Solaris 2.7, Unixware): |
| There are several file descriptors that may need to be open |
| for any given process or LWP. The ones we're intereted in are: |
| - control (ctl) write-only change the state |
| - status (status) read-only query the state |
| - address space (as) read/write access memory |
| - map (map) read-only virtual addr map |
| Most of these are opened lazily as they are needed. |
| The pathnames for the 'files' for an LWP look slightly |
| different from those of a first-class process: |
| Pathnames for a process (<proc-id>): |
| /proc/<proc-id>/ctl |
| /proc/<proc-id>/status |
| /proc/<proc-id>/as |
| /proc/<proc-id>/map |
| Pathnames for an LWP (lwp-id): |
| /proc/<proc-id>/lwp/<lwp-id>/lwpctl |
| /proc/<proc-id>/lwp/<lwp-id>/lwpstatus |
| An LWP has no map or address space file descriptor, since |
| the memory map and address space are shared by all LWPs. |
| |
| Everyone else (Solaris 2.5, Irix, OSF) |
| There is only one file descriptor for each process or LWP. |
| For convenience, we copy the same file descriptor into all |
| three fields of the procinfo struct (ctl_fd, status_fd, and |
| as_fd, see NEW_PROC_API above) so that code that uses them |
| doesn't need any #ifdef's. |
| Pathname for all: |
| /proc/<proc-id> |
| |
| Solaris 2.5 LWP's: |
| Each LWP has an independent file descriptor, but these |
| are not obtained via the 'open' system call like the rest: |
| instead, they're obtained thru an ioctl call (PIOCOPENLWP) |
| to the file descriptor of the parent process. |
| |
| OSF threads: |
| These do not even have their own independent file descriptor. |
| All operations are carried out on the file descriptor of the |
| parent process. Therefore we just call open again for each |
| thread, getting a new handle for the same 'file'. */ |
| |
| #ifdef NEW_PROC_API |
| /* In this case, there are several different file descriptors that |
| we might be asked to open. The control file descriptor will be |
| opened early, but the others will be opened lazily as they are |
| needed. */ |
| |
| strcpy (tmp, pi->pathname); |
| switch (which) { /* Which file descriptor to open? */ |
| case FD_CTL: |
| if (pi->tid) |
| strcat (tmp, "/lwpctl"); |
| else |
| strcat (tmp, "/ctl"); |
| fd = open_with_retry (tmp, O_WRONLY); |
| if (fd < 0) |
| return 0; /* fail */ |
| pi->ctl_fd = fd; |
| break; |
| case FD_AS: |
| if (pi->tid) |
| return 0; /* There is no 'as' file descriptor for an lwp. */ |
| strcat (tmp, "/as"); |
| fd = open_with_retry (tmp, O_RDWR); |
| if (fd < 0) |
| return 0; /* fail */ |
| pi->as_fd = fd; |
| break; |
| case FD_STATUS: |
| if (pi->tid) |
| strcat (tmp, "/lwpstatus"); |
| else |
| strcat (tmp, "/status"); |
| fd = open_with_retry (tmp, O_RDONLY); |
| if (fd < 0) |
| return 0; /* fail */ |
| pi->status_fd = fd; |
| break; |
| default: |
| return 0; /* unknown file descriptor */ |
| } |
| #else /* not NEW_PROC_API */ |
| /* In this case, there is only one file descriptor for each procinfo |
| (ie. each process or LWP). In fact, only the file descriptor for |
| the process can actually be opened by an 'open' system call. The |
| ones for the LWPs have to be obtained thru an IOCTL call on the |
| process's file descriptor. |
| |
| For convenience, we copy each procinfo's single file descriptor |
| into all of the fields occupied by the several file descriptors |
| of the NEW_PROC_API implementation. That way, the code that uses |
| them can be written without ifdefs. */ |
| |
| |
| #ifdef PIOCTSTATUS /* OSF */ |
| /* Only one FD; just open it. */ |
| if ((fd = open_with_retry (pi->pathname, O_RDWR)) < 0) |
| return 0; |
| #else /* Sol 2.5, Irix, other? */ |
| if (pi->tid == 0) /* Master procinfo for the process */ |
| { |
| fd = open_with_retry (pi->pathname, O_RDWR); |
| if (fd < 0) |
| return 0; /* fail */ |
| } |
| else /* LWP thread procinfo */ |
| { |
| #ifdef PIOCOPENLWP /* Sol 2.5, thread/LWP */ |
| procinfo *process; |
| int lwpid = pi->tid; |
| |
| /* Find the procinfo for the entire process. */ |
| if ((process = find_procinfo (pi->pid, 0)) == NULL) |
| return 0; /* fail */ |
| |
| /* Now obtain the file descriptor for the LWP. */ |
| if ((fd = ioctl (process->ctl_fd, PIOCOPENLWP, &lwpid)) < 0) |
| return 0; /* fail */ |
| #else /* Irix, other? */ |
| return 0; /* Don't know how to open threads. */ |
| #endif /* Sol 2.5 PIOCOPENLWP */ |
| } |
| #endif /* OSF PIOCTSTATUS */ |
| pi->ctl_fd = pi->as_fd = pi->status_fd = fd; |
| #endif /* NEW_PROC_API */ |
| |
| return 1; /* success */ |
| } |
| |
| /* Allocate a data structure and link it into the procinfo list. |
| First tries to find a pre-existing one (FIXME: why?). Returns the |
| pointer to new procinfo struct. */ |
| |
| static procinfo * |
| create_procinfo (int pid, int tid) |
| { |
| procinfo *pi, *parent = NULL; |
| |
| if ((pi = find_procinfo (pid, tid))) |
| return pi; /* Already exists, nothing to do. */ |
| |
| /* Find parent before doing malloc, to save having to cleanup. */ |
| if (tid != 0) |
| parent = find_procinfo_or_die (pid, 0); /* FIXME: should I |
| create it if it |
| doesn't exist yet? */ |
| |
| pi = (procinfo *) xmalloc (sizeof (procinfo)); |
| memset (pi, 0, sizeof (procinfo)); |
| pi->pid = pid; |
| pi->tid = tid; |
| |
| #ifdef DYNAMIC_SYSCALLS |
| load_syscalls (pi); |
| #endif |
| |
| pi->saved_entryset = sysset_t_alloc (pi); |
| pi->saved_exitset = sysset_t_alloc (pi); |
| |
| /* Chain into list. */ |
| if (tid == 0) |
| { |
| sprintf (pi->pathname, MAIN_PROC_NAME_FMT, pid); |
| pi->next = procinfo_list; |
| procinfo_list = pi; |
| } |
| else |
| { |
| #ifdef NEW_PROC_API |
| sprintf (pi->pathname, "/proc/%05d/lwp/%d", pid, tid); |
| #else |
| sprintf (pi->pathname, MAIN_PROC_NAME_FMT, pid); |
| #endif |
| pi->next = parent->thread_list; |
| parent->thread_list = pi; |
| } |
| return pi; |
| } |
| |
| /* Close all file descriptors associated with the procinfo. */ |
| |
| static void |
| close_procinfo_files (procinfo *pi) |
| { |
| if (pi->ctl_fd > 0) |
| close (pi->ctl_fd); |
| #ifdef NEW_PROC_API |
| if (pi->as_fd > 0) |
| close (pi->as_fd); |
| if (pi->status_fd > 0) |
| close (pi->status_fd); |
| #endif |
| pi->ctl_fd = pi->as_fd = pi->status_fd = 0; |
| } |
| |
| /* Destructor function. Close, unlink and deallocate the object. */ |
| |
| static void |
| destroy_one_procinfo (procinfo **list, procinfo *pi) |
| { |
| procinfo *ptr; |
| |
| /* Step one: unlink the procinfo from its list. */ |
| if (pi == *list) |
| *list = pi->next; |
| else |
| for (ptr = *list; ptr; ptr = ptr->next) |
| if (ptr->next == pi) |
| { |
| ptr->next = pi->next; |
| break; |
| } |
| |
| /* Step two: close any open file descriptors. */ |
| close_procinfo_files (pi); |
| |
| /* Step three: free the memory. */ |
| #ifdef DYNAMIC_SYSCALLS |
| free_syscalls (pi); |
| #endif |
| xfree (pi->saved_entryset); |
| xfree (pi->saved_exitset); |
| xfree (pi); |
| } |
| |
| static void |
| destroy_procinfo (procinfo *pi) |
| { |
| procinfo *tmp; |
| |
| if (pi->tid != 0) /* Destroy a thread procinfo. */ |
| { |
| tmp = find_procinfo (pi->pid, 0); /* Find the parent process. */ |
| destroy_one_procinfo (&tmp->thread_list, pi); |
| } |
| else /* Destroy a process procinfo and all its threads. */ |
| { |
| /* First destroy the children, if any; */ |
| while (pi->thread_list != NULL) |
| destroy_one_procinfo (&pi->thread_list, pi->thread_list); |
| /* Then destroy the parent. Genocide!!! */ |
| destroy_one_procinfo (&procinfo_list, pi); |
| } |
| } |
| |
| static void |
| do_destroy_procinfo_cleanup (void *pi) |
| { |
| destroy_procinfo (pi); |
| } |
| |
| enum { NOKILL, KILL }; |
| |
| /* To be called on a non_recoverable error for a procinfo. Prints |
| error messages, optionally sends a SIGKILL to the process, then |
| destroys the data structure. */ |
| |
| static void |
| dead_procinfo (procinfo *pi, char *msg, int kill_p) |
| { |
| char procfile[80]; |
| |
| if (pi->pathname) |
| { |
| print_sys_errmsg (pi->pathname, errno); |
| } |
| else |
| { |
| sprintf (procfile, "process %d", pi->pid); |
| print_sys_errmsg (procfile, errno); |
| } |
| if (kill_p == KILL) |
| kill (pi->pid, SIGKILL); |
| |
| destroy_procinfo (pi); |
| error ("%s", msg); |
| } |
| |
| /* Returns the (complete) size of a sysset_t struct. Normally, this |
| is just sizeof (sysset_t), but in the case of Monterey/64, the |
| actual size of sysset_t isn't known until runtime. */ |
| |
| static int |
| sysset_t_size (procinfo * pi) |
| { |
| #ifndef DYNAMIC_SYSCALLS |
| return sizeof (sysset_t); |
| #else |
| return sizeof (sysset_t) - sizeof (uint64_t) |
| + sizeof (uint64_t) * ((pi->num_syscalls + (8 * sizeof (uint64_t) - 1)) |
| / (8 * sizeof (uint64_t))); |
| #endif |
| } |
| |
| /* Allocate and (partially) initialize a sysset_t struct. */ |
| |
| static sysset_t * |
| sysset_t_alloc (procinfo * pi) |
| { |
| sysset_t *ret; |
| int size = sysset_t_size (pi); |
| |
| ret = xmalloc (size); |
| #ifdef DYNAMIC_SYSCALLS |
| ret->pr_size = ((pi->num_syscalls + (8 * sizeof (uint64_t) - 1)) |
| / (8 * sizeof (uint64_t))); |
| #endif |
| return ret; |
| } |
| |
| #ifdef DYNAMIC_SYSCALLS |
| |
| /* Extract syscall numbers and names from /proc/<pid>/sysent. Initialize |
| pi->num_syscalls with the number of syscalls and pi->syscall_names |
| with the names. (Certain numbers may be skipped in which case the |
| names for these numbers will be left as NULL.) */ |
| |
| #define MAX_SYSCALL_NAME_LENGTH 256 |
| #define MAX_SYSCALLS 65536 |
| |
| static void |
| load_syscalls (procinfo *pi) |
| { |
| char pathname[MAX_PROC_NAME_SIZE]; |
| int sysent_fd; |
| prsysent_t header; |
| prsyscall_t *syscalls; |
| int i, size, maxcall; |
| struct cleanup *cleanups; |
| |
| pi->num_syscalls = 0; |
| pi->syscall_names = 0; |
| |
| /* Open the file descriptor for the sysent file. */ |
| sprintf (pathname, "/proc/%d/sysent", pi->pid); |
| sysent_fd = open_with_retry (pathname, O_RDONLY); |
| if (sysent_fd < 0) |
| { |
| error (_("load_syscalls: Can't open /proc/%d/sysent"), pi->pid); |
| } |
| cleanups = make_cleanup_close (sysent_fd); |
| |
| size = sizeof header - sizeof (prsyscall_t); |
| if (read (sysent_fd, &header, size) != size) |
| { |
| error (_("load_syscalls: Error reading /proc/%d/sysent"), pi->pid); |
| } |
| |
| if (header.pr_nsyscalls == 0) |
| { |
| error (_("load_syscalls: /proc/%d/sysent contains no syscalls!"), |
| pi->pid); |
| } |
| |
| size = header.pr_nsyscalls * sizeof (prsyscall_t); |
| syscalls = xmalloc (size); |
| make_cleanup (free_current_contents, &syscalls); |
| |
| if (read (sysent_fd, syscalls, size) != size) |
| error (_("load_syscalls: Error reading /proc/%d/sysent"), pi->pid); |
| |
| /* Find maximum syscall number. This may not be the same as |
| pr_nsyscalls since that value refers to the number of entries |
| in the table. (Also, the docs indicate that some system |
| call numbers may be skipped.) */ |
| |
| maxcall = syscalls[0].pr_number; |
| |
| for (i = 1; i < header.pr_nsyscalls; i++) |
| if (syscalls[i].pr_number > maxcall |
| && syscalls[i].pr_nameoff > 0 |
| && syscalls[i].pr_number < MAX_SYSCALLS) |
| maxcall = syscalls[i].pr_number; |
| |
| pi->num_syscalls = maxcall+1; |
| pi->syscall_names = xmalloc (pi->num_syscalls * sizeof (char *)); |
| |
| for (i = 0; i < pi->num_syscalls; i++) |
| pi->syscall_names[i] = NULL; |
| |
| /* Read the syscall names in. */ |
| for (i = 0; i < header.pr_nsyscalls; i++) |
| { |
| char namebuf[MAX_SYSCALL_NAME_LENGTH]; |
| int nread; |
| int callnum; |
| |
| if (syscalls[i].pr_number >= MAX_SYSCALLS |
| || syscalls[i].pr_number < 0 |
| || syscalls[i].pr_nameoff <= 0 |
| || (lseek (sysent_fd, (off_t) syscalls[i].pr_nameoff, SEEK_SET) |
| != (off_t) syscalls[i].pr_nameoff)) |
| continue; |
| |
| nread = read (sysent_fd, namebuf, sizeof namebuf); |
| if (nread <= 0) |
| continue; |
| |
| callnum = syscalls[i].pr_number; |
| |
| if (pi->syscall_names[callnum] != NULL) |
| { |
| /* FIXME: Generate warning. */ |
| continue; |
| } |
| |
| namebuf[nread-1] = '\0'; |
| size = strlen (namebuf) + 1; |
| pi->syscall_names[callnum] = xmalloc (size); |
| strncpy (pi->syscall_names[callnum], namebuf, size-1); |
| pi->syscall_names[callnum][size-1] = '\0'; |
| } |
| |
| do_cleanups (cleanups); |
| } |
| |
| /* Free the space allocated for the syscall names from the procinfo |
| structure. */ |
| |
| static void |
| free_syscalls (procinfo *pi) |
| { |
| if (pi->syscall_names) |
| { |
| int i; |
| |
| for (i = 0; i < pi->num_syscalls; i++) |
| if (pi->syscall_names[i] != NULL) |
| xfree (pi->syscall_names[i]); |
| |
| xfree (pi->syscall_names); |
| pi->syscall_names = 0; |
| } |
| } |
| |
| /* Given a name, look up (and return) the corresponding syscall number. |
| If no match is found, return -1. */ |
| |
| static int |
| find_syscall (procinfo *pi, char *name) |
| { |
| int i; |
| |
| for (i = 0; i < pi->num_syscalls; i++) |
| { |
| if (pi->syscall_names[i] && strcmp (name, pi->syscall_names[i]) == 0) |
| return i; |
| } |
| return -1; |
| } |
| #endif |
| |
| /* =================== END, STRUCT PROCINFO "MODULE" =================== */ |
| |
| /* =================== /proc "MODULE" =================== */ |
| |
| /* This "module" is the interface layer between the /proc system API |
| and the gdb target vector functions. This layer consists of access |
| functions that encapsulate each of the basic operations that we |
| need to use from the /proc API. |
| |
| The main motivation for this layer is to hide the fact that there |
| are two very different implementations of the /proc API. Rather |
| than have a bunch of #ifdefs all thru the gdb target vector |
| functions, we do our best to hide them all in here. */ |
| |
| static long proc_flags (procinfo * pi); |
| static int proc_why (procinfo * pi); |
| static int proc_what (procinfo * pi); |
| static int proc_set_current_signal (procinfo * pi, int signo); |
| static int proc_get_current_thread (procinfo * pi); |
| static int proc_iterate_over_threads |
| (procinfo * pi, |
| int (*func) (procinfo *, procinfo *, void *), |
| void *ptr); |
| |
| static void |
| proc_warn (procinfo *pi, char *func, int line) |
| { |
| sprintf (errmsg, "procfs: %s line %d, %s", func, line, pi->pathname); |
| print_sys_errmsg (errmsg, errno); |
| } |
| |
| static void |
| proc_error (procinfo *pi, char *func, int line) |
| { |
| sprintf (errmsg, "procfs: %s line %d, %s", func, line, pi->pathname); |
| perror_with_name (errmsg); |
| } |
| |
| /* Updates the status struct in the procinfo. There is a 'valid' |
| flag, to let other functions know when this function needs to be |
| called (so the status is only read when it is needed). The status |
| file descriptor is also only opened when it is needed. Returns |
| non-zero for success, zero for failure. */ |
| |
| static int |
| proc_get_status (procinfo *pi) |
| { |
| /* Status file descriptor is opened "lazily". */ |
| if (pi->status_fd == 0 && |
| open_procinfo_files (pi, FD_STATUS) == 0) |
| { |
| pi->status_valid = 0; |
| return 0; |
| } |
| |
| #ifdef NEW_PROC_API |
| if (lseek (pi->status_fd, 0, SEEK_SET) < 0) |
| pi->status_valid = 0; /* fail */ |
| else |
| { |
| /* Sigh... I have to read a different data structure, |
| depending on whether this is a main process or an LWP. */ |
| if (pi->tid) |
| pi->status_valid = (read (pi->status_fd, |
| (char *) &pi->prstatus.pr_lwp, |
| sizeof (lwpstatus_t)) |
| == sizeof (lwpstatus_t)); |
| else |
| { |
| pi->status_valid = (read (pi->status_fd, |
| (char *) &pi->prstatus, |
| sizeof (gdb_prstatus_t)) |
| == sizeof (gdb_prstatus_t)); |
| #if 0 /*def UNIXWARE*/ |
| if (pi->status_valid && |
| (pi->prstatus.pr_lwp.pr_flags & PR_ISTOP) && |
| pi->prstatus.pr_lwp.pr_why == PR_REQUESTED) |
| /* Unixware peculiarity -- read the damn thing again! */ |
| pi->status_valid = (read (pi->status_fd, |
| (char *) &pi->prstatus, |
| sizeof (gdb_prstatus_t)) |
| == sizeof (gdb_prstatus_t)); |
| #endif /* UNIXWARE */ |
| } |
| } |
| #else /* ioctl method */ |
| #ifdef PIOCTSTATUS /* osf */ |
| if (pi->tid == 0) /* main process */ |
| { |
| /* Just read the danged status. Now isn't that simple? */ |
| pi->status_valid = |
| (ioctl (pi->status_fd, PIOCSTATUS, &pi->prstatus) >= 0); |
| } |
| else |
| { |
| int win; |
| struct { |
| long pr_count; |
| tid_t pr_error_thread; |
| struct prstatus status; |
| } thread_status; |
| |
| thread_status.pr_count = 1; |
| thread_status.status.pr_tid = pi->tid; |
| win = (ioctl (pi->status_fd, PIOCTSTATUS, &thread_status) >= 0); |
| if (win) |
| { |
| memcpy (&pi->prstatus, &thread_status.status, |
| sizeof (pi->prstatus)); |
| pi->status_valid = 1; |
| } |
| } |
| #else |
| /* Just read the danged status. Now isn't that simple? */ |
| pi->status_valid = (ioctl (pi->status_fd, PIOCSTATUS, &pi->prstatus) >= 0); |
| #endif |
| #endif |
| |
| if (pi->status_valid) |
| { |
| PROC_PRETTYFPRINT_STATUS (proc_flags (pi), |
| proc_why (pi), |
| proc_what (pi), |
| proc_get_current_thread (pi)); |
| } |
| |
| /* The status struct includes general regs, so mark them valid too. */ |
| pi->gregs_valid = pi->status_valid; |
| #ifdef NEW_PROC_API |
| /* In the read/write multiple-fd model, the status struct includes |
| the fp regs too, so mark them valid too. */ |
| pi->fpregs_valid = pi->status_valid; |
| #endif |
| return pi->status_valid; /* True if success, false if failure. */ |
| } |
| |
| /* Returns the process flags (pr_flags field). */ |
| |
| static long |
| proc_flags (procinfo *pi) |
| { |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return 0; /* FIXME: not a good failure value (but what is?) */ |
| |
| #ifdef NEW_PROC_API |
| # ifdef UNIXWARE |
| /* UnixWare 7.1 puts process status flags, e.g. PR_ASYNC, in |
| pstatus_t and LWP status flags, e.g. PR_STOPPED, in lwpstatus_t. |
| The two sets of flags don't overlap. */ |
| return pi->prstatus.pr_flags | pi->prstatus.pr_lwp.pr_flags; |
| # else |
| return pi->prstatus.pr_lwp.pr_flags; |
| # endif |
| #else |
| return pi->prstatus.pr_flags; |
| #endif |
| } |
| |
| /* Returns the pr_why field (why the process stopped). */ |
| |
| static int |
| proc_why (procinfo *pi) |
| { |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return 0; /* FIXME: not a good failure value (but what is?) */ |
| |
| #ifdef NEW_PROC_API |
| return pi->prstatus.pr_lwp.pr_why; |
| #else |
| return pi->prstatus.pr_why; |
| #endif |
| } |
| |
| /* Returns the pr_what field (details of why the process stopped). */ |
| |
| static int |
| proc_what (procinfo *pi) |
| { |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return 0; /* FIXME: not a good failure value (but what is?) */ |
| |
| #ifdef NEW_PROC_API |
| return pi->prstatus.pr_lwp.pr_what; |
| #else |
| return pi->prstatus.pr_what; |
| #endif |
| } |
| |
| /* This function is only called when PI is stopped by a watchpoint. |
| Assuming the OS supports it, write to *ADDR the data address which |
| triggered it and return 1. Return 0 if it is not possible to know |
| the address. */ |
| |
| static int |
| proc_watchpoint_address (procinfo *pi, CORE_ADDR *addr) |
| { |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return 0; |
| |
| #ifdef NEW_PROC_API |
| *addr = (CORE_ADDR) gdbarch_pointer_to_address (target_gdbarch, |
| builtin_type (target_gdbarch)->builtin_data_ptr, |
| (gdb_byte *) &pi->prstatus.pr_lwp.pr_info.si_addr); |
| #else |
| *addr = (CORE_ADDR) gdbarch_pointer_to_address (target_gdbarch, |
| builtin_type (target_gdbarch)->builtin_data_ptr, |
| (gdb_byte *) &pi->prstatus.pr_info.si_addr); |
| #endif |
| return 1; |
| } |
| |
| #ifndef PIOCSSPCACT /* The following is not supported on OSF. */ |
| |
| /* Returns the pr_nsysarg field (number of args to the current |
| syscall). */ |
| |
| static int |
| proc_nsysarg (procinfo *pi) |
| { |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return 0; |
| |
| #ifdef NEW_PROC_API |
| return pi->prstatus.pr_lwp.pr_nsysarg; |
| #else |
| return pi->prstatus.pr_nsysarg; |
| #endif |
| } |
| |
| /* Returns the pr_sysarg field (pointer to the arguments of current |
| syscall). */ |
| |
| static long * |
| proc_sysargs (procinfo *pi) |
| { |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return NULL; |
| |
| #ifdef NEW_PROC_API |
| return (long *) &pi->prstatus.pr_lwp.pr_sysarg; |
| #else |
| return (long *) &pi->prstatus.pr_sysarg; |
| #endif |
| } |
| #endif /* PIOCSSPCACT */ |
| |
| #ifdef PROCFS_DONT_PIOCSSIG_CURSIG |
| /* Returns the pr_cursig field (current signal). */ |
| |
| static long |
| proc_cursig (struct procinfo *pi) |
| { |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return 0; /* FIXME: not a good failure value (but what is?) */ |
| |
| #ifdef NEW_PROC_API |
| return pi->prstatus.pr_lwp.pr_cursig; |
| #else |
| return pi->prstatus.pr_cursig; |
| #endif |
| } |
| #endif /* PROCFS_DONT_PIOCSSIG_CURSIG */ |
| |
| /* === I appologize for the messiness of this function. |
| === This is an area where the different versions of |
| === /proc are more inconsistent than usual. |
| |
| Set or reset any of the following process flags: |
| PR_FORK -- forked child will inherit trace flags |
| PR_RLC -- traced process runs when last /proc file closed. |
| PR_KLC -- traced process is killed when last /proc file closed. |
| PR_ASYNC -- LWP's get to run/stop independently. |
| |
| There are three methods for doing this function: |
| 1) Newest: read/write [PCSET/PCRESET/PCUNSET] |
| [Sol6, Sol7, UW] |
| 2) Middle: PIOCSET/PIOCRESET |
| [Irix, Sol5] |
| 3) Oldest: PIOCSFORK/PIOCRFORK/PIOCSRLC/PIOCRRLC |
| [OSF, Sol5] |
| |
| Note: Irix does not define PR_ASYNC. |
| Note: OSF does not define PR_KLC. |
| Note: OSF is the only one that can ONLY use the oldest method. |
| |
| Arguments: |
| pi -- the procinfo |
| flag -- one of PR_FORK, PR_RLC, or PR_ASYNC |
| mode -- 1 for set, 0 for reset. |
| |
| Returns non-zero for success, zero for failure. */ |
| |
| enum { FLAG_RESET, FLAG_SET }; |
| |
| static int |
| proc_modify_flag (procinfo *pi, long flag, long mode) |
| { |
| long win = 0; /* default to fail */ |
| |
| /* These operations affect the process as a whole, and applying them |
| to an individual LWP has the same meaning as applying them to the |
| main process. Therefore, if we're ever called with a pointer to |
| an LWP's procinfo, let's substitute the process's procinfo and |
| avoid opening the LWP's file descriptor unnecessarily. */ |
| |
| if (pi->pid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API /* Newest method: UnixWare and newer Solarii. */ |
| /* First normalize the PCUNSET/PCRESET command opcode |
| (which for no obvious reason has a different definition |
| from one operating system to the next...) */ |
| #ifdef PCUNSET |
| #define GDBRESET PCUNSET |
| #else |
| #ifdef PCRESET |
| #define GDBRESET PCRESET |
| #endif |
| #endif |
| { |
| procfs_ctl_t arg[2]; |
| |
| if (mode == FLAG_SET) /* Set the flag (RLC, FORK, or ASYNC). */ |
| arg[0] = PCSET; |
| else /* Reset the flag. */ |
| arg[0] = GDBRESET; |
| |
| arg[1] = flag; |
| win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); |
| } |
| #else |
| #ifdef PIOCSET /* Irix/Sol5 method */ |
| if (mode == FLAG_SET) /* Set the flag (hopefully RLC, FORK, or ASYNC). */ |
| { |
| win = (ioctl (pi->ctl_fd, PIOCSET, &flag) >= 0); |
| } |
| else /* Reset the flag. */ |
| { |
| win = (ioctl (pi->ctl_fd, PIOCRESET, &flag) >= 0); |
| } |
| |
| #else |
| #ifdef PIOCSRLC /* Oldest method: OSF */ |
| switch (flag) { |
| case PR_RLC: |
| if (mode == FLAG_SET) /* Set run-on-last-close */ |
| { |
| win = (ioctl (pi->ctl_fd, PIOCSRLC, NULL) >= 0); |
| } |
| else /* Clear run-on-last-close */ |
| { |
| win = (ioctl (pi->ctl_fd, PIOCRRLC, NULL) >= 0); |
| } |
| break; |
| case PR_FORK: |
| if (mode == FLAG_SET) /* Set inherit-on-fork */ |
| { |
| win = (ioctl (pi->ctl_fd, PIOCSFORK, NULL) >= 0); |
| } |
| else /* Clear inherit-on-fork */ |
| { |
| win = (ioctl (pi->ctl_fd, PIOCRFORK, NULL) >= 0); |
| } |
| break; |
| default: |
| win = 0; /* Fail -- unknown flag (can't do PR_ASYNC). */ |
| break; |
| } |
| #endif |
| #endif |
| #endif |
| #undef GDBRESET |
| /* The above operation renders the procinfo's cached pstatus |
| obsolete. */ |
| pi->status_valid = 0; |
| |
| if (!win) |
| warning (_("procfs: modify_flag failed to turn %s %s"), |
| flag == PR_FORK ? "PR_FORK" : |
| flag == PR_RLC ? "PR_RLC" : |
| #ifdef PR_ASYNC |
| flag == PR_ASYNC ? "PR_ASYNC" : |
| #endif |
| #ifdef PR_KLC |
| flag == PR_KLC ? "PR_KLC" : |
| #endif |
| "<unknown flag>", |
| mode == FLAG_RESET ? "off" : "on"); |
| |
| return win; |
| } |
| |
| /* Set the run_on_last_close flag. Process with all threads will |
| become runnable when debugger closes all /proc fds. Returns |
| non-zero for success, zero for failure. */ |
| |
| static int |
| proc_set_run_on_last_close (procinfo *pi) |
| { |
| return proc_modify_flag (pi, PR_RLC, FLAG_SET); |
| } |
| |
| /* Reset the run_on_last_close flag. The process will NOT become |
| runnable when debugger closes its file handles. Returns non-zero |
| for success, zero for failure. */ |
| |
| static int |
| proc_unset_run_on_last_close (procinfo *pi) |
| { |
| return proc_modify_flag (pi, PR_RLC, FLAG_RESET); |
| } |
| |
| /* Reset inherit_on_fork flag. If the process forks a child while we |
| are registered for events in the parent, then we will NOT recieve |
| events from the child. Returns non-zero for success, zero for |
| failure. */ |
| |
| static int |
| proc_unset_inherit_on_fork (procinfo *pi) |
| { |
| return proc_modify_flag (pi, PR_FORK, FLAG_RESET); |
| } |
| |
| #ifdef PR_ASYNC |
| /* Set PR_ASYNC flag. If one LWP stops because of a debug event |
| (signal etc.), the remaining LWPs will continue to run. Returns |
| non-zero for success, zero for failure. */ |
| |
| static int |
| proc_set_async (procinfo *pi) |
| { |
| return proc_modify_flag (pi, PR_ASYNC, FLAG_SET); |
| } |
| |
| /* Reset PR_ASYNC flag. If one LWP stops because of a debug event |
| (signal etc.), then all other LWPs will stop as well. Returns |
| non-zero for success, zero for failure. */ |
| |
| static int |
| proc_unset_async (procinfo *pi) |
| { |
| return proc_modify_flag (pi, PR_ASYNC, FLAG_RESET); |
| } |
| #endif /* PR_ASYNC */ |
| |
| /* Request the process/LWP to stop. Does not wait. Returns non-zero |
| for success, zero for failure. */ |
| |
| static int |
| proc_stop_process (procinfo *pi) |
| { |
| int win; |
| |
| /* We might conceivably apply this operation to an LWP, and the |
| LWP's ctl file descriptor might not be open. */ |
| |
| if (pi->ctl_fd == 0 && |
| open_procinfo_files (pi, FD_CTL) == 0) |
| return 0; |
| else |
| { |
| #ifdef NEW_PROC_API |
| procfs_ctl_t cmd = PCSTOP; |
| |
| win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd)); |
| #else /* ioctl method */ |
| win = (ioctl (pi->ctl_fd, PIOCSTOP, &pi->prstatus) >= 0); |
| /* Note: the call also reads the prstatus. */ |
| if (win) |
| { |
| pi->status_valid = 1; |
| PROC_PRETTYFPRINT_STATUS (proc_flags (pi), |
| proc_why (pi), |
| proc_what (pi), |
| proc_get_current_thread (pi)); |
| } |
| #endif |
| } |
| |
| return win; |
| } |
| |
| /* Wait for the process or LWP to stop (block until it does). Returns |
| non-zero for success, zero for failure. */ |
| |
| static int |
| proc_wait_for_stop (procinfo *pi) |
| { |
| int win; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| { |
| procfs_ctl_t cmd = PCWSTOP; |
| |
| win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd)); |
| /* We been runnin' and we stopped -- need to update status. */ |
| pi->status_valid = 0; |
| } |
| #else /* ioctl method */ |
| win = (ioctl (pi->ctl_fd, PIOCWSTOP, &pi->prstatus) >= 0); |
| /* Above call also refreshes the prstatus. */ |
| if (win) |
| { |
| pi->status_valid = 1; |
| PROC_PRETTYFPRINT_STATUS (proc_flags (pi), |
| proc_why (pi), |
| proc_what (pi), |
| proc_get_current_thread (pi)); |
| } |
| #endif |
| |
| return win; |
| } |
| |
| /* Make the process or LWP runnable. |
| |
| Options (not all are implemented): |
| - single-step |
| - clear current fault |
| - clear current signal |
| - abort the current system call |
| - stop as soon as finished with system call |
| - (ioctl): set traced signal set |
| - (ioctl): set held signal set |
| - (ioctl): set traced fault set |
| - (ioctl): set start pc (vaddr) |
| |
| Always clears the current fault. PI is the process or LWP to |
| operate on. If STEP is true, set the process or LWP to trap after |
| one instruction. If SIGNO is zero, clear the current signal if |
| any; if non-zero, set the current signal to this one. Returns |
| non-zero for success, zero for failure. */ |
| |
| static int |
| proc_run_process (procinfo *pi, int step, int signo) |
| { |
| int win; |
| int runflags; |
| |
| /* We will probably have to apply this operation to individual |
| threads, so make sure the control file descriptor is open. */ |
| |
| if (pi->ctl_fd == 0 && |
| open_procinfo_files (pi, FD_CTL) == 0) |
| { |
| return 0; |
| } |
| |
| runflags = PRCFAULT; /* Always clear current fault. */ |
| if (step) |
| runflags |= PRSTEP; |
| if (signo == 0) |
| runflags |= PRCSIG; |
| else if (signo != -1) /* -1 means do nothing W.R.T. signals. */ |
| proc_set_current_signal (pi, signo); |
| |
| #ifdef NEW_PROC_API |
| { |
| procfs_ctl_t cmd[2]; |
| |
| cmd[0] = PCRUN; |
| cmd[1] = runflags; |
| win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd)); |
| } |
| #else /* ioctl method */ |
| { |
| prrun_t prrun; |
| |
| memset (&prrun, 0, sizeof (prrun)); |
| prrun.pr_flags = runflags; |
| win = (ioctl (pi->ctl_fd, PIOCRUN, &prrun) >= 0); |
| } |
| #endif |
| |
| return win; |
| } |
| |
| /* Register to trace signals in the process or LWP. Returns non-zero |
| for success, zero for failure. */ |
| |
| static int |
| proc_set_traced_signals (procinfo *pi, gdb_sigset_t *sigset) |
| { |
| int win; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| { |
| struct { |
| procfs_ctl_t cmd; |
| /* Use char array to avoid alignment issues. */ |
| char sigset[sizeof (gdb_sigset_t)]; |
| } arg; |
| |
| arg.cmd = PCSTRACE; |
| memcpy (&arg.sigset, sigset, sizeof (gdb_sigset_t)); |
| |
| win = (write (pi->ctl_fd, (char *) &arg, sizeof (arg)) == sizeof (arg)); |
| } |
| #else /* ioctl method */ |
| win = (ioctl (pi->ctl_fd, PIOCSTRACE, sigset) >= 0); |
| #endif |
| /* The above operation renders the procinfo's cached pstatus obsolete. */ |
| pi->status_valid = 0; |
| |
| if (!win) |
| warning (_("procfs: set_traced_signals failed")); |
| return win; |
| } |
| |
| /* Register to trace hardware faults in the process or LWP. Returns |
| non-zero for success, zero for failure. */ |
| |
| static int |
| proc_set_traced_faults (procinfo *pi, fltset_t *fltset) |
| { |
| int win; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| { |
| struct { |
| procfs_ctl_t cmd; |
| /* Use char array to avoid alignment issues. */ |
| char fltset[sizeof (fltset_t)]; |
| } arg; |
| |
| arg.cmd = PCSFAULT; |
| memcpy (&arg.fltset, fltset, sizeof (fltset_t)); |
| |
| win = (write (pi->ctl_fd, (char *) &arg, sizeof (arg)) == sizeof (arg)); |
| } |
| #else /* ioctl method */ |
| win = (ioctl (pi->ctl_fd, PIOCSFAULT, fltset) >= 0); |
| #endif |
| /* The above operation renders the procinfo's cached pstatus obsolete. */ |
| pi->status_valid = 0; |
| |
| return win; |
| } |
| |
| /* Register to trace entry to system calls in the process or LWP. |
| Returns non-zero for success, zero for failure. */ |
| |
| static int |
| proc_set_traced_sysentry (procinfo *pi, sysset_t *sysset) |
| { |
| int win; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| { |
| struct gdb_proc_ctl_pcsentry { |
| procfs_ctl_t cmd; |
| /* Use char array to avoid alignment issues. */ |
| char sysset[sizeof (sysset_t)]; |
| } *argp; |
| int argp_size = sizeof (struct gdb_proc_ctl_pcsentry) |
| - sizeof (sysset_t) |
| + sysset_t_size (pi); |
| |
| argp = xmalloc (argp_size); |
| |
| argp->cmd = PCSENTRY; |
| memcpy (&argp->sysset, sysset, sysset_t_size (pi)); |
| |
| win = (write (pi->ctl_fd, (char *) argp, argp_size) == argp_size); |
| xfree (argp); |
| } |
| #else /* ioctl method */ |
| win = (ioctl (pi->ctl_fd, PIOCSENTRY, sysset) >= 0); |
| #endif |
| /* The above operation renders the procinfo's cached pstatus |
| obsolete. */ |
| pi->status_valid = 0; |
| |
| return win; |
| } |
| |
| /* Register to trace exit from system calls in the process or LWP. |
| Returns non-zero for success, zero for failure. */ |
| |
| static int |
| proc_set_traced_sysexit (procinfo *pi, sysset_t *sysset) |
| { |
| int win; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| { |
| struct gdb_proc_ctl_pcsexit { |
| procfs_ctl_t cmd; |
| /* Use char array to avoid alignment issues. */ |
| char sysset[sizeof (sysset_t)]; |
| } *argp; |
| int argp_size = sizeof (struct gdb_proc_ctl_pcsexit) |
| - sizeof (sysset_t) |
| + sysset_t_size (pi); |
| |
| argp = xmalloc (argp_size); |
| |
| argp->cmd = PCSEXIT; |
| memcpy (&argp->sysset, sysset, sysset_t_size (pi)); |
| |
| win = (write (pi->ctl_fd, (char *) argp, argp_size) == argp_size); |
| xfree (argp); |
| } |
| #else /* ioctl method */ |
| win = (ioctl (pi->ctl_fd, PIOCSEXIT, sysset) >= 0); |
| #endif |
| /* The above operation renders the procinfo's cached pstatus |
| obsolete. */ |
| pi->status_valid = 0; |
| |
| return win; |
| } |
| |
| /* Specify the set of blocked / held signals in the process or LWP. |
| Returns non-zero for success, zero for failure. */ |
| |
| static int |
| proc_set_held_signals (procinfo *pi, gdb_sigset_t *sighold) |
| { |
| int win; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| { |
| struct { |
| procfs_ctl_t cmd; |
| /* Use char array to avoid alignment issues. */ |
| char hold[sizeof (gdb_sigset_t)]; |
| } arg; |
| |
| arg.cmd = PCSHOLD; |
| memcpy (&arg.hold, sighold, sizeof (gdb_sigset_t)); |
| win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); |
| } |
| #else |
| win = (ioctl (pi->ctl_fd, PIOCSHOLD, sighold) >= 0); |
| #endif |
| /* The above operation renders the procinfo's cached pstatus |
| obsolete. */ |
| pi->status_valid = 0; |
| |
| return win; |
| } |
| |
| /* Returns the set of signals that are held / blocked. Will also copy |
| the sigset if SAVE is non-zero. */ |
| |
| static gdb_sigset_t * |
| proc_get_held_signals (procinfo *pi, gdb_sigset_t *save) |
| { |
| gdb_sigset_t *ret = NULL; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return NULL; |
| |
| #ifdef UNIXWARE |
| ret = &pi->prstatus.pr_lwp.pr_context.uc_sigmask; |
| #else |
| ret = &pi->prstatus.pr_lwp.pr_lwphold; |
| #endif /* UNIXWARE */ |
| #else /* not NEW_PROC_API */ |
| { |
| static gdb_sigset_t sigheld; |
| |
| if (ioctl (pi->ctl_fd, PIOCGHOLD, &sigheld) >= 0) |
| ret = &sigheld; |
| } |
| #endif /* NEW_PROC_API */ |
| if (save && ret) |
| memcpy (save, ret, sizeof (gdb_sigset_t)); |
| |
| return ret; |
| } |
| |
| /* Returns the set of signals that are traced / debugged. Will also |
| copy the sigset if SAVE is non-zero. */ |
| |
| static gdb_sigset_t * |
| proc_get_traced_signals (procinfo *pi, gdb_sigset_t *save) |
| { |
| gdb_sigset_t *ret = NULL; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return NULL; |
| |
| ret = &pi->prstatus.pr_sigtrace; |
| #else |
| { |
| static gdb_sigset_t sigtrace; |
| |
| if (ioctl (pi->ctl_fd, PIOCGTRACE, &sigtrace) >= 0) |
| ret = &sigtrace; |
| } |
| #endif |
| if (save && ret) |
| memcpy (save, ret, sizeof (gdb_sigset_t)); |
| |
| return ret; |
| } |
| |
| /* Returns the set of hardware faults that are traced /debugged. Will |
| also copy the faultset if SAVE is non-zero. */ |
| |
| static fltset_t * |
| proc_get_traced_faults (procinfo *pi, fltset_t *save) |
| { |
| fltset_t *ret = NULL; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return NULL; |
| |
| ret = &pi->prstatus.pr_flttrace; |
| #else |
| { |
| static fltset_t flttrace; |
| |
| if (ioctl (pi->ctl_fd, PIOCGFAULT, &flttrace) >= 0) |
| ret = &flttrace; |
| } |
| #endif |
| if (save && ret) |
| memcpy (save, ret, sizeof (fltset_t)); |
| |
| return ret; |
| } |
| |
| /* Returns the set of syscalls that are traced /debugged on entry. |
| Will also copy the syscall set if SAVE is non-zero. */ |
| |
| static sysset_t * |
| proc_get_traced_sysentry (procinfo *pi, sysset_t *save) |
| { |
| sysset_t *ret = NULL; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return NULL; |
| |
| #ifndef DYNAMIC_SYSCALLS |
| ret = &pi->prstatus.pr_sysentry; |
| #else /* DYNAMIC_SYSCALLS */ |
| { |
| static sysset_t *sysentry; |
| size_t size; |
| |
| if (!sysentry) |
| sysentry = sysset_t_alloc (pi); |
| ret = sysentry; |
| if (pi->status_fd == 0 && open_procinfo_files (pi, FD_STATUS) == 0) |
| return NULL; |
| if (pi->prstatus.pr_sysentry_offset == 0) |
| { |
| gdb_premptysysset (sysentry); |
| } |
| else |
| { |
| int rsize; |
| |
| if (lseek (pi->status_fd, (off_t) pi->prstatus.pr_sysentry_offset, |
| SEEK_SET) |
| != (off_t) pi->prstatus.pr_sysentry_offset) |
| return NULL; |
| size = sysset_t_size (pi); |
| gdb_premptysysset (sysentry); |
| rsize = read (pi->status_fd, sysentry, size); |
| if (rsize < 0) |
| return NULL; |
| } |
| } |
| #endif /* DYNAMIC_SYSCALLS */ |
| #else /* !NEW_PROC_API */ |
| { |
| static sysset_t sysentry; |
| |
| if (ioctl (pi->ctl_fd, PIOCGENTRY, &sysentry) >= 0) |
| ret = &sysentry; |
| } |
| #endif /* NEW_PROC_API */ |
| if (save && ret) |
| memcpy (save, ret, sysset_t_size (pi)); |
| |
| return ret; |
| } |
| |
| /* Returns the set of syscalls that are traced /debugged on exit. |
| Will also copy the syscall set if SAVE is non-zero. */ |
| |
| static sysset_t * |
| proc_get_traced_sysexit (procinfo *pi, sysset_t *save) |
| { |
| sysset_t * ret = NULL; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return NULL; |
| |
| #ifndef DYNAMIC_SYSCALLS |
| ret = &pi->prstatus.pr_sysexit; |
| #else /* DYNAMIC_SYSCALLS */ |
| { |
| static sysset_t *sysexit; |
| size_t size; |
| |
| if (!sysexit) |
| sysexit = sysset_t_alloc (pi); |
| ret = sysexit; |
| if (pi->status_fd == 0 && open_procinfo_files (pi, FD_STATUS) == 0) |
| return NULL; |
| if (pi->prstatus.pr_sysexit_offset == 0) |
| { |
| gdb_premptysysset (sysexit); |
| } |
| else |
| { |
| int rsize; |
| |
| if (lseek (pi->status_fd, (off_t) pi->prstatus.pr_sysexit_offset, |
| SEEK_SET) |
| != (off_t) pi->prstatus.pr_sysexit_offset) |
| return NULL; |
| size = sysset_t_size (pi); |
| gdb_premptysysset (sysexit); |
| rsize = read (pi->status_fd, sysexit, size); |
| if (rsize < 0) |
| return NULL; |
| } |
| } |
| #endif /* DYNAMIC_SYSCALLS */ |
| #else |
| { |
| static sysset_t sysexit; |
| |
| if (ioctl (pi->ctl_fd, PIOCGEXIT, &sysexit) >= 0) |
| ret = &sysexit; |
| } |
| #endif |
| if (save && ret) |
| memcpy (save, ret, sysset_t_size (pi)); |
| |
| return ret; |
| } |
| |
| /* The current fault (if any) is cleared; the associated signal will |
| not be sent to the process or LWP when it resumes. Returns |
| non-zero for success, zero for failure. */ |
| |
| static int |
| proc_clear_current_fault (procinfo *pi) |
| { |
| int win; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| { |
| procfs_ctl_t cmd = PCCFAULT; |
| |
| win = (write (pi->ctl_fd, (void *) &cmd, sizeof (cmd)) == sizeof (cmd)); |
| } |
| #else |
| win = (ioctl (pi->ctl_fd, PIOCCFAULT, 0) >= 0); |
| #endif |
| |
| return win; |
| } |
| |
| /* Set the "current signal" that will be delivered next to the |
| process. NOTE: semantics are different from those of KILL. This |
| signal will be delivered to the process or LWP immediately when it |
| is resumed (even if the signal is held/blocked); it will NOT |
| immediately cause another event of interest, and will NOT first |
| trap back to the debugger. Returns non-zero for success, zero for |
| failure. */ |
| |
| static int |
| proc_set_current_signal (procinfo *pi, int signo) |
| { |
| int win; |
| struct { |
| procfs_ctl_t cmd; |
| /* Use char array to avoid alignment issues. */ |
| char sinfo[sizeof (gdb_siginfo_t)]; |
| } arg; |
| gdb_siginfo_t mysinfo; |
| ptid_t wait_ptid; |
| struct target_waitstatus wait_status; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #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 > 0 && |
| signo == proc_cursig (pi)) |
| return 1; /* I assume this is a success? */ |
| #endif |
| |
| /* The pointer is just a type alias. */ |
| get_last_target_status (&wait_ptid, &wait_status); |
| if (ptid_equal (wait_ptid, inferior_ptid) |
| && wait_status.kind == TARGET_WAITKIND_STOPPED |
| && wait_status.value.sig == gdb_signal_from_host (signo) |
| && proc_get_status (pi) |
| #ifdef NEW_PROC_API |
| && pi->prstatus.pr_lwp.pr_info.si_signo == signo |
| #else |
| && pi->prstatus.pr_info.si_signo == signo |
| #endif |
| ) |
| /* Use the siginfo associated with the signal being |
| redelivered. */ |
| #ifdef NEW_PROC_API |
| memcpy (arg.sinfo, &pi->prstatus.pr_lwp.pr_info, sizeof (gdb_siginfo_t)); |
| #else |
| memcpy (arg.sinfo, &pi->prstatus.pr_info, sizeof (gdb_siginfo_t)); |
| #endif |
| else |
| { |
| mysinfo.si_signo = signo; |
| mysinfo.si_code = 0; |
| mysinfo.si_pid = getpid (); /* ?why? */ |
| mysinfo.si_uid = getuid (); /* ?why? */ |
| memcpy (arg.sinfo, &mysinfo, sizeof (gdb_siginfo_t)); |
| } |
| |
| #ifdef NEW_PROC_API |
| arg.cmd = PCSSIG; |
| win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); |
| #else |
| win = (ioctl (pi->ctl_fd, PIOCSSIG, (void *) &arg.sinfo) >= 0); |
| #endif |
| |
| return win; |
| } |
| |
| /* The current signal (if any) is cleared, and is not sent to the |
| process or LWP when it resumes. Returns non-zero for success, zero |
| for failure. */ |
| |
| static int |
| proc_clear_current_signal (procinfo *pi) |
| { |
| int win; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| #ifdef NEW_PROC_API |
| { |
| struct { |
| procfs_ctl_t cmd; |
| /* Use char array to avoid alignment issues. */ |
| char sinfo[sizeof (gdb_siginfo_t)]; |
| } arg; |
| gdb_siginfo_t mysinfo; |
| |
| arg.cmd = PCSSIG; |
| /* The pointer is just a type alias. */ |
| mysinfo.si_signo = 0; |
| mysinfo.si_code = 0; |
| mysinfo.si_errno = 0; |
| mysinfo.si_pid = getpid (); /* ?why? */ |
| mysinfo.si_uid = getuid (); /* ?why? */ |
| memcpy (arg.sinfo, &mysinfo, sizeof (gdb_siginfo_t)); |
| |
| win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); |
| } |
| #else |
| win = (ioctl (pi->ctl_fd, PIOCSSIG, 0) >= 0); |
| #endif |
| |
| return win; |
| } |
| |
| /* Return the general-purpose registers for the process or LWP |
| corresponding to PI. Upon failure, return NULL. */ |
| |
| static gdb_gregset_t * |
| proc_get_gregs (procinfo *pi) |
| { |
| if (!pi->status_valid || !pi->gregs_valid) |
| if (!proc_get_status (pi)) |
| return NULL; |
| |
| /* OK, sorry about the ifdef's. There's three cases instead of two, |
| because in this case Unixware and Solaris/RW differ. */ |
| |
| #ifdef NEW_PROC_API |
| # ifdef UNIXWARE /* FIXME: Should be autoconfigured. */ |
| return &pi->prstatus.pr_lwp.pr_context.uc_mcontext.gregs; |
| # else |
| return &pi->prstatus.pr_lwp.pr_reg; |
| # endif |
| #else |
| return &pi->prstatus.pr_reg; |
| #endif |
| } |
| |
| /* Return the general-purpose registers for the process or LWP |
| corresponding to PI. Upon failure, return NULL. */ |
| |
| static gdb_fpregset_t * |
| proc_get_fpregs (procinfo *pi) |
| { |
| #ifdef NEW_PROC_API |
| if (!pi->status_valid || !pi->fpregs_valid) |
| if (!proc_get_status (pi)) |
| return NULL; |
| |
| # ifdef UNIXWARE /* FIXME: Should be autoconfigured. */ |
| return &pi->prstatus.pr_lwp.pr_context.uc_mcontext.fpregs; |
| # else |
| return &pi->prstatus.pr_lwp.pr_fpreg; |
| # endif |
| |
| #else /* not NEW_PROC_API */ |
| if (pi->fpregs_valid) |
| return &pi->fpregset; /* Already got 'em. */ |
| else |
| { |
| if (pi->ctl_fd == 0 && open_procinfo_files (pi, FD_CTL) == 0) |
| { |
| return NULL; |
| } |
| else |
| { |
| # ifdef PIOCTGFPREG |
| struct { |
| long pr_count; |
| tid_t pr_error_thread; |
| tfpregset_t thread_1; |
| } thread_fpregs; |
| |
| thread_fpregs.pr_count = 1; |
| thread_fpregs.thread_1.tid = pi->tid; |
| |
| if (pi->tid == 0 |
| && ioctl (pi->ctl_fd, PIOCGFPREG, &pi->fpregset) >= 0) |
| { |
| pi->fpregs_valid = 1; |
| return &pi->fpregset; /* Got 'em now! */ |
| } |
| else if (pi->tid != 0 |
| && ioctl (pi->ctl_fd, PIOCTGFPREG, &thread_fpregs) >= 0) |
| { |
| memcpy (&pi->fpregset, &thread_fpregs.thread_1.pr_fpregs, |
| sizeof (pi->fpregset)); |
| pi->fpregs_valid = 1; |
| return &pi->fpregset; /* Got 'em now! */ |
| } |
| else |
| { |
| return NULL; |
| } |
| # else |
| if (ioctl (pi->ctl_fd, PIOCGFPREG, &pi->fpregset) >= 0) |
| { |
| pi->fpregs_valid = 1; |
| return &pi->fpregset; /* Got 'em now! */ |
| } |
| else |
| { |
| return NULL; |
| } |
| # endif |
| } |
| } |
| #endif /* NEW_PROC_API */ |
| } |
| |
| /* Write the general-purpose registers back to the process or LWP |
| corresponding to PI. Return non-zero for success, zero for |
| failure. */ |
| |
| static int |
| proc_set_gregs (procinfo *pi) |
| { |
| gdb_gregset_t *gregs; |
| int win; |
| |
| gregs = proc_get_gregs (pi); |
| if (gregs == NULL) |
| return 0; /* proc_get_regs has already warned. */ |
| |
| if (pi->ctl_fd == 0 && open_procinfo_files (pi, FD_CTL) == 0) |
| { |
| return 0; |
| } |
| else |
| { |
| #ifdef NEW_PROC_API |
| struct { |
| procfs_ctl_t cmd; |
| /* Use char array to avoid alignment issues. */ |
| char gregs[sizeof (gdb_gregset_t)]; |
| } arg; |
| |
| arg.cmd = PCSREG; |
| memcpy (&arg.gregs, gregs, sizeof (arg.gregs)); |
| win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); |
| #else |
| win = (ioctl (pi->ctl_fd, PIOCSREG, gregs) >= 0); |
| #endif |
| } |
| |
| /* Policy: writing the registers invalidates our cache. */ |
| pi->gregs_valid = 0; |
| return win; |
| } |
| |
| /* Write the floating-pointer registers back to the process or LWP |
| corresponding to PI. Return non-zero for success, zero for |
| failure. */ |
| |
| static int |
| proc_set_fpregs (procinfo *pi) |
| { |
| gdb_fpregset_t *fpregs; |
| int win; |
| |
| fpregs = proc_get_fpregs (pi); |
| if (fpregs == NULL) |
| return 0; /* proc_get_fpregs has already warned. */ |
| |
| if (pi->ctl_fd == 0 && open_procinfo_files (pi, FD_CTL) == 0) |
| { |
| return 0; |
| } |
| else |
| { |
| #ifdef NEW_PROC_API |
| struct { |
| procfs_ctl_t cmd; |
| /* Use char array to avoid alignment issues. */ |
| char fpregs[sizeof (gdb_fpregset_t)]; |
| } arg; |
| |
| arg.cmd = PCSFPREG; |
| memcpy (&arg.fpregs, fpregs, sizeof (arg.fpregs)); |
| win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg)); |
| #else |
| # ifdef PIOCTSFPREG |
| if (pi->tid == 0) |
| win = (ioctl (pi->ctl_fd, PIOCSFPREG, fpregs) >= 0); |
| else |
| { |
| struct { |
| long pr_count; |
| tid_t pr_error_thread; |
| tfpregset_t thread_1; |
| } thread_fpregs; |
| |
| thread_fpregs.pr_count = 1; |
| thread_fpregs.thread_1.tid = pi->tid; |
| memcpy (&thread_fpregs.thread_1.pr_fpregs, fpregs, |
| sizeof (*fpregs)); |
| win = (ioctl (pi->ctl_fd, PIOCTSFPREG, &thread_fpregs) >= 0); |
| } |
| # else |
| win = (ioctl (pi->ctl_fd, PIOCSFPREG, fpregs) >= 0); |
| # endif |
| #endif /* NEW_PROC_API */ |
| } |
| |
| /* Policy: writing the registers invalidates our cache. */ |
| pi->fpregs_valid = 0; |
| return win; |
| } |
| |
| /* Send a signal to the proc or lwp with the semantics of "kill()". |
| Returns non-zero for success, zero for failure. */ |
| |
| static int |
| proc_kill (procinfo *pi, int signo) |
| { |
| int win; |
| |
| /* We might conceivably apply this operation to an LWP, and the |
| LWP's ctl file descriptor might not be open. */ |
| |
| if (pi->ctl_fd == 0 && |
| open_procinfo_files (pi, FD_CTL) == 0) |
| { |
| return 0; |
| } |
| else |
| { |
| #ifdef NEW_PROC_API |
| procfs_ctl_t cmd[2]; |
| |
| cmd[0] = PCKILL; |
| cmd[1] = signo; |
| win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd)); |
| #else /* ioctl method */ |
| /* FIXME: do I need the Alpha OSF fixups present in |
| procfs.c/unconditionally_kill_inferior? Perhaps only for SIGKILL? */ |
| win = (ioctl (pi->ctl_fd, PIOCKILL, &signo) >= 0); |
| #endif |
| } |
| |
| return win; |
| } |
| |
| /* Find the pid of the process that started this one. Returns the |
| parent process pid, or zero. */ |
| |
| static int |
| proc_parent_pid (procinfo *pi) |
| { |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return 0; |
| |
| return pi->prstatus.pr_ppid; |
| } |
| |
| /* Convert a target address (a.k.a. CORE_ADDR) into a host address |
| (a.k.a void pointer)! */ |
| |
| #if (defined (PCWATCH) || defined (PIOCSWATCH)) \ |
| && !(defined (PIOCOPENLWP) || defined (UNIXWARE)) |
| static void * |
| procfs_address_to_host_pointer (CORE_ADDR addr) |
| { |
| struct type *ptr_type = builtin_type (target_gdbarch)->builtin_data_ptr; |
| void *ptr; |
| |
| gdb_assert (sizeof (ptr) == TYPE_LENGTH (ptr_type)); |
| gdbarch_address_to_pointer (target_gdbarch, ptr_type, |
| (gdb_byte *) &ptr, addr); |
| return ptr; |
| } |
| #endif |
| |
| static int |
| proc_set_watchpoint (procinfo *pi, CORE_ADDR addr, int len, int wflags) |
| { |
| #if !defined (PCWATCH) && !defined (PIOCSWATCH) |
| /* If neither or these is defined, we can't support watchpoints. |
| This just avoids possibly failing to compile the below on such |
| systems. */ |
| return 0; |
| #else |
| /* Horrible hack! Detect Solaris 2.5, because this doesn't work on 2.5. */ |
| #if defined (PIOCOPENLWP) || defined (UNIXWARE) /* Solaris 2.5: bail out. */ |
| return 0; |
| #else |
| struct { |
| procfs_ctl_t cmd; |
| char watch[sizeof (prwatch_t)]; |
| } arg; |
| prwatch_t pwatch; |
| |
| /* NOTE: cagney/2003-02-01: Even more horrible hack. Need to |
| convert a target address into something that can be stored in a |
| native data structure. */ |
| #ifdef PCAGENT /* Horrible hack: only defined on Solaris 2.6+ */ |
| pwatch.pr_vaddr = (uintptr_t) procfs_address_to_host_pointer (addr); |
| #else |
| pwatch.pr_vaddr = (caddr_t) procfs_address_to_host_pointer (addr); |
| #endif |
| pwatch.pr_size = len; |
| pwatch.pr_wflags = wflags; |
| #if defined(NEW_PROC_API) && defined (PCWATCH) |
| arg.cmd = PCWATCH; |
| memcpy (arg.watch, &pwatch, sizeof (prwatch_t)); |
| return (write (pi->ctl_fd, &arg, sizeof (arg)) == sizeof (arg)); |
| #else |
| #if defined (PIOCSWATCH) |
| return (ioctl (pi->ctl_fd, PIOCSWATCH, &pwatch) >= 0); |
| #else |
| return 0; /* Fail */ |
| #endif |
| #endif |
| #endif |
| #endif |
| } |
| |
| #if (defined(__i386__) || defined(__x86_64__)) && defined (sun) |
| |
| #include <sys/sysi86.h> |
| |
| /* The KEY is actually the value of the lower 16 bits of the GS |
| register for the LWP that we're interested in. Returns the |
| matching ssh struct (LDT entry). */ |
| |
| struct ssd * |
| proc_get_LDT_entry (procinfo *pi, int key) |
| { |
| static struct ssd *ldt_entry = NULL; |
| #ifdef NEW_PROC_API |
| char pathname[MAX_PROC_NAME_SIZE]; |
| struct cleanup *old_chain = NULL; |
| int fd; |
| |
| /* Allocate space for one LDT entry. |
| This alloc must persist, because we return a pointer to it. */ |
| if (ldt_entry == NULL) |
| ldt_entry = (struct ssd *) xmalloc (sizeof (struct ssd)); |
| |
| /* Open the file descriptor for the LDT table. */ |
| sprintf (pathname, "/proc/%d/ldt", pi->pid); |
| if ((fd = open_with_retry (pathname, O_RDONLY)) < 0) |
| { |
| proc_warn (pi, "proc_get_LDT_entry (open)", __LINE__); |
| return NULL; |
| } |
| /* Make sure it gets closed again! */ |
| old_chain = make_cleanup_close (fd); |
| |
| /* Now 'read' thru the table, find a match and return it. */ |
| while (read (fd, ldt_entry, sizeof (struct ssd)) == sizeof (struct ssd)) |
| { |
| if (ldt_entry->sel == 0 && |
| ldt_entry->bo == 0 && |
| ldt_entry->acc1 == 0 && |
| ldt_entry->acc2 == 0) |
| break; /* end of table */ |
| /* If key matches, return this entry. */ |
| if (ldt_entry->sel == key) |
| return ldt_entry; |
| } |
| /* Loop ended, match not found. */ |
| return NULL; |
| #else |
| int nldt, i; |
| static int nalloc = 0; |
| |
| /* Get the number of LDT entries. */ |
| if (ioctl (pi->ctl_fd, PIOCNLDT, &nldt) < 0) |
| { |
| proc_warn (pi, "proc_get_LDT_entry (PIOCNLDT)", __LINE__); |
| return NULL; |
| } |
| |
| /* Allocate space for the number of LDT entries. */ |
| /* This alloc has to persist, 'cause we return a pointer to it. */ |
| if (nldt > nalloc) |
| { |
| ldt_entry = (struct ssd *) |
| xrealloc (ldt_entry, (nldt + 1) * sizeof (struct ssd)); |
| nalloc = nldt; |
| } |
| |
| /* Read the whole table in one gulp. */ |
| if (ioctl (pi->ctl_fd, PIOCLDT, ldt_entry) < 0) |
| { |
| proc_warn (pi, "proc_get_LDT_entry (PIOCLDT)", __LINE__); |
| return NULL; |
| } |
| |
| /* Search the table and return the (first) entry matching 'key'. */ |
| for (i = 0; i < nldt; i++) |
| if (ldt_entry[i].sel == key) |
| return &ldt_entry[i]; |
| |
| /* Loop ended, match not found. */ |
| return NULL; |
| #endif |
| } |
| |
| /* Returns the pointer to the LDT entry of PTID. */ |
| |
| struct ssd * |
| procfs_find_LDT_entry (ptid_t ptid) |
| { |
| gdb_gregset_t *gregs; |
| int key; |
| procinfo *pi; |
| |
| /* Find procinfo for the lwp. */ |
| if ((pi = find_procinfo (PIDGET (ptid), TIDGET (ptid))) == NULL) |
| { |
| warning (_("procfs_find_LDT_entry: could not find procinfo for %d:%ld."), |
| PIDGET (ptid), TIDGET (ptid)); |
| return NULL; |
| } |
| /* get its general registers. */ |
| if ((gregs = proc_get_gregs (pi)) == NULL) |
| { |
| warning (_("procfs_find_LDT_entry: could not read gregs for %d:%ld."), |
| PIDGET (ptid), TIDGET (ptid)); |
| return NULL; |
| } |
| /* Now extract the GS register's lower 16 bits. */ |
| key = (*gregs)[GS] & 0xffff; |
| |
| /* Find the matching entry and return it. */ |
| return proc_get_LDT_entry (pi, key); |
| } |
| |
| #endif |
| |
| /* =============== END, non-thread part of /proc "MODULE" =============== */ |
| |
| /* =================== Thread "MODULE" =================== */ |
| |
| /* NOTE: you'll see more ifdefs and duplication of functions here, |
| since there is a different way to do threads on every OS. */ |
| |
| /* Returns the number of threads for the process. */ |
| |
| #if defined (PIOCNTHR) && defined (PIOCTLIST) |
| /* OSF version */ |
| static int |
| proc_get_nthreads (procinfo *pi) |
| { |
| int nthreads = 0; |
| |
| if (ioctl (pi->ctl_fd, PIOCNTHR, &nthreads) < 0) |
| proc_warn (pi, "procfs: PIOCNTHR failed", __LINE__); |
| |
| return nthreads; |
| } |
| |
| #else |
| #if defined (SYS_lwpcreate) || defined (SYS_lwp_create) /* FIXME: multiple */ |
| /* Solaris and Unixware version */ |
| static int |
| proc_get_nthreads (procinfo *pi) |
| { |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return 0; |
| |
| /* NEW_PROC_API: only works for the process procinfo, because the |
| LWP procinfos do not get prstatus filled in. */ |
| #ifdef NEW_PROC_API |
| if (pi->tid != 0) /* Find the parent process procinfo. */ |
| pi = find_procinfo_or_die (pi->pid, 0); |
| #endif |
| return pi->prstatus.pr_nlwp; |
| } |
| |
| #else |
| /* Default version */ |
| static int |
| proc_get_nthreads (procinfo *pi) |
| { |
| return 0; |
| } |
| #endif |
| #endif |
| |
| /* LWP version. |
| |
| Return the ID of the thread that had an event of interest. |
| (ie. the one that hit a breakpoint or other traced event). All |
| other things being equal, this should be the ID of a thread that is |
| currently executing. */ |
| |
| #if defined (SYS_lwpcreate) || defined (SYS_lwp_create) /* FIXME: multiple */ |
| /* Solaris and Unixware version */ |
| static int |
| proc_get_current_thread (procinfo *pi) |
| { |
| /* Note: this should be applied to the root procinfo for the |
| process, not to the procinfo for an LWP. If applied to the |
| procinfo for an LWP, it will simply return that LWP's ID. In |
| that case, find the parent process procinfo. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| if (!pi->status_valid) |
| if (!proc_get_status (pi)) |
| return 0; |
| |
| #ifdef NEW_PROC_API |
| return pi->prstatus.pr_lwp.pr_lwpid; |
| #else |
| return pi->prstatus.pr_who; |
| #endif |
| } |
| |
| #else |
| #if defined (PIOCNTHR) && defined (PIOCTLIST) |
| /* OSF version */ |
| static int |
| proc_get_current_thread (procinfo *pi) |
| { |
| #if 0 /* FIXME: not ready for prime time? */ |
| return pi->prstatus.pr_tid; |
| #else |
| return 0; |
| #endif |
| } |
| |
| #else |
| /* Default version */ |
| static int |
| proc_get_current_thread (procinfo *pi) |
| { |
| return 0; |
| } |
| |
| #endif |
| #endif |
| |
| /* Discover the IDs of all the threads within the process, and create |
| a procinfo for each of them (chained to the parent). This |
| unfortunately requires a different method on every OS. Returns |
| non-zero for success, zero for failure. */ |
| |
| static int |
| proc_delete_dead_threads (procinfo *parent, procinfo *thread, void *ignore) |
| { |
| if (thread && parent) /* sanity */ |
| { |
| thread->status_valid = 0; |
| if (!proc_get_status (thread)) |
| destroy_one_procinfo (&parent->thread_list, thread); |
| } |
| return 0; /* keep iterating */ |
| } |
| |
| #if defined (PIOCLSTATUS) |
| /* Solaris 2.5 (ioctl) version */ |
| static int |
| proc_update_threads (procinfo *pi) |
| { |
| gdb_prstatus_t *prstatus; |
| struct cleanup *old_chain = NULL; |
| procinfo *thread; |
| int nlwp, i; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| proc_iterate_over_threads (pi, proc_delete_dead_threads, NULL); |
| |
| if ((nlwp = proc_get_nthreads (pi)) <= 1) |
| return 1; /* Process is not multi-threaded; nothing to do. */ |
| |
| prstatus = xmalloc (sizeof (gdb_prstatus_t) * (nlwp + 1)); |
| |
| old_chain = make_cleanup (xfree, prstatus); |
| if (ioctl (pi->ctl_fd, PIOCLSTATUS, prstatus) < 0) |
| proc_error (pi, "update_threads (PIOCLSTATUS)", __LINE__); |
| |
| /* Skip element zero, which represents the process as a whole. */ |
| for (i = 1; i < nlwp + 1; i++) |
| { |
| if ((thread = create_procinfo (pi->pid, prstatus[i].pr_who)) == NULL) |
| proc_error (pi, "update_threads, create_procinfo", __LINE__); |
| |
| memcpy (&thread->prstatus, &prstatus[i], sizeof (*prstatus)); |
| thread->status_valid = 1; |
| } |
| pi->threads_valid = 1; |
| do_cleanups (old_chain); |
| return 1; |
| } |
| #else |
| #ifdef NEW_PROC_API |
| /* Unixware and Solaris 6 (and later) version. */ |
| static void |
| do_closedir_cleanup (void *dir) |
| { |
| closedir (dir); |
| } |
| |
| static int |
| proc_update_threads (procinfo *pi) |
| { |
| char pathname[MAX_PROC_NAME_SIZE + 16]; |
| struct dirent *direntry; |
| struct cleanup *old_chain = NULL; |
| procinfo *thread; |
| DIR *dirp; |
| int lwpid; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| proc_iterate_over_threads (pi, proc_delete_dead_threads, NULL); |
| |
| /* Unixware |
| |
| Note: this brute-force method is the only way I know of to |
| accomplish this task on Unixware. This method will also work on |
| Solaris 2.6 and 2.7. There is a much simpler and more elegant |
| way to do this on Solaris, but the margins of this manuscript are |
| too small to write it here... ;-) */ |
| |
| strcpy (pathname, pi->pathname); |
| strcat (pathname, "/lwp"); |
| if ((dirp = opendir (pathname)) == NULL) |
| proc_error (pi, "update_threads, opendir", __LINE__); |
| |
| old_chain = make_cleanup (do_closedir_cleanup, dirp); |
| while ((direntry = readdir (dirp)) != NULL) |
| if (direntry->d_name[0] != '.') /* skip '.' and '..' */ |
| { |
| lwpid = atoi (&direntry->d_name[0]); |
| if ((thread = create_procinfo (pi->pid, lwpid)) == NULL) |
| proc_error (pi, "update_threads, create_procinfo", __LINE__); |
| } |
| pi->threads_valid = 1; |
| do_cleanups (old_chain); |
| return 1; |
| } |
| #else |
| #ifdef PIOCTLIST |
| /* OSF version */ |
| static int |
| proc_update_threads (procinfo *pi) |
| { |
| int nthreads, i; |
| tid_t *threads; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| proc_iterate_over_threads (pi, proc_delete_dead_threads, NULL); |
| |
| nthreads = proc_get_nthreads (pi); |
| if (nthreads < 2) |
| return 0; /* Nothing to do for 1 or fewer threads. */ |
| |
| threads = xmalloc (nthreads * sizeof (tid_t)); |
| |
| if (ioctl (pi->ctl_fd, PIOCTLIST, threads) < 0) |
| proc_error (pi, "procfs: update_threads (PIOCTLIST)", __LINE__); |
| |
| for (i = 0; i < nthreads; i++) |
| { |
| if (!find_procinfo (pi->pid, threads[i])) |
| if (!create_procinfo (pi->pid, threads[i])) |
| proc_error (pi, "update_threads, create_procinfo", __LINE__); |
| } |
| pi->threads_valid = 1; |
| return 1; |
| } |
| #else |
| /* Default version */ |
| static int |
| proc_update_threads (procinfo *pi) |
| { |
| return 0; |
| } |
| #endif /* OSF PIOCTLIST */ |
| #endif /* NEW_PROC_API */ |
| #endif /* SOL 2.5 PIOCLSTATUS */ |
| |
| /* Given a pointer to a function, call that function once for each lwp |
| in the procinfo list, until the function returns non-zero, in which |
| event return the value returned by the function. |
| |
| Note: this function does NOT call update_threads. If you want to |
| discover new threads first, you must call that function explicitly. |
| This function just makes a quick pass over the currently-known |
| procinfos. |
| |
| PI is the parent process procinfo. FUNC is the per-thread |
| function. PTR is an opaque parameter for function. Returns the |
| first non-zero return value from the callee, or zero. */ |
| |
| static int |
| proc_iterate_over_threads (procinfo *pi, |
| int (*func) (procinfo *, procinfo *, void *), |
| void *ptr) |
| { |
| procinfo *thread, *next; |
| int retval = 0; |
| |
| /* We should never have to apply this operation to any procinfo |
| except the one for the main process. If that ever changes for |
| any reason, then take out the following clause and replace it |
| with one that makes sure the ctl_fd is open. */ |
| |
| if (pi->tid != 0) |
| pi = find_procinfo_or_die (pi->pid, 0); |
| |
| for (thread = pi->thread_list; thread != NULL; thread = next) |
| { |
| next = thread->next; /* In case thread is destroyed. */ |
| if ((retval = (*func) (pi, thread, ptr)) != 0) |
| break; |
| } |
| |
| return retval; |
| } |
| |
| /* =================== END, Thread "MODULE" =================== */ |
| |
| /* =================== END, /proc "MODULE" =================== */ |
| |
| /* =================== GDB "MODULE" =================== */ |
| |
| /* Here are all of the gdb target vector functions and their |
| friends. */ |
| |
| static ptid_t do_attach (ptid_t ptid); |
| static void do_detach (int signo); |
| static void proc_trace_syscalls_1 (procinfo *pi, int syscallnum, |
| int entry_or_exit, int mode, int from_tty); |
| |
| /* On mips-irix, we need to insert a breakpoint at __dbx_link during |
| the startup phase. The following two variables are used to record |
| the address of the breakpoint, and the code that was replaced by |
| a breakpoint. */ |
| static int dbx_link_bpt_addr = 0; |
| static void *dbx_link_bpt; |
| |
| /* Sets up the inferior to be debugged. Registers to trace signals, |
| hardware faults, and syscalls. Note: does not set RLC flag: caller |
| may want to customize that. Returns zero for success (note! |
| unlike most functions in this module); on failure, returns the LINE |
| NUMBER where it failed! */ |
| |
| static int |
| procfs_debug_inferior (procinfo *pi) |
| { |
| fltset_t traced_faults; |
| gdb_sigset_t traced_signals; |
| sysset_t *traced_syscall_entries; |
| sysset_t *traced_syscall_exits; |
| int status; |
| |
| #ifdef PROCFS_DONT_TRACE_FAULTS |
| /* On some systems (OSF), we don't trace hardware faults. |
| Apparently it's enough that we catch them as signals. |
| Wonder why we don't just do that in general? */ |
| premptyset (&traced_faults); /* don't trace faults. */ |
| #else |
| /* Register to trace hardware faults in the child. */ |
| prfillset (&traced_faults); /* trace all faults... */ |
| gdb_prdelset (&traced_faults, FLTPAGE); /* except page fault. */ |
| #endif |
| if (!proc_set_traced_faults (pi, &traced_faults)) |
| return __LINE__; |
| |
| /* Initially, register to trace all signals in the child. */ |
| prfillset (&traced_signals); |
| if (!proc_set_traced_signals (pi, &traced_signals)) |
| return __LINE__; |
| |
| |
| /* Register to trace the 'exit' system call (on entry). */ |
| traced_syscall_entries = sysset_t_alloc (pi); |
| gdb_premptysysset (traced_syscall_entries); |
| #ifdef SYS_exit |
| gdb_praddsysset (traced_syscall_entries, SYS_exit); |
| #endif |
| #ifdef SYS_lwpexit |
| gdb_praddsysset (traced_syscall_entries, SYS_lwpexit);/* And _lwp_exit... */ |
| #endif |
| #ifdef SYS_lwp_exit |
| gdb_praddsysset (traced_syscall_entries, SYS_lwp_exit); |
| #endif |
| #ifdef DYNAMIC_SYSCALLS |
| { |
| int callnum = find_syscall (pi, "_exit"); |
| |
| if (callnum >= 0) |
| gdb_praddsysset (traced_syscall_entries, callnum); |
| } |
| #endif |
| |
| status = proc_set_traced_sysentry (pi, traced_syscall_entries); |
| xfree (traced_syscall_entries); |
| if (!status) |
| return __LINE__; |
| |
| #ifdef PRFS_STOPEXEC /* defined on OSF */ |
| /* OSF method for tracing exec syscalls. Quoting: |
| Under Alpha OSF/1 we have to use a PIOCSSPCACT ioctl to trace |
| exits from exec system calls because of the user level loader. */ |
| /* FIXME: make nice and maybe move into an access function. */ |
| { |
| int prfs_flags; |
| |
| if (ioctl (pi->ctl_fd, PIOCGSPCACT, &prfs_flags) < 0) |
| return __LINE__; |
| |
| prfs_flags |= PRFS_STOPEXEC; |
| |
| if (ioctl (pi->ctl_fd, PIOCSSPCACT, &prfs_flags) < 0) |
| return __LINE__; |
| } |
| #else /* not PRFS_STOPEXEC */ |
| /* Everyone else's (except OSF) method for tracing exec syscalls. */ |
| /* 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. */ |
| |
| traced_syscall_exits = sysset_t_alloc (pi); |
| gdb_premptysysset (traced_syscall_exits); |
| #ifdef SYS_exec |
| gdb_praddsysset (traced_syscall_exits, SYS_exec); |
| #endif |
| #ifdef SYS_execve |
| gdb_praddsysset (traced_syscall_exits, SYS_execve); |
| #endif |
| #ifdef SYS_execv |
| gdb_praddsysset (traced_syscall_exits, SYS_execv); |
| #endif |
| |
| #ifdef SYS_lwpcreate |
| gdb_praddsysset (traced_syscall_exits, SYS_lwpcreate); |
| gdb_praddsysset (traced_syscall_exits, SYS_lwpexit); |
| #endif |
| |
| #ifdef SYS_lwp_create /* FIXME: once only, please. */ |
| gdb_praddsysset (traced_syscall_exits, SYS_lwp_create); |
| gdb_praddsysset (traced_syscall_exits, SYS_lwp_exit); |
| #endif |
| |
| #ifdef DYNAMIC_SYSCALLS |
| { |
| int callnum = find_syscall (pi, "execve"); |
| |
| if (callnum >= 0) |
| gdb_praddsysset (traced_syscall_exits, callnum); |
| callnum = find_syscall (pi, "ra_execve"); |
| if (callnum >= 0) |
| gdb_praddsysset (traced_syscall_exits, callnum); |
| } |
| #endif |
| |
| status = proc_set_traced_sysexit (pi, traced_syscall_exits); |
| xfree (traced_syscall_exits); |
| if (!status) |
| return __LINE__; |
| |
| #endif /* PRFS_STOPEXEC */ |
| return 0; |
| } |
| |
| static void |
| procfs_attach (struct target_ops *ops, char *args, int from_tty) |
| { |
| char *exec_file; |
| int pid; |
| |
| pid = parse_pid_to_attach (args); |
| |
| if (pid == getpid ()) |
| error (_("Attaching GDB to itself is not a good idea...")); |
| |
| if (from_tty) |
| { |
| exec_file = get_exec_file (0); |
| |
| if (exec_file) |
| printf_filtered (_("Attaching to program `%s', %s\n"), |
| exec_file, target_pid_to_str (pid_to_ptid (pid))); |
| else |
| printf_filtered (_("Attaching to %s\n"), |
| target_pid_to_str (pid_to_ptid (pid))); |
| |
| fflush (stdout); |
| } |
| inferior_ptid = do_attach (pid_to_ptid (pid)); |
| push_target (ops); |
| } |
| |
| static void |
| procfs_detach (struct target_ops *ops, char *args, int from_tty) |
| { |
| int sig = 0; |
| int pid = PIDGET (inferior_ptid); |
| |
| if (args) |
| sig = atoi (args); |
| |
| if (from_tty) |
| { |
| char *exec_file; |
| |
| exec_file = get_exec_file (0); |
| if (exec_file == NULL) |
| exec_file = ""; |
| |
| printf_filtered (_("Detaching from program: %s, %s\n"), exec_file, |
| target_pid_to_str (pid_to_ptid (pid))); |
| gdb_flush (gdb_stdout); |
| } |
| |
| do_detach (sig); |
| |
| inferior_ptid = null_ptid; |
| detach_inferior (pid); |
| unpush_target (ops); |
| } |
| |
| static ptid_t |
| do_attach (ptid_t ptid) |
| { |
| procinfo *pi; |
| struct inferior *inf; |
| int fail; |
| int lwpid; |
| |
| if ((pi = create_procinfo (PIDGET (ptid), 0)) == NULL) |
| perror (_("procfs: out of memory in 'attach'")); |
| |
| if (!open_procinfo_files (pi, FD_CTL)) |
| { |
| fprintf_filtered (gdb_stderr, "procfs:%d -- ", __LINE__); |
| sprintf (errmsg, "do_attach: couldn't open /proc file for process %d", |
| PIDGET (ptid)); |
| dead_procinfo (pi, errmsg, NOKILL); |
| } |
| |
| /* Stop the process (if it isn't already stopped). */ |
| if (proc_flags (pi) & (PR_STOPPED | PR_ISTOP)) |
| { |
| pi->was_stopped = 1; |
| proc_prettyprint_why (proc_why (pi), proc_what (pi), 1); |
| } |
| else |
| { |
| pi->was_stopped = 0; |
| /* Set the process to run again when we close it. */ |
| if (!proc_set_run_on_last_close (pi)) |
| dead_procinfo (pi, "do_attach: couldn't set RLC.", NOKILL); |
| |
| /* Now stop the process. */ |
| if (!proc_stop_process (pi)) |
| dead_procinfo (pi, "do_attach: couldn't stop the process.", NOKILL); |
| pi->ignore_next_sigstop = 1; |
| } |
| /* Save some of the /proc state to be restored if we detach. */ |
| if (!proc_get_traced_faults (pi, &pi->saved_fltset)) |
| dead_procinfo (pi, "do_attach: couldn't save traced faults.", NOKILL); |
| if (!proc_get_traced_signals (pi, &pi->saved_sigset)) |
| dead_procinfo (pi, "do_attach: couldn't save traced signals.", NOKILL); |
| if (!proc_get_traced_sysentry (pi, pi->saved_entryset)) |
| dead_procinfo (pi, "do_attach: couldn't save traced syscall entries.", |
| NOKILL); |
| if (!proc_get_traced_sysexit (pi, pi->saved_exitset)) |
| dead_procinfo (pi, "do_attach: couldn't save traced syscall exits.", |
| NOKILL); |
| if (!proc_get_held_signals (pi, &pi->saved_sighold)) |
| dead_procinfo (pi, "do_attach: couldn't save held signals.", NOKILL); |
| |
| if ((fail = procfs_debug_inferior (pi)) != 0) |
| dead_procinfo (pi, "do_attach: failed in procfs_debug_inferior", NOKILL); |
| |
| inf = current_inferior (); |
| inferior_appeared (inf, pi->pid); |
| /* Let GDB know that the inferior was attached. */ |
| inf->attach_flag = 1; |
| |
| /* Create a procinfo for the current lwp. */ |
| lwpid = proc_get_current_thread (pi); |
| create_procinfo (pi->pid, lwpid); |
| |
| /* Add it to gdb's thread list. */ |
| ptid = MERGEPID (pi->pid, lwpid); |
| add_thread (ptid); |
| |
| return ptid; |
| } |
| |
| static void |
| do_detach (int signo) |
| { |
| procinfo *pi; |
| |
| /* Find procinfo for the main process. */ |
| pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0); /* FIXME: threads */ |
| if (signo) |
| if (!proc_set_current_signal (pi, signo)) |
| proc_warn (pi, "do_detach, set_current_signal", __LINE__); |
| |
| if (!proc_set_traced_signals (pi, &pi->saved_sigset)) |
| proc_warn (pi, "do_detach, set_traced_signal", __LINE__); |
| |
| if (!proc_set_traced_faults (pi, &pi->saved_fltset)) |
| proc_warn (pi, "do_detach, set_traced_faults", __LINE__); |
| |
| if (!proc_set_traced_sysentry (pi, pi->saved_entryset)) |
| proc_warn (pi, "do_detach, set_traced_sysentry", __LINE__); |
| |
| if (!proc_set_traced_sysexit (pi, pi->saved_exitset)) |
| proc_warn (pi, "do_detach, set_traced_sysexit", __LINE__); |
| |
| if (!proc_set_held_signals (pi, &pi->saved_sighold)) |
| proc_warn (pi, "do_detach, set_held_signals", __LINE__); |
| |
| if (signo || (proc_flags (pi) & (PR_STOPPED | PR_ISTOP))) |
| if (signo || !(pi->was_stopped) || |
| query (_("Was stopped when attached, make it runnable again? "))) |
| { |
| /* Clear any pending signal. */ |
| if (!proc_clear_current_fault (pi)) |
| proc_warn (pi, "do_detach, clear_current_fault", __LINE__); |
| |
| if (signo == 0 && !proc_clear_current_signal (pi)) |
| proc_warn (pi, "do_detach, clear_current_signal", __LINE__); |
| |
| if (!proc_set_run_on_last_close (pi)) |
| proc_warn (pi, "do_detach, set_rlc", __LINE__); |
| } |
| |
| destroy_procinfo (pi); |
| } |
| |
| /* Fetch register REGNUM from the inferior. If REGNUM is -1, do this |
| for all registers. |
| |
| ??? Is the following note still relevant? We can't get individual |
| registers with the PT_GETREGS ptrace(2) request either, yet we |
| don't bother with caching at all in that case. |
| |
| NOTE: Since the /proc interface cannot give us individual |
| registers, we pay no attention to REGNUM, and just fetch them all. |
| This results in the possibility that we will do unnecessarily many |
| |