* gdbarch.sh (make_corefile_notes): New architecture callback.
authorUlrich Weigand <uweigand@de.ibm.com>
Fri, 20 Jan 2012 09:56:56 +0000 (09:56 +0000)
committerUlrich Weigand <uweigand@de.ibm.com>
Fri, 20 Jan 2012 09:56:56 +0000 (09:56 +0000)
* gdbarch.c: Regenerate.
* gdbarch.h: Likewise.

* gcore.c (write_gcore_file): Try gdbarch_make_corefile_notes
before target_make_corefile_notes.  If NULL is returned, the
target does not support core file generation.

* linux-nat.c: Include "linux-tdep.h".
(find_signalled_thread, find_stop_signal): Remove.
(linux_nat_do_thread_registers): Likewise.
(struct linux_nat_corefile_thread_data): Likewise.
(linux_nat_corefile_thread_callback): Likewise.
(iterate_over_spus): Likewise.
(struct linux_spu_corefile_data): Likewise.
(linux_spu_corefile_callback): Likewise.
(linux_spu_make_corefile_notes): Likewise.
(linux_nat_collect_thread_registers): New function.
(linux_nat_make_corefile_notes): Replace contents by call to
linux_make_corefile_notes passing linux_nat_collect_thread_registers
as native-only callback.

* linux-tdep.h: Include "bfd.h".
(struct regcache): Add forward declaration.
(linux_collect_thread_registers_ftype): New typedef.
(linux_make_corefile_notes): Add prototype.
* linux-tdep.c: Include "gdbthread.h", "gdbcore.h", "regcache.h",
"regset.h", and "elf-bfd.h".
(find_signalled_thread, find_stop_signal): New functions.
(linux_spu_make_corefile_notes): Likewise.
(linux_collect_thread_registers): Likewise.
(struct linux_corefile_thread_data): New data structure.
(linux_corefile_thread_callback): New funcion.
(linux_make_corefile_notes): Likewise.
(linux_make_corefile_notes_1): Likewise.
(linux_init_abi): Install it.

gdb/ChangeLog
gdb/gcore.c
gdb/gdbarch.c
gdb/gdbarch.h
gdb/gdbarch.sh
gdb/linux-nat.c
gdb/linux-tdep.c
gdb/linux-tdep.h

index 1e41e81..ab0407e 100644 (file)
@@ -1,5 +1,44 @@
 2012-01-20  Ulrich Weigand  <ulrich.weigand@linaro.org>
 
+       * gdbarch.sh (make_corefile_notes): New architecture callback.
+       * gdbarch.c: Regenerate.
+       * gdbarch.h: Likewise.
+
+       * gcore.c (write_gcore_file): Try gdbarch_make_corefile_notes
+       before target_make_corefile_notes.  If NULL is returned, the
+       target does not support core file generation.
+
+       * linux-nat.c: Include "linux-tdep.h".
+       (find_signalled_thread, find_stop_signal): Remove.
+       (linux_nat_do_thread_registers): Likewise.
+       (struct linux_nat_corefile_thread_data): Likewise.
+       (linux_nat_corefile_thread_callback): Likewise.
+       (iterate_over_spus): Likewise.
+       (struct linux_spu_corefile_data): Likewise.
+       (linux_spu_corefile_callback): Likewise.
+       (linux_spu_make_corefile_notes): Likewise.
+       (linux_nat_collect_thread_registers): New function.
+       (linux_nat_make_corefile_notes): Replace contents by call to
+       linux_make_corefile_notes passing linux_nat_collect_thread_registers
+       as native-only callback.
+
+       * linux-tdep.h: Include "bfd.h".
+       (struct regcache): Add forward declaration.
+       (linux_collect_thread_registers_ftype): New typedef.
+       (linux_make_corefile_notes): Add prototype.
+       * linux-tdep.c: Include "gdbthread.h", "gdbcore.h", "regcache.h",
+       "regset.h", and "elf-bfd.h".
+       (find_signalled_thread, find_stop_signal): New functions.
+       (linux_spu_make_corefile_notes): Likewise.
+       (linux_collect_thread_registers): Likewise.
+       (struct linux_corefile_thread_data): New data structure.
+       (linux_corefile_thread_callback): New funcion.
+       (linux_make_corefile_notes): Likewise.
+       (linux_make_corefile_notes_1): Likewise.
+       (linux_init_abi): Install it.
+
+2012-01-20  Ulrich Weigand  <ulrich.weigand@linaro.org>
+
        * gdbarch.sh (info_proc): New callback.
        * gdbarch.c, gdbarch.h: Regenerate.
 
