From: Petr Machata Date: Thu, 15 Oct 2009 17:21:47 +0000 (+0200) Subject: dwarflint: Rewrite testing for better extensibility X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=314c88f2c25afb84effd9aa72bb022b5ba117f7c;p=thirdparty%2Felfutils.git dwarflint: Rewrite testing for better extensibility * all tests are actually still done the way they were, but the framework is in place --- diff --git a/src/dwarflint-checks.hh b/src/dwarflint-checks.hh new file mode 100644 index 000000000..37f569b20 --- /dev/null +++ b/src/dwarflint-checks.hh @@ -0,0 +1,61 @@ +#ifndef DWARFLINT_CHECKS_HH +#define DWARFLINT_CHECKS_HH + +#include +#include +#include +#include "dwarflint-where.h" +#include "dwarflint-main.hh" + +struct check_base +{ + struct failed + : public std::runtime_error + { + failed (std::string const &msg) + : std::runtime_error (msg) + { + } + }; +}; + +template +class check + : public check_base +{ +public: + static void const *key () + { + return reinterpret_cast (&key); + } +}; + +template +struct reg + : public dwarflint::check_registrar::item +{ + reg () + { + dwarflint::check_registrar::inst ()->add (this); + } + + virtual void run (dwarflint &lint) { + lint.toplev_check (); + } +}; + +template +void +toplev_check (dwarflint &lint) +{ + try + { + lint.check (); + } + catch (check_base::failed const &f) + { + std::cout << f.what () << std::endl; + } +} + +#endif//DWARFLINT_CHECKS_HH diff --git a/src/dwarflint-main.cc b/src/dwarflint-main.cc index 4cf4a202e..2b18c4ccf 100644 --- a/src/dwarflint-main.cc +++ b/src/dwarflint-main.cc @@ -37,6 +37,8 @@ #include "dwarflint.h" #include "dwarflint-config.h" +#include "dwarflint-main.hh" +#include "dwarflint-readctx.h" /* Bug report address. */ const char *argp_program_bug_address = PACKAGE_BUGREPORT; @@ -254,6 +256,129 @@ layout_rel_file (Elf *elf) return 0; } +dwarflint::dwarflint (Elf *elf) +{ + check_registrar::inst ()->enroll (*this); + + struct elf_file file; + if (!elf_file_init (&file, elf)) + return; + + struct abbrev_table *abbrev_chain = NULL; + struct cu *cu_chain = NULL; + struct read_ctx ctx; + + /* Don't attempt to do high-level checks if we couldn't initialize + high-level context. The wrapper takes care of printing out error + messages if any. */ + struct hl_ctx *hlctx = do_high_level ? hl_ctx_new (elf) : NULL; + +#define SEC(sec) (file.debugsec[sec_##sec]) +#define HAS_SEC(sec) (SEC(sec) != NULL && SEC(sec)->data != NULL) + + if (likely (HAS_SEC(abbrev))) + { + read_ctx_init (&ctx, &file, SEC(abbrev)->data); + abbrev_chain = abbrev_table_load (&ctx); + } + else if (!tolerate_nodebug) + /* Hard error, not a message. We can't debug without this. */ + wr_error (NULL, ".debug_abbrev data not found.\n"); + + Elf_Data *str_data = NULL; + if (SEC(str) != NULL) + { + str_data = SEC(str)->data; + if (str_data == NULL) + { + where wh = WHERE (sec_str, NULL); + wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf, &wh, + ": the section is present but empty.\n"); + } + } + + struct cu_coverage *cu_coverage = NULL; + if (abbrev_chain != NULL) + { + if (likely (HAS_SEC(info))) + { + cu_coverage + = (struct cu_coverage *)calloc (1, sizeof (struct cu_coverage)); + cu_chain = check_info_structural (&file, SEC(info), abbrev_chain, + str_data, cu_coverage); + if (cu_chain != NULL && hlctx != NULL) + check_expected_trees (hlctx); + } + else if (!tolerate_nodebug) + /* Hard error, not a message. We can't debug without this. */ + wr_error (NULL, ".debug_info data not found.\n"); + } + + bool ranges_sound; + if (HAS_SEC(ranges) && cu_chain != NULL) + ranges_sound = check_loc_or_range_structural (&file, SEC(ranges), + cu_chain, cu_coverage); + else + ranges_sound = false; + + if (HAS_SEC(loc) && cu_chain != NULL + && check_loc_or_range_structural (&file, SEC(loc), cu_chain, NULL) + && cu_chain != NULL && hlctx != NULL) + check_range_out_of_scope (hlctx); + + if (HAS_SEC(aranges)) + { + read_ctx_init (&ctx, &file, SEC(aranges)->data); + + /* If ranges were needed and not loaded, don't pass them down + for CU/aranges coverage analysis. */ + struct coverage *cov + = (cu_coverage != NULL + && cu_coverage->need_ranges) ? NULL : &cu_coverage->cov; + + if (check_aranges_structural (&file, SEC(aranges), cu_chain, cov) + && ranges_sound && hlctx != NULL && !be_tolerant && !be_gnu) + check_matching_ranges (hlctx); + } + + if (HAS_SEC(pubnames)) + check_pub_structural (&file, SEC(pubnames), cu_chain); + else if (!tolerate_nodebug) + { + where wh = WHERE (sec_pubnames, NULL); + wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf, + &wh, ": data not found.\n"); + } + + if (HAS_SEC(pubtypes)) + check_pub_structural (&file, SEC(pubtypes), cu_chain); + else if (!tolerate_nodebug) + { + where wh = WHERE (sec_pubtypes, NULL); + wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf | mc_pubtypes, + &wh, ": data not found.\n"); + } + + if (HAS_SEC(line)) + check_line_structural (&file, SEC(line), cu_chain); + else if (!tolerate_nodebug) + { + where wh = WHERE (sec_line, NULL); + wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf | mc_loc, + &wh, ": data not found.\n"); + } + + cu_free (cu_chain); + abbrev_table_free (abbrev_chain); + if (file.ebl != NULL) + ebl_closebackend (file.ebl); + free (file.sec); + hl_ctx_delete (hlctx); + +#undef SEC +#undef HAS_SEC +} + int main (int argc, char *argv[]) { @@ -328,7 +453,9 @@ main (int argc, char *argv[]) if (layout_rel_file (elf)) goto invalid_elf; - process_file (elf, argv[remaining], only_one); + if (!only_one) + std::cout << std::endl << argv[remaining] << ":" << std::endl; + dwarflint lint (elf); elf_errno (); /* Clear errno. */ elf_end (elf); diff --git a/src/dwarflint-main.hh b/src/dwarflint-main.hh new file mode 100644 index 000000000..a6cd8b89d --- /dev/null +++ b/src/dwarflint-main.hh @@ -0,0 +1,72 @@ +#ifndef DWARFLINT_HH +#define DWARFLINT_HH + +#include +#include +#include + +class dwarflint +{ + typedef std::map check_map; + check_map _m_checks; + +public: + struct check_registrar + { + struct item + { + virtual void run (dwarflint &lint) = 0; + }; + + static check_registrar *inst () + { + static check_registrar inst; + return &inst; + } + + void add (item *i) + { + _m_items.push_back (i); + } + + private: + friend class dwarflint; + void enroll (dwarflint &lint) + { + for (std::vector ::iterator it = _m_items.begin (); + it != _m_items.end (); ++it) + (*it)->run (lint); + } + + std::vector _m_items; + }; + + dwarflint (Elf *elf); + + template + T * + check () + { + void const *key = T::key (); + check_map::iterator it = _m_checks.find (key); + T *c; + if (it != _m_checks.end ()) + c = static_cast (it->second); + else + { + c = new T (*this); + if (!_m_checks.insert (std::make_pair (key, c)).second) + throw std::runtime_error ("duplicate key"); + } + return c; + } + + template + T * + check (T *fake) + { + return check (); + } +}; + +#endif//DWARFLINT_HH diff --git a/src/dwarflint-readctx.h b/src/dwarflint-readctx.h index 4adcdf56e..62112eb21 100644 --- a/src/dwarflint-readctx.h +++ b/src/dwarflint-readctx.h @@ -4,6 +4,11 @@ #include #include "../libelf/libelf.h" +#ifdef __cplusplus +extern "C" +{ +#endif + /* Functions and data structures related to bounds-checked reading. */ @@ -44,4 +49,8 @@ const char *read_ctx_read_str (struct read_ctx *ctx); bool read_ctx_skip (struct read_ctx *ctx, uint64_t len); bool read_ctx_eof (struct read_ctx *ctx); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/dwarflint.c b/src/dwarflint.c index e83647c6e..0ef853992 100644 --- a/src/dwarflint.c +++ b/src/dwarflint.c @@ -107,12 +107,6 @@ struct abbrev_table abbrevs should be skipped. */ }; -static struct abbrev_table *abbrev_table_load (struct read_ctx *ctx); -static void abbrev_table_free (struct abbrev_table *abbr); -static struct abbrev *abbrev_table_find_abbrev (struct abbrev_table *abbrevs, - uint64_t abbrev_code); - - /* Functions and data structures for address record handling. We use that to check that all DIE references actually point to an existing die, not somewhere mid-DIE, where it just happens to be @@ -176,37 +170,8 @@ struct cu bool has_pubtypes; // Likewise for pubtypes. }; -static void cu_free (struct cu *cu_chain); static struct cu *cu_find_cu (struct cu *cu_chain, uint64_t offset); -struct cu_coverage -{ - struct coverage cov; - bool need_ranges; /* If all CU DIEs have high_pc/low_pc - attribute pair, we don't need separate - range pass. Otherwise we do. As soon as - ranges are projected into cov, the flag - is set to false again. */ -}; - - -/* Functions for checking of structural integrity. */ - -static struct cu * check_info_structural (struct elf_file *file, - struct sec *sec, - struct abbrev_table *abbrev_chain, - Elf_Data *strings, - struct cu_coverage *cu_coverage); - -static bool check_aranges_structural (struct elf_file *file, - struct sec *sec, - struct cu *cu_chain, - struct coverage *coverage); - -static bool check_pub_structural (struct elf_file *file, - struct sec *sec, - struct cu *cu_chain); - static bool check_location_expression (struct elf_file *file, struct read_ctx *ctx, struct cu *cu, @@ -215,20 +180,11 @@ static bool check_location_expression (struct elf_file *file, size_t length, struct where *wh); -static bool check_loc_or_range_structural (struct elf_file *file, - struct sec *sec, - struct cu *cu_chain, - struct cu_coverage *cu_coverage); - static bool read_rel (struct elf_file *file, struct sec *sec, Elf_Data *reldata, bool elf_64); -static bool check_line_structural (struct elf_file *file, - struct sec *sec, - struct cu *cu_chain); - static bool address_aligned (uint64_t addr, uint64_t align) { @@ -241,7 +197,7 @@ necessary_alignment (uint64_t start, uint64_t length, uint64_t align) return address_aligned (start + length, align) && length < align; } -static bool +bool elf_file_init (struct elf_file *file, Elf *elf) { WIPE (*file); @@ -437,118 +393,6 @@ elf_file_init (struct elf_file *file, Elf *elf) return true; } -void -process_file (Elf *elf, const char *fname, bool only_one) -{ - if (!only_one) - printf ("\n%s:\n", fname); - - struct elf_file file; - if (!elf_file_init (&file, elf)) - return; - - struct abbrev_table *abbrev_chain = NULL; - struct cu *cu_chain = NULL; - struct read_ctx ctx; - /* Don't attempt to do high-level checks if we couldn't initialize - high-level context. The wrapper takes care of printing out error - messages if any. */ - struct hl_ctx *hlctx = do_high_level ? hl_ctx_new (elf) : NULL; - -#define SEC(sec) (file.debugsec[sec_##sec]) -#define HAS_SEC(sec) (SEC(sec) != NULL && SEC(sec)->data != NULL) - - if (likely (HAS_SEC(abbrev))) - { - read_ctx_init (&ctx, &file, SEC(abbrev)->data); - abbrev_chain = abbrev_table_load (&ctx); - } - else if (!tolerate_nodebug) - /* Hard error, not a message. We can't debug without this. */ - wr_error (NULL, ".debug_abbrev data not found.\n"); - - Elf_Data *str_data = NULL; - if (SEC(str) != NULL) - { - str_data = SEC(str)->data; - if (str_data == NULL) - wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf, - &WHERE (sec_str, NULL), - ": the section is present but empty.\n"); - } - - struct cu_coverage *cu_coverage = NULL; - if (abbrev_chain != NULL) - { - if (likely (HAS_SEC(info))) - { - cu_coverage = calloc (1, sizeof (struct cu_coverage)); - cu_chain = check_info_structural (&file, SEC(info), abbrev_chain, - str_data, cu_coverage); - if (cu_chain != NULL && hlctx != NULL) - check_expected_trees (hlctx); - } - else if (!tolerate_nodebug) - /* Hard error, not a message. We can't debug without this. */ - wr_error (NULL, ".debug_info data not found.\n"); - } - - bool ranges_sound; - if (HAS_SEC(ranges) && cu_chain != NULL) - ranges_sound = check_loc_or_range_structural (&file, SEC(ranges), - cu_chain, cu_coverage); - else - ranges_sound = false; - - if (HAS_SEC(loc) && cu_chain != NULL - && check_loc_or_range_structural (&file, SEC(loc), cu_chain, NULL) - && cu_chain != NULL && hlctx != NULL) - check_range_out_of_scope (hlctx); - - if (HAS_SEC(aranges)) - { - read_ctx_init (&ctx, &file, SEC(aranges)->data); - - /* If ranges were needed and not loaded, don't pass them down - for CU/aranges coverage analysis. */ - struct coverage *cov - = (cu_coverage != NULL - && cu_coverage->need_ranges) ? NULL : &cu_coverage->cov; - - if (check_aranges_structural (&file, SEC(aranges), cu_chain, cov) - && ranges_sound && hlctx != NULL && !be_tolerant && !be_gnu) - check_matching_ranges (hlctx); - } - - if (HAS_SEC(pubnames)) - check_pub_structural (&file, SEC(pubnames), cu_chain); - else if (!tolerate_nodebug) - wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf, - &WHERE (sec_pubnames, NULL), ": data not found.\n"); - - if (HAS_SEC(pubtypes)) - check_pub_structural (&file, SEC(pubtypes), cu_chain); - else if (!tolerate_nodebug) - wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf | mc_pubtypes, - &WHERE (sec_pubtypes, NULL), ": data not found.\n"); - - if (HAS_SEC(line)) - check_line_structural (&file, SEC(line), cu_chain); - else if (!tolerate_nodebug) - wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf | mc_loc, - &WHERE (sec_line, NULL), ": data not found.\n"); - - cu_free (cu_chain); - abbrev_table_free (abbrev_chain); - if (file.ebl != NULL) - ebl_closebackend (file.ebl); - free (file.sec); - hl_ctx_delete (hlctx); - -#undef SEC -#undef HAS_SEC -} - static bool checked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret, struct where *where, const char *what) @@ -702,7 +546,7 @@ is_location_attrib (uint64_t name) } } -static struct abbrev_table * +struct abbrev_table * abbrev_table_load (struct read_ctx *ctx) { struct abbrev_table *section_chain = NULL; @@ -1011,7 +855,7 @@ abbrev_table_load (struct read_ctx *ctx) return NULL; } -static void +void abbrev_table_free (struct abbrev_table *abbr) { for (struct abbrev_table *it = abbr; it != NULL; ) @@ -1026,7 +870,7 @@ abbrev_table_free (struct abbrev_table *abbr) } } -static struct abbrev * +struct abbrev * abbrev_table_find_abbrev (struct abbrev_table *abbrevs, uint64_t abbrev_code) { size_t a = 0; @@ -1389,7 +1233,7 @@ coverage_map_free (struct coverage_map *coverage_map) } -static void +void cu_free (struct cu *cu_chain) { for (struct cu *it = cu_chain; it != NULL; ) @@ -2635,7 +2479,7 @@ check_cu_structural (struct elf_file *file, return retval; } -static struct cu * +struct cu * check_info_structural (struct elf_file *file, struct sec *sec, struct abbrev_table *abbrev_chain, @@ -2898,7 +2742,7 @@ compare_coverage (struct elf_file *file, /* COVERAGE is portion of address space covered by CUs (either via low_pc/high_pc pairs, or via DW_AT_ranges references). If non-NULL, analysis of arange coverage is done against that set. */ -static bool +bool check_aranges_structural (struct elf_file *file, struct sec *sec, struct cu *cu_chain, @@ -3142,7 +2986,7 @@ check_aranges_structural (struct elf_file *file, return retval; } -static bool +bool check_pub_structural (struct elf_file *file, struct sec *sec, struct cu *cu_chain) @@ -3670,7 +3514,7 @@ check_loc_or_range_ref (struct elf_file *file, return retval; } -static bool +bool check_loc_or_range_structural (struct elf_file *file, struct sec *sec, struct cu *cu_chain, @@ -3949,7 +3793,7 @@ read_rel (struct elf_file *file, return true; } -static bool +bool check_line_structural (struct elf_file *file, struct sec *sec, struct cu *cu_chain) diff --git a/src/dwarflint.h b/src/dwarflint.h index bb815951f..cecd6ab5d 100644 --- a/src/dwarflint.h +++ b/src/dwarflint.h @@ -30,6 +30,7 @@ #include "../libebl/libebl.h" #include "dwarflint-coverage.h" #include "dwarflint-messages.h" +#include "dwarflint-readctx.h" #ifdef __cplusplus # include @@ -39,28 +40,16 @@ extern "C" # include #endif - /* Entry points for high-level checks. */ - struct hl_ctx; - - /* Check that .debug_aranges and .debug_ranges match. */ - extern struct hl_ctx *hl_ctx_new (Elf *elf); - extern void hl_ctx_delete (struct hl_ctx *hlctx); - extern bool check_matching_ranges (struct hl_ctx *hlctx); - extern bool check_expected_trees (struct hl_ctx *hlctx); - extern bool check_range_out_of_scope (struct hl_ctx *hlctx); - extern void process_file (Elf *elf, const char *fname, bool only_one); - struct relocation { uint64_t offset; uint64_t addend; int symndx; int type; - bool invalid; /* Whether this one relocation should be - ignored. Necessary so that we don't - double-report invalid & missing - relocation. */ + bool invalid; /* Whether this one relocation should be + ignored. Necessary so that we don't report + invalid & missing relocation twice. */ }; struct relocation_data @@ -105,8 +94,23 @@ extern "C" bool addr_64; /* True if it's 64-bit Elf. */ bool other_byte_order; /* True if the file has a byte order different from the host. */ + // xxx add CUs etc here? }; + /* Check that .debug_aranges and .debug_ranges match. */ + extern struct hl_ctx *hl_ctx_new (Elf *elf); + extern void hl_ctx_delete (struct hl_ctx *hlctx); + extern bool check_matching_ranges (struct hl_ctx *hlctx); + extern bool check_expected_trees (struct hl_ctx *hlctx); + extern bool check_range_out_of_scope (struct hl_ctx *hlctx); + extern bool elf_file_init (struct elf_file *file, Elf *elf); + + // xxx will go away + extern struct abbrev_table * abbrev_table_load (struct read_ctx *ctx); + extern void abbrev_table_free (struct abbrev_table *abbr); + extern struct abbrev *abbrev_table_find_abbrev (struct abbrev_table *abbrevs, + uint64_t abbrev_code); + struct section_coverage { struct sec *sec; @@ -125,6 +129,39 @@ extern "C" bool allow_overlap; }; + struct cu_coverage + { + struct coverage cov; + bool need_ranges; /* If all CU DIEs have high_pc/low_pc + attribute pair, we don't need separate + range pass. Otherwise we do. As soon as + ranges are projected into cov, the flag + is set to false again. */ + }; + + // xxx low-level check entry points, will go away + extern struct cu * check_info_structural (struct elf_file *file, + struct sec *sec, + struct abbrev_table *abbrev_chain, + Elf_Data *strings, + struct cu_coverage *cu_coverage); + extern bool check_loc_or_range_structural (struct elf_file *file, + struct sec *sec, + struct cu *cu_chain, + struct cu_coverage *cu_coverage); + extern bool check_aranges_structural (struct elf_file *file, + struct sec *sec, + struct cu *cu_chain, + struct coverage *coverage); + extern bool check_pub_structural (struct elf_file *file, + struct sec *sec, + struct cu *cu_chain); + extern bool check_line_structural (struct elf_file *file, + struct sec *sec, + struct cu *cu_chain); + extern void cu_free (struct cu *cu_chain); + + void section_coverage_init (struct section_coverage *sco, struct sec *sec, bool warn); bool coverage_map_init (struct coverage_map *coverage_map,