diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index b2f631d7b8b..13107954699 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,52 @@
+Fri Feb 16 14:00:54 1996  Fred Fish  <fnf@cygnus.com>
+
+	* bcache.c, bcache.h: New files to implement a byte cache.
+	* Makefile.in (SFILES): Add bcache.c.
+	(symtab_h): Add bcache.h.
+	(HFILES_NO_SRCDIR): add bcache.h
+	(COMMON_OBJS): Add bcache.o
+	(bcache.o): New target.
+	* dbxread.c (start_psymtab): Make global_syms & static_syms
+ 	type "partial_symbol **".
+	* hpread.c (hpread_start_symtab):  Ditto.
+	* os9kread.c (os9k_start_psymtab):  Ditto.
+	* stabsread.h (start_psymtab):  Ditto.
+	* {symfile.c, symfile.h} (start_psymtab_common):  Ditto.
+	* maint.c (maintenance_print_statistics): Call
+ 	print_symbol_bcache_statistics.
+	* objfiles.c (allocate_objfile): Initialize psymbol bcache malloc
+	and free pointers.
+	* solib.c (allocate_rt_common_objfile): Ditto.
+	* symfile.c (reread_symbols):  Ditto.
+	(free_objfile): Free psymbol bcache when objfile is freed.	
+	(objfile_relocate): Use new indirect psymbol pointers.
+	* objfiles.h (struct objfile): Add psymbol cache.
+	* symfile.c (compare_psymbols): Now passed pointers to pointers to
+	psymbols.
+	(reread_symbols): Free psymbol bcache when freeing other objfile
+	resources.
+	(add_psymbol_to_list, add_psymbol_addr_to_list): Initialize new
+	psymbol using the psymbol bcache.
+	(init_psymbol_list): Psymbol lists now contain pointers rather than
+	the actual psymbols.
+	* symfile.h (psymbol_allocation_list): Psymbol lists now dynamically
+	grown arrays of pointers.
+	(ADD_PSYMBOL_VT_TO_LIST): Initialize new symbol using the psymbol
+	bcache.
+	* symmisc.c (print_partial_symbols): Now takes pointer to pointer
+	to partial symbol.
+	(print_symbol_bcache_statistics): New function to print per objfile
+ 	bcache statistics.
+	(print_partial_symbol, print_partial_symbols,
+ 	maintenance_check_symtabs, extend_psymbol_list):
+ 	Account for change to pointer to pointer to partial symbol.
+	* symtab.c (find_pc_psymbol, lookup_partial_symbol, decode_line_2,
+	make_symbol_completion_list): 
+ 	Account for change to pointer to pointer to partial symbol.
+	* symtab.h (bcache.h): Include.
+	* xcoffread.c (xcoff_start_psymtab): Make global_syms & static_syms
+ 	type "partial_symbol **".
+
 Fri Feb 16 10:02:34 1996  Fred Fish  <fnf@cygnus.com>
 
 	* dwarfread.c (free_utypes): New function.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 51d8e3843f1..e1167cba0fb 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -145,7 +145,6 @@ ENABLE_CFLAGS= @ENABLE_CFLAGS@
 ENABLE_CLIBS= @ENABLE_CLIBS@
 ENABLE_OBS= @ENABLE_OBS@
 
-
 # -I. for config files.
 # -I$(srcdir) for gdb internal headers and possibly for gnu-regex.h also.
 # -I$(srcdir)/config for more generic config files.
@@ -197,10 +196,11 @@ REGEX1 = gnu-regex.o
 # If you have the Cygnus libraries installed,
 # you can use 'CLIBS=$(INSTALLED_LIBS)' 'CDEPS='
 INSTALLED_LIBS=-lbfd -lreadline $(TERMCAP) -lopcodes -lmmalloc \
-	-liberty $(XM_CLIBS) $(TM_CLIBS) $(NAT_CLIBS) $(ENABLE_CLIBS)
+	-liberty $(XM_CLIBS) $(TM_CLIBS) $(NAT_CLIBS) $(ENABLE_CLIBS) \
+	@LIBS@
 CLIBS = $(SIM) $(BFD) $(READLINE) $(OPCODES) $(MMALLOC) $(LIBIBERTY) \
 	$(ENABLE_CLIBS) $(TERMCAP) $(XM_CLIBS) $(TM_CLIBS) $(NAT_CLIBS) \
-	$(LIBIBERTY) $(LIBS)
+	$(LIBIBERTY) @LIBS@
 CDEPS = $(XM_CDEPS) $(TM_CDEPS) $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE) \
 	$(OPCODES) $(MMALLOC) $(LIBIBERTY)
 
@@ -345,10 +345,10 @@ TARGET_FLAGS_TO_PASS = \
 # Links made at configuration time should not be specified here, since
 # SFILES is used in building the distribution archive.
 
-SFILES = blockframe.c breakpoint.c buildsym.c callback.c c-exp.y c-lang.c \
-	c-typeprint.c c-valprint.c ch-exp.c ch-lang.c ch-typeprint.c \
-	ch-valprint.c coffread.c command.c complaints.c corefile.c cp-valprint.c \
-	dbxread.c demangle.c dwarfread.c \
+SFILES = bcache.c blockframe.c breakpoint.c buildsym.c callback.c c-exp.y \
+	c-lang.c c-typeprint.c c-valprint.c ch-exp.c ch-lang.c \
+	ch-typeprint.c ch-valprint.c coffread.c command.c complaints.c \
+	corefile.c cp-valprint.c dbxread.c demangle.c dwarfread.c \
 	elfread.c environ.c eval.c expprint.c \
 	f-exp.y f-lang.c f-typeprint.c f-valprint.c findvar.c \
 	gdbtypes.c infcmd.c inflow.c infrun.c language.c \
