]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdb/procfs.c
2003-02-06 Andrew Cagney <ac131313@redhat.com>
[thirdparty/binutils-gdb.git] / gdb / procfs.c
index 990130c680fd4701eeb2134642977e01de45e3c4..969bbcb2b614aa921cf8610a35ec6f9728e13abf 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
 
@@ -23,6 +23,7 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "inferior.h"
 #include "target.h"
 #include "gdbcore.h"
+#include "elf-bfd.h"           /* for elfcore_write_* */
 #include "gdbcmd.h"
 #include "gdbthread.h"
 
@@ -41,6 +42,8 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include <sys/wait.h>
 #include <signal.h>
 #include <ctype.h>
+#include "gdb_assert.h"
+#include "inflow.h"
 
 /* 
  * PROCFS.C
@@ -55,7 +58,7 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  *   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.
@@ -127,52 +130,66 @@ static int procfs_thread_alive (ptid_t);
 void procfs_find_new_threads (void);
 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 *);
+
+static int procfs_can_use_hw_breakpoint (int, int, 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_save_ours  = terminal_save_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_can_use_hw_breakpoint = procfs_can_use_hw_breakpoint;
+  procfs_ops.to_magic               = OPS_MAGIC;
 }
 
 /* =================== END, TARGET_OPS "MODULE" =================== */
@@ -674,6 +691,9 @@ create_procinfo (int pid, int tid)
   load_syscalls (pi);
 #endif
 
+  pi->saved_entryset = sysset_t_alloc (pi);
+  pi->saved_exitset = sysset_t_alloc (pi);
+
   /* Chain into list.  */
   if (tid == 0)
     {
@@ -743,6 +763,8 @@ destroy_one_procinfo (procinfo **list, procinfo *pi)
 #ifdef DYNAMIC_SYSCALLS
   free_syscalls (pi);
 #endif
+  xfree (pi->saved_entryset);
+  xfree (pi->saved_exitset);
   xfree (pi);
 }
 
@@ -2821,6 +2843,19 @@ proc_parent_pid (procinfo *pi)
 }
 
 
+/* Convert a target address (a.k.a. CORE_ADDR) into a host address
+   (a.k.a void pointer)!  */
+
+static void *
+procfs_address_to_host_pointer (CORE_ADDR addr)
+{
+  void *ptr;
+
+  gdb_assert (sizeof (ptr) == TYPE_LENGTH (builtin_type_void_data_ptr));
+  ADDRESS_TO_POINTER (builtin_type_void_data_ptr, &ptr, addr);
+  return ptr;
+}
+
 /*
  * Function: proc_set_watchpoint
  *
@@ -2843,7 +2878,14 @@ proc_set_watchpoint (procinfo *pi, CORE_ADDR addr, int len, int wflags)
   prwatch_t *pwatch;
 
   pwatch            = (prwatch_t *) &arg.watch;
-  pwatch->pr_vaddr  = address_to_host_pointer (addr);
+  /* 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)
@@ -2860,118 +2902,6 @@ proc_set_watchpoint (procinfo *pi, CORE_ADDR addr, int len, int wflags)
 #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_ptid), 0);
-
-#ifdef NEW_PROC_API
-  /* Open map fd.  */
-  sprintf (pathname, "/proc/%d/map", pi->pid);
-  if ((map_fd = open_with_retry (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_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 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>
@@ -3715,6 +3645,9 @@ do_detach (int signo)
        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__);
       }
@@ -4674,8 +4607,6 @@ procfs_can_run (void)
 static void
 procfs_stop (void)
 {
-  extern pid_t inferior_process_group;
-
   kill (-inferior_process_group, SIGINT);
 }
 
@@ -5230,6 +5161,38 @@ procfs_set_watchpoint (ptid_t ptid, CORE_ADDR addr, int len, int rwflag,
   return 0;
 }
 
+/* Return non-zero if we can set a hardware watchpoint of type TYPE.  TYPE
+   is one of bp_hardware_watchpoint, bp_read_watchpoint, bp_write_watchpoint,
+   or bp_hardware_watchpoint.  CNT is the number of watchpoints used so
+   far.
+   
+   Note:  procfs_can_use_hw_breakpoint() is not yet used by all
+   procfs.c targets due to the fact that some of them still define
+   TARGET_CAN_USE_HARDWARE_WATCHPOINT.  */
+
+static int
+procfs_can_use_hw_breakpoint (int type, int cnt, int othertype)
+{
+#ifndef TARGET_HAS_HARDWARE_WATCHPOINTS
+  return 0;
+#else
+  /* Due to the way that proc_set_watchpoint() is implemented, host
+     and target pointers must be of the same size.  If they are not,
+     we can't use hardware watchpoints.  This limitation is due to the
+     fact that proc_set_watchpoint() calls
+     procfs_address_to_host_pointer(); a close inspection of
+     procfs_address_to_host_pointer will reveal that an internal error
+     will be generated when the host and target pointer sizes are
+     different.  */
+  if (sizeof (void *) != TYPE_LENGTH (builtin_type_void_data_ptr))
+    return 0;
+
+  /* Other tests here???  */
+
+  return 1;
+#endif
+}
+
 /*
  * Function: stopped_by_watchpoint
  *
@@ -5305,18 +5268,327 @@ procfs_find_LDT_entry (ptid_t ptid)
 }
 #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 (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)
@@ -5338,6 +5610,10 @@ info_proc_cmd (char *args, int from_tty)
        {
          tid = strtoul (argv[0] + 1, NULL, 10);
        }
+      else if (strncmp (argv[0], "mappings", strlen (argv[0])) == 0)
+       {
+         mappings = 1;
+       }
       else
        {
          /* [...] */
@@ -5384,6 +5660,11 @@ info_proc_cmd (char *args, int from_tty)
        proc_prettyprint_why (proc_why (thread), proc_what (thread), 1);
     }
 
+  if (mappings)
+    {
+      info_proc_mappings (process, 0);
+    }
+
   do_cleanups (old_chain);
 }
 
@@ -5461,8 +5742,9 @@ _initialize_procfs (void)
   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, 
@@ -5477,7 +5759,7 @@ Default is the process being debugged.");
 
 
 
-/* miscelaneous stubs:                                             */
+/* miscellaneous stubs:                                             */
 /* The following satisfy a few random symbols mostly created by    */
 /* the solaris threads implementation, which I will chase down     */
 /* later.        */
@@ -5492,3 +5774,138 @@ procfs_first_available (void)
 {
   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
+    {
+      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" =================== */