index f9e7590..2a769d3 100644 (file)
@@ -71,35 +71,37 @@ write_gcore_file (bfd *obfd)
   asection *note_sec = NULL;
 
   /* An external target method must build the notes section.  */
-  note_data = target_make_corefile_notes (obfd, &note_size);
+  /* FIXME: uweigand/2011-10-06: All architectures that support core file
+     generation should be converted to gdbarch_make_corefile_notes; at that
+     point, the target vector method can be removed.  */
+  if (!gdbarch_make_corefile_notes_p (target_gdbarch))
+    note_data = target_make_corefile_notes (obfd, &note_size);
+  else
+    note_data = gdbarch_make_corefile_notes (target_gdbarch, obfd, &note_size);
 
-  /* Create the note section.  */
-  if (note_data != NULL && note_size != 0)
-    {
-      note_sec = bfd_make_section_anyway_with_flags (obfd, "note0",
-                                                    SEC_HAS_CONTENTS
-                                                    | SEC_READONLY
-                                                    | SEC_ALLOC);
-      if (note_sec == NULL)
-       error (_("Failed to create 'note' section for corefile: %s"),
-              bfd_errmsg (bfd_get_error ()));
+  if (note_data == NULL || note_size == 0)
+    error (_("Target does not support core file generation."));
 
-      bfd_set_section_vma (obfd, note_sec, 0);
-      bfd_set_section_alignment (obfd, note_sec, 0);
-      bfd_set_section_size (obfd, note_sec, note_size);
-    }
+  /* Create the note section.  */
+  note_sec = bfd_make_section_anyway_with_flags (obfd, "note0",
+                                                SEC_HAS_CONTENTS
+                                                | SEC_READONLY
+                                                | SEC_ALLOC);
+  if (note_sec == NULL)
+    error (_("Failed to create 'note' section for corefile: %s"),
+          bfd_errmsg (bfd_get_error ()));
+
+  bfd_set_section_vma (obfd, note_sec, 0);
+  bfd_set_section_alignment (obfd, note_sec, 0);
+  bfd_set_section_size (obfd, note_sec, note_size);
 
   /* Now create the memory/load sections.  */
   if (gcore_memory_sections (obfd) == 0)
     error (_("gcore: failed to get corefile memory sections from target."));
 
   /* Write out the contents of the note section.  */
-  if (note_data != NULL && note_size != 0)
-    {
-      if (!bfd_set_section_contents (obfd, note_sec, note_data, 0, note_size))
-       warning (_("writing note section (%s)"), 
-                bfd_errmsg (bfd_get_error ()));
-    }
+  if (!bfd_set_section_contents (obfd, note_sec, note_data, 0, note_size))
+    warning (_("writing note section (%s)"), bfd_errmsg (bfd_get_error ()));
 }
 
 static void
index 2dbdda9..7a1db10 100644 (file)
@@ -239,6 +239,7 @@ struct gdbarch
   gdbarch_fetch_pointer_argument_ftype *fetch_pointer_argument;
   gdbarch_regset_from_core_section_ftype *regset_from_core_section;
   struct core_regset_section * core_regset_sections;
+  gdbarch_make_corefile_notes_ftype *make_corefile_notes;
   gdbarch_core_xfer_shared_libraries_ftype *core_xfer_shared_libraries;
   gdbarch_core_pid_to_str_ftype *core_pid_to_str;
   const char * gcore_bfd_target;
@@ -395,6 +396,7 @@ struct gdbarch startup_gdbarch =
   0,  /* fetch_pointer_argument */
   0,  /* regset_from_core_section */
   0,  /* core_regset_sections */
+  0,  /* make_corefile_notes */
   0,  /* core_xfer_shared_libraries */
   0,  /* core_pid_to_str */
   0,  /* gcore_bfd_target */
@@ -683,6 +685,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of register_reggroup_p, invalid_p == 0 */
   /* Skip verify of fetch_pointer_argument, has predicate.  */
   /* Skip verify of regset_from_core_section, has predicate.  */
