aboutsummaryrefslogtreecommitdiff
path: root/gdb/linux-proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/linux-proc.c')
-rw-r--r--gdb/linux-proc.c243
1 files changed, 241 insertions, 2 deletions
diff --git a/gdb/linux-proc.c b/gdb/linux-proc.c
index 4e25923..4d48b8d 100644
--- a/gdb/linux-proc.c
+++ b/gdb/linux-proc.c
@@ -1,5 +1,5 @@
-/* Generate a core file for the inferior process -- Linux version.
- Copyright 2001 Free Software Foundation, Inc.
+/* Linux-specific methods for using the /proc file system.
+ Copyright 2001, 2002 Free Software Foundation, Inc.
This file is part of GDB.
@@ -19,7 +19,20 @@
Boston, MA 02111-1307, USA. */
#include "defs.h"
+#include "inferior.h"
#include <sys/param.h> /* for MAXPATHLEN */
+#include <sys/procfs.h>
+#include "gregset.h" /* for gregset */
+#include "gdbcore.h" /* for get_exec_file */
+#include "gdbthread.h" /* for struct thread_info etc. */
+#include "elf-bfd.h"
+
+/* Function: child_pid_to_exec_file
+ *
+ * Accepts an integer pid
+ * Returns a string representing a file that can be opened
+ * to get the symbols for the child process.
+ */
char *
child_pid_to_exec_file (int pid)
@@ -30,3 +43,229 @@ child_pid_to_exec_file (int pid)
/* FIXME use readlink to get the real name. */
return fname;
}
+
+/* Function: linux_find_memory_regions
+ *
+ * Fills the "to_find_memory_regions" target vector.
+ * Lists the memory regions in the inferior for a corefile.
+ */
+
+static int
+linux_find_memory_regions (int (*func) (CORE_ADDR,
+ unsigned long,
+ int, int, int,
+ void *),
+ void *obfd)
+{
+ long long pid = PIDGET (inferior_ptid);
+ char procfilename[MAXPATHLEN];
+ FILE *procfile;
+ long long addr, endaddr, size, offset, inode;
+ char perms[8], dev[8], filename[MAXPATHLEN];
+ int read, write, exec;
+ int ret;
+
+ /* Compose the filename for the /proc memory map, and open it. */
+ sprintf (procfilename, "/proc/%lld/maps", pid);
+ if ((procfile = fopen (procfilename, "r")) == NULL)
+ error ("Could not open %s\n", procfilename);
+
+ if (info_verbose)
+ fprintf_filtered (gdb_stdout,
+ "Reading memory regions from %s\n", procfilename);
+
+ /* Read the first memory segment descriptor from the maps file. */
+ ret = fscanf (procfile, "%llx-%llx %s %llx %s %llx",
+ &addr, &endaddr, perms, &offset, dev, &inode);
+ if (inode)
+ fscanf (procfile, " %s\n", filename);
+ else
+ {
+ filename[0] = '\0';
+ fscanf (procfile, "\n");
+ }
+
+ /* Now iterate until end-of-file. */
+ while (ret > 0 && ret != EOF)
+ {
+ size = endaddr - addr;
+
+ /* Get the segment's permissions. */
+ read = (strchr (perms, 'r') != 0);
+ write = (strchr (perms, 'w') != 0);
+ exec = (strchr (perms, 'x') != 0);
+
+ if (info_verbose)
+ {
+ fprintf_filtered (gdb_stdout,
+ "Save segment, %lld bytes at 0x%s (%c%c%c)",
+ size, paddr_nz (addr),
+ read ? 'r' : ' ',
+ write ? 'w' : ' ',
+ exec ? 'x' : ' ');
+ if (filename && filename[0])
+ fprintf_filtered (gdb_stdout,
+ " for %s", filename);
+ fprintf_filtered (gdb_stdout, "\n");
+ }
+
+ /* Invoke the callback function to create the corefile segment. */
+ func (addr, size, read, write, exec, obfd);
+
+ /* Read the next memory region. */
+ filename[0] = '\0';
+ ret = fscanf (procfile, "%llx-%llx %s %llx %s %llx",
+ &addr, &endaddr, perms, &offset, dev, &inode);
+ if (inode)
+ fscanf (procfile, " %s\n", filename);
+ else
+ {
+ filename[0] = '\0';
+ fscanf (procfile, "\n");
+ }
+ }
+
+ fclose (procfile);
+ return 0;
+}
+
+/* Function: linux_do_thread_registers
+ *
+ * Records the thread's register state for the corefile note section.
+ */
+
+static char *
+linux_do_thread_registers (bfd *obfd, ptid_t ptid,
+ char *note_data, int *note_size)
+{
+ gdb_gregset_t gregs;
+ gdb_fpregset_t fpregs;
+#ifdef HAVE_PTRACE_GETFPXREGS
+ gdb_fpxregset_t fpxregs;
+#endif
+ unsigned long merged_pid = ptid_get_tid (ptid) << 16 | ptid_get_pid (ptid);
+
+ fill_gregset (&gregs, -1);
+ note_data = (char *) elfcore_write_prstatus (obfd,
+ note_data,
+ note_size,
+ merged_pid,
+ stop_signal,
+ &gregs);
+
+ fill_fpregset (&fpregs, -1);
+ note_data = (char *) elfcore_write_prfpreg (obfd,
+ note_data,
+ note_size,
+ &fpregs,
+ sizeof (fpregs));
+#ifdef HAVE_PTRACE_GETFPXREGS
+ fill_fpxregset (&fpxregs, -1);
+ note_data = (char *) elfcore_write_prxfpreg (obfd,
+ note_data,
+ note_size,
+ &fpxregs,
+ sizeof (fpxregs));
+#endif
+ return note_data;
+}
+
+struct linux_corefile_thread_data {
+ bfd *obfd;
+ char *note_data;
+ int *note_size;
+};
+
+/* Function: linux_corefile_thread_callback
+ *
+ * Called by gdbthread.c once per thread.
+ * Records the thread's register state for the corefile note section.
+ */
+
+static int
+linux_corefile_thread_callback (struct thread_info *ti, void *data)
+{
+ struct linux_corefile_thread_data *args = data;
+ ptid_t saved_ptid = inferior_ptid;
+
+ inferior_ptid = ti->ptid;
+ registers_changed ();
+ target_fetch_registers (-1); /* FIXME should not be necessary;
+ fill_gregset should do it automatically. */
+ args->note_data = linux_do_thread_registers (args->obfd,
+ ti->ptid,
+ args->note_data,
+ args->note_size);
+ inferior_ptid = saved_ptid;
+ registers_changed ();
+ target_fetch_registers (-1); /* FIXME should not be necessary;
+ fill_gregset should do it automatically. */
+ return 0;
+}
+
+/* Function: linux_make_note_section
+ *
+ * Fills the "to_make_corefile_note" target vector.
+ * Builds the note section for a corefile, and returns it
+ * in a malloc buffer.
+ */
+
+static char *
+linux_make_note_section (bfd *obfd, int *note_size)
+{
+ struct linux_corefile_thread_data thread_args;
+ struct cleanup *old_chain;
+ char fname[16] = {'\0'};
+ char psargs[80] = {'\0'};
+ char *note_data = NULL;
+ ptid_t current_ptid = inferior_ptid;
+
+ if (get_exec_file (0))
+ {
+ strncpy (fname, strrchr (get_exec_file (0), '/') + 1, sizeof (fname));
+ strncpy (psargs, get_exec_file (0),
+ sizeof (psargs));
+ if (get_inferior_args ())
+ {
+ strncat (psargs, " ",
+ sizeof (psargs) - strlen (psargs));
+ strncat (psargs, get_inferior_args (),
+ sizeof (psargs) - strlen (psargs));
+ }
+ note_data = (char *) elfcore_write_prpsinfo (obfd,
+ note_data,
+ note_size,
+ fname,
+ psargs);
+ }
+
+ /* Dump information for threads. */
+ thread_args.obfd = obfd;
+ thread_args.note_data = note_data;
+ thread_args.note_size = note_size;
+ iterate_over_threads (linux_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 = linux_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;
+}
+
+void
+_initialize_linux_proc (void)
+{
+ extern void inftarg_set_find_memory_regions ();
+ extern void inftarg_set_make_corefile_notes ();
+
+ inftarg_set_find_memory_regions (linux_find_memory_regions);
+ inftarg_set_make_corefile_notes (linux_make_note_section);
+}