@@ -393,7 +393,7 @@ udiheaders = \
 gdbcore_h =	gdbcore.h $(bfd_h)
 
 frame_h =	frame.h
-symtab_h =	symtab.h
+symtab_h =	symtab.h bcache.h
 gdbtypes_h =	gdbtypes.h
 expression_h =	expression.h
 value_h =	value.h $(symtab_h) $(gdbtypes_h) $(expression_h)
@@ -413,8 +413,8 @@ inferior_h =	inferior.h $(breakpoint_h)
 # wrong if TAGS has files twice).  Because this is tricky to get
 # right, it is probably easiest just to list .h files here directly.
 
-HFILES_NO_SRCDIR = buildsym.h call-cmds.h coff-solib.h defs.h dst.h environ.h \
-	$(gdbcmd_h) gdbcore.h \
+HFILES_NO_SRCDIR = bcache.h buildsym.h call-cmds.h coff-solib.h defs.h \
+	dst.h environ.h $(gdbcmd_h) gdbcore.h \
 	gdb-stabs.h $(inferior_h) language.h minimon.h monitor.h \
 	objfiles.h parser-defs.h partial-stab.h serial.h signals.h solib.h \
 	symfile.h stabsread.h target.h terminal.h typeprint.h xcoffsolib.h \
@@ -461,7 +461,7 @@ COMMON_OBS = version.o blockframe.o breakpoint.o findvar.o stack.o thread.o \
 	symtab.o symfile.o symmisc.o infcmd.o infrun.o command.o \
 	expprint.o environ.o gdbtypes.o copying.o $(DEPFILES) \
 	mem-break.o target.o parse.o language.o $(YYOBJ) buildsym.o \
-	exec.o objfiles.o minsyms.o maint.o demangle.o \
+	exec.o bcache.o objfiles.o minsyms.o maint.o demangle.o \
 	dbxread.o coffread.o elfread.o \
 	dwarfread.o mipsread.o stabsread.o corefile.o \
 	c-lang.o ch-exp.o ch-lang.o f-lang.o m2-lang.o \
@@ -743,7 +743,7 @@ maintainer-clean realclean: clean
 	rm -f Makefile
 
 diststuff: $(DISTSTUFF)
-	cd doc; $(MAKE) $(MFLAGS) diststuff
+	cd doc; $(MAKE) $(MFLAGS) all-doc
 
 subdir_do: force
 	@for i in $(DODIRS); do \
@@ -998,6 +998,8 @@ annotate.o: annotate.c $(defs_h) annotate.h $(value_h) target.h $(gdbtypes_h)
 
 arm-tdep.o: arm-tdep.c $(gdbcmd_h) $(gdbcore_h) $(inferior_h) $(defs_h)
 
+bcache.o: bcache.c bcache.h $(defs_h)
+
 blockframe.o: blockframe.c $(defs_h) $(gdbcore_h) $(inferior_h) \
 	objfiles.h symfile.h target.h
 
diff --git a/gdb/NEWS b/gdb/NEWS
index 813b60aafbd..935c437d0d5 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,12 @@
 
 *** Changes since GDB-4.15:
 
+* Memory use reductions and statistics collection
+
+We have begun to implement changes that reduce gdb's memory requirements
+and to report statistics about memory usage.  Try the "maint print statistics"
+command, for example.
+
 * New native configurations
 
 Windows 95, Windows NT				i[345]86-*-win32
