[rfc,v2,2/3] Remote core file generation: register notes

Message ID 201201201817.q0KIHIME011522@d06av02.portsmouth.uk.ibm.com
State Accepted
Headers show

Commit Message

Ulrich Weigand Jan. 20, 2012, 6:17 p.m.
http://sourceware.org/ml/gdb-patches/2011-12/msg00015.html

Hello,

this is pretty much unchanged compared to the original version.

Bye,
Ulrich


ChangeLog:

	* 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: Do not include "gdb_stat.h", "gdb_dirent.h",
	or <sys/vfs.h>.  Include "linux-tdep.h".
	(SPUFS_MAGIC): Remove.
	(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.

Patch

Index: gdb-head/gdb/gcore.c
===================================================================
--- gdb-head.orig/gdb/gcore.c
+++ gdb-head/gdb/gcore.c
@@ -72,35 +72,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);
+
+  if (note_data == NULL || note_size == 0)
+    error (_("Target does not support core file generation."));
 
   /* 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 ()));
+  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);
-    }
+  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: gdb-head/gdb/gdbarch.c
===================================================================
--- gdb-head.orig/gdb/gdbarch.c
+++ gdb-head/gdb/gdbarch.c
@@ -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, s
                       "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
 }
 
 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: gdb-head/gdb/gdbarch.h
===================================================================
--- gdb-head.orig/gdb/gdbarch.h
+++ gdb-head/gdb/gdbarch.h
@@ -709,6 +709,14 @@  extern void set_gdbarch_regset_from_core
 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: gdb-head/gdb/gdbarch.sh
===================================================================
--- gdb-head.orig/gdb/gdbarch.sh
+++ gdb-head/gdb/gdbarch.sh
@@ -632,6 +632,9 @@  M:const struct regset *:regset_from_core
 # 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: gdb-head/gdb/linux-nat.c
===================================================================
--- gdb-head.orig/gdb/linux-nat.c
+++ gdb-head/gdb/linux-nat.c
@@ -46,24 +46,18 @@ 
 #include "gdbcore.h"		/* for get_exec_file */
 #include <ctype.h>		/* for isdigit */
 #include "gdbthread.h"		/* for struct thread_info etc.  */
-#include "gdb_stat.h"		/* for struct stat */
 #include <fcntl.h>		/* for O_RDONLY */
 #include "inf-loop.h"
 #include "event-loop.h"
 #include "event-top.h"
 #include <pwd.h>
 #include <sys/types.h>
-#include "gdb_dirent.h"
 #include "xml-support.h"
 #include "terminal.h"
-#include <sys/vfs.h>
 #include "solib.h"
 #include "linux-osdata.h"
 #include "linux-procfs.h"
-
-#ifndef SPUFS_MAGIC
-#define SPUFS_MAGIC 0x23c9b64e
-#endif
+#include "linux-tdep.h"
 
 #ifdef HAVE_PERSONALITY
 # include <sys/personality.h>
@@ -4425,325 +4419,59 @@  linux_nat_find_memory_regions (find_memo
   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: gdb-head/gdb/linux-tdep.c
===================================================================
--- gdb-head.orig/gdb/linux-tdep.c
+++ gdb-head/gdb/linux-tdep.c
@@ -22,7 +22,12 @@ 
 #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 <ctype.h>
@@ -503,6 +508,275 @@  linux_info_proc (struct gdbarch *gdbarch
     }
 }
 
+/* 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.  */
 
@@ -511,6 +785,7 @@  linux_init_abi (struct gdbarch_info info
 {
   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: gdb-head/gdb/linux-tdep.h
===================================================================
--- gdb-head.orig/gdb/linux-tdep.h
+++ gdb-head/gdb/linux-tdep.h
@@ -20,6 +20,18 @@ 
 #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);