+  /* Skip verify of make_corefile_notes, has predicate.  */
   /* Skip verify of core_xfer_shared_libraries, has predicate.  */
   /* Skip verify of core_pid_to_str, has predicate.  */
   /* Skip verify of gcore_bfd_target, has predicate.  */
@@ -1039,6 +1042,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: long_long_bit = %s\n",
                       plongest (gdbarch->long_long_bit));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_make_corefile_notes_p() = %d\n",
+                      gdbarch_make_corefile_notes_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: make_corefile_notes = <%s>\n",
+                      host_address_to_string (gdbarch->make_corefile_notes));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: gdbarch_max_insn_length_p() = %d\n",
                       gdbarch_max_insn_length_p (gdbarch));
   fprintf_unfiltered (file,
@@ -3245,6 +3254,30 @@ set_gdbarch_core_regset_sections (struct gdbarch *gdbarch,
 }
 
 int
+gdbarch_make_corefile_notes_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->make_corefile_notes != NULL;
+}
+
+char *
+gdbarch_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->make_corefile_notes != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_make_corefile_notes called\n");
+  return gdbarch->make_corefile_notes (gdbarch, obfd, note_size);
+}
+
+void
+set_gdbarch_make_corefile_notes (struct gdbarch *gdbarch,
+                                 gdbarch_make_corefile_notes_ftype make_corefile_notes)
+{
+  gdbarch->make_corefile_notes = make_corefile_notes;
+}
+
+int
 gdbarch_core_xfer_shared_libraries_p (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
index 7d1671d..b611760 100644 (file)
@@ -709,6 +709,14 @@ extern void set_gdbarch_regset_from_core_section (struct gdbarch *gdbarch, gdbar
 extern struct core_regset_section * gdbarch_core_regset_sections (struct gdbarch *gdbarch);
 extern void set_gdbarch_core_regset_sections (struct gdbarch *gdbarch, struct core_regset_section * core_regset_sections);
 
+/* Create core file notes */
+
+extern int gdbarch_make_corefile_notes_p (struct gdbarch *gdbarch);
+
+typedef char * (gdbarch_make_corefile_notes_ftype) (struct gdbarch *gdbarch, bfd *obfd, int *note_size);
+extern char * gdbarch_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size);
+extern void set_gdbarch_make_corefile_notes (struct gdbarch *gdbarch, gdbarch_make_corefile_notes_ftype *make_corefile_notes);
+
 /* Read offset OFFSET of TARGET_OBJECT_LIBRARIES formatted shared libraries list from
    core file into buffer READBUF with length LEN. */
 
index eb8527c..c4ff5d1 100755 (executable)
@@ -631,6 +631,9 @@ M:const struct regset *:regset_from_core_section:const char *sect_name, size_t s
 # Supported register notes in a core file.
 v:struct core_regset_section *:core_regset_sections:const char *name, int len::::::host_address_to_string (gdbarch->core_regset_sections)
 
+# Create core file notes
+M:char *:make_corefile_notes:bfd *obfd, int *note_size:obfd, note_size
+
 # Read offset OFFSET of TARGET_OBJECT_LIBRARIES formatted shared libraries list from
 # core file into buffer READBUF with length LEN.
 M:LONGEST:core_xfer_shared_libraries:gdb_byte *readbuf, ULONGEST offset, LONGEST len:readbuf, offset, len
index 0a46e83..3a40c03 100644 (file)
@@ -58,6 +58,7 @@
 #include <sys/vfs.h>
 #include "solib.h"
 #include "linux-osdata.h"
+#include "linux-tdep.h"
 
 #ifndef SPUFS_MAGIC
 #define SPUFS_MAGIC 0x23c9b64e
@@ -4479,325 +4480,59 @@ linux_nat_find_memory_regions (find_memory_region_ftype func, void *obfd)
   return 0;
 }
 
-static int
-find_signalled_thread (struct thread_info *info, void *data)
-{
-  if (info->suspend.stop_signal != TARGET_SIGNAL_0
-      && ptid_get_pid (info->ptid) == ptid_get_pid (inferior_ptid))
-    return 1;
-
-  return 0;
-}
-
-static enum target_signal
-find_stop_signal (void)
-{
-  struct thread_info *info =
-    iterate_over_threads (find_signalled_thread, NULL);
-
-  if (info)
-    return info->suspend.stop_signal;
-  else
-    return TARGET_SIGNAL_0;
-}
-
 /* Records the thread's register state for the corefile note
    section.  */
 
 static char *
