daily update
[binutils-gdb.git] / gdb / symfile.c
index 287c8feb6308d6e89774953570f77140cb757da3..aee7ef53595a17dd345bc364225d7b2e6044f3cf 100644 (file)
@@ -1,6 +1,6 @@
 /* Generic symbol file reading for the GNU debugger, GDB.
 
-   Copyright (C) 1990-2013 Free Software Foundation, Inc.
+   Copyright (C) 1990-2014 Free Software Foundation, Inc.
 
    Contributed by Cygnus Support, using pieces from other GDB modules.
 
@@ -60,8 +60,8 @@
 
 #include <sys/types.h>
 #include <fcntl.h>
-#include "gdb_string.h"
-#include "gdb_stat.h"
+#include <string.h>
+#include <sys/stat.h>
 #include <ctype.h>
 #include <time.h>
 #include <sys/time.h>
@@ -130,10 +130,18 @@ void _initialize_symfile (void);
    calls add_symtab_fns() to register information on each format it is
    prepared to read.  */
 
-typedef const struct sym_fns *sym_fns_ptr;
-DEF_VEC_P (sym_fns_ptr);
+typedef struct
+{
+  /* BFD flavour that we handle.  */
+  enum bfd_flavour sym_flavour;
+
+  /* The "vtable" of symbol functions.  */
+  const struct sym_fns *sym_fns;
+} registered_sym_fns;
 
-static VEC (sym_fns_ptr) *symtab_fns = NULL;
+DEF_VEC_O (registered_sym_fns);
+
+static VEC (registered_sym_fns) *symtab_fns = NULL;
 
 /* If non-zero, shared library symbols will be added automatically
    when the inferior is created, new libraries are loaded, or when
@@ -791,13 +799,13 @@ default_symfile_segments (bfd *abfd)
   low = bfd_get_section_vma (abfd, sect);
   high = low + bfd_get_section_size (sect);
 
-  data = XZALLOC (struct symfile_segment_data);
+  data = XCNEW (struct symfile_segment_data);
   data->num_segments = 1;
-  data->segment_bases = XCALLOC (1, CORE_ADDR);
-  data->segment_sizes = XCALLOC (1, CORE_ADDR);
+  data->segment_bases = XCNEW (CORE_ADDR);
+  data->segment_sizes = XCNEW (CORE_ADDR);
 
   num_sections = bfd_count_sections (abfd);
-  data->segment_info = XCALLOC (num_sections, int);
+  data->segment_info = XCNEWVEC (int, num_sections);
 
   for (i = 0, sect = abfd->sections; sect != NULL; i++, sect = sect->next)
     {
@@ -828,6 +836,7 @@ static void
 read_symbols (struct objfile *objfile, int add_flags)
 {
   (*objfile->sf->sym_read) (objfile, add_flags);
+  objfile->per_bfd->minsyms_read = 1;
 
   /* find_separate_debug_file_in_section should be called only if there is
      single binary with no existing separate debug info file.  */
@@ -839,7 +848,14 @@ read_symbols (struct objfile *objfile, int add_flags)
       struct cleanup *cleanup = make_cleanup_bfd_unref (abfd);
 
       if (abfd != NULL)
-       symbol_file_add_separate (abfd, add_flags, objfile);
+       {
+         /* find_separate_debug_file_in_section uses the same filename for the
+            virtual section-as-bfd like the bfd filename containing the
+            section.  Therefore use also non-canonical name form for the same
+            file containing the section.  */
+         symbol_file_add_separate (abfd, objfile->original_name, add_flags,
+                                   objfile);
+       }
 
       do_cleanups (cleanup);
     }
