This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 08/12] attach most section data to the section, not to the BFD


Most of the time, the section data read by dwarf2read.c is actually
objfile-independent.  This means that it makes sense to share
this data across objfiles.

This patch implements this sharing.  It works by attaching the
section data to the BFD section itself.  It uses the section's
user data field for this; this field is not otherwise used in gdb.

	* dwarf2read.c: Don't include zlib.h or sys/mman.h.
	(pagesize): Remove.
	(struct dwarf2_section_info) <map_addr, map_len>: Remove.
	(zlib_decompress_section): Remove.
	(dwarf2_read_section): Use gdb_bfd_map_section.
	(munmap_section_buffer): Remove.
	(free_dwo_file, dwarf2_per_objfile_free): Don't use
	munmap_section_buffer.
	* gdb_bfd.c: Include zlib.h, sys/mman.h.
	(struct gdb_bfd_section_data): New.
	(free_one_bfd_section): New function.
	(gdb_bfd_close_or_warn): Use free_one_bfd_section.
	(get_section_descriptor, zlib_decompress_section)
	(gdb_bfd_map_section): New functions.
	* gdb_bfd.h (gdb_bfd_map_section): Declare.
---
 gdb/dwarf2read.c |  200 +++----------------------------------------
 gdb/gdb_bfd.c    |  254 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 gdb/gdb_bfd.h    |   14 +++-
 3 files changed, 277 insertions(+), 191 deletions(-)

diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 00bc72e..8796e28 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -69,15 +69,6 @@
 #include "gdb_string.h"
 #include "gdb_assert.h"
 #include <sys/types.h>
-#ifdef HAVE_ZLIB_H
-#include <zlib.h>
-#endif
-#ifdef HAVE_MMAP
-#include <sys/mman.h>
-#ifndef MAP_FAILED
-#define MAP_FAILED ((void *) -1)
-#endif
-#endif
 
 typedef struct symbol *symbolp;
 DEF_VEC_P (symbolp);
@@ -91,8 +82,6 @@ static int check_physname = 0;
 /* When non-zero, do not reject deprecated .gdb_index sections.  */
 int use_deprecated_index_sections = 0;
 
-static int pagesize;
-
 /* When set, the file that we're processing is known to have debugging
    info for C++ namespaces.  GCC 3.3.x did not produce this information,
    but later versions do.  */
@@ -106,10 +95,6 @@ struct dwarf2_section_info
   asection *asection;
   gdb_byte *buffer;
   bfd_size_type size;
-  /* Not NULL if the section was actually mmapped.  */
-  void *map_addr;
-  /* Page aligned size of mmapped area.  */
-  bfd_size_type map_len;
   /* True if we have tried to read this section.  */
   int readin;
 };
@@ -1451,8 +1436,6 @@ static struct dwo_unit *lookup_dwo_type_unit
 
 static void free_dwo_file_cleanup (void *);
 
-static void munmap_section_buffer (struct dwarf2_section_info *);
-
 static void process_cu_includes (void);
 
 #if WORDS_BIGENDIAN
@@ -1622,85 +1605,6 @@ dwarf2_locate_sections (bfd *abfd, asection *sectp, void *vnames)
     dwarf2_per_objfile->has_section_at_zero = 1;
 }
 
