readelf: Consolidate --syms --use-dynamic with --dyn-syms
[deliverable/binutils-gdb.git] / gdb / symtab.c
index 8f46321f43d79019446180e62296d3f6a7c0a4d0..13a5a108e6fe9dd1f530312e4bf1feb1b71965e6 100644 (file)
@@ -1,6 +1,6 @@
 /* Symbol table lookup for the GNU debugger, GDB.
 
-   Copyright (C) 1986-2019 Free Software Foundation, Inc.
+   Copyright (C) 1986-2020 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -183,6 +183,16 @@ struct symbol_cache_slot
   } value;
 };
 
+/* Clear out SLOT.  */
+
+static void
+symbol_cache_clear_slot (struct symbol_cache_slot *slot)
+{
+  if (slot->state == SYMBOL_SLOT_NOT_FOUND)
+    xfree (slot->value.not_found.name);
+  slot->state = SYMBOL_SLOT_UNUSED;
+}
+
 /* Symbols don't specify global vs static block.
    So keep them in separate caches.  */
 
@@ -201,6 +211,19 @@ struct block_symbol_cache
   struct symbol_cache_slot symbols[1];
 };
 
+/* Clear all slots of BSC and free BSC.  */
+
+static void
+destroy_block_symbol_cache (struct block_symbol_cache *bsc)
+{
+  if (bsc != nullptr)
+    {
+      for (unsigned int i = 0; i < bsc->size; i++)
+       symbol_cache_clear_slot (&bsc->symbols[i]);
+      xfree (bsc);
+    }
+}
+
 /* The symbol cache.
 
    Searching for symbols in the static and global blocks over multiple objfiles
@@ -217,8 +240,8 @@ struct symbol_cache
 
   ~symbol_cache ()
   {
-    xfree (global_symbols);
-    xfree (static_symbols);
+    destroy_block_symbol_cache (global_symbols);
+    destroy_block_symbol_cache (static_symbols);
   }
 
   struct block_symbol_cache *global_symbols = nullptr;
@@ -652,7 +675,7 @@ symbol_set_demangled_name (struct general_symbol_info *gsymbol,
                            const char *name,
                            struct obstack *obstack)
 {
-  if (gsymbol->language == language_ada)
+  if (gsymbol->language () == language_ada)
     {
       if (name == NULL)
        {
@@ -674,7 +697,7 @@ symbol_set_demangled_name (struct general_symbol_info *gsymbol,
 const char *
 symbol_get_demangled_name (const struct general_symbol_info *gsymbol)
 {
-  if (gsymbol->language == language_ada)
+  if (gsymbol->language () == language_ada)
     {
       if (!gsymbol->ada_mangled)
        return NULL;
@@ -689,28 +712,26 @@ symbol_get_demangled_name (const struct general_symbol_info *gsymbol)
    depending upon the language for the symbol.  */
 
 void
-symbol_set_language (struct general_symbol_info *gsymbol,
-                     enum language language,
-                    struct obstack *obstack)
+general_symbol_info::set_language (enum language language,
+                                  struct obstack *obstack)
 {
-  gsymbol->language = language;
-  if (gsymbol->language == language_cplus
-      || gsymbol->language == language_d
-      || gsymbol->language == language_go
-      || gsymbol->language == language_objc
-      || gsymbol->language == language_fortran)
+  m_language = language;
+  if (language == language_cplus
+      || language == language_d
+      || language == language_go
+      || language == language_objc
+      || language == language_fortran)
     {
-      symbol_set_demangled_name (gsymbol, NULL, obstack);
+      symbol_set_demangled_name (this, NULL, obstack);
     }
-  else if (gsymbol->language == language_ada)
+  else if (language == language_ada)
     {
-      gdb_assert (gsymbol->ada_mangled == 0);
-      gsymbol->language_specific.obstack = obstack;
+      gdb_assert (ada_mangled == 0);
+      language_specific.obstack = obstack;
     }
   else
     {
-      memset (&gsymbol->language_specific, 0,
-             sizeof (gsymbol->language_specific));
+      memset (&language_specific, 0, sizeof (language_specific));
     }
 }
 
