/* Machine independent support for SVR4 /proc (process file system) for GDB.
- Copyright 1999, 2000, 2001 Free Software Foundation, Inc.
+ Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
Written by Michael Snyder at Cygnus Solutions.
Based on work by Fred Fish, Stu Grossman, Geoff Noer, and others.
#include "inferior.h"
#include "target.h"
#include "gdbcore.h"
+#include "elf-bfd.h" /* for elfcore_write_* */
#include "gdbcmd.h"
#include "gdbthread.h"
* Unixware
* AIX5
*
- * /proc works by immitating a file system: you open a simulated file
+ * /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.
static void procfs_open (char *, int);
static void procfs_attach (char *, int);
static void procfs_detach (char *, int);
-static void procfs_resume (int, int, enum target_signal);
+static void procfs_resume (ptid_t, int, enum target_signal);
static int procfs_can_run (void);
static void procfs_stop (void);
static void procfs_files_info (struct target_ops *);
static void procfs_fetch_registers (int);
static void procfs_store_registers (int);
-static void procfs_notice_signals (int);
+static void procfs_notice_signals (ptid_t);
static void procfs_prepare_to_store (void);
static void procfs_kill_inferior (void);
static void procfs_mourn_inferior (void);
static void procfs_create_inferior (char *, char *, char **);
-static int procfs_wait (int, struct target_waitstatus *);
+static ptid_t procfs_wait (ptid_t, struct target_waitstatus *);
static int procfs_xfer_memory (CORE_ADDR, char *, int, int,
struct mem_attrib *attrib,
struct target_ops *);
-static int procfs_thread_alive (int);
+static int procfs_thread_alive (ptid_t);
void procfs_find_new_threads (void);
-char *procfs_pid_to_str (int);
+char *procfs_pid_to_str (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 *);
struct target_ops procfs_ops; /* the target vector */
static void
init_procfs_ops (void)
{
- procfs_ops.to_shortname = "procfs";
- procfs_ops.to_longname = "Unix /proc child process";
- procfs_ops.to_doc =
+ procfs_ops.to_shortname = "procfs";
+ procfs_ops.to_longname = "Unix /proc child process";
+ procfs_ops.to_doc =
"Unix /proc child process (started by the \"run\" command).";
- procfs_ops.to_open = procfs_open;
- procfs_ops.to_can_run = procfs_can_run;
- procfs_ops.to_create_inferior = procfs_create_inferior;
- procfs_ops.to_kill = procfs_kill_inferior;
- procfs_ops.to_mourn_inferior = procfs_mourn_inferior;
- procfs_ops.to_attach = procfs_attach;
- procfs_ops.to_detach = procfs_detach;
- procfs_ops.to_wait = procfs_wait;
- procfs_ops.to_resume = procfs_resume;
- procfs_ops.to_prepare_to_store = procfs_prepare_to_store;
- procfs_ops.to_fetch_registers = procfs_fetch_registers;
- procfs_ops.to_store_registers = procfs_store_registers;
- procfs_ops.to_xfer_memory = procfs_xfer_memory;
- procfs_ops.to_insert_breakpoint = memory_insert_breakpoint;
- procfs_ops.to_remove_breakpoint = memory_remove_breakpoint;
- procfs_ops.to_notice_signals = procfs_notice_signals;
- procfs_ops.to_files_info = procfs_files_info;
- procfs_ops.to_stop = procfs_stop;
-
- procfs_ops.to_terminal_init = terminal_init_inferior;
- procfs_ops.to_terminal_inferior = terminal_inferior;
+ procfs_ops.to_open = procfs_open;
+ procfs_ops.to_can_run = procfs_can_run;
+ procfs_ops.to_create_inferior = procfs_create_inferior;
+ procfs_ops.to_kill = procfs_kill_inferior;
+ procfs_ops.to_mourn_inferior = procfs_mourn_inferior;
+ procfs_ops.to_attach = procfs_attach;
+ procfs_ops.to_detach = procfs_detach;
+ procfs_ops.to_wait = procfs_wait;
+ procfs_ops.to_resume = procfs_resume;
+ procfs_ops.to_prepare_to_store = procfs_prepare_to_store;
+ procfs_ops.to_fetch_registers = procfs_fetch_registers;
+ procfs_ops.to_store_registers = procfs_store_registers;
+ procfs_ops.to_xfer_memory = procfs_xfer_memory;
+ procfs_ops.to_insert_breakpoint = memory_insert_breakpoint;
+ procfs_ops.to_remove_breakpoint = memory_remove_breakpoint;
+ procfs_ops.to_notice_signals = procfs_notice_signals;
+ procfs_ops.to_files_info = procfs_files_info;
+ procfs_ops.to_stop = procfs_stop;
+
+ procfs_ops.to_terminal_init = terminal_init_inferior;
+ procfs_ops.to_terminal_inferior = terminal_inferior;
procfs_ops.to_terminal_ours_for_output = terminal_ours_for_output;
- procfs_ops.to_terminal_ours = terminal_ours;
- procfs_ops.to_terminal_info = child_terminal_info;
-
- procfs_ops.to_find_new_threads = procfs_find_new_threads;
- procfs_ops.to_thread_alive = procfs_thread_alive;
- procfs_ops.to_pid_to_str = procfs_pid_to_str;
-
- procfs_ops.to_has_all_memory = 1;
- procfs_ops.to_has_memory = 1;
- procfs_ops.to_has_execution = 1;
- procfs_ops.to_has_stack = 1;
- procfs_ops.to_has_registers = 1;
- procfs_ops.to_stratum = process_stratum;
- procfs_ops.to_has_thread_control = tc_schedlock;
- procfs_ops.to_magic = OPS_MAGIC;
+ procfs_ops.to_terminal_ours = terminal_ours;
+ procfs_ops.to_terminal_info = child_terminal_info;
+
+ procfs_ops.to_find_new_threads = procfs_find_new_threads;
+ procfs_ops.to_thread_alive = procfs_thread_alive;
+ procfs_ops.to_pid_to_str = procfs_pid_to_str;
+
+ procfs_ops.to_has_all_memory = 1;
+ procfs_ops.to_has_memory = 1;
+ procfs_ops.to_has_execution = 1;
+ procfs_ops.to_has_stack = 1;
+ procfs_ops.to_has_registers = 1;
+ procfs_ops.to_stratum = process_stratum;
+ procfs_ops.to_has_thread_control = tc_schedlock;
+ procfs_ops.to_find_memory_regions = proc_find_memory_regions;
+ procfs_ops.to_make_corefile_notes = procfs_make_note_section;
+ procfs_ops.to_magic = OPS_MAGIC;
}
/* =================== END, TARGET_OPS "MODULE" =================== */
typedef prstatus_t gdb_lwpstatus_t;
#endif /* NEW_PROC_API */
-
-/* Provide default composite pid manipulation macros for systems that
- don't have threads. */
-
-#ifndef PIDGET
-#define PIDGET(PID) (PID)
-#define TIDGET(PID) (PID)
-#endif
-#ifndef MERGEPID
-#define MERGEPID(PID, TID) (PID)
-#endif
-
typedef struct procinfo {
struct procinfo *next;
int pid; /* Process ID */
return pi;
}
+/* open_with_retry() is a 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, open_with_retry() will 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;
+}
+
/*
* Function: open_procinfo_files
*
strcat (tmp, "/lwpctl");
else
strcat (tmp, "/ctl");
- fd = open (tmp, O_WRONLY);
+ fd = open_with_retry (tmp, O_WRONLY);
if (fd <= 0)
return 0; /* fail */
pi->ctl_fd = fd;
if (pi->tid)
return 0; /* there is no 'as' file descriptor for an lwp */
strcat (tmp, "/as");
- fd = open (tmp, O_RDWR);
+ fd = open_with_retry (tmp, O_RDWR);
if (fd <= 0)
return 0; /* fail */
pi->as_fd = fd;
strcat (tmp, "/lwpstatus");
else
strcat (tmp, "/status");
- fd = open (tmp, O_RDONLY);
+ fd = open_with_retry (tmp, O_RDONLY);
if (fd <= 0)
return 0; /* fail */
pi->status_fd = fd;
#ifdef PIOCTSTATUS /* OSF */
- if ((fd = open (pi->pathname, O_RDWR)) == 0) /* Only one FD; just open it. */
+ /* 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 (pi->pathname, O_RDWR);
+ fd = open_with_retry (pi->pathname, O_RDWR);
if (fd <= 0)
return 0; /* fail */
}
load_syscalls (pi);
#endif
+ pi->saved_entryset = sysset_t_alloc (pi);
+ pi->saved_exitset = sysset_t_alloc (pi);
+
/* Chain into list. */
if (tid == 0)
{
#ifdef DYNAMIC_SYSCALLS
free_syscalls (pi);
#endif
+ xfree (pi->saved_entryset);
+ xfree (pi->saved_exitset);
xfree (pi);
}
/* Open the file descriptor for the sysent file */
sprintf (pathname, "/proc/%d/sysent", pi->pid);
- sysent_fd = open (pathname, O_RDONLY);
+ sysent_fd = open_with_retry (pathname, O_RDONLY);
if (sysent_fd < 0)
{
error ("load_syscalls: Can't open /proc/%d/sysent", pi->pid);
prwatch_t *pwatch;
pwatch = (prwatch_t *) &arg.watch;
- pwatch->pr_vaddr = address_to_host_pointer (addr);
+#ifdef PCAGENT /* Horrible hack: only defined on Solaris 2.6+ */
+ pwatch->pr_vaddr = (uintptr_t) address_to_host_pointer (addr);
+#else
+ pwatch->pr_vaddr = (caddr_t) address_to_host_pointer (addr);
+#endif
pwatch->pr_size = len;
pwatch->pr_wflags = wflags;
#if defined(NEW_PROC_API) && defined (PCWATCH)
#endif
}
-/*
- * Function: proc_iterate_over_mappings
- *
- * Given a pointer to a function, call that function once for every
- * mapped address space in the process. The callback function
- * receives an open file descriptor for the file corresponding to
- * that mapped address space (if there is one), and the base address
- * of the mapped space. Quit when the callback function returns a
- * nonzero value, or at teh end of the mappings.
- *
- * Returns: the first non-zero return value of the callback function,
- * or zero.
- */
-
-/* FIXME: it's probably a waste to cache this FD.
- It doesn't get called that often... and if I open it
- every time, I don't need to lseek it. */
-int
-proc_iterate_over_mappings (int (*func) (int, CORE_ADDR))
-{
- struct prmap *map;
- procinfo *pi;
-#ifndef NEW_PROC_API /* avoid compiler warning */
- int nmaps = 0;
- int i;
-#else
- int map_fd;
- char pathname[MAX_PROC_NAME_SIZE];
-#endif
- int funcstat = 0;
- int fd;
-
- pi = find_procinfo_or_die (PIDGET (inferior_pid), 0);
-
-#ifdef NEW_PROC_API
- /* Open map fd. */
- sprintf (pathname, "/proc/%d/map", pi->pid);
- if ((map_fd = open (pathname, O_RDONLY)) < 0)
- proc_error (pi, "proc_iterate_over_mappings (open)", __LINE__);
-
- /* Make sure it gets closed again. */
- make_cleanup_close (map_fd);
-
- /* Allocate space for mapping (lifetime only for this function). */
- map = alloca (sizeof (struct prmap));
-
- /* Now read the mappings from the file,
- open a file descriptor for those that have a name,
- and call the callback function. */
- while (read (map_fd,
- (void *) map,
- sizeof (struct prmap)) == sizeof (struct prmap))
- {
- char name[MAX_PROC_NAME_SIZE + sizeof (map->pr_mapname)];
-
- if (map->pr_vaddr == 0 && map->pr_size == 0)
- break; /* sanity */
-
- if (map->pr_mapname[0] == 0)
- {
- fd = -1; /* no map file */
- }
- else
- {
- sprintf (name, "/proc/%d/object/%s", pi->pid, map->pr_mapname);
- /* Note: caller's responsibility to close this fd! */
- fd = open (name, O_RDONLY);
- /* Note: we don't test the above call for failure;
- we just pass the FD on as given. Sometimes there is
- no file, so the ioctl may return failure, but that's
- not a problem. */
- }
-
- /* Stop looping if the callback returns non-zero. */
- if ((funcstat = (*func) (fd, (CORE_ADDR) map->pr_vaddr)) != 0)
- break;
- }
-#else
- /* Get the number of mapping entries. */
- if (ioctl (pi->ctl_fd, PIOCNMAP, &nmaps) < 0)
- proc_error (pi, "proc_iterate_over_mappings (PIOCNMAP)", __LINE__);
-
- /* Allocate space for mappings (lifetime only this function). */
- map = (struct prmap *) alloca ((nmaps + 1) * sizeof (struct prmap));
-
- /* Read in all the mappings. */
- if (ioctl (pi->ctl_fd, PIOCMAP, map) < 0)
- proc_error (pi, "proc_iterate_over_mappings (PIOCMAP)", __LINE__);
-
- /* Now loop through the mappings, open an fd for each, and
- call the callback function. */
- for (i = 0;
- i < nmaps && map[i].pr_size != 0;
- i++)
- {
- /* Note: caller's responsibility to close this fd! */
- fd = ioctl (pi->ctl_fd, PIOCOPENM, &map[i].pr_vaddr);
- /* Note: we don't test the above call for failure;
- we just pass the FD on as given. Sometimes there is
- no file, so the ioctl may return failure, but that's
- not a problem. */
-
- /* Stop looping if the callback returns non-zero. */
- funcstat = (*func) (fd, host_pointer_to_address (map[i].pr_vaddr));
- if (funcstat != 0)
- break;
- }
-#endif
-
- return funcstat;
-}
-
#ifdef TM_I386SOL2_H /* Is it hokey to use this? */
#include <sys/sysi86.h>
/* Open the file descriptor for the LDT table. */
sprintf (pathname, "/proc/%d/ldt", pi->pid);
- if ((fd = open (pathname, O_RDONLY)) < 0)
+ if ((fd = open_with_retry (pathname, O_RDONLY)) < 0)
{
proc_warn (pi, "proc_get_LDT_entry (open)", __LINE__);
return NULL;
* Here are all of the gdb target vector functions and their friends.
*/
-static int do_attach (int pid);
+static ptid_t do_attach (ptid_t ptid);
static void do_detach (int signo);
static int register_gdb_signals (procinfo *, gdb_sigset_t *);
if (exec_file)
printf_filtered ("Attaching to program `%s', %s\n",
- exec_file, target_pid_to_str (pid));
+ exec_file, target_pid_to_str (pid_to_ptid (pid)));
else
- printf_filtered ("Attaching to %s\n", target_pid_to_str (pid));
+ printf_filtered ("Attaching to %s\n",
+ target_pid_to_str (pid_to_ptid (pid)));
fflush (stdout);
}
- inferior_pid = do_attach (pid);
+ inferior_ptid = do_attach (pid_to_ptid (pid));
push_target (&procfs_ops);
}
if (exec_file == 0)
exec_file = "";
printf_filtered ("Detaching from program: %s %s\n",
- exec_file, target_pid_to_str (inferior_pid));
+ exec_file, target_pid_to_str (inferior_ptid));
fflush (stdout);
}
if (args)
signo = atoi (args);
do_detach (signo);
- inferior_pid = 0;
+ inferior_ptid = null_ptid;
unpush_target (&procfs_ops); /* Pop out of handling an inferior */
}
-static int
-do_attach (int pid)
+static ptid_t
+do_attach (ptid_t ptid)
{
procinfo *pi;
int fail;
- if ((pi = create_procinfo (pid, 0)) == NULL)
+ 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",
- pid);
+ PIDGET (ptid));
dead_procinfo (pi, errmsg, NOKILL);
}
procinfo *pi;
/* Find procinfo for the main process */
- pi = find_procinfo_or_die (PIDGET (inferior_pid), 0); /* FIXME: threads */
+ 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__);
int pid;
int tid;
- pid = PIDGET (inferior_pid);
- tid = TIDGET (inferior_pid);
+ pid = PIDGET (inferior_ptid);
+ tid = TIDGET (inferior_ptid);
/* First look up procinfo for the main process. */
pi = find_procinfo_or_die (pid, 0);
/* If the event thread is not the same as GDB's requested thread
- (ie. inferior_pid), then look up procinfo for the requested
+ (ie. inferior_ptid), then look up procinfo for the requested
thread. */
if ((tid != 0) &&
(tid != proc_get_current_thread (pi)))
if (pi == NULL)
error ("procfs: fetch_registers failed to find procinfo for %s",
- target_pid_to_str (inferior_pid));
+ target_pid_to_str (inferior_ptid));
if ((gregs = proc_get_gregs (pi)) == NULL)
proc_error (pi, "fetch_registers, get_gregs", __LINE__);
int pid;
int tid;
- pid = PIDGET (inferior_pid);
- tid = TIDGET (inferior_pid);
+ pid = PIDGET (inferior_ptid);
+ tid = TIDGET (inferior_ptid);
/* First find procinfo for main process */
pi = find_procinfo_or_die (pid, 0);
/* If current lwp for process is not the same as requested thread
- (ie. inferior_pid), then find procinfo for the requested thread. */
+ (ie. inferior_ptid), then find procinfo for the requested thread. */
if ((tid != 0) &&
(tid != proc_get_current_thread (pi)))
if (pi == NULL)
error ("procfs: store_registers: failed to find procinfo for %s",
- target_pid_to_str (inferior_pid));
+ target_pid_to_str (inferior_ptid));
if ((gregs = proc_get_gregs (pi)) == NULL)
proc_error (pi, "store_registers, get_gregs", __LINE__);
* event codes are returned thru a pointer parameter.
*/
-static int
-procfs_wait (int pid, struct target_waitstatus *status)
+static ptid_t
+procfs_wait (ptid_t ptid, struct target_waitstatus *status)
{
/* First cut: loosely based on original version 2.1 */
procinfo *pi;
- int temp, wstat;
- int retval;
+ int wstat;
+ int temp_tid;
+ ptid_t retval, temp_ptid;
int why, what, flags;
int retry = 0;
retry++;
wstat = 0;
- retval = -1;
+ retval = pid_to_ptid (-1);
/* Find procinfo for main process */
- pi = find_procinfo_or_die (PIDGET (inferior_pid), 0);
+ pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0);
if (pi)
{
/* We must assume that the status is stale now... */
/* wait_for_stop failed: has the child terminated? */
if (errno == ENOENT)
{
+ int wait_retval;
+
/* /proc file not found; presumably child has terminated. */
- retval = wait (&wstat); /* "wait" for the child's exit */
+ wait_retval = wait (&wstat); /* "wait" for the child's exit */
- if (retval != PIDGET (inferior_pid)) /* wrong child? */
+ if (wait_retval != PIDGET (inferior_ptid)) /* wrong child? */
error ("procfs: couldn't stop process %d: wait returned %d\n",
- inferior_pid, retval);
+ PIDGET (inferior_ptid), wait_retval);
/* FIXME: might I not just use waitpid?
Or try find_procinfo to see if I know about this child? */
+ retval = pid_to_ptid (wait_retval);
}
else if (errno == EINTR)
goto wait_again;
return a "success" exit code. Bogus: what if
it returns something else? */
wstat = 0;
- retval = inferior_pid; /* ? ? ? */
+ retval = inferior_ptid; /* ? ? ? */
}
else
{
/* If wait returns -1, that's what we return to GDB. */
if (temp < 0)
- retval = temp;
+ retval = pid_to_ptid (temp);
}
}
else
{
/* How to exit gracefully, returning "unknown event" */
status->kind = TARGET_WAITKIND_SPURIOUS;
- return inferior_pid;
+ return inferior_ptid;
}
else
{
/* How to keep going without returning to wfi: */
- target_resume (pid, 0, TARGET_SIGNAL_0);
+ target_resume (ptid, 0, TARGET_SIGNAL_0);
goto wait_again;
}
}
*/
/* If not in procinfo list, add it. */
- temp = proc_get_current_thread (pi);
- if (!find_procinfo (pi->pid, temp))
- create_procinfo (pi->pid, temp);
+ temp_tid = proc_get_current_thread (pi);
+ if (!find_procinfo (pi->pid, temp_tid))
+ create_procinfo (pi->pid, temp_tid);
- temp = MERGEPID (pi->pid, temp);
+ temp_ptid = MERGEPID (pi->pid, temp_tid);
/* If not in GDB's thread list, add it. */
- if (!in_thread_list (temp))
+ if (!in_thread_list (temp_ptid))
{
- printf_filtered ("[New %s]\n", target_pid_to_str (temp));
- add_thread (temp);
+ printf_filtered ("[New %s]\n",
+ target_pid_to_str (temp_ptid));
+ add_thread (temp_ptid);
}
/* Return to WFI, but tell it to immediately resume. */
status->kind = TARGET_WAITKIND_SPURIOUS;
- return inferior_pid;
+ return inferior_ptid;
}
else if (syscall_is_lwp_exit (pi, what))
{
}
#endif
status->kind = TARGET_WAITKIND_SPURIOUS;
- return inferior_pid;
+ return inferior_ptid;
}
break;
case PR_REQUESTED:
else
{
/* If not in procinfo list, add it. */
- temp = proc_get_current_thread (pi);
- if (!find_procinfo (pi->pid, temp))
- create_procinfo (pi->pid, temp);
+ temp_tid = proc_get_current_thread (pi);
+ if (!find_procinfo (pi->pid, temp_tid))
+ create_procinfo (pi->pid, temp_tid);
/* If not in GDB's thread list, add it. */
- temp = MERGEPID (pi->pid, temp);
- if (!in_thread_list (temp))
+ temp_ptid = MERGEPID (pi->pid, temp_tid);
+ if (!in_thread_list (temp_ptid))
{
printf_filtered ("[New %s]\n",
- target_pid_to_str (temp));
- add_thread (temp);
+ target_pid_to_str (temp_ptid));
+ add_thread (temp_ptid);
}
status->kind = TARGET_WAITKIND_STOPPED;
break;
case FLTPAGE: /* Recoverable page fault */
default: /* FIXME: use si_signo if possible for fault */
- retval = -1;
+ retval = pid_to_ptid (-1);
printf_filtered ("procfs:%d -- ", __LINE__);
printf_filtered ("child stopped for unknown reason:\n");
proc_prettyprint_why (why, what, 1);
* Got this far without error:
* If retval isn't in the threads database, add it.
*/
- if (retval > 0 &&
- retval != inferior_pid &&
+ if (PIDGET (retval) > 0 &&
+ !ptid_equal (retval, inferior_ptid) &&
!in_thread_list (retval))
{
/*
/* In addition, it's possible that this is the first
* new thread we've seen, in which case we may not
- * have created entries for inferior_pid yet.
+ * have created entries for inferior_ptid yet.
*/
- if (TIDGET (inferior_pid) != 0)
+ if (TIDGET (inferior_ptid) != 0)
{
- if (!in_thread_list (inferior_pid))
- add_thread (inferior_pid);
- if (find_procinfo (PIDGET (inferior_pid),
- TIDGET (inferior_pid)) == NULL)
- create_procinfo (PIDGET (inferior_pid),
- TIDGET (inferior_pid));
+ if (!in_thread_list (inferior_ptid))
+ add_thread (inferior_ptid);
+ if (find_procinfo (PIDGET (inferior_ptid),
+ TIDGET (inferior_ptid)) == NULL)
+ create_procinfo (PIDGET (inferior_ptid),
+ TIDGET (inferior_ptid));
}
}
}
int nbytes = 0;
/* Find procinfo for main process */
- pi = find_procinfo_or_die (PIDGET (inferior_pid), 0);
+ pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0);
if (pi->as_fd == 0 &&
open_procinfo_files (pi, FD_AS) == 0)
{
*/
static void
-procfs_resume (int pid, int step, enum target_signal signo)
+procfs_resume (ptid_t ptid, int step, enum target_signal signo)
{
procinfo *pi, *thread;
int native_signo;
to proc_run_process (for use in the prrun struct by ioctl). */
/* Find procinfo for main process */
- pi = find_procinfo_or_die (PIDGET (inferior_pid), 0);
+ pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0);
/* First cut: ignore pid argument */
errno = 0;
/* Void the process procinfo's caches. */
invalidate_cache (NULL, pi, NULL);
- if (pid != -1)
+ if (PIDGET (ptid) != -1)
{
/* Resume a specific thread, presumably suppressing the others. */
- thread = find_procinfo (PIDGET (pid), TIDGET (pid));
- if (thread == NULL)
- warning ("procfs: resume can't find thread %d -- resuming all.",
- TIDGET (pid));
- else
+ thread = find_procinfo (PIDGET (ptid), TIDGET (ptid));
+ if (thread != NULL)
{
if (thread->tid != 0)
{
*/
static void
-procfs_notice_signals (int pid)
+procfs_notice_signals (ptid_t ptid)
{
gdb_sigset_t signals;
- procinfo *pi = find_procinfo_or_die (PIDGET (pid), 0);
+ procinfo *pi = find_procinfo_or_die (PIDGET (ptid), 0);
if (proc_get_traced_signals (pi, &signals) &&
register_gdb_signals (pi, &signals))
{
printf_filtered ("\tUsing the running image of %s %s via /proc.\n",
attach_flag? "attached": "child",
- target_pid_to_str (inferior_pid));
+ target_pid_to_str (inferior_ptid));
}
/*
static void
procfs_kill_inferior (void)
{
- if (inferior_pid != 0) /* ? */
+ if (!ptid_equal (inferior_ptid, null_ptid)) /* ? */
{
/* Find procinfo for main process */
- procinfo *pi = find_procinfo (PIDGET (inferior_pid), 0);
+ procinfo *pi = find_procinfo (PIDGET (inferior_ptid), 0);
if (pi)
unconditionally_kill_inferior (pi);
{
procinfo *pi;
- if (inferior_pid != 0)
+ if (!ptid_equal (inferior_ptid, null_ptid))
{
/* Find procinfo for main process */
- pi = find_procinfo (PIDGET (inferior_pid), 0);
+ pi = find_procinfo (PIDGET (inferior_ptid), 0);
if (pi)
destroy_procinfo (pi);
}
/* The 'process ID' we return to GDB is composed of
the actual process ID plus the lwp ID. */
- inferior_pid = MERGEPID (pi->pid, proc_get_current_thread (pi));
+ inferior_ptid = MERGEPID (pi->pid, proc_get_current_thread (pi));
#ifdef START_INFERIOR_TRAPS_EXPECTED
startup_inferior (START_INFERIOR_TRAPS_EXPECTED);
static int
procfs_notice_thread (procinfo *pi, procinfo *thread, void *ptr)
{
- int gdb_threadid = MERGEPID (pi->pid, thread->tid);
+ ptid_t gdb_threadid = MERGEPID (pi->pid, thread->tid);
if (!in_thread_list (gdb_threadid))
add_thread (gdb_threadid);
procinfo *pi;
/* Find procinfo for main process */
- pi = find_procinfo_or_die (PIDGET (inferior_pid), 0);
+ pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0);
proc_update_threads (pi);
proc_iterate_over_threads (pi, procfs_notice_thread, NULL);
}
*/
static int
-procfs_thread_alive (int pid)
+procfs_thread_alive (ptid_t ptid)
{
int proc, thread;
procinfo *pi;
- proc = PIDGET (pid);
- thread = TIDGET (pid);
+ proc = PIDGET (ptid);
+ thread = TIDGET (ptid);
/* If I don't know it, it ain't alive! */
if ((pi = find_procinfo (proc, thread)) == NULL)
return 0;
*/
char *
-procfs_pid_to_str (int pid)
+procfs_pid_to_str (ptid_t ptid)
{
static char buf[80];
int proc, thread;
procinfo *pi;
- proc = PIDGET (pid);
- thread = TIDGET (pid);
+ proc = PIDGET (ptid);
+ thread = TIDGET (ptid);
pi = find_procinfo (proc, thread);
if (thread == 0)
*/
int
-procfs_set_watchpoint (int pid, CORE_ADDR addr, int len, int rwflag, int after)
+procfs_set_watchpoint (ptid_t ptid, CORE_ADDR addr, int len, int rwflag,
+ int after)
{
#ifndef UNIXWARE
#ifndef AIX5
int pflags = 0;
procinfo *pi;
- pi = find_procinfo_or_die (pid == -1 ?
- PIDGET (inferior_pid) : PIDGET (pid), 0);
+ pi = find_procinfo_or_die (PIDGET (ptid) == -1 ?
+ PIDGET (inferior_ptid) : PIDGET (ptid), 0);
/* Translate from GDB's flags to /proc's */
if (len > 0) /* len == 0 means delete watchpoint */
*/
int
-procfs_stopped_by_watchpoint (int pid)
+procfs_stopped_by_watchpoint (ptid_t ptid)
{
procinfo *pi;
- pi = find_procinfo (pid == -1 ?
- PIDGET (inferior_pid) : PIDGET (pid), 0);
+ pi = find_procinfo_or_die (PIDGET (ptid) == -1 ?
+ PIDGET (inferior_ptid) : PIDGET (ptid), 0);
if (!pi) /* If no process, then not stopped by watchpoint! */
return 0;
* Function: procfs_find_LDT_entry
*
* Input:
- * int pid; // The GDB-style pid-plus-LWP.
+ * ptid_t ptid; // The GDB-style pid-plus-LWP.
*
* Return:
* pointer to the corresponding LDT entry.
*/
struct ssd *
-procfs_find_LDT_entry (int pid)
+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 (pid), TIDGET (pid))) == NULL)
+ if ((pi = find_procinfo (PIDGET (ptid), TIDGET (ptid))) == NULL)
{
- warning ("procfs_find_LDT_entry: could not find procinfi for %d.",
- pid);
+ warning ("procfs_find_LDT_entry: could not find procinfo for %d:%d.",
+ 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.",
- pid);
+ warning ("procfs_find_LDT_entry: could not read gregs for %d:%d.",
+ PIDGET (ptid), TIDGET (ptid));
return NULL;
}
/* Now extract the GS register's lower 16 bits. */
}
#endif /* TM_I386SOL2_H */
+/*
+ * Memory Mappings Functions:
+ */
+
+/*
+ * Function: iterate_over_mappings
+ *
+ * Call a callback function once for each mapping, passing it the mapping,
+ * an optional secondary callback function, and some optional opaque data.
+ * Quit and return the first non-zero value returned from the callback.
+ *
+ * Arguments:
+ * pi -- procinfo struct for the process to be mapped.
+ * func -- callback function to be called by this iterator.
+ * data -- optional opaque data to be passed to the callback function.
+ * child_func -- optional secondary function pointer to be passed
+ * to the child function.
+ *
+ * Return: First non-zero return value from the callback function,
+ * or zero.
+ */
+
+static int
+iterate_over_mappings (procinfo *pi, int (*child_func) (), void *data,
+ int (*func) (struct prmap *map,
+ int (*child_func) (),
+ void *data))
+{
+ char pathname[MAX_PROC_NAME_SIZE];
+ struct prmap *prmaps;
+ struct prmap *prmap;
+ int funcstat;
+ int map_fd;
+ int nmap;
+#ifdef NEW_PROC_API
+ struct stat sbuf;
+#endif
+
+ /* Get the number of mappings, allocate space,
+ and read the mappings into prmaps. */
+#ifdef NEW_PROC_API
+ /* Open map fd. */
+ sprintf (pathname, "/proc/%d/map", pi->pid);
+ if ((map_fd = open (pathname, O_RDONLY)) < 0)
+ proc_error (pi, "iterate_over_mappings (open)", __LINE__);
+
+ /* Make sure it gets closed again. */
+ make_cleanup_close (map_fd);
+
+ /* Use stat to determine the file size, and compute
+ the number of prmap_t objects it contains. */
+ if (fstat (map_fd, &sbuf) != 0)
+ proc_error (pi, "iterate_over_mappings (fstat)", __LINE__);
+
+ nmap = sbuf.st_size / sizeof (prmap_t);
+ prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps));
+ if (read (map_fd, (char *) prmaps, nmap * sizeof (*prmaps))
+ != (nmap * sizeof (*prmaps)))
+ proc_error (pi, "iterate_over_mappings (read)", __LINE__);
+#else
+ /* Use ioctl command PIOCNMAP to get number of mappings. */
+ if (ioctl (pi->ctl_fd, PIOCNMAP, &nmap) != 0)
+ proc_error (pi, "iterate_over_mappings (PIOCNMAP)", __LINE__);
+
+ prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps));
+ if (ioctl (pi->ctl_fd, PIOCMAP, prmaps) != 0)
+ proc_error (pi, "iterate_over_mappings (PIOCMAP)", __LINE__);
+#endif
+
+ for (prmap = prmaps; nmap > 0; prmap++, nmap--)
+ if ((funcstat = (*func) (prmap, child_func, data)) != 0)
+ return funcstat;
+
+ return 0;
+}
+
+/*
+ * Function: solib_mappings_callback
+ *
+ * Calls the supplied callback function once for each mapped address
+ * space in the process. The callback function receives an open
+ * file descriptor for the file corresponding to that mapped
+ * address space (if there is one), and the base address of the
+ * mapped space. Quit when the callback function returns a
+ * nonzero value, or at teh end of the mappings.
+ *
+ * Returns: the first non-zero return value of the callback function,
+ * or zero.
+ */
+
+int solib_mappings_callback (struct prmap *map,
+ int (*func) (int, CORE_ADDR),
+ void *data)
+{
+ procinfo *pi = data;
+ int fd;
+
+#ifdef NEW_PROC_API
+ char name[MAX_PROC_NAME_SIZE + sizeof (map->pr_mapname)];
+
+ if (map->pr_vaddr == 0 && map->pr_size == 0)
+ return -1; /* sanity */
+
+ if (map->pr_mapname[0] == 0)
+ {
+ fd = -1; /* no map file */
+ }
+ else
+ {
+ sprintf (name, "/proc/%d/object/%s", pi->pid, map->pr_mapname);
+ /* Note: caller's responsibility to close this fd! */
+ fd = open_with_retry (name, O_RDONLY);
+ /* Note: we don't test the above call for failure;
+ we just pass the FD on as given. Sometimes there is
+ no file, so the open may return failure, but that's
+ not a problem. */
+ }
+#else
+ fd = ioctl (pi->ctl_fd, PIOCOPENM, &map->pr_vaddr);
+ /* Note: we don't test the above call for failure;
+ we just pass the FD on as given. Sometimes there is
+ no file, so the ioctl may return failure, but that's
+ not a problem. */
+#endif
+ return (*func) (fd, (CORE_ADDR) map->pr_vaddr);
+}
+
+/*
+ * Function: proc_iterate_over_mappings
+ *
+ * Uses the unified "iterate_over_mappings" function
+ * to implement the exported interface to solib-svr4.c.
+ *
+ * Given a pointer to a function, call that function once for every
+ * mapped address space in the process. The callback function
+ * receives an open file descriptor for the file corresponding to
+ * that mapped address space (if there is one), and the base address
+ * of the mapped space. Quit when the callback function returns a
+ * nonzero value, or at teh end of the mappings.
+ *
+ * Returns: the first non-zero return value of the callback function,
+ * or zero.
+ */
+
+int
+proc_iterate_over_mappings (int (*func) (int, CORE_ADDR))
+{
+ procinfo *pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0);
+
+ return iterate_over_mappings (pi, func, pi, solib_mappings_callback);
+}
+
+/*
+ * Function: find_memory_regions_callback
+ *
+ * Implements the to_find_memory_regions method.
+ * Calls an external function for each memory region.
+ * External function will have the signiture:
+ *
+ * int callback (CORE_ADDR vaddr,
+ * unsigned long size,
+ * int read, int write, int execute,
+ * void *data);
+ *
+ * Returns the integer value returned by the callback.
+ */
+
+static int
+find_memory_regions_callback (struct prmap *map,
+ int (*func) (CORE_ADDR,
+ unsigned long,
+ int, int, int,
+ void *),
+ void *data)
+{
+ return (*func) ((CORE_ADDR) map->pr_vaddr,
+ map->pr_size,
+ (map->pr_mflags & MA_READ) != 0,
+ (map->pr_mflags & MA_WRITE) != 0,
+ (map->pr_mflags & MA_EXEC) != 0,
+ data);
+}
+
+/*
+ * Function: proc_find_memory_regions
+ *
+ * External interface. Calls a callback function once for each
+ * mapped memory region in the child process, passing as arguments
+ * CORE_ADDR virtual_address,
+ * unsigned long size,
+ * int read, TRUE if region is readable by the child
+ * int write, TRUE if region is writable by the child
+ * int execute TRUE if region is executable by the child.
+ *
+ * Stops iterating and returns the first non-zero value
+ * returned by the callback.
+ */
+
+static int
+proc_find_memory_regions (int (*func) (CORE_ADDR,
+ unsigned long,
+ int, int, int,
+ void *),
+ void *data)
+{
+ procinfo *pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0);
+
+ return iterate_over_mappings (pi, func, data,
+ find_memory_regions_callback);
+}
+
+/*
+ * Function: mappingflags
+ *
+ * Returns an ascii representation of a memory mapping's flags.
+ */
+
+static char *
+mappingflags (flags)
+ long flags;
+{
+ static char asciiflags[8];
+
+ strcpy (asciiflags, "-------");
+#if defined (MA_PHYS)
+ if (flags & MA_PHYS)
+ asciiflags[0] = 'd';
+#endif
+ if (flags & MA_STACK)
+ asciiflags[1] = 's';
+ if (flags & MA_BREAK)
+ asciiflags[2] = 'b';
+ if (flags & MA_SHARED)
+ asciiflags[3] = 's';
+ if (flags & MA_READ)
+ asciiflags[4] = 'r';
+ if (flags & MA_WRITE)
+ asciiflags[5] = 'w';
+ if (flags & MA_EXEC)
+ asciiflags[6] = 'x';
+ return (asciiflags);
+}
+
+/*
+ * Function: info_mappings_callback
+ *
+ * Callback function, does the actual work for 'info proc mappings'.
+ */
+
+/* ARGSUSED */
+static int
+info_mappings_callback (struct prmap *map, int (*ignore) (), void *unused)
+{
+ char *data_fmt_string;
+
+ if (TARGET_ADDR_BIT == 32)
+ data_fmt_string = "\t%#10lx %#10lx %#10x %#10x %7s\n";
+ else
+ data_fmt_string = " %#18lx %#18lx %#10x %#10x %7s\n";
+
+ printf_filtered (data_fmt_string,
+ (unsigned long) map->pr_vaddr,
+ (unsigned long) map->pr_vaddr + map->pr_size - 1,
+ map->pr_size,
+#ifdef PCAGENT /* Horrible hack: only defined on Solaris 2.6+ */
+ (unsigned int) map->pr_offset,
+#else
+ map->pr_off,
+#endif
+ mappingflags (map->pr_mflags));
+
+ return 0;
+}
+
+/*
+ * Function: info_proc_mappings
+ *
+ * Implement the "info proc mappings" subcommand.
+ */
+
+static void
+info_proc_mappings (procinfo *pi, int summary)
+{
+ char *header_fmt_string;
+
+ if (TARGET_PTR_BIT == 32)
+ header_fmt_string = "\t%10s %10s %10s %10s %7s\n";
+ else
+ header_fmt_string = " %18s %18s %10s %10s %7s\n";
+
+ if (summary)
+ return; /* No output for summary mode. */
+
+ printf_filtered ("Mapped address spaces:\n\n");
+ printf_filtered (header_fmt_string,
+ "Start Addr",
+ " End Addr",
+ " Size",
+ " Offset",
+ "Flags");
+ iterate_over_mappings (pi, NULL, NULL, info_mappings_callback);
+ printf_filtered ("\n");
+}
+
+/*
+ * Function: info_proc_cmd
+ *
+ * Implement the "info proc" command.
+ */
static void
info_proc_cmd (char *args, int from_tty)
{
struct cleanup *old_chain;
- procinfo *process = NULL;
- procinfo *thread = NULL;
- char **argv = NULL;
- char *tmp = NULL;
- int pid = 0;
- int tid = 0;
+ procinfo *process = NULL;
+ procinfo *thread = NULL;
+ char **argv = NULL;
+ char *tmp = NULL;
+ int pid = 0;
+ int tid = 0;
+ int mappings = 0;
old_chain = make_cleanup (null_cleanup, 0);
if (args)
{
tid = strtoul (argv[0] + 1, NULL, 10);
}
+ else if (strncmp (argv[0], "mappings", strlen (argv[0])) == 0)
+ {
+ mappings = 1;
+ }
else
{
/* [...] */
argv++;
}
if (pid == 0)
- pid = PIDGET (inferior_pid);
+ pid = PIDGET (inferior_ptid);
if (pid == 0)
error ("No current process: you must name one.");
else
proc_prettyprint_why (proc_why (thread), proc_what (thread), 1);
}
+ if (mappings)
+ {
+ info_proc_mappings (process, 0);
+ }
+
do_cleanups (old_chain);
}
sysset_t *sysset;
int syscallnum = 0;
- if (inferior_pid <= 0)
+ if (PIDGET (inferior_ptid) <= 0)
error ("you must be debugging a process to use this command.");
if (args == NULL || args[0] == 0)
error_no_arg ("system call to trace");
- pi = find_procinfo_or_die (PIDGET (inferior_pid), 0);
+ pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0);
if (isdigit (args[0]))
{
syscallnum = atoi (args);
init_procfs_ops ();
add_target (&procfs_ops);
add_info ("proc", info_proc_cmd,
- "Show /proc process information about any running process.\
-Default is the process being debugged.");
+ "Show /proc process information about any running process.\n\
+Specify process id, or use the program being debugged by default.\n\
+Specify keyword 'mappings' for detailed info on memory mappings.");
add_com ("proc-trace-entry", no_class, proc_trace_sysentry_cmd,
"Give a trace of entries into the syscall.");
add_com ("proc-trace-exit", no_class, proc_trace_sysexit_cmd,
-/* miscelaneous stubs: */
+/* miscellaneous stubs: */
/* The following satisfy a few random symbols mostly created by */
/* the solaris threads implementation, which I will chase down */
/* later. */
* we will be able to find a 'live' procinfo.
*/
-int
+ptid_t
procfs_first_available (void)
{
- if (procinfo_list)
- return procinfo_list->pid;
+ return pid_to_ptid (procinfo_list ? procinfo_list->pid : -1);
+}
+
+/* =================== GCORE .NOTE "MODULE" =================== */
+#if defined (UNIXWARE) || defined (PIOCOPENLWP) || defined (PCAGENT)
+/* gcore only implemented on solaris and unixware (so far) */
+
+static char *
+procfs_do_thread_registers (bfd *obfd, ptid_t ptid,
+ char *note_data, int *note_size)
+{
+ gdb_gregset_t gregs;
+ gdb_fpregset_t fpregs;
+ unsigned long merged_pid;
+
+ merged_pid = TIDGET (ptid) << 16 | PIDGET (ptid);
+
+ fill_gregset (&gregs, -1);
+#if defined (UNIXWARE)
+ note_data = (char *) elfcore_write_lwpstatus (obfd,
+ note_data,
+ note_size,
+ merged_pid,
+ stop_signal,
+ &gregs);
+#else
+ note_data = (char *) elfcore_write_prstatus (obfd,
+ note_data,
+ note_size,
+ merged_pid,
+ stop_signal,
+ &gregs);
+#endif
+ fill_fpregset (&fpregs, -1);
+ note_data = (char *) elfcore_write_prfpreg (obfd,
+ note_data,
+ note_size,
+ &fpregs,
+ sizeof (fpregs));
+ return note_data;
+}
+
+struct procfs_corefile_thread_data {
+ bfd *obfd;
+ char *note_data;
+ int *note_size;
+};
+
+static int
+procfs_corefile_thread_callback (procinfo *pi, procinfo *thread, void *data)
+{
+ struct procfs_corefile_thread_data *args = data;
+
+ if (pi != NULL && thread->tid != 0)
+ {
+ ptid_t saved_ptid = inferior_ptid;
+ inferior_ptid = MERGEPID (pi->pid, thread->tid);
+ args->note_data = procfs_do_thread_registers (args->obfd, inferior_ptid,
+ args->note_data,
+ args->note_size);
+ inferior_ptid = saved_ptid;
+ }
+ return 0;
+}
+
+static char *
+procfs_make_note_section (bfd *obfd, int *note_size)
+{
+ struct cleanup *old_chain;
+ gdb_gregset_t gregs;
+ gdb_fpregset_t fpregs;
+ char fname[16] = {'\0'};
+ char psargs[80] = {'\0'};
+ procinfo *pi = find_procinfo_or_die (PIDGET (inferior_ptid), 0);
+ char *note_data = NULL;
+ char *inf_args;
+ struct procfs_corefile_thread_data thread_args;
+
+ if (get_exec_file (0))
+ {
+ strncpy (fname, strrchr (get_exec_file (0), '/') + 1, sizeof (fname));
+ strncpy (psargs, get_exec_file (0),
+ sizeof (psargs));
+
+ inf_args = get_inferior_args ();
+ if (inf_args && *inf_args &&
+ strlen (inf_args) < ((int) sizeof (psargs) - (int) strlen (psargs)))
+ {
+ strncat (psargs, " ",
+ sizeof (psargs) - strlen (psargs));
+ strncat (psargs, inf_args,
+ sizeof (psargs) - strlen (psargs));
+ }
+ }
+
+ note_data = (char *) elfcore_write_prpsinfo (obfd,
+ note_data,
+ note_size,
+ fname,
+ psargs);
+
+#ifdef UNIXWARE
+ fill_gregset (&gregs, -1);
+ note_data = elfcore_write_pstatus (obfd, note_data, note_size,
+ PIDGET (inferior_ptid),
+ stop_signal, &gregs);
+#endif
+
+ thread_args.obfd = obfd;
+ thread_args.note_data = note_data;
+ thread_args.note_size = note_size;
+ proc_iterate_over_threads (pi, procfs_corefile_thread_callback, &thread_args);
+
+ if (thread_args.note_data == note_data)
+ {
+ /* iterate_over_threads didn't come up with any threads;
+ just use inferior_ptid. */
+ note_data = procfs_do_thread_registers (obfd, inferior_ptid,
+ note_data, note_size);
+ }
else
- return -1;
+ {
+ note_data = thread_args.note_data;
+ }
+
+ make_cleanup (xfree, note_data);
+ return note_data;
+}
+#else /* !(Solaris or Unixware) */
+static char *
+procfs_make_note_section (bfd *obfd, int *note_size)
+{
+ error ("gcore not implemented for this host.");
+ return NULL; /* lint */
}
+#endif /* Solaris or Unixware */
+/* =================== END GCORE .NOTE "MODULE" =================== */