-/* Decompress a section that was compressed using zlib.  Store the
-   decompressed buffer, and its size, in OUTBUF and OUTSIZE.  */
-
-static void
-zlib_decompress_section (struct objfile *objfile, asection *sectp,
-                         gdb_byte **outbuf, bfd_size_type *outsize)
-{
-  bfd *abfd = sectp->owner;
-#ifndef HAVE_ZLIB_H
-  error (_("Support for zlib-compressed DWARF data (from '%s') "
-           "is disabled in this copy of GDB"),
-         bfd_get_filename (abfd));
-#else
-  bfd_size_type compressed_size = bfd_get_section_size (sectp);
-  gdb_byte *compressed_buffer = xmalloc (compressed_size);
-  struct cleanup *cleanup = make_cleanup (xfree, compressed_buffer);
-  bfd_size_type uncompressed_size;
-  gdb_byte *uncompressed_buffer;
-  z_stream strm;
-  int rc;
-  int header_size = 12;
-
-  if (bfd_seek (abfd, sectp->filepos, SEEK_SET) != 0
-      || bfd_bread (compressed_buffer,
-		    compressed_size, abfd) != compressed_size)
-    error (_("Dwarf Error: Can't read DWARF data from '%s'"),
-           bfd_get_filename (abfd));
-
-  /* Read the zlib header.  In this case, it should be "ZLIB" followed
-     by the uncompressed section size, 8 bytes in big-endian order.  */
-  if (compressed_size < header_size
-      || strncmp (compressed_buffer, "ZLIB", 4) != 0)
-    error (_("Dwarf Error: Corrupt DWARF ZLIB header from '%s'"),
-           bfd_get_filename (abfd));
-  uncompressed_size = compressed_buffer[4]; uncompressed_size <<= 8;
-  uncompressed_size += compressed_buffer[5]; uncompressed_size <<= 8;
-  uncompressed_size += compressed_buffer[6]; uncompressed_size <<= 8;
-  uncompressed_size += compressed_buffer[7]; uncompressed_size <<= 8;
-  uncompressed_size += compressed_buffer[8]; uncompressed_size <<= 8;
-  uncompressed_size += compressed_buffer[9]; uncompressed_size <<= 8;
-  uncompressed_size += compressed_buffer[10]; uncompressed_size <<= 8;
-  uncompressed_size += compressed_buffer[11];
-
-  /* It is possible the section consists of several compressed
-     buffers concatenated together, so we uncompress in a loop.  */
-  strm.zalloc = NULL;
-  strm.zfree = NULL;
-  strm.opaque = NULL;
-  strm.avail_in = compressed_size - header_size;
-  strm.next_in = (Bytef*) compressed_buffer + header_size;
-  strm.avail_out = uncompressed_size;
-  uncompressed_buffer = obstack_alloc (&objfile->objfile_obstack,
-                                       uncompressed_size);
-  rc = inflateInit (&strm);
-  while (strm.avail_in > 0)
-    {
-      if (rc != Z_OK)
-        error (_("Dwarf Error: setting up DWARF uncompression in '%s': %d"),
-               bfd_get_filename (abfd), rc);
-      strm.next_out = ((Bytef*) uncompressed_buffer
-                       + (uncompressed_size - strm.avail_out));
-      rc = inflate (&strm, Z_FINISH);
-      if (rc != Z_STREAM_END)
-        error (_("Dwarf Error: zlib error uncompressing from '%s': %d"),
-               bfd_get_filename (abfd), rc);
-      rc = inflateReset (&strm);
-    }
-  rc = inflateEnd (&strm);
-  if (rc != Z_OK
-      || strm.avail_out != 0)
-    error (_("Dwarf Error: concluding DWARF uncompression in '%s': %d"),
-           bfd_get_filename (abfd), rc);
-
-  do_cleanups (cleanup);
-  *outbuf = uncompressed_buffer;
-  *outsize = uncompressed_size;
-#endif
-}
-
 /* A helper function that decides whether a section is empty,
    or not present.  */
 
@@ -1727,56 +1631,27 @@ dwarf2_read_section (struct objfile *objfile, struct dwarf2_section_info *info)
   if (info->readin)
     return;
   info->buffer = NULL;
-  info->map_addr = NULL;
   info->readin = 1;
 
   if (dwarf2_section_empty_p (info))
     return;
 
-  /* Note that ABFD may not be from OBJFILE, e.g. a DWO section.  */
   abfd = sectp->owner;
 