@@ -796,12 +817,12 @@ symbol_find_demangled_name (struct general_symbol_info *gsymbol,
   char *demangled = NULL;
   int i;
 
-  if (gsymbol->language == language_unknown)
-    gsymbol->language = language_auto;
+  if (gsymbol->language () == language_unknown)
+    gsymbol->m_language = language_auto;
 
-  if (gsymbol->language != language_auto)
+  if (gsymbol->language () != language_auto)
     {
-      const struct language_defn *lang = language_def (gsymbol->language);
+      const struct language_defn *lang = language_def (gsymbol->language ());
 
       language_sniff_from_mangled_name (lang, mangled, &demangled);
       return demangled;
@@ -814,7 +835,7 @@ symbol_find_demangled_name (struct general_symbol_info *gsymbol,
 
       if (language_sniff_from_mangled_name (lang, mangled, &demangled))
        {
-         gsymbol->language = l;
+         gsymbol->m_language = l;
          return demangled;
        }
     }
@@ -834,28 +855,24 @@ symbol_find_demangled_name (struct general_symbol_info *gsymbol,
    so the pointer can be discarded after calling this function.  */
 
 void
-symbol_set_names (struct general_symbol_info *gsymbol,
-                 gdb::string_view linkage_name, bool copy_name,
-                 struct objfile_per_bfd_storage *per_bfd)
+general_symbol_info::compute_and_set_names (gdb::string_view linkage_name,
+                                           bool copy_name,
+                                           objfile_per_bfd_storage *per_bfd,
+                                           gdb::optional<hashval_t> hash)
 {
   struct demangled_name_entry **slot;
 
-  if (gsymbol->language == language_ada)
+  if (language () == language_ada)
     {
       /* In Ada, we do the symbol lookups using the mangled name, so
          we can save some space by not storing the demangled name.  */
       if (!copy_name)
-       gsymbol->name = linkage_name.data ();
+       m_name = linkage_name.data ();
       else
-       {
-         char *name = (char *) obstack_alloc (&per_bfd->storage_obstack,
-                                              linkage_name.length () + 1);
-
-         memcpy (name, linkage_name.data (), linkage_name.length ());
-         name[linkage_name.length ()] = '\0';
-         gsymbol->name = name;
-       }
-      symbol_set_demangled_name (gsymbol, NULL, &per_bfd->storage_obstack);
+       m_name = obstack_strndup (&per_bfd->storage_obstack,
+                                 linkage_name.data (),
+                                 linkage_name.length ());
+      symbol_set_demangled_name (this, NULL, &per_bfd->storage_obstack);
 
       return;
     }
@@ -864,15 +881,27 @@ symbol_set_names (struct general_symbol_info *gsymbol,
     create_demangled_names_hash (per_bfd);
 
   struct demangled_name_entry entry (linkage_name);
+  if (!hash.has_value ())
+    hash = hash_demangled_name_entry (&entry);
   slot = ((struct demangled_name_entry **)
-         htab_find_slot (per_bfd->demangled_names_hash.get (),
-                         &entry, INSERT));
+          htab_find_slot_with_hash (per_bfd->demangled_names_hash.get (),
+                                   &entry, *hash, INSERT));
+
+  /* The const_cast is safe because the only reason it is already
+     initialized is if we purposefully set it from a background
+     thread to avoid doing the work here.  However, it is still
+     allocated from the heap and needs to be freed by us, just
+     like if we called symbol_find_demangled_name here.  If this is
+     nullptr, we call symbol_find_demangled_name below, but we put
+     this smart pointer here to be sure that we don't leak this name.  */
+  gdb::unique_xmalloc_ptr<char> demangled_name
+    (const_cast<char *> (language_specific.demangled_name));
 
   /* If this name is not in the hash table, add it.  */
   if (*slot == NULL
       /* A C version of the symbol may have already snuck into the table.
         This happens to, e.g., main.init (__go_init_main).  Cope.  */
-      || (gsymbol->language == language_go && (*slot)->demangled == nullptr))
+      || (language () == language_go && (*slot)->demangled == nullptr))
     {
       /* A 0-terminated copy of the linkage name.  Callers must set COPY_NAME
          to true if the string might not be nullterminated.  We have to make
@@ -890,15 +919,9 @@ symbol_set_names (struct general_symbol_info *gsymbol,
       else
        linkage_name_copy = linkage_name;
 
-      /* The const_cast is safe because the only reason it is already
-         initialized is if we purposefully set it from a background
-         thread to avoid doing the work here.  However, it is still
-         allocated from the heap and needs to be freed by us, just
-         like if we called symbol_find_demangled_name here.  */
-      gdb::unique_xmalloc_ptr<char> demangled_name
-       (gsymbol->language_specific.demangled_name
-        ? const_cast<char *> (gsymbol->language_specific.demangled_name)
-        : symbol_find_demangled_name (gsymbol, linkage_name_copy.data ()));
+      if (demangled_name.get () == nullptr)
+        demangled_name.reset
+          (symbol_find_demangled_name (this, linkage_name_copy.data ()));
 
       /* Suppose we have demangled_name==NULL, copy_name==0, and
         linkage_name_copy==linkage_name.  In this case, we already have the
@@ -933,18 +956,14 @@ symbol_set_names (struct general_symbol_info *gsymbol,
            (gdb::string_view (mangled_ptr, linkage_name.length ()));
        }
       (*slot)->demangled = std::move (demangled_name);
-      (*slot)->language = gsymbol->language;
+      (*slot)->language = language ();
     }
-  else if (gsymbol->language == language_unknown
-          || gsymbol->language == language_auto)
-    gsymbol->language = (*slot)->language;
+  else if (language () == language_unknown || language () == language_auto)
+    m_language = (*slot)->language;
 
-  gsymbol->name = (*slot)->mangled.data ();
-  if ((*slot)->demangled != nullptr)
-    symbol_set_demangled_name (gsymbol, (*slot)->demangled.get (),
-                              &per_bfd->storage_obstack);
-  else
-    symbol_set_demangled_name (gsymbol, NULL, &per_bfd->storage_obstack);
+  m_name = (*slot)->mangled.data ();
+  symbol_set_demangled_name (this, (*slot)->demangled.get (),
+                            &per_bfd->storage_obstack);
 }
 
 /* See symtab.h.  */
@@ -952,7 +971,7 @@ symbol_set_names (struct general_symbol_info *gsymbol,
 const char *
 general_symbol_info::natural_name () const
 {
-  switch (language)
+  switch (language ())
     {
     case language_cplus:
     case language_d:
@@ -967,7 +986,7 @@ general_symbol_info::natural_name () const
     default:
       break;
     }
-  return name;
+  return linkage_name ();
 }
 
 /* See symtab.h.  */
@@ -977,7 +996,7 @@ general_symbol_info::demangled_name () const
 {
   const char *dem_name = NULL;
 
-  switch (language)
+  switch (language ())
     {
     case language_cplus:
     case language_d:
@@ -1000,8 +1019,8 @@ general_symbol_info::demangled_name () const
 const char *
 general_symbol_info::search_name () const
 {
-  if (language == language_ada)
-    return name;
+  if (language () == language_ada)
+    return linkage_name ();
   else
     return natural_name ();
 }
@@ -1013,7 +1032,7 @@ symbol_matches_search_name (const struct general_symbol_info *gsymbol,
                            const lookup_name_info &name)
 {
   symbol_name_matcher_ftype *name_match
-    = get_symbol_name_matcher (language_def (gsymbol->language), name);
+    = get_symbol_name_matcher (language_def (gsymbol->language ()), name);
   return name_match (gsymbol->search_name (), name, NULL);
 }
 
@@ -1193,8 +1212,7 @@ eq_symbol_entry (const struct symbol_cache_slot *slot,
          if (!SYMBOL_MATCHES_SEARCH_NAME (sym, lookup_name))
            return 0;
 
-         if (!symbol_matches_domain (SYMBOL_LANGUAGE (sym),
-                                     slot_domain, domain))
+         if (!symbol_matches_domain (sym->language (), slot_domain, domain))
            return 0;
        }
     }
@@ -1231,8 +1249,8 @@ resize_symbol_cache (struct symbol_cache *cache, unsigned int new_size)
          && new_size == 0))
     return;
 
-  xfree (cache->global_symbols);
-  xfree (cache->static_symbols);
+  destroy_block_symbol_cache (cache->global_symbols);
+  destroy_block_symbol_cache (cache->static_symbols);
 
   if (new_size == 0)
     {
@@ -1370,16 +1388,6 @@ symbol_cache_lookup (struct symbol_cache *cache,
   return {};
 }
 
-/* Clear out SLOT.  */
-
-static void
-symbol_cache_clear_slot (struct symbol_cache_slot *slot)
-{
-  if (slot->state == SYMBOL_SLOT_NOT_FOUND)
-    xfree (slot->value.not_found.name);
-  slot->state = SYMBOL_SLOT_UNUSED;
-}
-
 /* Mark SYMBOL as found in SLOT.
    OBJFILE_CONTEXT is the current objfile when the lookup was done, or NULL
    if it's not needed to distinguish lookups (STATIC_BLOCK).  It is *not*
@@ -1657,7 +1665,8 @@ fixup_section (struct general_symbol_info *ginfo,
      e.g. on PowerPC64, where the minimal symbol for a function will
      point to the function descriptor, while the debug symbol will
      point to the actual function code.  */
-  msym = lookup_minimal_symbol_by_pc_name (addr, ginfo->name, objfile);
+  msym = lookup_minimal_symbol_by_pc_name (addr, ginfo->linkage_name (),
+                                          objfile);
   if (msym)
     ginfo->section = MSYMBOL_SECTION (msym);
   else
@@ -1674,11 +1683,10 @@ fixup_section (struct general_symbol_info *ginfo,
 
         So, instead, search the section table when lookup by name has
         failed.  The ``addr'' and ``endaddr'' fields may have already
-        been relocated.  If so, the relocation offset (i.e. the
-        ANOFFSET value) needs to be subtracted from these values when
-        performing the comparison.  We unconditionally subtract it,
-        because, when no relocation has been performed, the ANOFFSET
-        value will simply be zero.
+        been relocated.  If so, the relocation offset needs to be
+        subtracted from these values when performing the comparison.
+        We unconditionally subtract it, because, when no relocation
+        has been performed, the value will simply be zero.
 
         The address of the symbol whose section we're fixing up HAS
         NOT BEEN adjusted (relocated) yet.  It can't have been since
@@ -1704,7 +1712,7 @@ fixup_section (struct general_symbol_info *ginfo,
       ALL_OBJFILE_OSECTIONS (objfile, s)
        {
          int idx = s - objfile->sections;
-         CORE_ADDR offset = ANOFFSET (objfile->section_offsets, idx);
+         CORE_ADDR offset = objfile->section_offsets[idx];
 
          if (fallback == -1)
            fallback = idx;
@@ -1782,7 +1790,7 @@ demangle_for_lookup_info::demangle_for_lookup_info
   if (lookup_name.ignore_parameters () && lang == language_cplus)
     {
       gdb::unique_xmalloc_ptr<char> without_params
-       = cp_remove_params_if_any (lookup_name.name ().c_str (),
+       = cp_remove_params_if_any (lookup_name.c_str (),
                                   lookup_name.completion_mode ());
 
       if (without_params != NULL)
@@ -1795,9 +1803,9 @@ demangle_for_lookup_info::demangle_for_lookup_info
     }
 
   if (lookup_name.match_type () == symbol_name_match_type::SEARCH_NAME)
-    m_demangled_name = lookup_name.name ();
+    m_demangled_name = lookup_name.c_str ();
   else
-    m_demangled_name = demangle_for_lookup (lookup_name.name ().c_str (),
+    m_demangled_name = demangle_for_lookup (lookup_name.c_str (),
                                            lang, storage);
 }
 
@@ -1808,7 +1816,7 @@ lookup_name_info::match_any ()
 {
   /* Lookup any symbol that "" would complete.  I.e., this matches all
      symbol names.  */
-  static const lookup_name_info lookup_name ({}, symbol_name_match_type::FULL,
+  static const lookup_name_info lookup_name ("", symbol_name_match_type::FULL,
                                             true);
 
   return lookup_name;
@@ -2552,6 +2560,33 @@ lookup_symbol_in_objfile (struct objfile *objfile, enum block_enum block_index,
   return result;
 }
 
+/* Find the language for partial symbol with NAME.  */
+
+static enum language
+find_quick_global_symbol_language (const char *name, const domain_enum domain)
+{
+  for (objfile *objfile : current_program_space->objfiles ())
+    {
+      if (objfile->sf && objfile->sf->qf
+         && objfile->sf->qf->lookup_global_symbol_language)
+       continue;
+      return language_unknown;
+    }
+
+  for (objfile *objfile : current_program_space->objfiles ())
+    {
+      bool symbol_found_p;
+      enum language lang
+       = objfile->sf->qf->lookup_global_symbol_language (objfile, name, domain,
+                                                         &symbol_found_p);
+      if (!symbol_found_p)
+       continue;
+      return lang;
+    }
+
+  return language_unknown;
+}
+
 /* Private data to be used with lookup_symbol_global_iterator_cb.  */
 
 struct global_or_static_sym_lookup_data
@@ -2830,8 +2865,7 @@ iterate_over_symbols (const struct block *block,
 
   ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
     {
-      if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
-                                SYMBOL_DOMAIN (sym), domain))
+      if (symbol_matches_domain (sym->language (), SYMBOL_DOMAIN (sym), domain))
        {
          struct block_symbol block_sym = {sym, block};
 
@@ -3151,7 +3185,17 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
          ;
        /* fall through */
        else
-         return find_pc_line (BMSYMBOL_VALUE_ADDRESS (mfunsym), 0);
+         {
+           /* Detect an obvious case of infinite recursion.  If this
+              should occur, we'd like to know about it, so error out,
+              fatally.  */
+           if (BMSYMBOL_VALUE_ADDRESS (mfunsym) == pc)
+             internal_error (__FILE__, __LINE__,
+               _("Infinite recursion detected in find_pc_sect_line;"
+                 "please file a bug report"));
+
+           return find_pc_line (BMSYMBOL_VALUE_ADDRESS (mfunsym), 0);
+         }
       }
 
   symtab_and_line val;
@@ -3207,7 +3251,12 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
       struct linetable_entry *last = item + len;
       item = std::upper_bound (first, last, pc, pc_compare);
       if (item != first)
-       prev = item - 1;                /* Found a matching item.  */
+       {
+         /* Found a matching item.  Skip backwards over any end of
+            sequence markers.  */
+         for (prev = item - 1; prev->line == 0 && prev != first; prev--)
+           /* Nothing.  */;
+       }
 
       /* At this point, prev points at the line whose start addr is <= pc, and
          item points at the next line.  If we ran off the end of the linetable
@@ -3224,6 +3273,23 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
          best = prev;
          best_symtab = iter_s;
 
+         /* If during the binary search we land on a non-statement entry,
+            scan backward through entries at the same address to see if
+            there is an entry marked as is-statement.  In theory this
+            duplication should have been removed from the line table
+            during construction, this is just a double check.  If the line
+            table has had the duplication removed then this should be
+            pretty cheap.  */
+         if (!best->is_stmt)
+           {
+             struct linetable_entry *tmp = best;
+             while (tmp > first && (tmp - 1)->pc == tmp->pc
+                    && (tmp - 1)->line != 0 && !tmp->is_stmt)
+               --tmp;
+             if (tmp->is_stmt)
+               best = tmp;
+           }
+
          /* Discard BEST_END if it's before the PC of the current BEST.  */
          if (best_end <= best->pc)
            best_end = 0;
@@ -3254,6 +3320,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
     }
   else
     {
+      val.is_stmt = best->is_stmt;
       val.symtab = best_symtab;
       val.line = best->line;
       val.pc = best->pc;
@@ -3422,7 +3489,8 @@ find_pcs_for_symtab_line (struct symtab *symtab, int line,
        {
          struct linetable_entry *item = &SYMTAB_LINETABLE (symtab)->item[idx];
 
-         if (*best_item == NULL || item->line < (*best_item)->line)
+         if (*best_item == NULL
+             || (item->line < (*best_item)->line && item->is_stmt))
            *best_item = item;
 
          break;
@@ -3533,6 +3601,10 @@ find_line_common (struct linetable *l, int lineno,
     {
       struct linetable_entry *item = &(l->item[i]);
 
+      /* Ignore non-statements.  */
+      if (!item->is_stmt)
+       continue;
+
       if (item->line == lineno)
        {
          /* Return the first (lowest address) entry which matches.  */
@@ -4367,8 +4439,8 @@ file_matches (const char *file, const std::vector<const char *> &filenames,
   return false;
 }
 
-/* Helper function for sort_search_symbols_remove_dups and qsort.  Can only
-   sort symbols, not minimal symbols.  */
+/* Helper function for std::sort on symbol_search objects.  Can only sort
+   symbols, not minimal symbols.  */
 
 int
 symbol_search::compare_search_syms (const symbol_search &sym_a,
@@ -4429,15 +4501,233 @@ treg_matches_sym_type_name (const compiled_regex &treg,
   return treg.exec (printed_sym_type_name.c_str (), 0, NULL, 0) == 0;
 }
 
+/* See symtab.h.  */
+
+bool
+global_symbol_searcher::is_suitable_msymbol
+       (const enum search_domain kind, const minimal_symbol *msymbol)
+{
+  switch (MSYMBOL_TYPE (msymbol))
+    {
+    case mst_data:
+    case mst_bss:
+    case mst_file_data:
+    case mst_file_bss:
+      return kind == VARIABLES_DOMAIN;
+    case mst_text:
+    case mst_file_text:
+    case mst_solib_trampoline:
+    case mst_text_gnu_ifunc:
+      return kind == FUNCTIONS_DOMAIN;
+    default:
+      return false;
+    }
+}
 
-/* Sort the symbols in RESULT and remove duplicates.  */
+/* See symtab.h.  */
 
-static void
-sort_search_symbols_remove_dups (std::vector<symbol_search> *result)
+bool
+global_symbol_searcher::expand_symtabs
+       (objfile *objfile, const gdb::optional<compiled_regex> &preg) const
+{
+  enum search_domain kind = m_kind;
+  bool found_msymbol = false;
+
+  if (objfile->sf)
+    objfile->sf->qf->expand_symtabs_matching
+      (objfile,
+       [&] (const char *filename, bool basenames)
+       {
+        return file_matches (filename, filenames, basenames);
+       },
+       lookup_name_info::match_any (),
+       [&] (const char *symname)
+       {
+        return (!preg.has_value ()
+                || preg->exec (symname, 0, NULL, 0) == 0);
+       },
+       NULL,
+       kind);
+
+  /* Here, we search through the minimal symbol tables for functions and
+     variables that match, and force their symbols to be read.  This is in
+     particular necessary for demangled variable names, which are no longer
+     put into the partial symbol tables.  The symbol will then be found
+     during the scan of symtabs later.
+
+     For functions, find_pc_symtab should succeed if we have debug info for
+     the function, for variables we have to call
+     lookup_symbol_in_objfile_from_linkage_name to determine if the
+     variable has debug info.  If the lookup fails, set found_msymbol so
+     that we will rescan to print any matching symbols without debug info.
+     We only search the objfile the msymbol came from, we no longer search
+     all objfiles.  In large programs (1000s of shared libs) searching all
+     objfiles is not worth the pain.  */
+  if (filenames.empty ()
+      && (kind == VARIABLES_DOMAIN || kind == FUNCTIONS_DOMAIN))
+    {
+      for (minimal_symbol *msymbol : objfile->msymbols ())
+       {
+         QUIT;
+
+         if (msymbol->created_by_gdb)
+           continue;
+
+         if (is_suitable_msymbol (kind, msymbol))
+           {
+             if (!preg.has_value ()
+                 || preg->exec (msymbol->natural_name (), 0,
+                                NULL, 0) == 0)
+               {
+                 /* An important side-effect of these lookup functions is
+                    to expand the symbol table if msymbol is found, later
+                    in the process we will add matching symbols or
+                    msymbols to the results list, and that requires that
+                    the symbols tables are expanded.  */
+                 if (kind == FUNCTIONS_DOMAIN
+                     ? (find_pc_compunit_symtab
+                        (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
+                        == NULL)
+                     : (lookup_symbol_in_objfile_from_linkage_name
+                        (objfile, msymbol->linkage_name (),
+                         VAR_DOMAIN)
+                        .symbol == NULL))
+                   found_msymbol = true;
+               }
+           }
+       }
+    }
+
+  return found_msymbol;
+}
+
+/* See symtab.h.  */
+
+bool
+global_symbol_searcher::add_matching_symbols
+       (objfile *objfile,
+        const gdb::optional<compiled_regex> &preg,
+        const gdb::optional<compiled_regex> &treg,
+        std::set<symbol_search> *result_set) const
 {
-  std::sort (result->begin (), result->end ());
-  result->erase (std::unique (result->begin (), result->end ()),
-                result->end ());
+  enum search_domain kind = m_kind;
+
+  /* Add matching symbols (if not already present).  */
+  for (compunit_symtab *cust : objfile->compunits ())
+    {
+      const struct blockvector *bv  = COMPUNIT_BLOCKVECTOR (cust);
+
+      for (block_enum block : { GLOBAL_BLOCK, STATIC_BLOCK })
+       {
+         struct block_iterator iter;
+         struct symbol *sym;
+         const struct block *b = BLOCKVECTOR_BLOCK (bv, block);
+
+         ALL_BLOCK_SYMBOLS (b, iter, sym)
+           {
+             struct symtab *real_symtab = symbol_symtab (sym);
+
+             QUIT;
+
+             /* Check first sole REAL_SYMTAB->FILENAME.  It does
+                not need to be a substring of symtab_to_fullname as
+                it may contain "./" etc.  */
+             if ((file_matches (real_symtab->filename, filenames, false)
+                  || ((basenames_may_differ
+                       || file_matches (lbasename (real_symtab->filename),
+                                        filenames, true))
+                      && file_matches (symtab_to_fullname (real_symtab),
+                                       filenames, false)))
+                 && ((!preg.has_value ()
+                      || preg->exec (sym->natural_name (), 0,
+                                     NULL, 0) == 0)
+                     && ((kind == VARIABLES_DOMAIN
+                          && SYMBOL_CLASS (sym) != LOC_TYPEDEF
+                          && SYMBOL_CLASS (sym) != LOC_UNRESOLVED
+                          && SYMBOL_CLASS (sym) != LOC_BLOCK
+                          /* LOC_CONST can be used for more than
+                             just enums, e.g., c++ static const
+                             members.  We only want to skip enums
+                             here.  */
+                          && !(SYMBOL_CLASS (sym) == LOC_CONST
+                               && (TYPE_CODE (SYMBOL_TYPE (sym))
+                                   == TYPE_CODE_ENUM))
+                          && (!treg.has_value ()
+                              || treg_matches_sym_type_name (*treg, sym)))
+                         || (kind == FUNCTIONS_DOMAIN
+                             && SYMBOL_CLASS (sym) == LOC_BLOCK
+                             && (!treg.has_value ()
+                                 || treg_matches_sym_type_name (*treg,
+                                                                sym)))
+                         || (kind == TYPES_DOMAIN
+                             && SYMBOL_CLASS (sym) == LOC_TYPEDEF
+                             && SYMBOL_DOMAIN (sym) != MODULE_DOMAIN)
+                         || (kind == MODULES_DOMAIN
+                             && SYMBOL_DOMAIN (sym) == MODULE_DOMAIN
+                             && SYMBOL_LINE (sym) != 0))))
+               {
+                 if (result_set->size () < m_max_search_results)
+                   {
+                     /* Match, insert if not already in the results.  */
+                     symbol_search ss (block, sym);
+                     if (result_set->find (ss) == result_set->end ())
+                       result_set->insert (ss);
+                   }
+                 else
+                   return false;
+               }
+           }
+       }
+    }
+
+  return true;
+}
+
+/* See symtab.h.  */
+
+bool
+global_symbol_searcher::add_matching_msymbols
+       (objfile *objfile, const gdb::optional<compiled_regex> &preg,
+        std::vector<symbol_search> *results) const
+{
+  enum search_domain kind = m_kind;
+
+  for (minimal_symbol *msymbol : objfile->msymbols ())
+    {
+      QUIT;
+
+      if (msymbol->created_by_gdb)
+       continue;
+
+      if (is_suitable_msymbol (kind, msymbol))
+       {
+         if (!preg.has_value ()
+             || preg->exec (msymbol->natural_name (), 0,
+                            NULL, 0) == 0)
+           {
+             /* For functions we can do a quick check of whether the
+                symbol might be found via find_pc_symtab.  */
+             if (kind != FUNCTIONS_DOMAIN
+                 || (find_pc_compunit_symtab
+                     (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
+                     == NULL))
+               {
+                 if (lookup_symbol_in_objfile_from_linkage_name
+                     (objfile, msymbol->linkage_name (),
+                      VAR_DOMAIN).symbol == NULL)
+                   {
+                     /* Matching msymbol, add it to the results list.  */
+                     if (results->size () < m_max_search_results)
+                       results->emplace_back (GLOBAL_BLOCK, msymbol, objfile);
+                     else
+                       return false;
+                   }
+               }
+           }
+       }
+    }
+
+  return true;
 }
 
 /* See symtab.h.  */
@@ -4445,35 +4735,11 @@ sort_search_symbols_remove_dups (std::vector<symbol_search> *result)
 std::vector<symbol_search>
 global_symbol_searcher::search () const
 {
-  const struct blockvector *bv;
-  const struct block *b;
-  int i = 0;
-  struct block_iterator iter;
-  struct symbol *sym;
-  int found_misc = 0;
-  static const enum minimal_symbol_type types[]
-    = {mst_data, mst_text, mst_unknown};
-  static const enum minimal_symbol_type types2[]
-    = {mst_bss, mst_file_text, mst_unknown};
-  static const enum minimal_symbol_type types3[]
-    = {mst_file_data, mst_solib_trampoline, mst_unknown};
-  static const enum minimal_symbol_type types4[]
-    = {mst_file_bss, mst_text_gnu_ifunc, mst_unknown};
-  enum minimal_symbol_type ourtype;
-  enum minimal_symbol_type ourtype2;
-  enum minimal_symbol_type ourtype3;
-  enum minimal_symbol_type ourtype4;
-  std::vector<symbol_search> result;
   gdb::optional<compiled_regex> preg;
   gdb::optional<compiled_regex> treg;
 
   gdb_assert (m_kind != ALL_DOMAIN);
 
-  ourtype = types[m_kind];
-  ourtype2 = types2[m_kind];
-  ourtype3 = types3[m_kind];
-  ourtype4 = types4[m_kind];
-
   if (m_symbol_name_regexp != NULL)
     {
       const char *symbol_name_regexp = m_symbol_name_regexp;
@@ -4526,187 +4792,38 @@ global_symbol_searcher::search () const
                    _("Invalid regexp"));
     }
 
-  /* Search through the partial symtabs *first* for all symbols matching
-     the m_symbol_name_regexp (in preg).  That way we don't have to
-     reproduce all of the machinery below.  */
-  expand_symtabs_matching ([&] (const char *filename, bool basenames)
-                          {
-                            return file_matches (filename, filenames,
-                                                 basenames);
-                          },
-                          lookup_name_info::match_any (),
-                          [&] (const char *symname)
-                          {
-                            return (!preg.has_value ()
-                                    || preg->exec (symname,
-                                                   0, NULL, 0) == 0);
-                          },
-                          NULL,
-                          m_kind);
-
-  /* Here, we search through the minimal symbol tables for functions
-     and variables that match, and force their symbols to be read.
-     This is in particular necessary for demangled variable names,
-     which are no longer put into the partial symbol tables.
-     The symbol will then be found during the scan of symtabs below.
-
-     For functions, find_pc_symtab should succeed if we have debug info
-     for the function, for variables we have to call
-     lookup_symbol_in_objfile_from_linkage_name to determine if the variable
-     has debug info.
-     If the lookup fails, set found_misc so that we will rescan to print
-     any matching symbols without debug info.
-     We only search the objfile the msymbol came from, we no longer search
-     all objfiles.  In large programs (1000s of shared libs) searching all
-     objfiles is not worth the pain.  */
-
-  if (filenames.empty () && (m_kind == VARIABLES_DOMAIN
-                            || m_kind == FUNCTIONS_DOMAIN))
-    {
-      for (objfile *objfile : current_program_space->objfiles ())
-       {
-         for (minimal_symbol *msymbol : objfile->msymbols ())
-           {
-             QUIT;
-
-             if (msymbol->created_by_gdb)
-               continue;
-
-             if (MSYMBOL_TYPE (msymbol) == ourtype
-                 || MSYMBOL_TYPE (msymbol) == ourtype2
-                 || MSYMBOL_TYPE (msymbol) == ourtype3
-                 || MSYMBOL_TYPE (msymbol) == ourtype4)
-               {
-                 if (!preg.has_value ()
-                     || preg->exec (msymbol->natural_name (), 0,
-                                    NULL, 0) == 0)
-                   {
-                     /* Note: An important side-effect of these
-                        lookup functions is to expand the symbol
-                        table if msymbol is found, for the benefit of
-                        the next loop on compunits.  */
-                     if (m_kind == FUNCTIONS_DOMAIN
-                         ? (find_pc_compunit_symtab
-                            (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
-                            == NULL)
-                         : (lookup_symbol_in_objfile_from_linkage_name
-                            (objfile, msymbol->linkage_name (), VAR_DOMAIN)
-                            .symbol == NULL))
-                       found_misc = 1;
-                   }
-               }
-           }
-       }
-    }
-
+  bool found_msymbol = false;
+  std::set<symbol_search> result_set;
   for (objfile *objfile : current_program_space->objfiles ())
     {
-      for (compunit_symtab *cust : objfile->compunits ())
-       {
-         bv = COMPUNIT_BLOCKVECTOR (cust);
-         for (i = GLOBAL_BLOCK; i <= STATIC_BLOCK; i++)
-           {
-             b = BLOCKVECTOR_BLOCK (bv, i);
-             ALL_BLOCK_SYMBOLS (b, iter, sym)
-               {
-                 struct symtab *real_symtab = symbol_symtab (sym);
-
-                 QUIT;
-
-                 /* Check first sole REAL_SYMTAB->FILENAME.  It does
-                    not need to be a substring of symtab_to_fullname as
-                    it may contain "./" etc.  */
-                 if ((file_matches (real_symtab->filename, filenames, false)
-                      || ((basenames_may_differ
-                           || file_matches (lbasename (real_symtab->filename),
-                                            filenames, true))
-                          && file_matches (symtab_to_fullname (real_symtab),
-                                           filenames, false)))
-                     && ((!preg.has_value ()
-                          || preg->exec (sym->natural_name (), 0,
-                                         NULL, 0) == 0)
-                         && ((m_kind == VARIABLES_DOMAIN
-                              && SYMBOL_CLASS (sym) != LOC_TYPEDEF
-                              && SYMBOL_CLASS (sym) != LOC_UNRESOLVED
-                              && SYMBOL_CLASS (sym) != LOC_BLOCK
-                              /* LOC_CONST can be used for more than
-                                 just enums, e.g., c++ static const
-                                 members.  We only want to skip enums
-                                 here.  */
-                              && !(SYMBOL_CLASS (sym) == LOC_CONST
-                                   && (TYPE_CODE (SYMBOL_TYPE (sym))
-                                       == TYPE_CODE_ENUM))
-                              && (!treg.has_value ()
-                                  || treg_matches_sym_type_name (*treg, sym)))
-                             || (m_kind == FUNCTIONS_DOMAIN
-                                 && SYMBOL_CLASS (sym) == LOC_BLOCK
-                                 && (!treg.has_value ()
-                                     || treg_matches_sym_type_name (*treg,
-                                                                    sym)))
-                             || (m_kind == TYPES_DOMAIN
-                                 && SYMBOL_CLASS (sym) == LOC_TYPEDEF
-                                 && SYMBOL_DOMAIN (sym) != MODULE_DOMAIN)
-                             || (m_kind == MODULES_DOMAIN
-                                 && SYMBOL_DOMAIN (sym) == MODULE_DOMAIN
-                                 && SYMBOL_LINE (sym) != 0))))
-                   {
-                     /* match */
-                     result.emplace_back (i, sym);
-                   }
-               }
-           }
-       }
+      /* Expand symtabs within objfile that possibly contain matching
+        symbols.  */
+      found_msymbol |= expand_symtabs (objfile, preg);
+
+      /* Find matching symbols within OBJFILE and add them in to the
+        RESULT_SET set.  Use a set here so that we can easily detect
+        duplicates as we go, and can therefore track how many unique
+        matches we have found so far.  */
+      if (!add_matching_symbols (objfile, preg, treg, &result_set))
+       break;
     }
 
-  if (!result.empty ())
-    sort_search_symbols_remove_dups (&result);
+  /* Convert the result set into a sorted result list, as std::set is
+     defined to be sorted then no explicit call to std::sort is needed.  */
+  std::vector<symbol_search> result (result_set.begin (), result_set.end ());
 
   /* If there are no debug symbols, then add matching minsyms.  But if the
-     user wants to see symbols matching a type m_symbol_type_regexp, then
-     never give a minimal symbol, as we assume that a minimal symbol does
-     not have a type.  */
-
-  if ((found_misc || (filenames.empty () && m_kind != FUNCTIONS_DOMAIN))
+     user wants to see symbols matching a type regexp, then never give a
+     minimal symbol, as we assume that a minimal symbol does not have a
+     type.  */
+  if ((found_msymbol || (filenames.empty () && m_kind == VARIABLES_DOMAIN))
       && !m_exclude_minsyms
       && !treg.has_value ())
     {
+      gdb_assert (m_kind == VARIABLES_DOMAIN || m_kind == FUNCTIONS_DOMAIN);
       for (objfile *objfile : current_program_space->objfiles ())
-       {
-         for (minimal_symbol *msymbol : objfile->msymbols ())
-           {
-             QUIT;
-
-             if (msymbol->created_by_gdb)
-               continue;
-
-             if (MSYMBOL_TYPE (msymbol) == ourtype
-                 || MSYMBOL_TYPE (msymbol) == ourtype2
-                 || MSYMBOL_TYPE (msymbol) == ourtype3
-                 || MSYMBOL_TYPE (msymbol) == ourtype4)
-               {
-                 if (!preg.has_value ()
-                     || preg->exec (msymbol->natural_name (), 0,
-                                    NULL, 0) == 0)
-                   {
-                     /* For functions we can do a quick check of whether the
-                        symbol might be found via find_pc_symtab.  */
-                     if (m_kind != FUNCTIONS_DOMAIN
-                         || (find_pc_compunit_symtab
-                             (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
-                             == NULL))
-                       {
-                         if (lookup_symbol_in_objfile_from_linkage_name
-                             (objfile, msymbol->linkage_name (), VAR_DOMAIN)
-                             .symbol == NULL)
-                           {
-                             /* match */
-                             result.emplace_back (i, msymbol, objfile);
-                           }
-                       }
-                   }
-               }
-           }
-       }
+       if (!add_matching_msymbols (objfile, preg, &result))
+         break;
     }
 
   return result;
@@ -4911,13 +5028,13 @@ symtab_symbol_info (bool quiet, bool exclude_minsyms,
    and 'info functions' commands.  These correspond to the -q, -t, and -n
    options.  */
 
-struct info_print_options
+struct info_vars_funcs_options
 {
   bool quiet = false;
   bool exclude_minsyms = false;
   char *type_regexp = nullptr;
 
-  ~info_print_options ()
+  ~info_vars_funcs_options ()
   {
     xfree (type_regexp);
   }
@@ -4926,24 +5043,25 @@ struct info_print_options
 /* The options used by the 'info variables' and 'info functions'
    commands.  */
 
-static const gdb::option::option_def info_print_options_defs[] = {
-  gdb::option::boolean_option_def<info_print_options> {
+static const gdb::option::option_def info_vars_funcs_options_defs[] = {
+  gdb::option::boolean_option_def<info_vars_funcs_options> {
     "q",
-    [] (info_print_options *opt) { return &opt->quiet; },
+    [] (info_vars_funcs_options *opt) { return &opt->quiet; },
     nullptr, /* show_cmd_cb */
     nullptr /* set_doc */
   },
 
-  gdb::option::boolean_option_def<info_print_options> {
+  gdb::option::boolean_option_def<info_vars_funcs_options> {
     "n",
-    [] (info_print_options *opt) { return &opt->exclude_minsyms; },
+    [] (info_vars_funcs_options *opt) { return &opt->exclude_minsyms; },
     nullptr, /* show_cmd_cb */
     nullptr /* set_doc */
   },
 
-  gdb::option::string_option_def<info_print_options> {
+  gdb::option::string_option_def<info_vars_funcs_options> {
     "t",
-    [] (info_print_options *opt) { return &opt->type_regexp; },
+    [] (info_vars_funcs_options *opt) { return &opt->type_regexp;
+  },
     nullptr, /* show_cmd_cb */
     nullptr /* set_doc */
   }
@@ -4953,20 +5071,20 @@ static const gdb::option::option_def info_print_options_defs[] = {
    functions'.  */
 
 static gdb::option::option_def_group
-make_info_print_options_def_group (info_print_options *opts)
+make_info_vars_funcs_options_def_group (info_vars_funcs_options *opts)
 {
-  return {{info_print_options_defs}, opts};
+  return {{info_vars_funcs_options_defs}, opts};
 }
 
 /* Command completer for 'info variables' and 'info functions'.  */
 
 static void
-info_print_command_completer (struct cmd_list_element *ignore,
-                             completion_tracker &tracker,
-                             const char *text, const char * /* word */)
+info_vars_funcs_command_completer (struct cmd_list_element *ignore,
+                                  completion_tracker &tracker,
+                                  const char *text, const char * /* word */)
 {
   const auto group
-    = make_info_print_options_def_group (nullptr);
+    = make_info_vars_funcs_options_def_group (nullptr);
   if (gdb::option::complete_options
       (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
     return;
@@ -4980,8 +5098,8 @@ info_print_command_completer (struct cmd_list_element *ignore,
 static void
 info_variables_command (const char *args, int from_tty)
 {
-  info_print_options opts;
-  auto grp = make_info_print_options_def_group (&opts);
+  info_vars_funcs_options opts;
+  auto grp = make_info_vars_funcs_options_def_group (&opts);
   gdb::option::process_options
     (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
   if (args != nullptr && *args == '\0')
@@ -4996,8 +5114,9 @@ info_variables_command (const char *args, int from_tty)
 static void
 info_functions_command (const char *args, int from_tty)
 {
-  info_print_options opts;
-  auto grp = make_info_print_options_def_group (&opts);
+  info_vars_funcs_options opts;
+
+  auto grp = make_info_vars_funcs_options_def_group (&opts);
   gdb::option::process_options
     (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
   if (args != nullptr && *args == '\0')
@@ -5201,9 +5320,30 @@ completion_list_add_symbol (completion_tracker &tracker,
                            const lookup_name_info &lookup_name,
                            const char *text, const char *word)
 {
-  completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
+  completion_list_add_name (tracker, sym->language (),
                            sym->natural_name (),
                            lookup_name, text, word);
+
+  /* C++ function symbols include the parameters within both the msymbol
+     name and the symbol name.  The problem is that the msymbol name will
+     describe the parameters in the most basic way, with typedefs stripped
+     out, while the symbol name will represent the types as they appear in
+     the program.  This means we will see duplicate entries in the
+     completion tracker.  The following converts the symbol name back to
+     the msymbol name and removes the msymbol name from the completion
+     tracker.  */
+  if (sym->language () == language_cplus
+      && SYMBOL_DOMAIN (sym) == VAR_DOMAIN
+      && SYMBOL_CLASS (sym) == LOC_BLOCK)
+    {
+      /* The call to canonicalize returns the empty string if the input
+        string is already in canonical form, thanks to this we don't
+        remove the symbol we just added above.  */
+      std::string str
+       = cp_canonicalize_string_no_typedefs (sym->natural_name ());
+      if (!str.empty ())
+       tracker.remove_completion (str.c_str ());
+    }
 }
 
 /* completion_list_add_name wrapper for struct minimal_symbol.  */
@@ -5214,7 +5354,7 @@ completion_list_add_msymbol (completion_tracker &tracker,
                             const lookup_name_info &lookup_name,
                             const char *text, const char *word)
 {
-  completion_list_add_name (tracker, MSYMBOL_LANGUAGE (sym),
+  completion_list_add_name (tracker, sym->language (),
                            sym->natural_name (),
                            lookup_name, text, word);
 }
@@ -5348,7 +5488,7 @@ completion_list_add_fields (completion_tracker &tracker,
       if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
        for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
          if (TYPE_FIELD_NAME (t, j))
-           completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
+           completion_list_add_name (tracker, sym->language (),
                                      TYPE_FIELD_NAME (t, j),
                                      lookup_name, text, word);
     }
@@ -6031,6 +6171,16 @@ find_main_name (void)
 
   /* The languages above didn't identify the name of the main procedure.
      Fallback to "main".  */
+
+  /* Try to find language for main in psymtabs.  */
+  enum language lang
+    = find_quick_global_symbol_language ("main", VAR_DOMAIN);
+  if (lang != language_unknown)
+    {
+      set_main_name ("main", lang);
+      return;
+    }
+
   set_main_name ("main", language_unknown);
 }
 
@@ -6288,6 +6438,9 @@ get_symbol_address (const struct symbol *sym)
 
   for (objfile *objfile : current_program_space->objfiles ())
     {
+      if (objfile->separate_debug_objfile_backlink != nullptr)
+       continue;
+
       bound_minimal_symbol minsym
        = lookup_minimal_symbol_linkage (linkage_name, objfile);
       if (minsym.minsym != nullptr)
@@ -6308,7 +6461,8 @@ get_msymbol_address (struct objfile *objf, const struct minimal_symbol *minsym)
 
   for (objfile *objfile : current_program_space->objfiles ())
     {
-      if ((objfile->flags & OBJF_MAINLINE) != 0)
+      if (objfile->separate_debug_objfile_backlink == nullptr
+         && (objfile->flags & OBJF_MAINLINE) != 0)
        {
          bound_minimal_symbol found
            = lookup_minimal_symbol_linkage (linkage_name, objfile);
@@ -6316,8 +6470,7 @@ get_msymbol_address (struct objfile *objf, const struct minimal_symbol *minsym)
            return BMSYMBOL_VALUE_ADDRESS (found);
        }
     }
-  return (minsym->value.address
-         + ANOFFSET (objf->section_offsets, minsym->section));
+  return minsym->value.address + objf->section_offsets[minsym->section];
 }
 
 \f
@@ -6630,8 +6783,9 @@ info_module_var_func_command_completer (struct cmd_list_element *ignore,
 
 \f
 
+void _initialize_symtab ();
 void
-_initialize_symtab (void)
+_initialize_symtab ()
 {
   cmd_list_element *c;
 
@@ -6644,7 +6798,7 @@ Usage: info variables [-q] [-n] [-t TYPEREGEXP] [NAMEREGEXP]\n\
 Prints the global and static variables.\n"),
                                      _("global and static variables"),
                                      true));
-  set_cmd_completer_handle_brkchars (c, info_print_command_completer);
+  set_cmd_completer_handle_brkchars (c, info_vars_funcs_command_completer);
   if (dbx_commands)
     {
       c = add_com ("whereis", class_info, info_variables_command,
@@ -6654,7 +6808,7 @@ Usage: whereis [-q] [-n] [-t TYPEREGEXP] [NAMEREGEXP]\n\
 Prints the global and static variables.\n"),
                                         _("global and static variables"),
                                         true));
-      set_cmd_completer_handle_brkchars (c, info_print_command_completer);
+      set_cmd_completer_handle_brkchars (c, info_vars_funcs_command_completer);
     }
 
   c = add_info ("functions", info_functions_command,
@@ -6664,7 +6818,7 @@ Usage: info functions [-q] [-n] [-t TYPEREGEXP] [NAMEREGEXP]\n\
 Prints the functions.\n"),
                                      _("functions"),
                                      true));
-  set_cmd_completer_handle_brkchars (c, info_print_command_completer);
+  set_cmd_completer_handle_brkchars (c, info_vars_funcs_command_completer);
 
   c = add_info ("types", info_types_command, _("\
 All type names, or those matching REGEXP.\n\
This page took 0.038562 seconds and 4 git commands to generate.