From 5d91a0bf01b7c852e4e578524ee19d8682de5653 Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Fri, 8 Apr 2011 11:30:58 +0200 Subject: dwarflint: check_linkage_external_die keep symbol table and double check. 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 | 143 +++++++++++++++++++++++++++++--- 1 file changed, 132 insertions(+), 11 deletions(-) diff --git a/dwarflint/check_linkage_external_die.cc b/dwarflint/check_linkage_external_die.cc index 3731e106..760b9594 100644 --- a/dwarflint/check_linkage_external_die.cc +++ b/dwarflint/check_linkage_external_die.cc @@ -27,6 +27,10 @@ #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 _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 (name), global)); + } + } + } + } + } + } + + static bool is_external (all_dies_iterator 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::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; + } } } }; -- cgit v1.2.1