@@ -852,6 +868,12 @@ read_symbols (struct objfile *objfile, int add_flags)
 static void
 init_entry_point_info (struct objfile *objfile)
 {
+  struct entry_info *ei = &objfile->per_bfd->ei;
+
+  if (ei->initialized)
+    return;
+  ei->initialized = 1;
+
   /* Save startup file's range of PC addresses to help blockframe.c
      decide where the bottom of the stack is.  */
 
@@ -859,8 +881,8 @@ init_entry_point_info (struct objfile *objfile)
     {
       /* Executable file -- record its entry point so we'll recognize
          the startup file because it contains the entry point.  */
-      objfile->ei.entry_point = bfd_get_start_address (objfile->obfd);
-      objfile->ei.entry_point_p = 1;
+      ei->entry_point = bfd_get_start_address (objfile->obfd);
+      ei->entry_point_p = 1;
     }
   else if (bfd_get_file_flags (objfile->obfd) & DYNAMIC
           && bfd_get_start_address (objfile->obfd) != 0)
@@ -868,18 +890,20 @@ init_entry_point_info (struct objfile *objfile)
       /* Some shared libraries may have entry points set and be
         runnable.  There's no clear way to indicate this, so just check
         for values other than zero.  */
-      objfile->ei.entry_point = bfd_get_start_address (objfile->obfd);
-      objfile->ei.entry_point_p = 1;
+      ei->entry_point = bfd_get_start_address (objfile->obfd);
+      ei->entry_point_p = 1;
     }
   else
     {
       /* Examination of non-executable.o files.  Short-circuit this stuff.  */
-      objfile->ei.entry_point_p = 0;
+      ei->entry_point_p = 0;
     }
 
-  if (objfile->ei.entry_point_p)
+  if (ei->entry_point_p)
     {
-      CORE_ADDR entry_point =  objfile->ei.entry_point;
+      struct obj_section *osect;
+      CORE_ADDR entry_point =  ei->entry_point;
+      int found;
 
       /* Make certain that the address points at real code, and not a
         function descriptor.  */
@@ -890,8 +914,27 @@ init_entry_point_info (struct objfile *objfile)
 
       /* Remove any ISA markers, so that this matches entries in the
         symbol table.  */
-      objfile->ei.entry_point
+      ei->entry_point
        = gdbarch_addr_bits_remove (get_objfile_arch (objfile), entry_point);
+
+      found = 0;
+      ALL_OBJFILE_OSECTIONS (objfile, osect)
+       {
+         struct bfd_section *sect = osect->the_bfd_section;
+
+         if (entry_point >= bfd_get_section_vma (objfile->obfd, sect)
+             && entry_point < (bfd_get_section_vma (objfile->obfd, sect)
+                               + bfd_get_section_size (sect)))
+           {
+             ei->the_bfd_section_index
+               = gdb_bfd_section_index (objfile->obfd, sect);
+             found = 1;
+             break;
+           }
+       }
+
+      if (!found)
+       ei->the_bfd_section_index = SECT_OFF_TEXT (objfile);
     }
 }
 