-  /* Check if the file has a 4-byte header indicating compression.  */
-  if (info->size > sizeof (header)
-      && bfd_seek (abfd, sectp->filepos, SEEK_SET) == 0
-      && bfd_bread (header, sizeof (header), abfd) == sizeof (header))
-    {
-      /* Upon decompression, update the buffer and its size.  */
-      if (strncmp (header, "ZLIB", sizeof (header)) == 0)
-        {
-          zlib_decompress_section (objfile, sectp, &info->buffer,
-				   &info->size);
-          return;
-        }
-    }
-
-#ifdef HAVE_MMAP
-  if (pagesize == 0)
-    pagesize = getpagesize ();
-
-  /* Only try to mmap sections which are large enough: we don't want to
-     waste space due to fragmentation.  Also, only try mmap for sections
-     without relocations.  */
-
-  if (info->size > 4 * pagesize && (sectp->flags & SEC_RELOC) == 0)
+  /* If the section has relocations, we must read it ourselves.
+     Otherwise we attach it to the BFD.  */
+  if ((sectp->flags & SEC_RELOC) == 0)
     {
-      info->buffer = bfd_mmap (abfd, 0, info->size, PROT_READ,
-                         MAP_PRIVATE, sectp->filepos,
-                         &info->map_addr, &info->map_len);
+      const gdb_byte *bytes = gdb_bfd_map_section (sectp, &info->size);
 
-      if ((caddr_t)info->buffer != MAP_FAILED)
-	{
-#if HAVE_POSIX_MADVISE
-	  posix_madvise (info->map_addr, info->map_len, POSIX_MADV_WILLNEED);
-#endif
-	  return;
-	}
+      /* We have to cast away const here for historical reasons.
+	 Fixing dwarf2read to be const-correct would be quite nice.  */
+      info->buffer = (gdb_byte *) bytes;
+      return;
     }
-#endif
 
