summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wielaard <mjw@redhat.com>2011-04-08 11:30:58 +0200
committerMark Wielaard <mjw@redhat.com>2011-04-08 11:38:43 +0200
commit5d91a0bf01b7c852e4e578524ee19d8682de5653 (patch)
treea9f58feb90670238e0bb0b350e9f643f2196acfe
parent6a0cc8997d4554f5a64b406cd951a1b5e547d759 (diff)
downloadelfutils-5d91a0bf01b7c852e4e578524ee19d8682de5653.tar.gz
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.
-rw-r--r--dwarflint/check_linkage_external_die.cc143
1 files 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<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;
+ }
}
}
};