-linux_nat_do_thread_registers (bfd *obfd, ptid_t ptid,
-                              char *note_data, int *note_size,
-                              enum target_signal stop_signal)
+linux_nat_collect_thread_registers (const struct regcache *regcache,
+                                   ptid_t ptid, bfd *obfd,
+                                   char *note_data, int *note_size,
+                                   enum target_signal stop_signal)
 {
-  unsigned long lwp = ptid_get_lwp (ptid);
-  struct gdbarch *gdbarch = target_gdbarch;
-  struct regcache *regcache = get_thread_arch_regcache (ptid, gdbarch);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
   const struct regset *regset;
   int core_regset_p;
-  struct cleanup *old_chain;
-  struct core_regset_section *sect_list;
-  char *gdb_regset;
-
-  old_chain = save_inferior_ptid ();
-  inferior_ptid = ptid;
-  target_fetch_registers (regcache, -1);
-  do_cleanups (old_chain);
+  gdb_gregset_t gregs;
+  gdb_fpregset_t fpregs;
 
   core_regset_p = gdbarch_regset_from_core_section_p (gdbarch);
-  sect_list = gdbarch_core_regset_sections (gdbarch);
-
-  /* The loop below uses the new struct core_regset_section, which stores
-     the supported section names and sizes for the core file.  Note that
-     note PRSTATUS needs to be treated specially.  But the other notes are
-     structurally the same, so they can benefit from the new struct.  */
-  if (core_regset_p && sect_list != NULL)
-    while (sect_list->sect_name != NULL)
-      {
-       regset = gdbarch_regset_from_core_section (gdbarch,
-                                                  sect_list->sect_name,
-                                                  sect_list->size);
-       gdb_assert (regset && regset->collect_regset);
-       gdb_regset = xmalloc (sect_list->size);
-       regset->collect_regset (regset, regcache, -1,
-                               gdb_regset, sect_list->size);
-
-       if (strcmp (sect_list->sect_name, ".reg") == 0)
-         note_data = (char *) elfcore_write_prstatus
-                               (obfd, note_data, note_size,
-                                lwp, target_signal_to_host (stop_signal),
-                                gdb_regset);
-       else
-         note_data = (char *) elfcore_write_register_note
-                               (obfd, note_data, note_size,
-                                sect_list->sect_name, gdb_regset,
-                                sect_list->size);
-       xfree (gdb_regset);
-       sect_list++;
-      }
 
-  /* For architectures that does not have the struct core_regset_section
-     implemented, we use the old method.  When all the architectures have
-     the new support, the code below should be deleted.  */
+  if (core_regset_p
+      && (regset = gdbarch_regset_from_core_section (gdbarch, ".reg",
+                                                    sizeof (gregs)))
+        != NULL && regset->collect_regset != NULL)
+    regset->collect_regset (regset, regcache, -1, &gregs, sizeof (gregs));
   else
-    {
-      gdb_gregset_t gregs;
-      gdb_fpregset_t fpregs;
-
-      if (core_regset_p
-         && (regset = gdbarch_regset_from_core_section (gdbarch, ".reg",
-                                                        sizeof (gregs)))
-         != NULL && regset->collect_regset != NULL)
-       regset->collect_regset (regset, regcache, -1,
-                               &gregs, sizeof (gregs));
-      else
-       fill_gregset (regcache, &gregs, -1);
+    fill_gregset (regcache, &gregs, -1);
 
-      note_data = (char *) elfcore_write_prstatus
-       (obfd, note_data, note_size, lwp, target_signal_to_host (stop_signal),
-        &gregs);
+  note_data = (char *) elfcore_write_prstatus
+                        (obfd, note_data, note_size, ptid_get_lwp (ptid),
+                         target_signal_to_host (stop_signal), &gregs);
 
-      if (core_regset_p
-          && (regset = gdbarch_regset_from_core_section (gdbarch, ".reg2",
-                                                        sizeof (fpregs)))
+  if (core_regset_p
+      && (regset = gdbarch_regset_from_core_section (gdbarch, ".reg2",
+                                                    sizeof (fpregs)))
          != NULL && regset->collect_regset != NULL)
-       regset->collect_regset (regset, regcache, -1,
-                               &fpregs, sizeof (fpregs));
-      else
-       fill_fpregset (regcache, &fpregs, -1);
+    regset->collect_regset (regset, regcache, -1, &fpregs, sizeof (fpregs));
+  else
+    fill_fpregset (regcache, &fpregs, -1);
 
-      note_data = (char *) elfcore_write_prfpreg (obfd,
-                                                 note_data,
-                                                 note_size,
-                                                 &fpregs, sizeof (fpregs));
-    }
+  note_data = (char *) elfcore_write_prfpreg (obfd, note_data, note_size,
+                                             &fpregs, sizeof (fpregs));
 
   return note_data;
 }
 
-struct linux_nat_corefile_thread_data
-{
-  bfd *obfd;
-  char *note_data;
-  int *note_size;
-  int num_notes;
-  enum target_signal stop_signal;
-};
-
-/* Called by gdbthread.c once per thread.  Records the thread's
-   register state for the corefile note section.  */
-
-static int
-linux_nat_corefile_thread_callback (struct lwp_info *ti, void *data)
-{
-  struct linux_nat_corefile_thread_data *args = data;
-
-  args->note_data = linux_nat_do_thread_registers (args->obfd,
-                                                  ti->ptid,
-                                                  args->note_data,
-                                                  args->note_size,
-                                                  args->stop_signal);
-  args->num_notes++;
-
-  return 0;
-}
-
-/* Enumerate spufs IDs for process PID.  */
-
-static void
-iterate_over_spus (int pid, void (*callback) (void *, int), void *data)
-{
-  char path[128];
-  DIR *dir;
-  struct dirent *entry;
-
-  xsnprintf (path, sizeof path, "/proc/%d/fd", pid);
-  dir = opendir (path);
-  if (!dir)
-    return;
-
-  rewinddir (dir);
-  while ((entry = readdir (dir)) != NULL)
-    {
-      struct stat st;
-      struct statfs stfs;
-      int fd;
-
-      fd = atoi (entry->d_name);
-      if (!fd)
-       continue;
-
-      xsnprintf (path, sizeof path, "/proc/%d/fd/%d", pid, fd);
-      if (stat (path, &st) != 0)
-       continue;
-      if (!S_ISDIR (st.st_mode))
-       continue;
-
-      if (statfs (path, &stfs) != 0)
-       continue;
-      if (stfs.f_type != SPUFS_MAGIC)
-       continue;
-
-      callback (data, fd);
-    }
-
-  closedir (dir);
-}
-
-/* Generate corefile notes for SPU contexts.  */
-
-struct linux_spu_corefile_data
-{
-  bfd *obfd;
-  char *note_data;
-  int *note_size;
-};
-
-static void
-linux_spu_corefile_callback (void *data, int fd)
-{
-  struct linux_spu_corefile_data *args = data;
-  int i;
-
-  static const char *spu_files[] =
-    {
-      "object-id",
-      "mem",
-      "regs",
-      "fpcr",
-      "lslr",
-      "decr",
-      "decr_status",
-      "signal1",
-      "signal1_type",
-      "signal2",
-      "signal2_type",
-      "event_mask",
-      "event_status",
-      "mbox_info",
-      "ibox_info",
-      "wbox_info",
-      "dma_info",
-      "proxydma_info",
-   };
-
-  for (i = 0; i < sizeof (spu_files) / sizeof (spu_files[0]); i++)
-    {
-      char annex[32], note_name[32];
-      gdb_byte *spu_data;
-      LONGEST spu_len;
-
-      xsnprintf (annex, sizeof annex, "%d/%s", fd, spu_files[i]);
-      spu_len = target_read_alloc (&current_target, TARGET_OBJECT_SPU,
-                                  annex, &spu_data);
-      if (spu_len > 0)
-       {
-         xsnprintf (note_name, sizeof note_name, "SPU/%s", annex);
-         args->note_data = elfcore_write_note (args->obfd, args->note_data,
-                                               args->note_size, note_name,
-                                               NT_SPU, spu_data, spu_len);
-         xfree (spu_data);
-       }
-    }
-}
-
-static char *
-linux_spu_make_corefile_notes (bfd *obfd, char *note_data, int *note_size)
-{
-  struct linux_spu_corefile_data args;
-
-  args.obfd = obfd;
-  args.note_data = note_data;
-  args.note_size = note_size;
-
-  iterate_over_spus (PIDGET (inferior_ptid),
-                    linux_spu_corefile_callback, &args);
-
-  return args.note_data;
-}
-
 /* 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_nat_make_corefile_notes (bfd *obfd, int *note_size)
 {
-  struct linux_nat_corefile_thread_data thread_args;
-  /* The variable size must be >= sizeof (prpsinfo_t.pr_fname).  */
-  char fname[16] = { '\0' };
-  /* The variable size must be >= sizeof (prpsinfo_t.pr_psargs).  */
-  char psargs[80] = { '\0' };
-  char *note_data = NULL;
-  ptid_t filter = pid_to_ptid (ptid_get_pid (inferior_ptid));
-  gdb_byte *auxv;
-  int auxv_len;
-
-  if (get_exec_file (0))
-    {
-      strncpy (fname, lbasename (get_exec_file (0)), sizeof (fname));
-      strncpy (psargs, get_exec_file (0), sizeof (psargs));
-      if (get_inferior_args ())
-       {
-         char *string_end;
-         char *psargs_end = psargs + sizeof (psargs);
-
-         /* linux_elfcore_write_prpsinfo () handles zero unterminated
-            strings fine.  */
-         string_end = memchr (psargs, 0, sizeof (psargs));
-         if (string_end != NULL)
-           {
-             *string_end++ = ' ';
-             strncpy (string_end, get_inferior_args (),
-                      psargs_end - string_end);
-           }
-       }
-      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;
-  thread_args.num_notes = 0;
-  thread_args.stop_signal = find_stop_signal ();
-  iterate_over_lwps (filter, linux_nat_corefile_thread_callback, &thread_args);
-  gdb_assert (thread_args.num_notes != 0);
-  note_data = thread_args.note_data;
-
-  auxv_len = target_read_alloc (&current_target, TARGET_OBJECT_AUXV,
-                               NULL, &auxv);
-  if (auxv_len > 0)
-    {
-      note_data = elfcore_write_note (obfd, note_data, note_size,
-                                     "CORE", NT_AUXV, auxv, auxv_len);
-      xfree (auxv);
-    }
-
-  note_data = linux_spu_make_corefile_notes (obfd, note_data, note_size);
-
-  make_cleanup (xfree, note_data);
-  return note_data;
+  /* FIXME: uweigand/2011-10-06: Once all GNU/Linux architectures have been
+     converted to gdbarch_core_regset_sections, this function can go away.  */
+  return linux_make_corefile_notes (target_gdbarch, obfd, note_size,
+                                   linux_nat_collect_thread_registers);
 }
 
 /* Implement the to_xfer_partial interface for memory reads using the /proc
index ff649b8..60fe8b6 100644 (file)
 #include "linux-tdep.h"
 #include "auxv.h"
 #include "target.h"
+#include "gdbthread.h"
+#include "gdbcore.h"
+#include "regcache.h"
+#include "regset.h"
 #include "elf/common.h"
+#include "elf-bfd.h"            /* for elfcore_write_* */
 #include "inferior.h"
 #include "cli/cli-utils.h"
 
@@ -525,6 +530,275 @@ linux_info_proc (struct gdbarch *gdbarch, char *args,
     }
 }
 
