]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
dwarflint: check_linkage_external_die keep symbol table and double check.
authorMark Wielaard <mjw@redhat.com>
Fri, 8 Apr 2011 09:30:58 +0000 (11:30 +0200)
committerMark Wielaard <mjw@redhat.com>
Fri, 8 Apr 2011 09:38:43 +0000 (11:38 +0200)
We pull the symbol table from the dwarf file and check when a linkage_name
is set that the symbol table contains that name and is marked not marked
local, with the following exceptions:

- No symbol in table, OK, if not a defining or const object.
  Or GNU extension, anonymous structs can have a linkage_name.
- Symbol in table marked local, OK if not a defining object
  and marked external.  Which means it comes from some external
  symbol table.

dwarflint/check_linkage_external_die.cc

index 3731e106e873a68b11e7fe6e7ce7fc09a2939f76..760b95947c8c12117af60eee0fdd90b6baf3e16d 100644 (file)
 #include "pri.hh"
 #include "messages.hh"
 
+
+#include "../libelf/gelf.h"
+#include "../libdw/libdw.h"
+
 using elfutils::dwarf;
 
 namespace
@@ -34,6 +38,9 @@ namespace
   class check_linkage_external_die
     : public die_check
   {
+  private:
+    std::map<std::string, bool> _m_symbols;
+
   public:
     static checkdescriptor const *descriptor ()
     {
@@ -46,9 +53,75 @@ namespace
       return &cd;
     }
 
-    check_linkage_external_die (highlevel_check_i *, checkstack &, dwarflint &)
+    check_linkage_external_die (highlevel_check_i *check,
+                               checkstack &, dwarflint &)
     {
-      // We don't keep any state for this die check.
+      // Extract all symbol table names for objects and functions
+      // and store whether they are global or not in _m_symbols.
+      Dwarf *dwarf = check->c_dw;
+      Elf *elf = dwarf_getelf (dwarf);
+      Elf_Scn *scn = NULL;
+      while ((scn = elf_nextscn (elf, scn)) != NULL)
+       {
+         GElf_Shdr shdr_mem;
+         GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+         if (shdr != NULL && (shdr->sh_type == SHT_DYNSYM
+                              || shdr->sh_type == SHT_SYMTAB))
+           {
+             Elf_Data *data = elf_getdata (scn, NULL);
+             size_t shstrndx;
+             elf_getshdrstrndx (elf, &shstrndx);
+             unsigned int syms = shdr->sh_size / shdr->sh_entsize;
+             for (unsigned int cnt = 0; cnt < syms; ++cnt)
+               {
+                 GElf_Sym sym_mem;
+                 GElf_Sym *sym = gelf_getsym (data, cnt, &sym_mem);
+                 if (sym != NULL
+                     && (GELF_ST_TYPE (sym->st_info) == STT_OBJECT
+                         || GELF_ST_TYPE (sym->st_info) == STT_FUNC))
+                   {
+                     const char *name;
+                     name = elf_strptr (elf, shdr->sh_link, sym->st_name);
+                     if (name != NULL)
+                       {
+                         // Regard anything not explicitly marked as local
+                         // a global symbol, it could be STB_GLOBAL,
+                         // STB_WEAK, STB_GNU_UNIQUE, ...
+                         unsigned int binding = GELF_ST_BIND (sym->st_info);
+                         bool global = binding != STB_LOCAL;
+                         using namespace std;
+                         _m_symbols.insert (pair<string, bool>
+                                            (string (name), global));
+                       }
+                   }
+               }
+           }
+       }
+    }
+
+    static bool is_external (all_dies_iterator<dwarf> const &it)
+    {
+      bool candidates = true;
+      dwarf::debug_info_entry entry = *it;
+      do
+       {
+         dwarf::debug_info_entry::attributes_type attrs = entry.attributes ();
+         if (attrs.find (DW_AT_external) != attrs.end ())
+           return true;
+
+         dwarf::debug_info_entry::attributes_type::const_iterator origin
+           = attrs.find (DW_AT_abstract_origin);
+         if (origin == attrs.end ())
+           origin = attrs.find (DW_AT_specification);
+
+         if (origin != attrs.end ())
+           entry = *(*origin).second.reference ();
+         else
+           candidates = false;
+       }
+      while (candidates);
+
+      return false;
     }
 
     virtual void
@@ -56,16 +129,64 @@ namespace
     {
       dwarf::debug_info_entry const &entry = *it;
       dwarf::debug_info_entry::attributes_type attrs = entry.attributes ();
-      if ((attrs.find (DW_AT_linkage_name) != attrs.end ()
-          || attrs.find (DW_AT_MIPS_linkage_name) != attrs.end ())
-         && attrs.find (DW_AT_external) == attrs.end ())
+      dwarf::debug_info_entry::attributes_type::const_iterator linkage_name
+       = attrs.find (DW_AT_linkage_name);
+      if (linkage_name == attrs.end ())
+       linkage_name = attrs.find (DW_AT_MIPS_linkage_name);
+      if (linkage_name != attrs.end ())
        {
-         wr_message (to_where (entry),
-                     mc_impact_3 | mc_acc_suboptimal | mc_die_other)
-           .id (descriptor ())
-           << elfutils::dwarf::tags::name (entry.tag ())
-           << " has linkage_name attribute, but no external attribute."
-           << std::endl;
+         using namespace std;
+         const char *name = (*linkage_name).second.string ();
+         map<string, bool>::iterator s = _m_symbols.find (string (name));
+         if (s == _m_symbols.end ())
+           {
+             // No symbol in table, OK, if not a defining or const object.
+             // GNU extension, anonymous structs can have a linkage_name.
+             if (attrs.find (DW_AT_declaration) == attrs.end ()
+                 && attrs.find (DW_AT_const_value) == attrs.end ()
+                 && ((entry.tag () != DW_TAG_structure_type
+                     && entry.tag () != DW_TAG_enumeration_type)
+                     || attrs.find (DW_AT_name) != attrs.end ()))
+               {
+                 wr_message (to_where (entry),
+                             mc_impact_3 | mc_acc_suboptimal | mc_die_other)
+                   .id (descriptor ())
+                   << elfutils::dwarf::tags::name (entry.tag ())
+                   << " has linkage_name attribute `"
+                   << name << "', which is not in string table,"
+                   << " but DIE is not marked as a declaration"
+                   << " or const value."
+                   << std::endl;
+               }
+           }
+         else if ((*s).second == false)
+           {
+             // Local symbol in table, OK if not a defining object
+             // and marked external. Which means it comes from an
+             // external symbol table.
+             if (attrs.find (DW_AT_declaration) == attrs.end ()
+                 && is_external (it))
+               {
+                 wr_message (to_where (entry),
+                             mc_impact_3 | mc_acc_suboptimal | mc_die_other)
+                   .id (descriptor ())
+                   << elfutils::dwarf::tags::name (entry.tag ())
+                   << " has linkage_name attribute `"
+                   << name << "', which is a local symbol."
+                   << std::endl;
+               }
+           }
+         else if (! is_external (it))
+           {
+             // Global symbol in symbol table, not marked external.
+             // Always bad.
+             wr_message (to_where (entry),
+                         mc_impact_3 | mc_acc_suboptimal | mc_die_other)
+               .id (descriptor ())
+               << elfutils::dwarf::tags::name (entry.tag ())
+               << " has linkage_name attribute, but no external attribute."
+               << std::endl;
+           }
        }
     }
   };