-  /* If we get here, we are a normal, not-compressed section.  */
-  info->buffer = buf
-    = obstack_alloc (&objfile->objfile_obstack, info->size);
+  buf = obstack_alloc (&objfile->objfile_obstack, info->size);
+  info->buffer = buf;
 
   /* When debugging .o files, we may need to apply relocations; see
      http://sourceware.org/ml/gdb-patches/2002-04/msg00136.html .
@@ -7166,19 +7041,6 @@ free_dwo_file (struct dwo_file *dwo_file, struct objfile *objfile)
   gdb_assert (dwo_file->dwo_bfd != objfile->obfd);
   gdb_bfd_unref (dwo_file->dwo_bfd);
 
-  munmap_section_buffer (&dwo_file->sections.abbrev);
-  munmap_section_buffer (&dwo_file->sections.info);
-  munmap_section_buffer (&dwo_file->sections.line);
-  munmap_section_buffer (&dwo_file->sections.loc);
-  munmap_section_buffer (&dwo_file->sections.str);
-  munmap_section_buffer (&dwo_file->sections.str_offsets);
-
-  for (ix = 0;
-       VEC_iterate (dwarf2_section_info_def, dwo_file->sections.types,
-		    ix, section);
-       ++ix)
-    munmap_section_buffer (section);
-
   VEC_free (dwarf2_section_info_def, dwo_file->sections.types);
 }
 
@@ -17097,53 +16959,13 @@ show_dwarf2_cmd (char *args, int from_tty)
   cmd_show_list (show_dwarf2_cmdlist, from_tty, "");
 }
 
-/* If section described by INFO was mmapped, munmap it now.  */
-
-static void
-munmap_section_buffer (struct dwarf2_section_info *info)
-{
-  if (info->map_addr != NULL)
-    {
-#ifdef HAVE_MMAP
-      int res;
-
-      res = munmap (info->map_addr, info->map_len);
-      gdb_assert (res == 0);
-#else
-      /* Without HAVE_MMAP, we should never be here to begin with.  */
-      gdb_assert_not_reached ("no mmap support");
-#endif
-    }
-}
-
-/* munmap debug sections for OBJFILE, if necessary.  */
+/* Free data associated with OBJFILE, if necessary.  */
 
 static void
 dwarf2_per_objfile_free (struct objfile *objfile, void *d)
 {
   struct dwarf2_per_objfile *data = d;
   int ix;
-  struct dwarf2_section_info *section;
-
-  /* This is sorted according to the order they're defined in to make it easier
-     to keep in sync.  */
-  munmap_section_buffer (&data->info);
-  munmap_section_buffer (&data->abbrev);
-  munmap_section_buffer (&data->line);
-  munmap_section_buffer (&data->loc);
-  munmap_section_buffer (&data->macinfo);
-  munmap_section_buffer (&data->macro);
-  munmap_section_buffer (&data->str);
-  munmap_section_buffer (&data->ranges);
-  munmap_section_buffer (&data->addr);
-  munmap_section_buffer (&data->frame);
-  munmap_section_buffer (&data->eh_frame);
-  munmap_section_buffer (&data->gdb_index);
-
-  for (ix = 0;
-       VEC_iterate (dwarf2_section_info_def, data->types, ix, section);
-       ++ix)
-    munmap_section_buffer (section);
 
   for (ix = 0; ix < dwarf2_per_objfile->n_comp_units; ++ix)
     VEC_free (dwarf2_per_cu_ptr,
diff --git a/gdb/gdb_bfd.c b/gdb/gdb_bfd.c
index 0b921bb..b43553f 100644
--- a/gdb/gdb_bfd.c
+++ b/gdb/gdb_bfd.c
@@ -1,6 +1,6 @@
 /* Definitions for BFD wrappers used by GDB.
 
-   Copyright (C) 2011
+   Copyright (C) 2011, 2012
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -23,6 +23,30 @@
 #include "gdb_assert.h"
 #include "gdb_string.h"
 #include "hashtab.h"
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *) -1)
+#endif
+#endif
+
+/* An object of this type is stored in the section's user data when
+   mapping a section.  */
+
+struct gdb_bfd_section_data
+{
+  /* Size of the data.  */
+  bfd_size_type size;
+  /* If the data was mmapped, this is the length of the map.  */
+  bfd_size_type map_len;
+  /* The data.  If NULL, the section data has not been read.  */
+  void *data;
+  /* If the data was mmapped, this is the map address.  */
+  void *map_addr;
+};
 
 /* See gdb_bfd.h.  */
 
@@ -147,6 +171,30 @@ gdb_bfd_open (const char *name, const char *target, int fd)
   return gdb_bfd_ref (abfd);
 }
 
+/* A helper function that releases any section data attached to the
+   BFD.  */
+
+static void
+free_one_bfd_section (bfd *abfd, asection *sectp, void *ignore)
+{
+  struct gdb_bfd_section_data *sect = bfd_get_section_userdata (abfd, sectp);
+
+  if (sect != NULL && sect->data != NULL)
+    {
+#ifdef HAVE_MMAP
+      if (sect->map_addr != NULL)
+	{
+	  int res;
+
+	  res = munmap (sect->map_addr, sect->map_len);
+	  gdb_assert (res == 0);
+	}
+      else
+#endif
+	xfree (sect->data);
+    }
+}
+
 /* Close ABFD, and warn if that fails.  */
 
 static int
@@ -155,6 +203,8 @@ gdb_bfd_close_or_warn (struct bfd *abfd)
   int ret;
   char *name = bfd_get_filename (abfd);
 
+  bfd_map_over_sections (abfd, free_one_bfd_section, NULL);
+
   ret = bfd_close (abfd);
 
   if (!ret)
@@ -227,3 +277,205 @@ gdb_bfd_unref (struct bfd *abfd)
 
   gdb_bfd_close_or_warn (abfd);
 }