diff --git a/gdb/bcache.c b/gdb/bcache.c
new file mode 100644
index 00000000000..af80afbbdd8
--- /dev/null
+++ b/gdb/bcache.c
@@ -0,0 +1,188 @@
+/* Implement a cached obstack.
+   Written by Fred Fish (fnf@cygnus.com)
+   Copyright 1995 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include "defs.h"
+#include "obstack.h"
+#include "bcache.h"
+
+/* FIXME:  Incredibly simplistic hash generator.  Probably way too expensive
+ (consider long strings) and unlikely to have good distribution across hash
+ values for typical input. */
+
+static unsigned int
+hash (bytes, count)
+     void *bytes;
+     int count;
+{
+  unsigned int len;
+  unsigned long hashval;
+  unsigned int c;
+  const unsigned char *data = bytes;
+
+  hashval = 0;
+  len = 0;
+  while (count-- > 0)
+    {
+      c = *data++;
+      hashval += c + (c << 17);
+      hashval ^= hashval >> 2;
+      ++len;
+    }
+  hashval += len + (len << 17);
+  hashval ^= hashval >> 2;
+  return (hashval % BCACHE_HASHSIZE);
+}
+
+static void *
+lookup_cache (bytes, count, hashval, bcachep)
+     void *bytes;
+     int count;
+     int hashval;
+     struct bcache *bcachep;
+{
+  void *location = NULL;
+  struct hashlink **hashtablep;
+  struct hashlink *linkp;
+
+  hashtablep = bcachep -> indextable[count];
+  if (hashtablep != NULL)
+    {
+      linkp = hashtablep[hashval];
+      while (linkp != NULL)
+	{
+	  if (memcmp (linkp -> data, bytes, count) == 0)
+	    {
+	      location = linkp -> data;
+	      break;
+	    }
+	  linkp = linkp -> next;
+	}
+    }
+  return (location);
+}
+
+void *
+bcache (bytes, count, bcachep)
+     void *bytes;
+     int count;
+     struct bcache *bcachep;
+{
+  int hashval;
+  void *location;
+  struct hashlink *newlink;
+  struct hashlink **linkpp;
+  struct hashlink ***hashtablepp;
+
+  if (count > BCACHE_MAXLENGTH)
+    {
+      /* Rare enough to just stash unique copies */
+      location = (void *) obstack_alloc (&bcachep->cache, count);
+      bcachep -> cache_bytes += count;
+      memcpy (location, bytes, count);
+      bcachep -> bcache_overflows++;
+    }
+  else
+    {
+      hashval = hash (bytes, count);
+      location = lookup_cache (bytes, count, hashval, bcachep);
+      if (location != NULL)
+	{
+	  bcachep -> cache_savings += count;
+	  bcachep -> cache_hits++;
+	}
+      else
+	{
+	  bcachep -> cache_misses++;
+	  hashtablepp = &bcachep -> indextable[count];
+	  if (*hashtablepp == NULL)
+	    {
+	      *hashtablepp = (struct hashlink **)
+		obstack_alloc (&bcachep->cache, BCACHE_HASHSIZE * sizeof (struct hashlink *));
+	      bcachep -> cache_bytes += sizeof (struct hashlink *);
+	      memset (*hashtablepp, 0, BCACHE_HASHSIZE * sizeof (struct hashlink *));
+	    }
+	  linkpp = &(*hashtablepp)[hashval];
+	  newlink = (struct hashlink *)
+	    obstack_alloc (&bcachep->cache, sizeof (struct hashlink *) + count);
+	  bcachep -> cache_bytes += sizeof (struct hashlink *) + count;
+	  memcpy (newlink -> data, bytes, count);
+	  newlink -> next = *linkpp;
+	  *linkpp = newlink;
+	  location = newlink -> data;
+	}
+    }
+  return (location);
+}
+
+#if MAINTENANCE_CMDS
+
+void
+print_bcache_statistics (bcachep, id)
+     struct bcache *bcachep;
+     char *id;
+{
+  struct hashlink **hashtablep;
+  struct hashlink *linkp;
+  int tidx, tcount, hidx, hcount, lcount, lmax, temp, lmaxt, lmaxh;
+
+  for (lmax = lcount = tcount = hcount = tidx = 0; tidx < BCACHE_MAXLENGTH; tidx++)
+    {
+      hashtablep = bcachep -> indextable[tidx];
+      if (hashtablep != NULL)
+	{
+	  tcount++;
+	  for (hidx = 0; hidx < BCACHE_HASHSIZE; hidx++)
+	    {
+	      linkp = hashtablep[hidx];
+	      if (linkp != NULL)
+		{
+		  hcount++;
+		  for (temp = 0; linkp != NULL; linkp = linkp -> next)
+		    {
+		      lcount++;
+		      temp++;
+		    }
+		  if (temp > lmax)
+		    {
+		      lmax = temp;
+		      lmaxt = tidx;
+		      lmaxh = hidx;
+		    }
+		}
+	    }
+	}
+    }
+  printf_filtered ("  Cached '%s' statistics:\n", id);
+  printf_filtered ("    Cache hits: %d\n", bcachep -> cache_hits);
+  printf_filtered ("    Cache misses: %d\n", bcachep -> cache_misses);
+  printf_filtered ("    Cache hit ratio: %d%%\n", ((bcachep -> cache_hits) * 100) /  (bcachep -> cache_hits + bcachep -> cache_misses));
+  printf_filtered ("    Space used for caching: %d\n", bcachep -> cache_bytes);
+  printf_filtered ("    Space saved by cache hits: %d\n", bcachep -> cache_savings);
+  printf_filtered ("    Number of bcache overflows: %d\n", bcachep -> bcache_overflows);
+  printf_filtered ("    Number of index buckets used: %d\n", tcount);
+  printf_filtered ("    Number of hash table buckets used: %d\n", hcount);
+  printf_filtered ("    Number of chained items: %d\n", lcount);
+  printf_filtered ("    Average hash table population: %d%%\n",
+		   (hcount * 100) / (tcount * BCACHE_HASHSIZE));
+  printf_filtered ("    Average chain length %d\n", lcount / hcount);
+  printf_filtered ("    Maximum chain length %d at %d:%d\n", lmax, lmaxt, lmaxh);
+}
+
+#endif	/* MAINTENANCE_CMDS */
diff --git a/gdb/bcache.h b/gdb/bcache.h
new file mode 100644
index 00000000000..e389c8eb16b
--- /dev/null
+++ b/gdb/bcache.h
@@ -0,0 +1,46 @@
+/* Include file cached obstack implementation.
+   Written by Fred Fish (fnf@cygnus.com)
+   Copyright 1995 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef BCACHE_H
+#define BCACHE_H 1
+
+#define BCACHE_HASHLENGTH	12	/* Number of bits in hash value */
+#define BCACHE_HASHSIZE	(1 << BCACHE_HASHLENGTH)
+#define BCACHE_MAXLENGTH	128
+
+struct hashlink {
+  struct hashlink *next;
+  char data[1];
+};
+
+struct bcache {
+  struct obstack cache;
+  struct hashlink **indextable[BCACHE_MAXLENGTH];
+  int cache_hits;
+  int cache_misses;
+  int cache_bytes;
+  int cache_savings;
+  int bcache_overflows;
+};
+
+extern void *
+bcache PARAMS ((void *bytes, int count, struct bcache *bcachep));
+
+#endif /* BCACHE_H */
diff --git a/gdb/hpread.c b/gdb/hpread.c
index c8fbec072ca..90a9ac9591c 100644
--- a/gdb/hpread.c
+++ b/gdb/hpread.c
@@ -160,7 +160,7 @@ void hpread_symfile_finish PARAMS ((struct objfile *));
 
 static struct partial_symtab *hpread_start_psymtab
   PARAMS ((struct objfile *, struct section_offsets *, char *, CORE_ADDR, int,
-	   struct partial_symbol *, struct partial_symbol *));
+	   struct partial_symbol **, struct partial_symbol **));
 
 static struct partial_symtab *hpread_end_psymtab
   PARAMS ((struct partial_symtab *, char **, int, int, CORE_ADDR,
@@ -827,8 +827,8 @@ hpread_start_psymtab (objfile, section_offsets,
      char *filename;
      CORE_ADDR textlow;
      int ldsymoff;
-     struct partial_symbol *global_syms;
-     struct partial_symbol *static_syms;
+     struct partial_symbol **global_syms;
+     struct partial_symbol **static_syms;
 {
   struct partial_symtab *result =
   start_psymtab_common (objfile, section_offsets,
diff --git a/gdb/maint.c b/gdb/maint.c
index 3b94eb90a32..5a0eca11f5a 100644
--- a/gdb/maint.c
+++ b/gdb/maint.c
@@ -249,7 +249,10 @@ maintenance_print_statistics (args, from_tty)
      char *args;
      int from_tty;
 {
+  int temp;
+
   print_objfile_statistics ();
+  print_symbol_bcache_statistics ();
 }
 
 /* The "maintenance print" command is defined as a prefix, with allow_unknown
diff --git a/gdb/objfiles.c b/gdb/objfiles.c
index e48655016ff..ed9f1cf3ae7 100644
--- a/gdb/objfiles.c
+++ b/gdb/objfiles.c
@@ -159,6 +159,8 @@ allocate_objfile (abfd, mapped)
 	    objfile -> md = md;
 	    objfile -> mmfd = fd;
 	    /* Update pointers to functions to *our* copies */
+	    obstack_chunkfun (&objfile -> psymbol_cache.cache, xmmalloc);
+	    obstack_freefun (&objfile -> psymbol_cache.cache, mfree);
 	    obstack_chunkfun (&objfile -> psymbol_obstack, xmmalloc);
 	    obstack_freefun (&objfile -> psymbol_obstack, mfree);
 	    obstack_chunkfun (&objfile -> symbol_obstack, xmmalloc);
@@ -186,6 +188,9 @@ allocate_objfile (abfd, mapped)
 	    objfile -> mmfd = fd;
 	    objfile -> flags |= OBJF_MAPPED;
 	    mmalloc_setkey (objfile -> md, 0, objfile);
+	    obstack_specify_allocation_with_arg (&objfile -> psymbol_cache.cache,
+						 0, 0, xmmalloc, mfree,
+						 objfile -> md);
 	    obstack_specify_allocation_with_arg (&objfile -> psymbol_obstack,
 						 0, 0, xmmalloc, mfree,
 						 objfile -> md);
@@ -228,6 +233,8 @@ allocate_objfile (abfd, mapped)
       objfile = (struct objfile *) xmalloc (sizeof (struct objfile));
       memset (objfile, 0, sizeof (struct objfile));
       objfile -> md = NULL;
+      obstack_specify_allocation (&objfile -> psymbol_cache.cache, 0, 0,
+				  xmalloc, free);
       obstack_specify_allocation (&objfile -> psymbol_obstack, 0, 0, xmalloc,
 				  free);
       obstack_specify_allocation (&objfile -> symbol_obstack, 0, 0, xmalloc,
@@ -433,6 +440,7 @@ free_objfile (objfile)
       if (objfile->static_psymbols.list)
 	mfree (objfile->md, objfile->static_psymbols.list);
       /* Free the obstacks for non-reusable objfiles */
+      obstack_free (&objfile -> psymbol_cache.cache, 0);
       obstack_free (&objfile -> psymbol_obstack, 0);
       obstack_free (&objfile -> symbol_obstack, 0);
       obstack_free (&objfile -> type_obstack, 0);
@@ -552,18 +560,18 @@ objfile_relocate (objfile, new_offsets)
   }
 
   {
-    struct partial_symbol *psym;
+    struct partial_symbol **psym;
 
     for (psym = objfile->global_psymbols.list;
 	 psym < objfile->global_psymbols.next;
 	 psym++)
-      if (SYMBOL_SECTION (psym) >= 0)
-	SYMBOL_VALUE_ADDRESS (psym) += ANOFFSET (delta, SYMBOL_SECTION (psym));
+      if (SYMBOL_SECTION (*psym) >= 0)
+	SYMBOL_VALUE_ADDRESS (*psym) += ANOFFSET (delta, SYMBOL_SECTION (*psym));
     for (psym = objfile->static_psymbols.list;
 	 psym < objfile->static_psymbols.next;
 	 psym++)
-      if (SYMBOL_SECTION (psym) >= 0)
-	SYMBOL_VALUE_ADDRESS (psym) += ANOFFSET (delta, SYMBOL_SECTION (psym));
+      if (SYMBOL_SECTION (*psym) >= 0)
+	SYMBOL_VALUE_ADDRESS (*psym) += ANOFFSET (delta, SYMBOL_SECTION (*psym));
   }
 
   {
diff --git a/gdb/objfiles.h b/gdb/objfiles.h
index 0a8bbd3803e..f53605609a0 100644
--- a/gdb/objfiles.h
+++ b/gdb/objfiles.h
@@ -252,6 +252,11 @@ struct objfile
   struct obstack symbol_obstack;	/* Full symbols */
   struct obstack type_obstack;		/* Types */
 
+  /* A byte cache where we can stash arbitrary "chunks" of bytes that
+     will not change. */
+
+  struct bcache psymbol_cache;		/* Byte cache for partial syms */
+
   /* Vectors of all partial symbols read in from file.  The actual data
      is stored in the psymbol_obstack. */
 
diff --git a/gdb/os9kread.c b/gdb/os9kread.c
index bcdf30834d3..2de292219b4 100644
--- a/gdb/os9kread.c
+++ b/gdb/os9kread.c
@@ -159,8 +159,8 @@ os9k_process_one_symbol PARAMS ((int, int, CORE_ADDR, char *,
 
 static struct partial_symtab *
 os9k_start_psymtab PARAMS ((struct objfile *, struct section_offsets *, char *,
-                       CORE_ADDR, int, int, struct partial_symbol *,
-                       struct partial_symbol *));
+                       CORE_ADDR, int, int, struct partial_symbol **,
+                       struct partial_symbol **));
 
 static struct partial_symtab *
 os9k_end_psymtab PARAMS ((struct partial_symtab *, char **, int, int, CORE_ADDR,
@@ -972,8 +972,8 @@ os9k_start_psymtab (objfile, section_offsets,
      CORE_ADDR textlow;
      int ldsymoff;
      int ldsymcnt;
-     struct partial_symbol *global_syms;
-     struct partial_symbol *static_syms;
+     struct partial_symbol **global_syms;
+     struct partial_symbol **static_syms;
 {
   struct partial_symtab *result =
       start_psymtab_common(objfile, section_offsets,
diff --git a/gdb/stabsread.h b/gdb/stabsread.h
index dbdb94395ac..b348738fc02 100644
--- a/gdb/stabsread.h
+++ b/gdb/stabsread.h
@@ -183,8 +183,8 @@ struct stab_section_list
 
 extern struct partial_symtab *
 start_psymtab PARAMS ((struct objfile *, struct section_offsets *, char *,
-		       CORE_ADDR, int, struct partial_symbol *,
-		       struct partial_symbol *));
+		       CORE_ADDR, int, struct partial_symbol **,
+		       struct partial_symbol **));
 
 extern struct partial_symtab *
 end_psymtab PARAMS ((struct partial_symtab *, char **, int, int, CORE_ADDR,
diff --git a/gdb/symfile.c b/gdb/symfile.c
index 98b25df5d37..8dc452ae60d 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -148,9 +148,9 @@ LOCAL FUNCTION
 
 DESCRIPTION
 
-	Given pointer to two partial symbol table entries, compare
-	them by name and return -N, 0, or +N (ala strcmp).  Typically
-	used by sorting routines like qsort().
+	Given pointers to pointers to two partial symbol table entries,
+	compare them by name and return -N, 0, or +N (ala strcmp).
+	Typically used by sorting routines like qsort().
 
 NOTES
 
@@ -167,8 +167,8 @@ compare_psymbols (s1p, s2p)
      const PTR s1p;
      const PTR s2p;
 {
-  register char *st1 = SYMBOL_NAME ((struct partial_symbol *) s1p);
-  register char *st2 = SYMBOL_NAME ((struct partial_symbol *) s2p);
+  register char *st1 = SYMBOL_NAME (*(struct partial_symbol **) s1p);
+  register char *st2 = SYMBOL_NAME (*(struct partial_symbol **) s2p);
 
   if ((st1[0] - st2[0]) || !st1[0])
     {
@@ -191,7 +191,7 @@ sort_pst_symbols (pst)
   /* Sort the global list; don't sort the static list */
 
   qsort (pst -> objfile -> global_psymbols.list + pst -> globals_offset,
-	 pst -> n_global_syms, sizeof (struct partial_symbol),
+	 pst -> n_global_syms, sizeof (struct partial_symbol *),
 	 compare_psymbols);
 }
 
@@ -1192,6 +1192,7 @@ reread_symbols ()
 	  objfile->static_psymbols.size = 0;
 
 	  /* Free the obstacks for non-reusable objfiles */
+	  obstack_free (&objfile -> psymbol_cache.cache, 0);
 	  obstack_free (&objfile -> psymbol_obstack, 0);
 	  obstack_free (&objfile -> symbol_obstack, 0);
 	  obstack_free (&objfile -> type_obstack, 0);
@@ -1211,6 +1212,8 @@ reread_symbols ()
 	  objfile -> md = NULL;
 	  /* obstack_specify_allocation also initializes the obstack so
 	     it is empty.  */
+	  obstack_specify_allocation (&objfile -> psymbol_cache.cache, 0, 0,
+				      xmalloc, free);
 	  obstack_specify_allocation (&objfile -> psymbol_obstack, 0, 0,
 				      xmalloc, free);
 	  obstack_specify_allocation (&objfile -> symbol_obstack, 0, 0,
@@ -1616,8 +1619,8 @@ start_psymtab_common (objfile, section_offsets,
      struct section_offsets *section_offsets;
      char *filename;
      CORE_ADDR textlow;
-     struct partial_symbol *global_syms;
-     struct partial_symbol *static_syms;
+     struct partial_symbol **global_syms;
+     struct partial_symbol **static_syms;
 {
   struct partial_symtab *psymtab;
 
@@ -1651,24 +1654,29 @@ add_psymbol_to_list (name, namelength, namespace, class, list, val, language,
      struct objfile *objfile;
 {
   register struct partial_symbol *psym;
-  register char *demangled_name;
+  char *buf = alloca (namelength + 1);
+  struct partial_symbol psymbol;
 
+  /* Create local copy of the partial symbol */
+  memcpy (buf, name, namelength);
+  buf[namelength] = '\0';
+  SYMBOL_NAME (&psymbol) = bcache (buf, namelength + 1, &objfile->psymbol_cache);
+  SYMBOL_VALUE (&psymbol) = val;
+  SYMBOL_SECTION (&psymbol) = 0;
+  SYMBOL_LANGUAGE (&psymbol) = language;
+  PSYMBOL_NAMESPACE (&psymbol) = namespace;
+  PSYMBOL_CLASS (&psymbol) = class;
+  SYMBOL_INIT_LANGUAGE_SPECIFIC (&psymbol, language);
+
+  /* Stash the partial symbol away in the cache */
+  psym = bcache (&psymbol, sizeof (struct partial_symbol), &objfile->psymbol_cache);
+
+  /* Save pointer to partial symbol in psymtab, growing symtab if needed. */
   if (list->next >= list->list + list->size)
     {
-      extend_psymbol_list (list,objfile);
+      extend_psymbol_list (list, objfile);
     }
-  psym = list->next++;
-  
-  SYMBOL_NAME (psym) =
-    (char *) obstack_alloc (&objfile->psymbol_obstack, namelength + 1);
-  memcpy (SYMBOL_NAME (psym), name, namelength);
-  SYMBOL_NAME (psym)[namelength] = '\0';
-  SYMBOL_VALUE (psym) = val;
-  SYMBOL_SECTION (psym) = 0;
-  SYMBOL_LANGUAGE (psym) = language;
-  PSYMBOL_NAMESPACE (psym) = namespace;
-  PSYMBOL_CLASS (psym) = class;
-  SYMBOL_INIT_LANGUAGE_SPECIFIC (psym, language);
+  *list->next++ = psym;
   OBJSTAT (objfile, n_psyms++);
 }
 
@@ -1687,24 +1695,29 @@ add_psymbol_addr_to_list (name, namelength, namespace, class, list, val,
      struct objfile *objfile;
 {
   register struct partial_symbol *psym;
-  register char *demangled_name;
+  char *buf = alloca (namelength + 1);
+  struct partial_symbol psymbol;
 
+  /* Create local copy of the partial symbol */
+  memcpy (buf, name, namelength);
+  buf[namelength] = '\0';
+  SYMBOL_NAME (&psymbol) = bcache (buf, namelength + 1, &objfile->psymbol_cache);
+  SYMBOL_VALUE_ADDRESS (&psymbol) = val;
+  SYMBOL_SECTION (&psymbol) = 0;
+  SYMBOL_LANGUAGE (&psymbol) = language;
+  PSYMBOL_NAMESPACE (&psymbol) = namespace;
+  PSYMBOL_CLASS (&psymbol) = class;
+  SYMBOL_INIT_LANGUAGE_SPECIFIC (&psymbol, language);
+
+  /* Stash the partial symbol away in the cache */
+  psym = bcache (&psymbol, sizeof (struct partial_symbol), &objfile->psymbol_cache);
+
+  /* Save pointer to partial symbol in psymtab, growing symtab if needed. */
   if (list->next >= list->list + list->size)
     {
-      extend_psymbol_list (list,objfile);
+      extend_psymbol_list (list, objfile);
     }
-  psym = list->next++;
-  
-  SYMBOL_NAME (psym) =
-    (char *) obstack_alloc (&objfile->psymbol_obstack, namelength + 1);
-  memcpy (SYMBOL_NAME (psym), name, namelength);
-  SYMBOL_NAME (psym)[namelength] = '\0';
-  SYMBOL_VALUE_ADDRESS (psym) = val;
-  SYMBOL_SECTION (psym) = 0;
-  SYMBOL_LANGUAGE (psym) = language;
-  PSYMBOL_NAMESPACE (psym) = namespace;
-  PSYMBOL_CLASS (psym) = class;
-  SYMBOL_INIT_LANGUAGE_SPECIFIC (psym, language);
+  *list->next++ = psym;
   OBJSTAT (objfile, n_psyms++);
 }
 
@@ -1735,13 +1748,13 @@ init_psymbol_list (objfile, total_symbols)
   objfile -> global_psymbols.size = total_symbols / 10;
   objfile -> static_psymbols.size = total_symbols / 10;
   objfile -> global_psymbols.next =
-    objfile -> global_psymbols.list = (struct partial_symbol *)
+    objfile -> global_psymbols.list = (struct partial_symbol **)
       xmmalloc (objfile -> md, objfile -> global_psymbols.size
-			     * sizeof (struct partial_symbol));
+			     * sizeof (struct partial_symbol *));
   objfile -> static_psymbols.next =
-    objfile -> static_psymbols.list = (struct partial_symbol *)
+    objfile -> static_psymbols.list = (struct partial_symbol **)
       xmmalloc (objfile -> md, objfile -> static_psymbols.size
-			     * sizeof (struct partial_symbol));
+			     * sizeof (struct partial_symbol *));
 }
 
 void
diff --git a/gdb/symfile.h b/gdb/symfile.h
index 6271a729696..3c968f64bae 100644
--- a/gdb/symfile.h
+++ b/gdb/symfile.h
@@ -23,10 +23,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 /* This file requires that you first include "bfd.h".  */
 
+/* Partial symbols are stored in the psymbol_cache and pointers to them
+   are kept in a dynamically grown array that is obtained from malloc and
+   grown as necessary via realloc.  Each objfile typically has two of these,
+   one for global symbols and one for static symbols.  Although this adds
+   a level of indirection for storing or accessing the partial symbols,
+   it allows us to throw away duplicate psymbols and set all pointers
+   to the single saved instance. */
+
 struct psymbol_allocation_list {
-  struct partial_symbol *list;
-  struct partial_symbol *next;
-  int size;
+  struct partial_symbol **list;	/* Pointer to first partial symbol pointer*/
+  struct partial_symbol **next;	/* Pointer to next avail storage for pointer */
+  int size;			/* Number of symbols */
 };
 
 /* Structure to keep track of symbol reading functions for various
@@ -116,21 +124,22 @@ extend_psymbol_list PARAMS ((struct psymbol_allocation_list *,
 #define	ADD_PSYMBOL_VT_TO_LIST(NAME,NAMELENGTH,NAMESPACE,CLASS,LIST,VALUE,VT,LANGUAGE, OBJFILE) \
   do {		        						\
     register struct partial_symbol *psym;				\
+    char *buf = alloca ((NAMELENGTH) + 1);				\
+    struct partial_symbol psymbol;					\
+    memcpy (buf, (NAME), (NAMELENGTH));					\
+    buf[(NAMELENGTH)] = '\0';						\
+    SYMBOL_NAME (&psymbol) = bcache (buf, (NAMELENGTH) + 1, &(OBJFILE)->psymbol_cache); \
+    VT (&psymbol) = (VALUE);						\
+    SYMBOL_SECTION (&psymbol) = 0;					\
+    SYMBOL_LANGUAGE (&psymbol) = (LANGUAGE);				\
+    PSYMBOL_NAMESPACE (&psymbol) = (NAMESPACE);				\
+    PSYMBOL_CLASS (&psymbol) = (CLASS);					\
+    SYMBOL_INIT_LANGUAGE_SPECIFIC (&psymbol, (LANGUAGE));		\
+    psym = bcache (&psymbol, sizeof (struct partial_symbol), &(OBJFILE)->psymbol_cache); \
     if ((LIST).next >= (LIST).list + (LIST).size)			\
-      extend_psymbol_list (&(LIST),(OBJFILE));				\
-    psym = (LIST).next++;						\
-    SYMBOL_NAME (psym) =						\
-      (char *) obstack_alloc (&objfile->psymbol_obstack,		\
-			      (NAMELENGTH) + 1);			\
-    memcpy (SYMBOL_NAME (psym), (NAME), (NAMELENGTH));			\
-    SYMBOL_NAME (psym)[(NAMELENGTH)] = '\0';				\
-    SYMBOL_NAMESPACE (psym) = (NAMESPACE);				\
-    SYMBOL_SECTION (psym) = 0;						\
-    PSYMBOL_CLASS (psym) = (CLASS);					\
-    VT (psym) = (VALUE); 						\
-    SYMBOL_LANGUAGE (psym) = (LANGUAGE);				\
-    SYMBOL_INIT_LANGUAGE_SPECIFIC (psym, LANGUAGE);			\
-    OBJSTAT (objfile, n_psyms++);					\
+      extend_psymbol_list (&(LIST), (OBJFILE));				\
+    *(LIST).next++ = psym;						\
+    OBJSTAT ((OBJFILE), n_psyms++);					\
   } while (0)
 
 /* Add a symbol with an integer value to a psymtab. */
@@ -174,8 +183,8 @@ new_symfile_objfile PARAMS ((struct objfile *, int, int));
 extern struct partial_symtab *
 start_psymtab_common PARAMS ((struct objfile *, struct section_offsets *,
 			      char *, CORE_ADDR,
-			      struct partial_symbol *,
-			      struct partial_symbol *));
+			      struct partial_symbol **,
+			      struct partial_symbol **));
 
 /* Sorting your symbols for fast lookup or alphabetical printing.  */
 
diff --git a/gdb/symmisc.c b/gdb/symmisc.c
index d4b3d688f2f..8485c596bd8 100644
--- a/gdb/symmisc.c
+++ b/gdb/symmisc.c
@@ -63,7 +63,7 @@ static int
 block_depth PARAMS ((struct block *));
 
 static void
-print_partial_symbol PARAMS ((struct partial_symbol *, int, char *, GDB_FILE *));
+print_partial_symbols PARAMS ((struct partial_symbol **, int, char *, GDB_FILE *));
 
 struct print_symbol_args {
   struct symbol *symbol;
@@ -152,6 +152,20 @@ free_symtab (s)
 
 #if MAINTENANCE_CMDS
 
+void
+print_symbol_bcache_statistics ()
+{
+  struct objfile *objfile;
+
+  immediate_quit++;
+  ALL_OBJFILES (objfile)
+    {
+      printf_filtered ("Cached obstack statistics for '%s':\n", objfile -> name);
+      print_bcache_statistics (&objfile -> psymbol_cache, "partial symbol obstack");
+    }
+  immediate_quit--;
+}
+
 void
 print_objfile_statistics ()
 {
@@ -373,13 +387,13 @@ dump_psymtab (objfile, psymtab, outfile)
     }
   if (psymtab -> n_global_syms > 0)
     {
-      print_partial_symbol (objfile -> global_psymbols.list
+      print_partial_symbols (objfile -> global_psymbols.list
 			    + psymtab -> globals_offset,
 			    psymtab -> n_global_syms, "Global", outfile);
     }
   if (psymtab -> n_static_syms > 0)
     {
-      print_partial_symbol (objfile -> static_psymbols.list
+      print_partial_symbols (objfile -> static_psymbols.list
 			    + psymtab -> statics_offset,
 			    psymtab -> n_static_syms, "Static", outfile);
     }
@@ -461,7 +475,7 @@ dump_symtab (objfile, symtab, outfile)
 	  s.depth = depth + 1;
 	  s.outfile = outfile;
 	  catch_errors (print_symbol, &s, "Error printing symbol:\n",
-			RETURN_MASK_ERROR);
+			RETURN_MASK_ALL);
 	}
     }
   fprintf_filtered (outfile, "\n");
@@ -730,23 +744,22 @@ maintenance_print_psymbols (args, from_tty)
 }
 
 static void
-print_partial_symbol (p, count, what, outfile)
-     struct partial_symbol *p;
+print_partial_symbols (p, count, what, outfile)
+     struct partial_symbol **p;
      int count;
      char *what;
      GDB_FILE *outfile;
 {
-
   fprintf_filtered (outfile, "  %s partial symbols:\n", what);
   while (count-- > 0)
     {
-      fprintf_filtered (outfile, "    `%s'", SYMBOL_NAME(p));
-      if (SYMBOL_DEMANGLED_NAME (p) != NULL)
+      fprintf_filtered (outfile, "    `%s'", SYMBOL_NAME(*p));
+      if (SYMBOL_DEMANGLED_NAME (*p) != NULL)
 	{
-	  fprintf_filtered (outfile, "  `%s'", SYMBOL_DEMANGLED_NAME (p));
+	  fprintf_filtered (outfile, "  `%s'", SYMBOL_DEMANGLED_NAME (*p));
 	}
       fputs_filtered (", ", outfile);
-      switch (SYMBOL_NAMESPACE (p))
+      switch (SYMBOL_NAMESPACE (*p))
 	{
 	case UNDEF_NAMESPACE:
 	  fputs_filtered ("undefined namespace, ", outfile);
@@ -764,7 +777,7 @@ print_partial_symbol (p, count, what, outfile)
 	  fputs_filtered ("<invalid namespace>, ", outfile);
 	  break;
 	}
-      switch (SYMBOL_CLASS (p))
+      switch (SYMBOL_CLASS (*p))
 	{
 	case LOC_UNDEF:
 	  fputs_filtered ("undefined", outfile);
@@ -822,7 +835,7 @@ print_partial_symbol (p, count, what, outfile)
       /* FIXME-32x64: Need to use SYMBOL_VALUE_ADDRESS, etc.; this
 	 could be 32 bits when some of the other fields in the union
 	 are 64.  */
-      fprintf_filtered (outfile, "0x%lx\n", SYMBOL_VALUE (p));
+      fprintf_filtered (outfile, "0x%lx\n", SYMBOL_VALUE (*p));
       p++;
     }
 }
@@ -901,7 +914,7 @@ maintenance_check_symtabs (ignore, from_tty)
      int from_tty;
 {
   register struct symbol *sym;
-  register struct partial_symbol *psym;
+  register struct partial_symbol **psym;
   register struct symtab *s = NULL;
   register struct partial_symtab *ps;
   struct blockvector *bv;
@@ -920,12 +933,12 @@ maintenance_check_symtabs (ignore, from_tty)
       length = ps->n_static_syms;
       while (length--)
 	{
-	  sym = lookup_block_symbol (b, SYMBOL_NAME (psym),
-				     SYMBOL_NAMESPACE (psym));
+	  sym = lookup_block_symbol (b, SYMBOL_NAME (*psym),
+				     SYMBOL_NAMESPACE (*psym));
 	  if (!sym)
 	    {
 	      printf_filtered ("Static symbol `");
-	      puts_filtered (SYMBOL_NAME (psym));
+	      puts_filtered (SYMBOL_NAME (*psym));
 	      printf_filtered ("' only found in ");
 	      puts_filtered (ps->filename);
 	      printf_filtered (" psymtab\n");
@@ -937,12 +950,12 @@ maintenance_check_symtabs (ignore, from_tty)
       length = ps->n_global_syms;
       while (length--)
 	{
-	  sym = lookup_block_symbol (b, SYMBOL_NAME (psym),
-				     SYMBOL_NAMESPACE (psym));
+	  sym = lookup_block_symbol (b, SYMBOL_NAME (*psym),
+				     SYMBOL_NAMESPACE (*psym));
 	  if (!sym)
 	    {
 	      printf_filtered ("Global symbol `");
-	      puts_filtered (SYMBOL_NAME (psym));
+	      puts_filtered (SYMBOL_NAME (*psym));
 	      printf_filtered ("' only found in ");
 	      puts_filtered (ps->filename);
 	      printf_filtered (" psymtab\n");
@@ -998,7 +1011,7 @@ block_depth (block)
 
 
 /* Increase the space allocated for LISTP, which is probably
-   global_psymbol_list or static_psymbol_list. This space will eventually
+   global_psymbols or static_psymbols. This space will eventually
    be freed in free_objfile().  */
 
 void
@@ -1010,15 +1023,15 @@ extend_psymbol_list (listp, objfile)
   if (listp->size == 0)
     {
       new_size = 255;
-      listp->list = (struct partial_symbol *)
-	xmmalloc (objfile -> md, new_size * sizeof (struct partial_symbol));
+      listp->list = (struct partial_symbol **)
+	xmmalloc (objfile -> md, new_size * sizeof (struct partial_symbol *));
     }
   else
     {
       new_size = listp->size * 2;
-      listp->list = (struct partial_symbol *)
+      listp->list = (struct partial_symbol **)
 	xmrealloc (objfile -> md, (char *) listp->list,
-		   new_size * sizeof (struct partial_symbol));
+		   new_size * sizeof (struct partial_symbol *));
     }
   /* Next assumes we only went one over.  Should be good if
      program works correctly */
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 49b0c1aa86e..c7703dd7fd4 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -25,6 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "obstack.h"
 #define obstack_chunk_alloc xmalloc
 #define obstack_chunk_free free
+#include "bcache.h"
 
 /* Don't do this; it means that if some .o's are compiled with GNU C
    and some are not (easy to do accidentally the way we configure
diff --git a/gdb/xcoffread.c b/gdb/xcoffread.c
index b5531024da4..7704a5fba40 100644
--- a/gdb/xcoffread.c
+++ b/gdb/xcoffread.c
@@ -1933,7 +1933,7 @@ static unsigned int first_fun_line_offset;
 
 static struct partial_symtab *xcoff_start_psymtab
   PARAMS ((struct objfile *, struct section_offsets *, char *, int,
-	   struct partial_symbol *, struct partial_symbol *));
+	   struct partial_symbol **, struct partial_symbol **));
 
 /* Allocate and partially fill a partial symtab.  It will be
    completely filled at the end of the symbol list.
@@ -1949,8 +1949,8 @@ xcoff_start_psymtab (objfile, section_offsets,
      struct section_offsets *section_offsets;
      char *filename;
      int first_symnum;
-     struct partial_symbol *global_syms;
-     struct partial_symbol *static_syms;
+     struct partial_symbol **global_syms;
+     struct partial_symbol **static_syms;
 {
   struct partial_symtab *result =
     start_psymtab_common (objfile, section_offsets,