@@ -925,7 +968,7 @@ syms_from_objfile_1 (struct objfile *objfile,
   struct cleanup *old_chain;
   const int mainline = add_flags & SYMFILE_MAINLINE;
 
-  objfile->sf = find_sym_fns (objfile->obfd);
+  objfile_set_sym_fns (objfile, find_sym_fns (objfile->obfd));
 
   if (objfile->sf == NULL)
     {
@@ -1047,6 +1090,8 @@ new_symfile_objfile (struct objfile *objfile, int add_flags)
    ABFD is a BFD already open on the file, as from symfile_bfd_open.
    A new reference is acquired by this function.
 
+   For NAME description see allocate_objfile's definition.
+
    ADD_FLAGS encodes verbosity, whether this is main symbol file or
    extra, such as dynamically loaded code, and what to do with breakpoins.
 
@@ -1060,12 +1105,11 @@ new_symfile_objfile (struct objfile *objfile, int add_flags)
    Upon failure, jumps back to command level (never returns).  */
 
 static struct objfile *
-symbol_file_add_with_addrs (bfd *abfd, int add_flags,
+symbol_file_add_with_addrs (bfd *abfd, const char *name, int add_flags,
                            struct section_addr_info *addrs,
                            int flags, struct objfile *parent)
 {
   struct objfile *objfile;
-  const char *name = bfd_get_filename (abfd);
   const int from_tty = add_flags & SYMFILE_VERBOSE;
   const int mainline = add_flags & SYMFILE_MAINLINE;
   const int should_print = ((from_tty || info_verbose)
@@ -1087,7 +1131,8 @@ symbol_file_add_with_addrs (bfd *abfd, int add_flags,
       && !query (_("Load new symbol table from \"%s\"? "), name))
     error (_("Not confirmed."));
 
-  objfile = allocate_objfile (abfd, flags | (mainline ? OBJF_MAINLINE : 0));
+  objfile = allocate_objfile (abfd, name,
+                             flags | (mainline ? OBJF_MAINLINE : 0));
 
   if (parent)
     add_separate_debug_objfile (objfile, parent);
@@ -1160,10 +1205,12 @@ symbol_file_add_with_addrs (bfd *abfd, int add_flags,
   return (objfile);
 }
 
-/* Add BFD as a separate debug file for OBJFILE.  */
+/* Add BFD as a separate debug file for OBJFILE.  For NAME description
+   see allocate_objfile's definition.  */
 
 void
-symbol_file_add_separate (bfd *bfd, int symfile_flags, struct objfile *objfile)
+symbol_file_add_separate (bfd *bfd, const char *name, int symfile_flags,
+                         struct objfile *objfile)
 {
   struct objfile *new_objfile;
   struct section_addr_info *sap;
@@ -1176,7 +1223,7 @@ symbol_file_add_separate (bfd *bfd, int symfile_flags, struct objfile *objfile)
   my_cleanup = make_cleanup_free_section_addr_info (sap);
 
   new_objfile = symbol_file_add_with_addrs
-    (bfd, symfile_flags, sap,
+    (bfd, name, symfile_flags, sap,
      objfile->flags & (OBJF_REORDERED | OBJF_SHARED | OBJF_READNOW
                       | OBJF_USERLOADED),
      objfile);
@@ -1189,11 +1236,12 @@ symbol_file_add_separate (bfd *bfd, int symfile_flags, struct objfile *objfile)
    See symbol_file_add_with_addrs's comments for details.  */
 
 struct objfile *
-symbol_file_add_from_bfd (bfd *abfd, int add_flags,
+symbol_file_add_from_bfd (bfd *abfd, const char *name, int add_flags,
                           struct section_addr_info *addrs,
                           int flags, struct objfile *parent)
 {
-  return symbol_file_add_with_addrs (abfd, add_flags, addrs, flags, parent);
+  return symbol_file_add_with_addrs (abfd, name, add_flags, addrs, flags,
+                                    parent);
 }
 
 /* Process a symbol file, as either the main file or as a dynamically
@@ -1207,7 +1255,7 @@ symbol_file_add (const char *name, int add_flags,
   struct cleanup *cleanup = make_cleanup_bfd_unref (bfd);
   struct objfile *objf;
 
-  objf = symbol_file_add_from_bfd (bfd, add_flags, addrs, flags, NULL);
+  objf = symbol_file_add_from_bfd (bfd, name, add_flags, addrs, flags, NULL);
   do_cleanups (cleanup);
   return objf;
 }
@@ -1607,11 +1655,9 @@ symbol_file_command (char *args, int from_tty)
 void
 set_initial_language (void)
 {
-  enum language lang = language_unknown;
+  enum language lang = main_language ();
 
-  if (language_of_main != language_unknown)
-    lang = language_of_main;
-  else
+  if (lang == language_unknown)
     {
       char *name = main_name ();
       struct symbol *sym = lookup_symbol (name, NULL, VAR_DOMAIN, NULL);
@@ -1734,14 +1780,18 @@ get_section_index (struct objfile *objfile, char *section_name)
     return -1;
 }
 
-/* Link SF into the global symtab_fns list.  Called on startup by the
-   _initialize routine in each object file format reader, to register
-   information about each format the reader is prepared to handle.  */
+/* Link SF into the global symtab_fns list.
+   FLAVOUR is the file format that SF handles.
+   Called on startup by the _initialize routine in each object file format
+   reader, to register information about each format the reader is prepared
+   to handle.  */
 
 void
-add_symtab_fns (const struct sym_fns *sf)
+add_symtab_fns (enum bfd_flavour flavour, const struct sym_fns *sf)
 {
-  VEC_safe_push (sym_fns_ptr, symtab_fns, sf);
+  registered_sym_fns fns = { flavour, sf };
+
+  VEC_safe_push (registered_sym_fns, symtab_fns, &fns);
 }
 
 /* Initialize OBJFILE to read symbols from its associated BFD.  It
@@ -1752,7 +1802,7 @@ add_symtab_fns (const struct sym_fns *sf)
 static const struct sym_fns *
 find_sym_fns (bfd *abfd)
 {
-  const struct sym_fns *sf;
+  registered_sym_fns *rsf;
   enum bfd_flavour our_flavour = bfd_get_flavour (abfd);
   int i;
 
@@ -1761,9 +1811,9 @@ find_sym_fns (bfd *abfd)
       || our_flavour == bfd_target_tekhex_flavour)
     return NULL;       /* No symbols.  */
 
-  for (i = 0; VEC_iterate (sym_fns_ptr, symtab_fns, i, sf); ++i)
-    if (our_flavour == sf->sym_flavour)
-      return sf;
+  for (i = 0; VEC_iterate (registered_sym_fns, symtab_fns, i, rsf); ++i)
+    if (our_flavour == rsf->sym_flavour)
+      return rsf->sym_fns;
 
   error (_("I'm sorry, Dave, I can't do that.  Symbol format `%s' unknown."),
         bfd_get_target (abfd));
@@ -2182,6 +2232,7 @@ add_symbol_file_command (char *args, int from_tty)
   int expecting_sec_name = 0;
   int expecting_sec_addr = 0;
   char **argv;
+  struct objfile *objf;
 
   struct sect_opt
   {
@@ -2308,8 +2359,10 @@ add_symbol_file_command (char *args, int from_tty)
   if (from_tty && (!query ("%s", "")))
     error (_("Not confirmed."));
 
-  symbol_file_add (filename, from_tty ? SYMFILE_VERBOSE : 0,
-                   section_addrs, flags);
+  objf = symbol_file_add (filename, from_tty ? SYMFILE_VERBOSE : 0,
+                         section_addrs, flags);
+
+  add_target_sections_of_objfile (objf);
 
   /* Getting new symbols may change our opinion about what is
      frameless.  */
@@ -2318,6 +2371,82 @@ add_symbol_file_command (char *args, int from_tty)
 }
 \f
 
+/* This function removes a symbol file that was added via add-symbol-file.  */
+
+static void
+remove_symbol_file_command (char *args, int from_tty)
+{
+  char **argv;
+  struct objfile *objf = NULL;
+  struct cleanup *my_cleanups;
+  struct program_space *pspace = current_program_space;
+  struct gdbarch *gdbarch = get_current_arch ();
+
+  dont_repeat ();
+
+  if (args == NULL)
+    error (_("remove-symbol-file: no symbol file provided"));
+
+  my_cleanups = make_cleanup (null_cleanup, NULL);
+
+  argv = gdb_buildargv (args);
+
+  if (strcmp (argv[0], "-a") == 0)
+    {
+      /* Interpret the next argument as an address.  */
+      CORE_ADDR addr;
+
+      if (argv[1] == NULL)
+       error (_("Missing address argument"));
+
+      if (argv[2] != NULL)
+       error (_("Junk after %s"), argv[1]);
+
+      addr = parse_and_eval_address (argv[1]);
+
+      ALL_OBJFILES (objf)
+       {
+         if (objf != 0
+             && objf->flags & OBJF_USERLOADED
+             && objf->pspace == pspace && is_addr_in_objfile (addr, objf))
+           break;
+       }
+    }
+  else if (argv[0] != NULL)
+    {
+      /* Interpret the current argument as a file name.  */
+      char *filename;
+
+      if (argv[1] != NULL)
+       error (_("Junk after %s"), argv[0]);
+
+      filename = tilde_expand (argv[0]);
+      make_cleanup (xfree, filename);
+
+      ALL_OBJFILES (objf)
+       {
+         if (objf != 0
+             && objf->flags & OBJF_USERLOADED
+             && objf->pspace == pspace
+             && filename_cmp (filename, objfile_name (objf)) == 0)
+           break;
+       }
+    }
+
+  if (objf == NULL)
+    error (_("No symbol file found"));
+
+  if (from_tty
+      && !query (_("Remove symbol table from file \"%s\"? "),
+                objfile_name (objf)))
+    error (_("Not confirmed."));
+
+  free_objfile (objf);
+  clear_symtab_users (0);
+
+  do_cleanups (my_cleanups);
+}
+
 typedef struct objfile *objfilep;
 
 DEF_VEC_P (objfilep);
@@ -2372,6 +2501,7 @@ reread_symbols (void)
          struct cleanup *old_cleanups;
          struct section_offsets *offsets;
          int num_offsets;
+         char *original_name;
 
          printf_unfiltered (_("`%s' has changed; re-reading symbols.\n"),
                             objfile_name (objfile));
@@ -2442,7 +2572,9 @@ reread_symbols (void)
            gdb_bfd_unref (obfd);
          }
 
-         objfile->original_name = bfd_get_filename (objfile->obfd);
+         original_name = xstrdup (objfile->original_name);
+         make_cleanup (xfree, original_name);
+
          /* bfd_openr sets cacheable to true, which is what we want.  */
          if (!bfd_check_format (objfile->obfd, bfd_object))
            error (_("Can't read symbols from %s: %s."), objfile_name (objfile),
@@ -2470,11 +2602,6 @@ reread_symbols (void)
          /* Free the obstacks for non-reusable objfiles.  */
          psymbol_bcache_free (objfile->psymbol_cache);
          objfile->psymbol_cache = psymbol_bcache_init ();
-         if (objfile->demangled_names_hash != NULL)
-           {
-             htab_delete (objfile->demangled_names_hash);
-             objfile->demangled_names_hash = NULL;
-           }
          obstack_free (&objfile->objfile_obstack, 0);
          objfile->sections = NULL;
          objfile->symtabs = NULL;
@@ -2482,24 +2609,26 @@ reread_symbols (void)
          objfile->psymtabs_addrmap = NULL;
          objfile->free_psymtabs = NULL;
          objfile->template_symbols = NULL;
-         objfile->msymbols = NULL;
-         objfile->minimal_symbol_count = 0;
-         memset (&objfile->msymbol_hash, 0,
-                 sizeof (objfile->msymbol_hash));
-         memset (&objfile->msymbol_demangled_hash, 0,
-                 sizeof (objfile->msymbol_demangled_hash));
-
-         set_objfile_per_bfd (objfile);
 
          /* obstack_init also initializes the obstack so it is
             empty.  We could use obstack_specify_allocation but
             gdb_obstack.h specifies the alloc/dealloc functions.  */
          obstack_init (&objfile->objfile_obstack);
 
+         /* set_objfile_per_bfd potentially allocates the per-bfd
+            data on the objfile's obstack (if sharing data across
+            multiple users is not possible), so it's important to
+            do it *after* the obstack has been initialized.  */
+         set_objfile_per_bfd (objfile);
+
+         objfile->original_name = obstack_copy0 (&objfile->objfile_obstack,
+                                                 original_name,
+                                                 strlen (original_name));
+
          /* Reset the sym_fns pointer.  The ELF reader can change it
             based on whether .gdb_index is present, and we need it to
             start over.  PR symtab/15885  */
-         objfile->sf = find_sym_fns (objfile->obfd);
+         objfile_set_sym_fns (objfile, find_sym_fns (objfile->obfd));
 
          build_objfile_section_table (objfile);
          terminate_minimal_symbol_table (objfile);
@@ -2773,8 +2902,8 @@ allocate_symtab (const char *filename, struct objfile *objfile)
   symtab = (struct symtab *)
     obstack_alloc (&objfile->objfile_obstack, sizeof (struct symtab));
   memset (symtab, 0, sizeof (*symtab));
-  symtab->filename = (char *) bcache (filename, strlen (filename) + 1,
-                                     objfile->per_bfd->filename_cache);
+  symtab->filename = bcache (filename, strlen (filename) + 1,
+                            objfile->per_bfd->filename_cache);
   symtab->fullname = NULL;
   symtab->language = deduce_language_from_filename (filename);
   symtab->debugformat = "unknown";
@@ -2785,7 +2914,9 @@ allocate_symtab (const char *filename, struct objfile *objfile)
   symtab->next = objfile->symtabs;
   objfile->symtabs = symtab;
 
-  if (symtab_create_debug)
+  /* This can be very verbose with lots of headers.
+     Only print at higher debug levels.  */
+  if (symtab_create_debug >= 2)
     {
       /* Be a bit clever with debugging messages, and don't print objfile
         every time, only when it changes.  */
@@ -3396,7 +3527,7 @@ read_target_long_array (CORE_ADDR memaddr, unsigned int *myaddr,
 static int
 simple_read_overlay_table (void)
 {
-  struct minimal_symbol *novlys_msym;
+  struct bound_minimal_symbol novlys_msym;
   struct bound_minimal_symbol ovly_table_msym;
   struct gdbarch *gdbarch;
   int word_size;
@@ -3404,7 +3535,7 @@ simple_read_overlay_table (void)
 
   simple_free_overlay_table ();
   novlys_msym = lookup_minimal_symbol ("_novlys", NULL, NULL);
-  if (! novlys_msym)
+  if (! novlys_msym.minsym)
     {
       error (_("Error reading inferior's overlay table: "
              "couldn't find `_novlys' variable\n"
@@ -3425,11 +3556,11 @@ simple_read_overlay_table (void)
   word_size = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT;
   byte_order = gdbarch_byte_order (gdbarch);
 
-  cache_novlys = read_memory_integer (SYMBOL_VALUE_ADDRESS (novlys_msym),
+  cache_novlys = read_memory_integer (BMSYMBOL_VALUE_ADDRESS (novlys_msym),
                                      4, byte_order);
   cache_ovly_table
     = (void *) xmalloc (cache_novlys * sizeof (*cache_ovly_table));
-  cache_ovly_table_base = SYMBOL_VALUE_ADDRESS (ovly_table_msym.minsym);
+  cache_ovly_table_base = BMSYMBOL_VALUE_ADDRESS (ovly_table_msym);
   read_target_long_array (cache_ovly_table_base,
                           (unsigned int *) cache_ovly_table,
                           cache_novlys * 4, word_size, byte_order);
@@ -3497,15 +3628,15 @@ simple_overlay_update (struct obj_section *osect)
       {
        /* Does its cached location match what's currently in the
           symtab?  */
-       struct minimal_symbol *minsym
+       struct bound_minimal_symbol minsym
          = lookup_minimal_symbol ("_ovly_table", NULL, NULL);
 
-       if (minsym == NULL)
+       if (minsym.minsym == NULL)
          error (_("Error reading inferior's overlay table: couldn't "
                   "find `_ovly_table' array\n"
                   "in inferior.  Use `overlay manual' mode."));
        
-       if (cache_ovly_table_base == SYMBOL_VALUE_ADDRESS (minsym))
+       if (cache_ovly_table_base == BMSYMBOL_VALUE_ADDRESS (minsym))
          /* Then go ahead and try to look up this single section in
             the cache.  */
          if (simple_overlay_update_1 (osect))
@@ -3717,11 +3848,62 @@ symfile_find_segment_sections (struct objfile *objfile)
   free_symfile_segment_data (data);
 }
 
+/* Listen for free_objfile events.  */
+
+static void
+symfile_free_objfile (struct objfile *objfile)
+{
+  /* Remove the target sections of user-added objfiles.  */
+  if (objfile != 0 && objfile->flags & OBJF_USERLOADED)
+    remove_target_sections ((void *) objfile);
+}
+
+/* Wrapper around the quick_symbol_functions expand_symtabs_matching "method".
+   Expand all symtabs that match the specified criteria.
+   See quick_symbol_functions.expand_symtabs_matching for details.  */
+
+void
+expand_symtabs_matching (expand_symtabs_file_matcher_ftype *file_matcher,
+                        expand_symtabs_symbol_matcher_ftype *symbol_matcher,
+                        enum search_domain kind,
+                        void *data)
+{
+  struct objfile *objfile;
+
+  ALL_OBJFILES (objfile)
+  {
+    if (objfile->sf)
+      objfile->sf->qf->expand_symtabs_matching (objfile, file_matcher,
+                                               symbol_matcher, kind,
+                                               data);
+  }
+}
+
+/* Wrapper around the quick_symbol_functions map_symbol_filenames "method".
+   Map function FUN over every file.
+   See quick_symbol_functions.map_symbol_filenames for details.  */
+
+void
+map_symbol_filenames (symbol_filename_ftype *fun, void *data,
+                     int need_fullname)
+{
+  struct objfile *objfile;
+
+  ALL_OBJFILES (objfile)
+  {
+    if (objfile->sf)
+      objfile->sf->qf->map_symbol_filenames (objfile, fun, data,
+                                            need_fullname);
+  }
+}
+
 void
 _initialize_symfile (void)
 {
   struct cmd_list_element *c;
 
+  observer_attach_free_objfile (symfile_free_objfile);
+
   c = add_cmd ("symbol-file", class_files, symbol_file_command, _("\
 Load symbol table from executable file FILE.\n\
 The `file' command can also load symbol tables, as well as setting the file\n\
@@ -3738,6 +3920,15 @@ with the text.  SECT is a section name to be loaded at SECT_ADDR."),
               &cmdlist);
   set_cmd_completer (c, filename_completer);
 
+  c = add_cmd ("remove-symbol-file", class_files,
+              remove_symbol_file_command, _("\
+Remove a symbol file added via the add-symbol-file command.\n\
+Usage: remove-symbol-file FILENAME\n\
+       remove-symbol-file -a ADDRESS\n\
+The file to remove can be identified by its filename or by an address\n\
+that lies within the boundaries of this symbol file in memory."),
+              &cmdlist);
+
   c = add_cmd ("load", class_files, load_command, _("\
 Dynamically load FILE into the running program, and record its symbols\n\
 for access from GDB.\n\