--- /dev/null
+#ifndef DWARFLINT_CHECKS_HH
+#define DWARFLINT_CHECKS_HH
+
+#include <iostream>
+#include <stdexcept>
+#include <string>
+#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 T>
+class check
+ : public check_base
+{
+public:
+ static void const *key ()
+ {
+ return reinterpret_cast <void const *> (&key);
+ }
+};
+
+template <class T>
+struct reg
+ : public dwarflint::check_registrar::item
+{
+ reg ()
+ {
+ dwarflint::check_registrar::inst ()->add (this);
+ }
+
+ virtual void run (dwarflint &lint) {
+ lint.toplev_check <T> ();
+ }
+};
+
+template <class T>
+void
+toplev_check (dwarflint &lint)
+{
+ try
+ {
+ lint.check<T> ();
+ }
+ catch (check_base::failed const &f)
+ {
+ std::cout << f.what () << std::endl;
+ }
+}
+
+#endif//DWARFLINT_CHECKS_HH
#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;
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[])
{
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);
--- /dev/null
+#ifndef DWARFLINT_HH
+#define DWARFLINT_HH
+
+#include <map>
+#include <vector>
+#include <stdexcept>
+
+class dwarflint
+{
+ typedef std::map <void const *, class check_base *> 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 <item *>::iterator it = _m_items.begin ();
+ it != _m_items.end (); ++it)
+ (*it)->run (lint);
+ }
+
+ std::vector <item *> _m_items;
+ };
+
+ dwarflint (Elf *elf);
+
+ template <class T>
+ 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 <T *> (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 <class T>
+ T *
+ check (T *fake)
+ {
+ return check<T> ();
+ }
+};
+
+#endif//DWARFLINT_HH
#include <stdbool.h>
#include "../libelf/libelf.h"
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
/* Functions and data structures related to bounds-checked
reading. */
bool read_ctx_skip (struct read_ctx *ctx, uint64_t len);
bool read_ctx_eof (struct read_ctx *ctx);
+#ifdef __cplusplus
+}
+#endif
+
#endif
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
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,
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)
{
return address_aligned (start + length, align) && length < align;
}
-static bool
+bool
elf_file_init (struct elf_file *file, Elf *elf)
{
WIPE (*file);
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)
}
}
-static struct abbrev_table *
+struct abbrev_table *
abbrev_table_load (struct read_ctx *ctx)
{
struct abbrev_table *section_chain = NULL;
return NULL;
}
-static void
+void
abbrev_table_free (struct abbrev_table *abbr)
{
for (struct abbrev_table *it = abbr; it != NULL; )
}
}
-static struct abbrev *
+struct abbrev *
abbrev_table_find_abbrev (struct abbrev_table *abbrevs, uint64_t abbrev_code)
{
size_t a = 0;
}
-static void
+void
cu_free (struct cu *cu_chain)
{
for (struct cu *it = cu_chain; it != NULL; )
return retval;
}
-static struct cu *
+struct cu *
check_info_structural (struct elf_file *file,
struct sec *sec,
struct abbrev_table *abbrev_chain,
/* 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,
return retval;
}
-static bool
+bool
check_pub_structural (struct elf_file *file,
struct sec *sec,
struct cu *cu_chain)
return retval;
}
-static bool
+bool
check_loc_or_range_structural (struct elf_file *file,
struct sec *sec,
struct cu *cu_chain,
return true;
}
-static bool
+bool
check_line_structural (struct elf_file *file,
struct sec *sec,
struct cu *cu_chain)
#include "../libebl/libebl.h"
#include "dwarflint-coverage.h"
#include "dwarflint-messages.h"
+#include "dwarflint-readctx.h"
#ifdef __cplusplus
# include <string>
# include <stdbool.h>
#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
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;
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,