+/* Determine which signal stopped execution.  */
+
+static int
+find_signalled_thread (struct thread_info *info, void *data)
+{
+  if (info->suspend.stop_signal != TARGET_SIGNAL_0
+      && ptid_get_pid (info->ptid) == ptid_get_pid (inferior_ptid))
+    return 1;
+
+  return 0;
+}
+
+static enum target_signal
+find_stop_signal (void)
+{
+  struct thread_info *info =
+    iterate_over_threads (find_signalled_thread, NULL);
+
+  if (info)
+    return info->suspend.stop_signal;
+  else
+    return TARGET_SIGNAL_0;
+}
+
+/* Generate corefile notes for SPU contexts.  */
+
+static char *
+linux_spu_make_corefile_notes (bfd *obfd, char *note_data, int *note_size)
+{
+  static const char *spu_files[] =
+    {
+      "object-id",
+      "mem",
+      "regs",
+      "fpcr",
+      "lslr",
+      "decr",
+      "decr_status",
+      "signal1",
+      "signal1_type",
+      "signal2",
+      "signal2_type",
+      "event_mask",
+      "event_status",
+      "mbox_info",
+      "ibox_info",
+      "wbox_info",
+      "dma_info",
+      "proxydma_info",
+   };
+
+  enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+  gdb_byte *spu_ids;
+  LONGEST i, j, size;
+
+  /* Determine list of SPU ids.  */
+  size = target_read_alloc (&current_target, TARGET_OBJECT_SPU,
+                           NULL, &spu_ids);
+
+  /* Generate corefile notes for each SPU file.  */
+  for (i = 0; i < size; i += 4)
+    {
+      int fd = extract_unsigned_integer (spu_ids + i, 4, byte_order);
+
+      for (j = 0; j < sizeof (spu_files) / sizeof (spu_files[0]); j++)
+       {
+         char annex[32], note_name[32];
+         gdb_byte *spu_data;
+         LONGEST spu_len;
+
+         xsnprintf (annex, sizeof annex, "%d/%s", fd, spu_files[j]);
+         spu_len = target_read_alloc (&current_target, TARGET_OBJECT_SPU,
+                                      annex, &spu_data);
+         if (spu_len > 0)
+           {
+             xsnprintf (note_name, sizeof note_name, "SPU/%s", annex);
+             note_data = elfcore_write_note (obfd, note_data, note_size,
+                                             note_name, NT_SPU,
+                                             spu_data, spu_len);
+             xfree (spu_data);
+
+             if (!note_data)
+               {
+                 xfree (spu_ids);
+                 return NULL;
+               }
+           }
+       }
+    }
+
+  if (size > 0)
+    xfree (spu_ids);
+
+  return note_data;
+}
+
+/* Records the thread's register state for the corefile note
+   section.  */
+
+static char *
+linux_collect_thread_registers (const struct regcache *regcache,
+                               ptid_t ptid, bfd *obfd,
+                               char *note_data, int *note_size,
+                               enum target_signal stop_signal)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct core_regset_section *sect_list;
+  unsigned long lwp;
+
+  sect_list = gdbarch_core_regset_sections (gdbarch);
+  gdb_assert (sect_list);
+
+  /* For remote targets the LWP may not be available, so use the TID.  */
+  lwp = ptid_get_lwp (ptid);
+  if (!lwp)
+    lwp = ptid_get_tid (ptid);
+
+  while (sect_list->sect_name != NULL)
+    {
+      const struct regset *regset;
+      char *buf;
+
+      regset = gdbarch_regset_from_core_section (gdbarch,
+                                                sect_list->sect_name,
+                                                sect_list->size);
+      gdb_assert (regset && regset->collect_regset);
+
+      buf = xmalloc (sect_list->size);
+      regset->collect_regset (regset, regcache, -1, buf, sect_list->size);
+
+      /* PRSTATUS still needs to be treated specially.  */
+      if (strcmp (sect_list->sect_name, ".reg") == 0)
+       note_data = (char *) elfcore_write_prstatus
+                              (obfd, note_data, note_size, lwp,
+                               target_signal_to_host (stop_signal), buf);
+      else
+       note_data = (char *) elfcore_write_register_note
+                              (obfd, note_data, note_size,
+                               sect_list->sect_name, buf, sect_list->size);
+      xfree (buf);
+      sect_list++;
+
+      if (!note_data)
+       return NULL;
+    }
+
+  return note_data;
+}
+
+struct linux_corefile_thread_data
+{
+  struct gdbarch *gdbarch;
+  int pid;
+  bfd *obfd;
+  char *note_data;
+  int *note_size;
+  int num_notes;
+  enum target_signal stop_signal;
+  linux_collect_thread_registers_ftype collect;
+};
+
+/* 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 *info, void *data)
+{
+  struct linux_corefile_thread_data *args = data;
+
+  if (ptid_get_pid (info->ptid) == args->pid)
+    {
+      struct cleanup *old_chain;
+      struct regcache *regcache;
+      regcache = get_thread_arch_regcache (info->ptid, args->gdbarch);
+
+      old_chain = save_inferior_ptid ();
+      inferior_ptid = info->ptid;
+      target_fetch_registers (regcache, -1);
+      do_cleanups (old_chain);
+
+      args->note_data = args->collect (regcache, info->ptid, args->obfd,
+                                      args->note_data, args->note_size,
+                                      args->stop_signal);
+      args->num_notes++;
+    }
+
+  return !args->note_data;
+}
+
+/* Fills the "to_make_corefile_note" target vector.  Builds the note
+   section for a corefile, and returns it in a malloc buffer.  */
+
+char *
+linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size,
+                          linux_collect_thread_registers_ftype collect)
+{
+  struct linux_corefile_thread_data thread_args;
+  char *note_data = NULL;
+  gdb_byte *auxv;
+  int auxv_len;
+
+  /* Process information.  */
+  if (get_exec_file (0))
+    {
+      const char *fname = lbasename (get_exec_file (0));
+      char *psargs = xstrdup (fname);
+
+      if (get_inferior_args ())
+        psargs = reconcat (psargs, psargs, " ", get_inferior_args (),
+                          (char *) NULL);
+
+      note_data = elfcore_write_prpsinfo (obfd, note_data, note_size,
+                                          fname, psargs);
+      xfree (psargs);
+
+      if (!note_data)
+       return NULL;
+    }
+
+  /* Thread register information.  */
+  thread_args.gdbarch = gdbarch;
+  thread_args.pid = ptid_get_pid (inferior_ptid);
+  thread_args.obfd = obfd;
+  thread_args.note_data = note_data;
+  thread_args.note_size = note_size;
+  thread_args.num_notes = 0;
+  thread_args.stop_signal = find_stop_signal ();
+  thread_args.collect = collect;
+  iterate_over_threads (linux_corefile_thread_callback, &thread_args);
+  note_data = thread_args.note_data;
+  if (!note_data)
+    return NULL;
+
+  /* Auxillary vector.  */
+  auxv_len = target_read_alloc (&current_target, TARGET_OBJECT_AUXV,
+                               NULL, &auxv);
+  if (auxv_len > 0)
+    {
+      note_data = elfcore_write_note (obfd, note_data, note_size,
+                                     "CORE", NT_AUXV, auxv, auxv_len);
+      xfree (auxv);
+
+      if (!note_data)
+       return NULL;
+    }
+
+  /* SPU information.  */
+  note_data = linux_spu_make_corefile_notes (obfd, note_data, note_size);
+  if (!note_data)
+    return NULL;
+
+  make_cleanup (xfree, note_data);
+  return note_data;
+}
+
+static char *
+linux_make_corefile_notes_1 (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
+{
+  /* FIXME: uweigand/2011-10-06: Once all GNU/Linux architectures have been
+     converted to gdbarch_core_regset_sections, we no longer need to fall back
+     to the target method at this point.  */
+
+  if (!gdbarch_core_regset_sections (gdbarch))
+    return target_make_corefile_notes (obfd, note_size);
+  else
+    return linux_make_corefile_notes (gdbarch, obfd, note_size,
+                                     linux_collect_thread_registers);
+}
+
 /* To be called from the various GDB_OSABI_LINUX handlers for the
    various GNU/Linux architectures and machine types.  */
 
@@ -533,6 +807,7 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
   set_gdbarch_core_pid_to_str (gdbarch, linux_core_pid_to_str);
   set_gdbarch_info_proc (gdbarch, linux_info_proc);
+  set_gdbarch_make_corefile_notes (gdbarch, linux_make_corefile_notes_1);
 }
 
 void
index 4e9f560..d2f4077 100644 (file)
 #ifndef LINUX_TDEP_H
 #define LINUX_TDEP_H
 
+#include "bfd.h"
+
+struct regcache;
+
+typedef char *(*linux_collect_thread_registers_ftype) (const struct regcache *,
+                                                      ptid_t,
+                                                      bfd *, char *, int *,
+                                                      enum target_signal);
+
+char *linux_make_corefile_notes (struct gdbarch *, bfd *, int *,
+                                 linux_collect_thread_registers_ftype);
+
 struct type *linux_get_siginfo_type (struct gdbarch *);
 
 extern void linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch);