This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc 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]

GNU C Library master sources branch, master, updated. glibc-2.14-11-g11988f8


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, master has been updated
       via  11988f8f9656042c3dfd9002ac85dff33173b9bd (commit)
       via  8621fc3b996a7bf5d60a0fb86cb15a078dcbf542 (commit)
       via  e80fab373c5cf01246e6acd302795b13dc50d175 (commit)
      from  9b849836f534bbd250a6733ebc1edf32e5f4d5dc (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://sources.redhat.com/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=11988f8f9656042c3dfd9002ac85dff33173b9bd

commit 11988f8f9656042c3dfd9002ac85dff33173b9bd
Author: Ulrich Drepper <drepper@gmail.com>
Date:   Thu Jun 9 07:06:21 2011 -0400

    Add pldd program

diff --git a/ChangeLog b/ChangeLog
index 5303220..965ebe0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2011-06-09  Ulrich Drepper  <drepper@gmail.com>
+
+	* elf/Makefile: Add rules to build pldd.
+	* elf/pldd.c: New file.
+	* elf/pldd-xx.c: New file.
+
 2011-06-07  Ulrich Drepper  <drepper@gmail.com>
 
 	* version.h: Update for 2.15 development version.
diff --git a/NEWS b/NEWS
index 602e050..ceadd18 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU C Library NEWS -- history of user-visible changes.  2011-6-7
+GNU C Library NEWS -- history of user-visible changes.  2011-6-9
 Copyright (C) 1992-2009, 2010, 2011 Free Software Foundation, Inc.
 See the end for copying conditions.
 
@@ -6,6 +6,9 @@ Please send GNU C library bug reports via <http://sources.redhat.com/bugzilla/>
 using `glibc' in the "product" field.
 
 Version 2.15
+
+* New program pldd to list loaded object of a process
+  Implemented by Ulrich Drepper.
 
 Version 2.14
 
diff --git a/elf/Makefile b/elf/Makefile
index 6d314a9..844c9ca 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -150,8 +150,8 @@ install-others	= $(inst_slibdir)/$(rtld-installed-name)
 install-bin-script = ldd
 endif
 
-others		= sprof sln
-install-bin	= sprof
+others		= sprof sln pldd
+install-bin	= sprof pldd
 others-static   = sln
 install-rootsbin = sln
 
@@ -164,6 +164,8 @@ install-rootsbin += ldconfig
 ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon
 extra-objs	+= $(ldconfig-modules:=.o)
 
+pldd-modules := xmalloc
+
 # To find xmalloc.c and xstrdup.c
 vpath %.c ../locale/programs
 
@@ -494,6 +496,9 @@ $(objpfx)ldd: ldd.bash.in $(common-objpfx)soversions.mk \
 $(objpfx)sprof: $(libdl)
 
 $(objpfx)ldconfig: $(ldconfig-modules:%=$(objpfx)%.o)
+
+$(objpfx)pldd: $(pldd-modules:%=$(objpfx)%.o)
+
 SYSCONF-FLAGS := -D'SYSCONFDIR="$(sysconfdir)"'
 CFLAGS-ldconfig.c = $(SYSCONF-FLAGS) -D'LIBDIR="$(libdir)"' \
 		    -D'SLIBDIR="$(slibdir)"' -DIS_IN_ldconfig=1
diff --git a/elf/pldd-xx.c b/elf/pldd-xx.c
new file mode 100644
index 0000000..0e3fcb7
--- /dev/null
+++ b/elf/pldd-xx.c
@@ -0,0 +1,238 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#define E(name) E_(name, CLASS)
+#define E_(name, cl) E__(name, cl)
+#define E__(name, cl) name##cl
+#define EW(type) EW_(Elf, CLASS, type)
+#define EW_(e, w, t) EW__(e, w, _##t)
+#define EW__(e, w, t) e##w##t
+
+#define static_assert(name, exp) \
+  typedef int __assert_##name[((exp) != 0) - 1]
+
+
+struct E(link_map)
+{
+  EW(Addr) l_addr;
+  EW(Addr) l_name;
+  EW(Addr) l_ld;
+  EW(Addr) l_next;
+  EW(Addr) l_prev;
+  EW(Addr) l_real;
+  Lmid_t l_ns;
+  EW(Addr) l_libname;
+};
+#if CLASS == __ELF_NATIVE_CLASS
+static_assert (l_addr, (offsetof (struct link_map, l_addr)
+			== offsetof (struct E(link_map), l_addr)));
+static_assert (l_name, (offsetof (struct link_map, l_name)
+			== offsetof (struct E(link_map), l_name)));
+static_assert (l_next, (offsetof (struct link_map, l_next)
+			== offsetof (struct E(link_map), l_next)));
+#endif
+
+
+struct E(libname_list)
+{
+  EW(Addr) name;
+  EW(Addr) next;
+};
+#if CLASS == __ELF_NATIVE_CLASS
+static_assert (name, (offsetof (struct libname_list, name)
+		      == offsetof (struct E(libname_list), name)));
+static_assert (next, (offsetof (struct libname_list, next)
+		      == offsetof (struct E(libname_list), next)));
+#endif
+
+struct E(r_debug)
+{
+  int r_version;
+  EW(Addr) r_map;
+};
+#if CLASS == __ELF_NATIVE_CLASS
+static_assert (r_version, (offsetof (struct r_debug, r_version)
+			   == offsetof (struct E(r_debug), r_version)));
+static_assert (r_map, (offsetof (struct r_debug, r_map)
+		       == offsetof (struct E(r_debug), r_map)));
+#endif
+
+
+static int
+E(find_maps) (pid_t pid, EW(Ehdr) *ehdr, void *auxv, size_t auxv_size)
+{
+  EW(Addr) phdr = 0;
+  unsigned int phnum = 0;
+  unsigned int phent = 0;
+
+  EW(auxv_t) *auxvXX = (EW(auxv_t) *) auxv;
+  for (int i = 0; i < auxv_size / sizeof (EW(auxv_t)); ++i)
+    switch (auxvXX[i].a_type)
+      {
+      case AT_PHDR:
+	phdr = auxvXX[i].a_un.a_val;
+	break;
+      case AT_PHNUM:
+	phnum = auxvXX[i].a_un.a_val;
+	break;
+      case AT_PHENT:
+	phent = auxvXX[i].a_un.a_val;
+	break;
+      default:
+	break;
+      }
+
+  if (phdr == 0 || phnum == 0 || phent == 0)
+    error (EXIT_FAILURE, 0, gettext ("cannot find program header of process"));
+
+  EW(Phdr) *p = alloca (phnum * phent);
+  if (pread64 (memfd, p, phnum * phent, phdr) != phnum * phent)
+    {
+      error (0, 0, gettext ("cannot read program header"));
+      return EXIT_FAILURE;
+    }
+
+  /* Determine the load offset.  We need this for interpreting the
+     other program header entries so we do this in a separate loop.
+     Fortunately it is the first time unless someone does something
+     stupid when linking the application.  */
+  EW(Addr) offset = 0;
+  for (unsigned int i = 0; i < phnum; ++i)
+    if (p[i].p_type == PT_PHDR)
+      {
+	offset = phdr - p[i].p_vaddr;
+	break;
+      }
+
+  EW(Addr) list = 0;
+  char *interp = NULL;
+  for (unsigned int i = 0; i < phnum; ++i)
+    if (p[i].p_type == PT_DYNAMIC)
+      {
+	EW(Dyn) *dyn = xmalloc (p[i].p_filesz);
+	if (pread64 (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr)
+	    != p[i].p_filesz)
+	  {
+	    error (0, 0, gettext ("cannot read dynamic section"));
+	    return EXIT_FAILURE;
+	  }
+
+	/* Search for the DT_DEBUG entry.  */
+	for (unsigned int j = 0; j < p[i].p_filesz / sizeof (EW(Dyn)); ++j)
+	  if (dyn[j].d_tag == DT_DEBUG && dyn[j].d_un.d_ptr != 0)
+	    {
+	      struct E(r_debug) r;
+	      if (pread64 (memfd, &r, sizeof (r), dyn[j].d_un.d_ptr)
+		  != sizeof (r))
+		{
+		  error (0, 0, gettext ("cannot read r_debug"));
+		  return EXIT_FAILURE;
+		}
+
+	      if (r.r_map != 0)
+		{
+		  list = r.r_map;
+		  break;
+		}
+	    }
+
+	free (dyn);
+	break;
+      }
+    else if (p[i].p_type == PT_INTERP)
+      {
+	interp = alloca (p[i].p_filesz);
+	if (pread64 (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr)
+	    != p[i].p_filesz)
+	  {
+	    error (0, 0, gettext ("cannot read program interpreter"));
+	    return EXIT_FAILURE;
+	  }
+      }
+
+  if (list == 0)
+    {
+      if (interp == NULL)
+	{
+	  // XXX check whether the executable itself is the loader
+	  return EXIT_FAILURE;
+	}
+
+      // XXX perhaps try finding ld.so and _r_debug in it
+
+      return EXIT_FAILURE;
+    }
+
+  /* Print the PID and program name first.  */
+  printf ("%lu:\t%s\n", (unsigned long int) pid, exe);
+
+  /* Iterate over the list of objects and print the information.  */
+  size_t strsize = 256;
+  char *str = alloca (strsize);
+  do
+    {
+      struct E(link_map) m;
+      if (pread64 (memfd, &m, sizeof (m), list) != sizeof (m))
+	{
+	  error (0, 0, gettext ("cannot read link map"));
+	  return EXIT_FAILURE;
+	}
+
+      EW(Addr) name_offset = m.l_name;
+    again:
+      while (1)
+	{
+	  ssize_t n = pread64 (memfd, str, strsize, name_offset);
+	  if (n == -1)
+	    {
+	      error (0, 0, gettext ("cannot read object name"));
+	      return EXIT_FAILURE;
+	    }
+
+	  if (memchr (str, '\0', n) != NULL)
+	    break;
+
+	  str = extend_alloca (str, strsize, strsize * 2);
+	}
+
+      if (str[0] == '\0' && name_offset == m.l_name
+	  && m.l_libname != 0)
+	{
+	  /* Try the l_libname element.  */
+	  struct E(libname_list) ln;
+	  if (pread64 (memfd, &ln, sizeof (ln), m.l_libname) == sizeof (ln))
+	    {
+	      name_offset = ln.name;
+	      goto again;
+	    }
+	}
+
+      /* Skip over the executable.  */
+      if (str[0] != '\0')
+	printf ("%s\n", str);
+
+      list = m.l_next;
+    }
+  while (list != 0);
+
+  return 0;
+}
+
+
+#undef CLASS
diff --git a/elf/pldd.c b/elf/pldd.c
new file mode 100644
index 0000000..9fac19e
--- /dev/null
+++ b/elf/pldd.c
@@ -0,0 +1,234 @@
+/* List dynamic shared objects linked into given process.
+   Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <alloca.h>
+#include <argp.h>
+#include <elf.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <link.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+
+#include <ldsodefs.h>
+#include <version.h>
+
+/* Global variables.  */
+extern char *program_invocation_short_name;
+#define PACKAGE _libc_intl_domainname
+
+/* External functions.  */
+extern void *xmalloc (size_t n);
+extern void *xrealloc (void *p, size_t n);
+
+/* Name and version of program.  */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
+
+/* Bug report address.  */
+const char *argp_program_bug_address = N_("\
+For bug reporting instructions, please see:\n\
+<http://www.gnu.org/software/libc/bugs.html>.\n");
+
+/* Definitions of arguments for argp functions.  */
+static const struct argp_option options[] =
+{
+  { NULL, 0, NULL, 0, NULL }
+};
+
+/* Short description of program.  */
+static const char doc[] = N_("\
+List dynamic shared objects loaded into process.");
+
+/* Strings for arguments in help texts.  */
+static const char args_doc[] = N_("PID");
+
+/* Prototype for option handler.  */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions.  */
+static struct argp argp =
+{
+  options, parse_opt, args_doc, doc, NULL, NULL, NULL
+};
+
+// File descriptor of /proc/*/mem file.
+static int memfd;
+
+/* Name of the executable  */
+static char *exe;
+
+/* Local functions.  */
+static int get_process_info (pid_t pid);
+
+
+int
+main (int argc, char *argv[])
+{
+  /* Parse and process arguments.  */
+  int remaining;
+  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+  if (remaining != argc - 1)
+    {
+      fprintf (stderr,
+	       gettext ("Exactly one parameter with process ID required.\n"));
+      argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
+      return 1;
+    }
+
+  char *endp;
+  errno = 0;
+  pid_t pid = strtoul (argv[remaining], &endp, 10);
+  if ((pid == ULONG_MAX && errno == ERANGE) || *endp != '\0')
+    error (EXIT_FAILURE, 0, gettext ("invalid process ID '%s'"),
+	   argv[remaining]);
+
+  /* Determine the program name.  */
+  char buf[11 + 3 * sizeof (pid)];
+  snprintf (buf, sizeof (buf), "/proc/%lu/exe", (unsigned long int) pid);
+  size_t exesize = 1024;
+  exe = alloca (exesize);
+  ssize_t nexe;
+  while ((nexe = readlink (buf, exe, exesize)) == exesize)
+    extend_alloca (exe, exesize, 2 * exesize);
+  if (nexe == -1)
+    exe = (char *) "<program name undetermined>";
+  else
+    exe[nexe] = '\0';
+
+  if (ptrace (PTRACE_ATTACH, pid, NULL, NULL) != 0)
+    error (EXIT_FAILURE, errno, gettext ("cannot attach to process %lu"),
+	   (unsigned long int) pid);
+
+  int status = get_process_info (pid);
+
+  ptrace (PTRACE_DETACH, pid, NULL, NULL);
+
+  return status;
+}
+
+
+/* Handle program arguments.  */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+  switch (key)
+    {
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
+
+/* Print the version information.  */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+  fprintf (stream, "pldd (GNU %s) %s\n", PACKAGE, VERSION);
+  fprintf (stream, gettext ("\
+Copyright (C) %s Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions.  There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "2011");
+  fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
+}
+
+
+#define CLASS 32
+#include "pldd-xx.c"
+#define CLASS 64
+#include "pldd-xx.c"
+
+
+static int
+get_process_info (pid_t pid)
+{
+  char buf[12 + 3 * sizeof (pid)];
+
+  snprintf (buf, sizeof (buf), "/proc/%lu/mem", (unsigned long int) pid);
+  memfd = open (buf, O_RDONLY);
+  if (memfd == -1)
+    goto no_info;
+
+  snprintf (buf, sizeof (buf), "/proc/%lu/exe", (unsigned long int) pid);
+  int fd = open (buf, O_RDONLY);
+  if (fd == -1)
+    {
+    no_info:
+      error (0, errno, gettext ("cannot get information about process %lu"),
+	     (unsigned long int) pid);
+      return EXIT_FAILURE;
+    }
+
+  union
+  {
+    Elf32_Ehdr ehdr32;
+    Elf64_Ehdr ehdr64;
+  } uehdr;
+  if (read (fd, &uehdr, sizeof (uehdr)) != sizeof (uehdr))
+    goto no_info;
+
+  close (fd);
+
+  if (memcmp (uehdr.ehdr32.e_ident, ELFMAG, SELFMAG) != 0)
+    {
+      error (0, 0, gettext ("process %lu is no ELF program"),
+	     (unsigned long int) pid);
+      return EXIT_FAILURE;
+    }
+
+  snprintf (buf, sizeof (buf), "/proc/%lu/auxv", (unsigned long int) pid);
+  fd = open (buf, O_RDONLY);
+  if (fd == -1)
+    goto no_info;
+
+  size_t auxv_size = 0;
+  void *auxv = NULL;
+  while (1)
+    {
+      auxv_size += 512;
+      auxv = xrealloc (auxv, auxv_size);
+
+      ssize_t n = read (fd, auxv, auxv_size);
+      if (n < 0)
+	goto no_info;
+      if (n < auxv_size)
+	{
+	  auxv_size = n;
+	  break;
+	}
+    }
+
+  close (fd);
+
+  if (uehdr.ehdr32.e_ident[EI_CLASS] == ELFCLASS32)
+    return find_maps32 (pid, &uehdr.ehdr32, auxv, auxv_size);
+  else
+    return find_maps64 (pid, &uehdr.ehdr64, auxv, auxv_size);
+}

http://sources.redhat.com/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=8621fc3b996a7bf5d60a0fb86cb15a078dcbf542

commit 8621fc3b996a7bf5d60a0fb86cb15a078dcbf542
Merge: e80fab3 9b84983
Author: Ulrich Drepper <drepper@gmail.com>
Date:   Thu Jun 9 07:03:25 2011 -0400

    Merge branch 'master' of ssh://sourceware.org/git/glibc
    
    Conflicts:
    	ChangeLog

diff --cc ChangeLog
index fcdb655,2826e82..5303220
--- a/ChangeLog
+++ b/ChangeLog
@@@ -1,7 -1,17 +1,21 @@@
 +2011-06-07  Ulrich Drepper  <drepper@gmail.com>
 +
 +	* version.h: Update for 2.15 development version.
 +
+ 2011-06-07  David S. Miller  <davem@davemloft.net>
+ 
+ 	* sysdeps/sparc/sparc32/dl-irel.h (elf_irela): Pass dl_hwcap to
+ 	ifuncs.
+ 	* sysdeps/sparc/sparc32/dl-machine.h (elf_machine_rela,
+ 	elf_machine_lazy_rel): Likewise.
+ 	* sysdeps/sparc/sparc64/dl-irel.h (elf_irela): Likewise.
+ 	* sysdeps/sparc/sparc64/dl-machine.h (elf_machine_rela,
+ 	elf_machine_lazy_rel): Likewise.
+ 	* sysdeps/sparc/sparc64/multiarch/memcpy.S (memcpy): Fetch
+ 	dl_hwcap via passed in argument.
+ 	* sysdeps/sparc/sparc64/multiarch/memset.S (memset, bzero):
+ 	Likewise.
+ 
  2011-06-06  Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>
  
  	* stdlib/longlong.h: Update from GCC.  Fix smul_ppmm for S/390.

http://sources.redhat.com/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=e80fab373c5cf01246e6acd302795b13dc50d175

commit e80fab373c5cf01246e6acd302795b13dc50d175
Author: Ulrich Drepper <drepper@gmail.com>
Date:   Tue Jun 7 22:22:10 2011 -0400

    Start 2.15 development

diff --git a/ChangeLog b/ChangeLog
index fd4836a..fcdb655 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2011-06-07  Ulrich Drepper  <drepper@gmail.com>
+
+	* version.h: Update for 2.15 development version.
+
 2011-06-06  Andreas Krebbel  <Andreas.Krebbel@de.ibm.com>
 
 	* stdlib/longlong.h: Update from GCC.  Fix smul_ppmm for S/390.
diff --git a/NEWS b/NEWS
index a6b3832..602e050 100644
--- a/NEWS
+++ b/NEWS
@@ -1,10 +1,12 @@
-GNU C Library NEWS -- history of user-visible changes.  2011-6-4
+GNU C Library NEWS -- history of user-visible changes.  2011-6-7
 Copyright (C) 1992-2009, 2010, 2011 Free Software Foundation, Inc.
 See the end for copying conditions.
 
 Please send GNU C library bug reports via <http://sources.redhat.com/bugzilla/>
 using `glibc' in the "product" field.
 
+Version 2.15
+
 Version 2.14
 
 * The following bugs are resolved with this release:
diff --git a/version.h b/version.h
index a5408b3..7672f61 100644
--- a/version.h
+++ b/version.h
@@ -1,4 +1,4 @@
 /* This file just defines the current version number of libc.  */
 
-#define RELEASE "stable"
-#define VERSION "2.14"
+#define RELEASE "development"
+#define VERSION "2.15"

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog     |   10 +++
 NEWS          |    7 ++-
 elf/Makefile  |    9 ++-
 elf/pldd-xx.c |  238 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 elf/pldd.c    |  234 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 version.h     |    4 +-
 6 files changed, 497 insertions(+), 5 deletions(-)
 create mode 100644 elf/pldd-xx.c
 create mode 100644 elf/pldd.c


hooks/post-receive
-- 
GNU C Library master sources


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