+
+/* A helper function that returns the section data descriptor
+   associated with SECTION.  If no such descriptor exists, a new one
+   is allocated and cleared.  */
+
+static struct gdb_bfd_section_data *
+get_section_descriptor (asection *section)
+{
+  struct gdb_bfd_section_data *result;
+
+  result = bfd_get_section_userdata (section->owner, section);
+
+  if (result == NULL)
+    {
+      result = bfd_zalloc (section->owner, sizeof (*result));
+      bfd_set_section_userdata (section->owner, section, result);
+    }
+
+  return result;
+}
+
+/* Decompress a section that was compressed using zlib.  Store the
+   decompressed buffer, and its size, in DESCRIPTOR.  */
+
+static void
+zlib_decompress_section (asection *sectp,
+			 struct gdb_bfd_section_data *descriptor)
+{
+  bfd *abfd = sectp->owner;
+#ifndef HAVE_ZLIB_H
+  error (_("Support for zlib-compressed data (from '%s', section '%s') "
+           "is disabled in this copy of GDB"),
+         bfd_get_filename (abfd),
+	 bfd_get_section_name (sectp));
+#else
+  bfd_size_type compressed_size = bfd_get_section_size (sectp);
+  gdb_byte *compressed_buffer = xmalloc (compressed_size);
+  struct cleanup *cleanup = make_cleanup (xfree, compressed_buffer);
+  struct cleanup *inner_cleanup;
+  bfd_size_type uncompressed_size;
+  gdb_byte *uncompressed_buffer;
+  z_stream strm;
+  int rc;
+  int header_size = 12;
+  struct dwarf2_per_bfd_section *section_data;
+
+  if (bfd_seek (abfd, sectp->filepos, SEEK_SET) != 0
+      || bfd_bread (compressed_buffer,
+		    compressed_size, abfd) != compressed_size)
+    error (_("can't read data from '%s', section '%s'"),
+           bfd_get_filename (abfd),
+	   bfd_get_section_name (abfd, sectp));
+
+  /* Read the zlib header.  In this case, it should be "ZLIB" followed
+     by the uncompressed section size, 8 bytes in big-endian order.  */
+  if (compressed_size < header_size
+      || strncmp (compressed_buffer, "ZLIB", 4) != 0)
+    error (_("corrupt ZLIB header from '%s', section '%s'"),
+           bfd_get_filename (abfd),
+	   bfd_get_section_name (abfd, sectp));
+  uncompressed_size = compressed_buffer[4]; uncompressed_size <<= 8;
+  uncompressed_size += compressed_buffer[5]; uncompressed_size <<= 8;
+  uncompressed_size += compressed_buffer[6]; uncompressed_size <<= 8;
+  uncompressed_size += compressed_buffer[7]; uncompressed_size <<= 8;
+  uncompressed_size += compressed_buffer[8]; uncompressed_size <<= 8;
+  uncompressed_size += compressed_buffer[9]; uncompressed_size <<= 8;
+  uncompressed_size += compressed_buffer[10]; uncompressed_size <<= 8;
+  uncompressed_size += compressed_buffer[11];
+
+  /* It is possible the section consists of several compressed
+     buffers concatenated together, so we uncompress in a loop.  */
+  strm.zalloc = NULL;
+  strm.zfree = NULL;
+  strm.opaque = NULL;
+  strm.avail_in = compressed_size - header_size;
+  strm.next_in = (Bytef*) compressed_buffer + header_size;
+  strm.avail_out = uncompressed_size;
+  uncompressed_buffer = xmalloc (uncompressed_size);
+  inner_cleanup = make_cleanup (xfree, uncompressed_buffer);
+  rc = inflateInit (&strm);
+  while (strm.avail_in > 0)
+    {
+      if (rc != Z_OK)
+        error (_("setting up uncompression in '%s', section '%s': %d"),
+               bfd_get_filename (abfd),
+	       bfd_get_section_name (abfd, sectp),
+	       rc);
+      strm.next_out = ((Bytef*) uncompressed_buffer
+                       + (uncompressed_size - strm.avail_out));
+      rc = inflate (&strm, Z_FINISH);
+      if (rc != Z_STREAM_END)
+        error (_("zlib error uncompressing from '%s', section '%s': %d"),
+               bfd_get_filename (abfd),
+	       bfd_get_section_name (abfd, sectp),
+	       rc);
+      rc = inflateReset (&strm);
+    }
+  rc = inflateEnd (&strm);
+  if (rc != Z_OK
+      || strm.avail_out != 0)
+    error (_("concluding uncompression in '%s', section '%s': %d"),
+           bfd_get_filename (abfd),
+	   bfd_get_section_name (abfd, sectp),
+	   rc);
+
+  discard_cleanups (inner_cleanup);
+  do_cleanups (cleanup);
+
+  /* Attach the data to the BFD section.  */
+  descriptor->data = uncompressed_buffer;
+  descriptor->size = uncompressed_size;
+#endif
+}
+
+/* See gdb_bfd.h.  */
+
+const gdb_byte *
+gdb_bfd_map_section (asection *sectp, bfd_size_type *size)
+{
+  bfd *abfd;
+  gdb_byte *buf, *retbuf;
+  unsigned char header[4];
+  struct gdb_bfd_section_data *descriptor;
+
+  gdb_assert ((sectp->flags & SEC_RELOC) == 0);
+  gdb_assert (size != NULL);
+
+  abfd = sectp->owner;
+
+  descriptor = get_section_descriptor (sectp);
+
+  /* If the data was already read for this BFD, just reuse it.  */
+  if (descriptor->data != NULL)
+    goto done;
+
+  /* Check if the file has a 4-byte header indicating compression.  */
+  if (bfd_get_section_size (sectp) > sizeof (header)
+      && bfd_seek (abfd, sectp->filepos, SEEK_SET) == 0
+      && bfd_bread (header, sizeof (header), abfd) == sizeof (header))
+    {
+      /* Upon decompression, update the buffer and its size.  */
+      if (strncmp (header, "ZLIB", sizeof (header)) == 0)
+        {
+          zlib_decompress_section (sectp, descriptor);
+	  goto done;
+        }
+    }
+
+#ifdef HAVE_MMAP
+  {
+    /* The page size, used when mmapping.  */
+    static int pagesize;
+
+    if (pagesize == 0)
+      pagesize = getpagesize ();
+
+    /* Only try to mmap sections which are large enough: we don't want
+       to waste space due to fragmentation.  */
+
+    if (bfd_get_section_size (sectp) > 4 * pagesize)
+      {
+	descriptor->size = bfd_get_section_size (sectp);
+	descriptor->data = bfd_mmap (abfd, 0, descriptor->size, PROT_READ,
+				     MAP_PRIVATE, sectp->filepos,
+				     &descriptor->map_addr,
+				     &descriptor->map_len);
+
+	if ((caddr_t)descriptor->data != MAP_FAILED)
+	  {
+#if HAVE_POSIX_MADVISE
+	    posix_madvise (descriptor->map_addr, descriptor->map_len,
+			   POSIX_MADV_WILLNEED);
+#endif
+	    goto done;
+	  }
+
+	/* On failure, clear out the section data and try again.  */
+	memset (descriptor, 0, sizeof (*descriptor));
+      }
+  }
+#endif /* HAVE_MMAP */
+
+  /* If we get here, we are a normal, not-compressed section.  */
+
+  descriptor->size = bfd_get_section_size (sectp);
+  descriptor->data = xmalloc (descriptor->size);
+
+  if (bfd_seek (abfd, sectp->filepos, SEEK_SET) != 0
+      || bfd_bread (descriptor->data, bfd_get_section_size (sectp),
+		    abfd) != bfd_get_section_size (sectp))
+    {
+      xfree (descriptor->data);
+      descriptor->data = NULL;
+      error (_("Can't read data for section '%s'"),
+	     bfd_get_filename (abfd));
+    }
+
+ done:
+  gdb_assert (descriptor->data != NULL);
+  *size = descriptor->size;
+  return descriptor->data;
+}
diff --git a/gdb/gdb_bfd.h b/gdb/gdb_bfd.h
index 78d303b..1dd82c3 100644
--- a/gdb/gdb_bfd.h
+++ b/gdb/gdb_bfd.h
@@ -1,6 +1,6 @@
 /* Definitions for BFD wrappers used by GDB.
 
-   Copyright (C) 2011
+   Copyright (C) 2011, 2012
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -46,4 +46,16 @@ struct bfd *gdb_bfd_ref (struct bfd *abfd);
 
 void gdb_bfd_unref (struct bfd *abfd);
 
+/* Try to read or map the contents of the section SECT.  If
+   successful, the section data is returned and *SIZE is set to the
+   size of the section data; this may not be the same as the size
+   according to bfd_get_section_size if the section was compressed.
+   The returned section data is associated with the BFD and will be
+   destroyed when the BFD is destroyed.  There is no other way to free
+   it; for temporary uses of section data, see
+   bfd_malloc_and_get_section.  SECT may not have relocations.  This
+   function will throw on error.  */
+
+const gdb_byte *gdb_bfd_map_section (asection *section, bfd_size_type *size);
+
 #endif /* GDB_BFD_H */
-- 
1.7.7.6


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]