From: Petr Machata Date: Fri, 20 Nov 2009 15:45:59 +0000 (+0100) Subject: dwarflint: Move .debug_info code into check_debug_info.cc X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8c9e456ecef41e7ff9380656b1ae48a6ba9fcda4;p=thirdparty%2Felfutils.git dwarflint: Move .debug_info code into check_debug_info.cc * also cut out parts of stuct cu to struct cu_head * also constify a bunch of where pointers that are passed around --- diff --git a/src/Makefile.am b/src/Makefile.am index 3f498e30e..b5aedd596 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -97,6 +97,7 @@ dwarflint_SOURCES = dwarfstrings.c \ dwarflint/all-dies-it.hh \ dwarflint/checks-high.hh \ dwarflint/check_debug_abbrev.cc \ + dwarflint/check_debug_info.cc \ dwarflint/check_debug_line.cc \ dwarflint/check_matching_ranges.cc \ dwarflint/check_range_out_of_scope.cc \ diff --git a/src/dwarflint/check_debug_info.cc b/src/dwarflint/check_debug_info.cc new file mode 100644 index 000000000..41f41f50c --- /dev/null +++ b/src/dwarflint/check_debug_info.cc @@ -0,0 +1,345 @@ +/* Routines related to .debug_info. + + Copyright (C) 2009 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +// xxx drop as soon as not necessary +#define __STDC_FORMAT_MACROS +#define PRI_DIE "DIE 0x%" PRIx64 +#include + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "messages.h" +#include "low.h" +#include "checks-low.hh" +#include "pri.hh" + +namespace +{ + bool + check_category (enum message_category cat) + { + return message_accept (&warning_criteria, cat); + } + + bool + check_global_die_references (struct cu *cu_chain) + { + bool retval = true; + for (struct cu *it = cu_chain; it != NULL; it = it->next) + for (size_t i = 0; i < it->die_refs.size; ++i) + { + struct ref *ref = it->die_refs.refs + i; + struct cu *ref_cu = NULL; + for (struct cu *jt = cu_chain; jt != NULL; jt = jt->next) + if (addr_record_has_addr (&jt->die_addrs, ref->addr)) + { + ref_cu = jt; + break; + } + + if (ref_cu == NULL) + { + wr_error (&ref->who, + ": unresolved (non-CU-local) reference to " PRI_DIE ".\n", + ref->addr); + retval = false; + } + else if (ref_cu == it) + /* This is technically not a problem, so long as the + reference is valid, which it is. But warn about this + anyway, perhaps local reference could be formed on fewer + number of bytes. */ + wr_message (mc_impact_2 | mc_acc_suboptimal | mc_die_rel, + &ref->who, + ": local reference to " PRI_DIE " formed as global.\n", + ref->addr); + } + + return retval; + } + + std::vector + read_info_headers (struct elf_file *file, + struct sec *sec) + { + struct read_ctx ctx; + read_ctx_init (&ctx, sec->data, file->other_byte_order); + + std::vector ret; + while (!read_ctx_eof (&ctx)) + { + const unsigned char *cu_begin = ctx.ptr; + struct where where = WHERE (sec_info, NULL); + where_reset_1 (&where, read_ctx_get_offset (&ctx)); + + cu_head head; + head.offset = where.addr1; + head.where = where; + + /* Reading CU head is a bit tricky, because we don't know if + we have run into (superfluous but allowed) zero padding + between CUs. */ + if (!read_ctx_need_data (&ctx, 4) + && check_zero_padding (&ctx, cat (mc_info, mc_header), &where)) + break; + + /* CU length. */ + uint32_t size32; + if (!read_ctx_read_4ubyte (&ctx, &size32)) + { + wr_error (where) << "can't read CU length." << std::endl; + throw check_base::failed (); + } + if (size32 == 0 + && check_zero_padding (&ctx, cat (mc_info, mc_header), &where)) + break; + + if (!read_size_extra (&ctx, size32, &head.size, + &head.offset_size, &where)) + throw check_base::failed (); + + if (!read_ctx_need_data (&ctx, head.size)) + { + wr_error (where) + << "section doesn't have enough data to read CU of size " + << head.size << '.' << std::endl; + throw check_base::failed (); + } + + /* version + debug_abbrev_offset + address_size */ + Dwarf_Off cu_head_size = 2 + head.offset_size + 1; + if (head.size < cu_head_size) + { + wr_error (where) + << "claimed length of " << head.size + << " doesn't even cover CU head." << std::endl; + throw check_base::failed (); + } + + const unsigned char *cu_end = ctx.ptr + head.size; + head.head_size = ctx.ptr - cu_begin; // Length of the head itself. + head.total_size = cu_end - cu_begin; // Length including the length field. + + if (!read_ctx_skip (&ctx, head.size)) + { + wr_error (where) << pri::not_enough ("next CU") << std::endl; + throw check_base::failed (); + } + + ret.push_back (head); + } + + return ret; + } + + 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) + { + struct ref_record die_refs; + WIPE (die_refs); + + struct cu *cu_chain = NULL; + bool success = true; + + struct coverage strings_coverage_mem, *strings_coverage = NULL; + if (strings != NULL && check_category (mc_strings)) + { + WIPE (strings_coverage_mem); + strings_coverage = &strings_coverage_mem; + } + + struct relocation_data *reloc = sec->rel.size > 0 ? &sec->rel : NULL; + + std::vector cu_headers = read_info_headers (file, sec); + struct read_ctx ctx; + read_ctx_init (&ctx, sec->data, file->other_byte_order); + for (std::vector ::const_iterator it = cu_headers.begin (); + it != cu_headers.end (); ++it) + { + cu_head const &head = *it; + where const &where = head.where; + struct cu *cur = (cu *)xcalloc (1, sizeof (*cur)); + cur->head = &head; + cur->low_pc = (uint64_t)-1; + cur->next = cu_chain; + cu_chain = cur; + + assert (read_ctx_need_data (&ctx, head.total_size)); + + // Make CU context begin just before the CU length, so that + // DIE offsets are computed correctly. + struct read_ctx cu_ctx; + const unsigned char *cu_end = ctx.ptr + head.total_size; + read_ctx_init_sub (&cu_ctx, &ctx, ctx.ptr, cu_end); + cu_ctx.ptr += head.head_size; + + if (!check_cu_structural (file, &cu_ctx, cur, abbrev_chain, + strings, strings_coverage, reloc, + cu_coverage)) + { + success = false; + break; + } + + if (cu_ctx.ptr != cu_ctx.end + && !check_zero_padding (&cu_ctx, mc_info, &where)) + { + // Garbage coordinates: + uint64_t start + = read_ctx_get_offset (&ctx) + read_ctx_get_offset (&cu_ctx); + uint64_t end = read_ctx_get_offset (&ctx) + head.total_size; + wr_message_padding_n0 (mc_info, &where, start, end); + } + + int i = read_ctx_skip (&ctx, head.total_size); + assert (i); + } + + if (success) + { + if (ctx.ptr != ctx.end) + /* Did we read up everything? */ + { + where wh = WHERE (sec_info, NULL); + wr_message (cat (mc_die_other, mc_impact_4), &wh, + ": CU lengths don't exactly match Elf_Data contents."); + } + else + /* Did we consume all the relocations? */ + relocation_skip_rest (&sec->rel, sec->id); + + /* If we managed to read up everything, now do abbrev usage + analysis. */ + for (struct abbrev_table *abbrevs = abbrev_chain; + abbrevs != NULL; abbrevs = abbrevs->next) + { + if (!abbrevs->used) + { + struct where wh = WHERE (sec_abbrev, NULL); + where_reset_1 (&wh, abbrevs->offset); + wr_message (mc_impact_4 | mc_acc_bloat | mc_abbrevs, &wh, + ": abbreviation table is never used.\n"); + } + else if (!abbrevs->skip_check) + for (size_t i = 0; i < abbrevs->size; ++i) + if (!abbrevs->abbr[i].used) + wr_message (mc_impact_3 | mc_acc_bloat | mc_abbrevs, + &abbrevs->abbr[i].where, + ": abbreviation is never used.\n"); + } + } + + + /* We used to check that all CUs have the same address size. Now + that we validate address_size of each CU against the ELF header, + that's not necessary anymore. */ + + bool references_sound = check_global_die_references (cu_chain); + ref_record_free (&die_refs); + + if (strings_coverage != NULL) + { + if (success) + { + struct hole_info info = {sec_str, mc_strings, strings->d_buf, 0}; + coverage_find_holes (strings_coverage, 0, strings->d_size, + found_hole, &info); + } + coverage_free (strings_coverage); + } + + if (!success || !references_sound) + { + cu_free (cu_chain); + cu_chain = NULL; + } + + /* Reverse the chain, so that it's organized "naturally". Has + significant impact on performance when handling loc_ref and + range_ref fields in loc/range validation. */ + struct cu *last = NULL; + for (struct cu *it = cu_chain; it != NULL; ) + { + struct cu *next = it->next; + it->next = last; + last = it; + it = next; + } + cu_chain = last; + + return cu_chain; + } +} + +check_debug_info::check_debug_info (dwarflint &lint) + : _m_sec_info (lint.check (_m_sec_info)) + , _m_sec_abbrev (lint.check (_m_sec_abbrev)) + , _m_sec_str (lint.check (_m_sec_str)) + , _m_abbrevs (lint.check (_m_abbrevs)) +{ + memset (&cu_cov, 0, sizeof (cu_cov)); + + /* xxx wrap C routine before proper loading is in place. */ + cu *chain = check_info_structural + (&_m_sec_info->file, &_m_sec_info->sect, + &_m_abbrevs->abbrevs.begin ()->second, + _m_sec_str->sect.data, &cu_cov); + + if (chain == NULL) + throw check_base::failed (); + + for (cu *cu = chain; cu != NULL; cu = cu->next) + cus.push_back (*cu); + + // re-link CUs so that they form a chain again. This is to + // interface with C-level code. The last CU's next is null, so we + // don't have to re-link it. + cu *last = NULL; + for (std::vector::iterator it = cus.begin (); + it != cus.end (); ++it) + { + cu *cur = &*it; + if (last != NULL) + last->next = cur; + last = cur; + } + + if (cus.size () > 0) + assert (cus.back ().next == NULL); +} + +check_debug_info::~check_debug_info () +{ + cu_free (&cus.back ()); +} diff --git a/src/dwarflint/checks-low.cc b/src/dwarflint/checks-low.cc index 778a0e787..9a19b668e 100644 --- a/src/dwarflint/checks-low.cc +++ b/src/dwarflint/checks-low.cc @@ -319,47 +319,6 @@ section_base::section_base (dwarflint &lint, section_id secid) { } -check_debug_info::check_debug_info (dwarflint &lint) - : _m_sec_info (lint.check (_m_sec_info)) - , _m_sec_abbrev (lint.check (_m_sec_abbrev)) - , _m_sec_str (lint.check (_m_sec_str)) - , _m_abbrevs (lint.check (_m_abbrevs)) -{ - memset (&cu_cov, 0, sizeof (cu_cov)); - - /* xxx wrap C routine before proper loading is in place. */ - cu *chain = check_info_structural - (&_m_sec_info->file, &_m_sec_info->sect, - &_m_abbrevs->abbrevs.begin ()->second, - _m_sec_str->sect.data, &cu_cov); - - if (chain == NULL) - throw check_base::failed (); - - for (cu *cu = chain; cu != NULL; cu = cu->next) - cus.push_back (*cu); - - // re-link CUs so that they form a chain again. This is to - // interface with C-level code. The last CU's next is null, so we - // don't have to re-link it. - cu *last = NULL; - for (std::vector::iterator it = cus.begin (); - it != cus.end (); ++it) - { - cu *cur = &*it; - if (last != NULL) - last->next = cur; - last = cur; - } - if (cus.size () > 0) - assert (cus.back ().next == NULL); -} - -check_debug_info::~check_debug_info () -{ - cu_free (&cus.back ()); -} - check_debug_ranges::check_debug_ranges (dwarflint &lint) : _m_sec_ranges (lint.check (_m_sec_ranges)) , _m_cus (lint.check (_m_cus)) diff --git a/src/dwarflint/checks.hh b/src/dwarflint/checks.hh index 84296b181..ca35a4df1 100644 --- a/src/dwarflint/checks.hh +++ b/src/dwarflint/checks.hh @@ -15,7 +15,9 @@ template class check : public check_base { -public: +private: + template + friend X *dwarflint::check (); static void const *key () { return reinterpret_cast (&key); diff --git a/src/dwarflint/low.c b/src/dwarflint/low.c index 2eea902db..3e3dbc688 100644 --- a/src/dwarflint/low.c +++ b/src/dwarflint/low.c @@ -54,12 +54,6 @@ be done. */ static const bool do_range_coverage = false; -static bool -check_category (enum message_category cat) -{ - return message_accept (&warning_criteria, cat); -} - #define PRI_CU "CU 0x%" PRIx64 #define PRI_DIE "DIE 0x%" PRIx64 @@ -480,7 +474,7 @@ static struct cu * cu_find_cu (struct cu *cu_chain, uint64_t offset) { for (struct cu *it = cu_chain; it != NULL; it = it->next) - if (it->offset == offset) + if (it->head->offset == offset) return it; return NULL; } @@ -504,43 +498,6 @@ check_die_references (struct cu *cu, return retval; } -static bool -check_global_die_references (struct cu *cu_chain) -{ - bool retval = true; - for (struct cu *it = cu_chain; it != NULL; it = it->next) - for (size_t i = 0; i < it->die_refs.size; ++i) - { - struct ref *ref = it->die_refs.refs + i; - struct cu *ref_cu = NULL; - for (struct cu *jt = cu_chain; jt != NULL; jt = jt->next) - if (addr_record_has_addr (&jt->die_addrs, ref->addr)) - { - ref_cu = jt; - break; - } - - if (ref_cu == NULL) - { - wr_error (&ref->who, - ": unresolved (non-CU-local) reference to " PRI_DIE ".\n", - ref->addr); - retval = false; - } - else if (ref_cu == it) - /* This is technically not a problem, so long as the - reference is valid, which it is. But warn about this - anyway, perhaps local reference could be formed on fewer - number of bytes. */ - wr_message (mc_impact_2 | mc_acc_suboptimal | mc_die_rel, - &ref->who, - ": local reference to " PRI_DIE " formed as global.\n", - ref->addr); - } - - return retval; -} - bool read_size_extra (struct read_ctx *ctx, uint32_t size32, uint64_t *sizep, int *offset_sizep, struct where *wh) @@ -573,7 +530,7 @@ read_size_extra (struct read_ctx *ctx, uint32_t size32, uint64_t *sizep, bool check_zero_padding (struct read_ctx *ctx, enum message_category category, - struct where *wh) + struct where const *wh) { assert (ctx->ptr != ctx->end); const unsigned char *save_ptr = ctx->ptr; @@ -772,11 +729,11 @@ read_die_chain (dwarf_version_h ver, while (!read_ctx_eof (ctx)) { - where = cu->where; + where = cu->head->where; die_off = read_ctx_get_offset (ctx); /* Shift reported DIE offset by CU offset, to match the way readelf reports DIEs. */ - where_reset_2 (&where, die_off + cu->offset); + where_reset_2 (&where, die_off + cu->head->offset); uint64_t abbr_code; @@ -785,7 +742,7 @@ read_die_chain (dwarf_version_h ver, #define DEF_PREV_WHERE \ struct where prev_where = where; \ - where_reset_2 (&prev_where, prev_die_off + cu->offset) + where_reset_2 (&prev_where, prev_die_off + cu->head->offset) /* Check sibling value advertised last time through the loop. */ if (sibling_addr != 0) @@ -847,7 +804,7 @@ read_die_chain (dwarf_version_h ver, } abbrev->used = true; - addr_record_add (&cu->die_addrs, cu->offset + die_off); + addr_record_add (&cu->die_addrs, cu->head->offset + die_off); uint64_t low_pc = (uint64_t)-1, high_pc = (uint64_t)-1; bool low_pc_relocated = false, high_pc_relocated = false; @@ -913,7 +870,7 @@ read_die_chain (dwarf_version_h ver, if (local_die_refs != NULL) /* Address holds a CU-local reference, so add CU offset to turn it into section offset. */ - ref_record_add (local_die_refs, addr += cu->offset, who); + ref_record_add (local_die_refs, addr += cu->head->offset, who); } /* Callback for global DIE references. */ @@ -972,7 +929,7 @@ read_die_chain (dwarf_version_h ver, ref_record_add (&cu->loc_refs, value, who); } - uint64_t ctx_offset = read_ctx_get_offset (ctx) + cu->offset; + uint64_t ctx_offset = read_ctx_get_offset (ctx) + cu->head->offset; bool type_is_rel = file->ehdr.e_type == ET_REL; /* Attribute value. */ @@ -1008,7 +965,7 @@ read_die_chain (dwarf_version_h ver, switch (form) { case DW_FORM_data8: - if (cu->offset_size == 4) + if (cu->head->offset_size == 4) wr_error (&where, ": location attribute with form \"%s\" in 32-bit CU.\n", dwarf_form_string (form)); @@ -1042,7 +999,7 @@ read_die_chain (dwarf_version_h ver, switch (form) { case DW_FORM_data8: - if (cu->offset_size == 4) + if (cu->head->offset_size == 4) wr_error (&where, ": %s with form DW_FORM_data8 in 32-bit CU.\n", dwarf_attr_string (it->name)); @@ -1093,7 +1050,7 @@ read_die_chain (dwarf_version_h ver, case DW_FORM_strp: value_check_cb = check_strp; case DW_FORM_sec_offset: - if (!read_ctx_read_offset (ctx, cu->offset_size == 8, &value)) + if (!read_ctx_read_offset (ctx, cu->head->offset_size == 8, &value)) { cant_read: wr_error (&where, ": can't read value of attribute %s.\n", @@ -1102,7 +1059,7 @@ read_die_chain (dwarf_version_h ver, } relocate = rel_require; - width = cu->offset_size; + width = cu->head->offset_size; break; case DW_FORM_string: @@ -1112,7 +1069,7 @@ read_die_chain (dwarf_version_h ver, case DW_FORM_ref_addr: value_check_cb = check_die_ref_global; - width = cu->offset_size; + width = cu->head->offset_size; if (cu->version == 2) case DW_FORM_addr: @@ -1217,7 +1174,7 @@ read_die_chain (dwarf_version_h ver, if (is_location_attrib (it->name)) { uint64_t expr_start - = cu->offset + read_ctx_get_offset (ctx); + = cu->head->offset + read_ctx_get_offset (ctx); if (!check_location_expression (file, ctx, cu, expr_start, reloc, length, &where)) return -1; @@ -1344,7 +1301,7 @@ static bool read_address_size (struct elf_file *file, struct read_ctx *ctx, uint8_t *address_sizep, - struct where *where) + struct where const *where) { uint8_t address_size; if (!read_ctx_read_ubyte (ctx, &address_size)) @@ -1372,7 +1329,7 @@ read_address_size (struct elf_file *file, return true; } -static bool +bool check_cu_structural (struct elf_file *file, struct read_ctx *ctx, struct cu *const cu, @@ -1383,48 +1340,48 @@ check_cu_structural (struct elf_file *file, struct cu_coverage *cu_coverage) { if (dump_die_offsets) - fprintf (stderr, "%s: CU starts\n", where_fmt (&cu->where, NULL)); + fprintf (stderr, "%s: CU starts\n", where_fmt (&cu->head->where, NULL)); bool retval = true; /* Version. */ uint16_t version; if (!read_ctx_read_2ubyte (ctx, &version)) { - wr_error (&cu->where, ": can't read version.\n"); + wr_error (&cu->head->where, ": can't read version.\n"); return false; } dwarf_version_h ver = get_dwarf_version (version); if (ver == NULL) return false; - if (version == 2 && cu->offset_size == 8) // xxx? + if (version == 2 && cu->head->offset_size == 8) // xxx? /* Keep going. It's a standard violation, but we may still be able to read the unit under consideration and do high-level checks. */ - wr_error (&cu->where, ": invalid 64-bit unit in DWARF 2 format.\n"); + wr_error (&cu->head->where, ": invalid 64-bit unit in DWARF 2 format.\n"); cu->version = version; /* Abbrev offset. */ uint64_t abbrev_offset; - uint64_t ctx_offset = read_ctx_get_offset (ctx) + cu->offset; - if (!read_ctx_read_offset (ctx, cu->offset_size == 8, &abbrev_offset)) + uint64_t ctx_offset = read_ctx_get_offset (ctx) + cu->head->offset; + if (!read_ctx_read_offset (ctx, cu->head->offset_size == 8, &abbrev_offset)) { - wr_error (&cu->where, ": can't read abbrev offset.\n"); + wr_error (&cu->head->where, ": can't read abbrev offset.\n"); return false; } struct relocation *rel - = relocation_next (reloc, ctx_offset, &cu->where, skip_mismatched); + = relocation_next (reloc, ctx_offset, &cu->head->where, skip_mismatched); if (rel != NULL) - relocate_one (file, reloc, rel, cu->offset_size, - &abbrev_offset, &cu->where, sec_abbrev, NULL); + relocate_one (file, reloc, rel, cu->head->offset_size, + &abbrev_offset, &cu->head->where, sec_abbrev, NULL); else if (file->ehdr.e_type == ET_REL) - wr_message (mc_impact_2 | mc_info | mc_reloc, &cu->where, + wr_message (mc_impact_2 | mc_info | mc_reloc, &cu->head->where, PRI_LACK_RELOCATION, "abbrev offset"); /* Address size. */ { uint8_t address_size; - if (!read_address_size (file, ctx, &address_size, &cu->where)) + if (!read_address_size (file, ctx, &address_size, &cu->head->where)) return false; cu->address_size = address_size; } @@ -1437,7 +1394,7 @@ check_cu_structural (struct elf_file *file, if (abbrevs == NULL) { - wr_error (&cu->where, + wr_error (&cu->head->where, ": couldn't find abbrev section with offset %" PRId64 ".\n", abbrev_offset); return false; @@ -1449,7 +1406,7 @@ check_cu_structural (struct elf_file *file, struct ref_record local_die_refs; WIPE (local_die_refs); - cu->cudie_offset = read_ctx_get_offset (ctx) + cu->offset; + cu->cudie_offset = read_ctx_get_offset (ctx) + cu->head->offset; if (read_die_chain (ver, file, ctx, cu, abbrevs, strings, &local_die_refs, strings_coverage, (reloc != NULL && reloc->size > 0) ? reloc : NULL, @@ -1465,194 +1422,6 @@ check_cu_structural (struct elf_file *file, return retval; } -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) -{ - struct read_ctx ctx; - read_ctx_init (&ctx, sec->data, file->other_byte_order); - - struct ref_record die_refs; - WIPE (die_refs); - - struct cu *cu_chain = NULL; - - bool success = true; - - struct coverage strings_coverage_mem, *strings_coverage = NULL; - if (strings != NULL && check_category (mc_strings)) - { - WIPE (strings_coverage_mem); - strings_coverage = &strings_coverage_mem; - } - - struct relocation_data *reloc = sec->rel.size > 0 ? &sec->rel : NULL; - while (!read_ctx_eof (&ctx)) - { - const unsigned char *cu_begin = ctx.ptr; - struct where where = WHERE (sec_info, NULL); - where_reset_1 (&where, read_ctx_get_offset (&ctx)); - - struct cu *cur = xcalloc (1, sizeof (*cur)); - cur->offset = where.addr1; - cur->next = cu_chain; - cur->where = where; - cur->low_pc = (uint64_t)-1; - cu_chain = cur; - - uint32_t size32; - uint64_t size; - - /* Reading CU header is a bit tricky, because we don't know if - we have run into (superfluous but allowed) zero padding. */ - if (!read_ctx_need_data (&ctx, 4) - && check_zero_padding (&ctx, mc_info | mc_header, &where)) - break; - - /* CU length. */ - if (!read_ctx_read_4ubyte (&ctx, &size32)) - { - wr_error (&where, ": can't read CU length.\n"); - success = false; - break; - } - if (size32 == 0 && check_zero_padding (&ctx, mc_info | mc_header, &where)) - break; - - if (!read_size_extra (&ctx, size32, &size, &cur->offset_size, &where)) - { - success = false; - break; - } - - if (!read_ctx_need_data (&ctx, size)) - { - wr_error (&where, - ": section doesn't have enough data" - " to read CU of size %" PRId64 ".\n", size); - ctx.ptr = ctx.end; - success = false; - break; - } - - const unsigned char *cu_end = ctx.ptr + size; - cur->length = cu_end - cu_begin; // Length including the length field. - - /* version + debug_abbrev_offset + address_size */ - uint64_t cu_header_size = 2 + cur->offset_size + 1; - if (size < cu_header_size) - { - wr_error (&where, ": claimed length of %" PRIx64 - " doesn't even cover CU header.\n", size); - success = false; - break; - } - else - { - /* Make CU context begin just before the CU length, so that DIE - offsets are computed correctly. */ - struct read_ctx cu_ctx; - if (!read_ctx_init_sub (&cu_ctx, &ctx, cu_begin, cu_end)) - { - not_enough: - wr_error (&where, PRI_NOT_ENOUGH, "next CU"); - success = false; - break; - } - cu_ctx.ptr = ctx.ptr; - - if (!check_cu_structural (file, &cu_ctx, cur, abbrev_chain, - strings, strings_coverage, reloc, - cu_coverage)) - { - success = false; - break; - } - if (cu_ctx.ptr != cu_ctx.end - && !check_zero_padding (&cu_ctx, mc_info, &where)) - wr_message_padding_n0 (mc_info, &where, - read_ctx_get_offset (&ctx), - read_ctx_get_offset (&ctx) + size); - } - - if (!read_ctx_skip (&ctx, size)) - goto not_enough; - } - - if (success) - { - if (ctx.ptr != ctx.end) - /* Did we read up everything? */ - wr_message (mc_die_other | mc_impact_4, - &WHERE (sec_info, NULL), - ": CU lengths don't exactly match Elf_Data contents."); - else - /* Did we consume all the relocations? */ - relocation_skip_rest (&sec->rel, sec->id); - - /* If we managed to read up everything, now do abbrev usage - analysis. */ - for (struct abbrev_table *abbrevs = abbrev_chain; - abbrevs != NULL; abbrevs = abbrevs->next) - { - if (!abbrevs->used) - { - struct where wh = WHERE (sec_abbrev, NULL); - where_reset_1 (&wh, abbrevs->offset); - wr_message (mc_impact_4 | mc_acc_bloat | mc_abbrevs, &wh, - ": abbreviation table is never used.\n"); - } - else if (!abbrevs->skip_check) - for (size_t i = 0; i < abbrevs->size; ++i) - if (!abbrevs->abbr[i].used) - wr_message (mc_impact_3 | mc_acc_bloat | mc_abbrevs, - &abbrevs->abbr[i].where, - ": abbreviation is never used.\n"); - } - } - - - /* We used to check that all CUs have the same address size. Now - that we validate address_size of each CU against the ELF header, - that's not necessary anymore. */ - - bool references_sound = check_global_die_references (cu_chain); - ref_record_free (&die_refs); - - if (strings_coverage != NULL) - { - if (success) - coverage_find_holes (strings_coverage, 0, strings->d_size, found_hole, - &((struct hole_info) - {sec_str, mc_strings, strings->d_buf, 0})); - coverage_free (strings_coverage); - } - - if (!success || !references_sound) - { - cu_free (cu_chain); - cu_chain = NULL; - } - - /* Reverse the chain, so that it's organized "naturally". Has - significant impact on performance when handling loc_ref and - range_ref fields in loc/range validation. */ - struct cu *last = NULL; - for (struct cu *it = cu_chain; it != NULL; ) - { - struct cu *next = it->next; - it->next = last; - last = it; - it = next; - } - cu_chain = last; - - return cu_chain; -} - static struct coverage_map * coverage_map_alloc_XA (struct elf_file *elf, bool allow_overlap) { @@ -2047,7 +1816,7 @@ check_pub_structural (struct elf_file *file, wr_error (&where, ": unresolved reference to " PRI_CU ".\n", cu_offset); if (cu != NULL) { - where.ref = &cu->where; + where.ref = &cu->head->where; bool *has = sec->id == sec_pubnames ? &cu->has_pubnames : &cu->has_pubtypes; if (*has) @@ -2065,11 +1834,12 @@ check_pub_structural (struct elf_file *file, retval = false; goto next; } - if (cu != NULL && cu_len != cu->length) + if (cu != NULL && cu_len != cu->head->total_size) { wr_error (&where, ": the table covers length %" PRId64 - " but CU has length %" PRId64 ".\n", cu_len, cu->length); + " but CU has length %" PRId64 ".\n", + cu_len, cu->head->total_size); retval = false; goto next; } @@ -2091,7 +1861,8 @@ check_pub_structural (struct elf_file *file, break; if (cu != NULL - && !addr_record_has_addr (&cu->die_addrs, offset + cu->offset)) + && !addr_record_has_addr (&cu->die_addrs, + offset + cu->head->offset)) { wr_error (&where, ": unresolved reference to " PRI_DIE ".\n", offset); diff --git a/src/dwarflint/low.h b/src/dwarflint/low.h index 35f7a3a99..795f34260 100644 --- a/src/dwarflint/low.h +++ b/src/dwarflint/low.h @@ -110,7 +110,7 @@ extern "C" size_t num_supported, struct where *where, ...); extern bool check_zero_padding (struct read_ctx *ctx, enum message_category category, - struct where *wh); + struct where const *wh); struct section_coverage { @@ -141,11 +141,15 @@ extern "C" }; // 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); + struct cu; + extern bool check_cu_structural (struct elf_file *file, + struct read_ctx *ctx, + struct cu *const cu, + struct abbrev_table *abbrev_chain, + Elf_Data *strings, + struct coverage *strings_coverage, + struct relocation_data *reloc, + struct cu_coverage *cu_coverage); extern bool check_loc_or_range_structural (struct elf_file *file, struct sec *sec, struct cu *cu_chain, @@ -217,22 +221,32 @@ extern "C" bool used; }; - struct cu + struct cu_head { - struct cu *next; uint64_t offset; + Dwarf_Off size; // Size of this CU. + Dwarf_Off head_size; // Size from begin to 1st byte of CU. + Dwarf_Off total_size; // size + head_size + + int offset_size; // Offset size in this CU. + struct where where; // Where was this section defined. + Dwarf_Off abbrev_offset; // Abbreviation section that this CU uses. + }; + + struct cu + { + struct cu *next; // For compatibility with C level. + // xxx will probably go away eventually + struct cu_head const *head; uint64_t cudie_offset; - uint64_t length; uint64_t low_pc; // DW_AT_low_pc value of CU DIE, -1 if not present. struct addr_record die_addrs; // Addresses where DIEs begin in this CU. struct ref_record die_refs; // DIE references into other CUs from this CU. struct ref_record loc_refs; // references into .debug_loc from this CU. struct ref_record range_refs; // references into .debug_ranges from this CU. - struct ref_record line_refs; // references into .debug_line from this CU. - struct where where; // Where was this section defined. + struct ref_record line_refs; // references into .debug_line from this CU. int address_size; // Address size in bytes on the target machine. - int offset_size; // Offset size in this CU. - int version; // CU version + int version; // CU version bool has_arange; // Whether we saw arange section pointing to this CU. bool has_pubnames; // Likewise for pubnames. bool has_pubtypes; // Likewise for pubtypes. diff --git a/src/dwarflint/messages.cc b/src/dwarflint/messages.cc index c6d647e80..1dba92e79 100644 --- a/src/dwarflint/messages.cc +++ b/src/dwarflint/messages.cc @@ -280,7 +280,7 @@ wr_message (where const &wh, message_category category) void wr_format_padding_message (unsigned long category, - struct where *wh, + struct where const *wh, uint64_t start, uint64_t end, char const *kind) { char msg[128]; @@ -289,7 +289,8 @@ wr_format_padding_message (unsigned long category, } void -wr_format_leb128_message (struct where *where, const char *what, +wr_format_leb128_message (struct where const *where, + const char *what, const char *purpose, const unsigned char *begin, const unsigned char *end) { @@ -305,7 +306,7 @@ wr_format_leb128_message (struct where *where, const char *what, void wr_message_padding_0 (unsigned long category, - struct where *wh, + struct where const *wh, uint64_t start, uint64_t end) { wr_format_padding_message (category | mc_acc_bloat | mc_impact_1, @@ -315,7 +316,7 @@ wr_message_padding_0 (unsigned long category, void wr_message_padding_n0 (unsigned long category, - struct where *wh, + struct where const *wh, uint64_t start, uint64_t end) { wr_format_padding_message (category | mc_acc_bloat | mc_impact_1, diff --git a/src/dwarflint/messages.h b/src/dwarflint/messages.h index 5f88722d4..19af4281a 100644 --- a/src/dwarflint/messages.h +++ b/src/dwarflint/messages.h @@ -103,21 +103,22 @@ extern "C" __attribute__ ((format (printf, 3, 4))); extern void wr_format_padding_message (unsigned long category, - struct where *wh, + struct where const *wh, uint64_t start, uint64_t end, char const *kind); - extern void wr_format_leb128_message (struct where *where, const char *what, + extern void wr_format_leb128_message (struct where const *where, + const char *what, const char *purpose, const unsigned char *begin, const unsigned char *end); extern void wr_message_padding_0 (unsigned long category, - struct where *wh, + struct where const *wh, uint64_t start, uint64_t end); extern void wr_message_padding_n0 (unsigned long category, - struct where *wh, + struct where const *wh, uint64_t start, uint64_t end); extern bool message_accept (struct message_criteria const *cri, diff --git a/src/dwarflint/reloc.cc b/src/dwarflint/reloc.cc index 8bab5ac98..c3ea6e2cc 100644 --- a/src/dwarflint/reloc.cc +++ b/src/dwarflint/reloc.cc @@ -6,7 +6,7 @@ #include static struct where -where_from_reloc (struct relocation_data *reloc, struct where *ref) +where_from_reloc (struct relocation_data *reloc, struct where const *ref) { struct where where = WHERE (reloc->type == SHT_REL ? sec_rel : sec_rela, NULL); @@ -17,7 +17,7 @@ where_from_reloc (struct relocation_data *reloc, struct where *ref) relocation * relocation_next (relocation_data *reloc, uint64_t offset, - struct where *where, enum skip_type st) + struct where const *where, enum skip_type st) { if (reloc == NULL || reloc->rel == NULL) return NULL; @@ -61,7 +61,7 @@ relocation_next (relocation_data *reloc, uint64_t offset, matching that offset is immediately yielded. */ void relocation_skip (struct relocation_data *reloc, uint64_t offset, - struct where *where, enum skip_type st) + struct where const *where, enum skip_type st) { if (reloc != NULL && reloc->rel != NULL) relocation_next (reloc, offset - 1, where, st); @@ -88,7 +88,8 @@ void relocate_one (struct elf_file *file, struct relocation_data *reloc, struct relocation *rel, - unsigned width, uint64_t *value, struct where *where, + unsigned width, uint64_t *value, + struct where const *where, enum section_id offset_into, GElf_Sym **symptr) { if (rel->invalid) diff --git a/src/dwarflint/reloc.h b/src/dwarflint/reloc.h index 0804155eb..d3a90748a 100644 --- a/src/dwarflint/reloc.h +++ b/src/dwarflint/reloc.h @@ -72,11 +72,12 @@ extern "C" }; struct relocation *relocation_next (struct relocation_data *reloc, - uint64_t offset, struct where *where, + uint64_t offset, + struct where const *where, enum skip_type st); void relocation_skip (struct relocation_data *reloc, uint64_t offset, - struct where *where, enum skip_type st); + struct where const *where, enum skip_type st); void relocation_skip_rest (struct relocation_data *reloc, enum section_id id); @@ -84,7 +85,8 @@ extern "C" void relocate_one (struct elf_file *file, struct relocation_data *reloc, struct relocation *rel, - unsigned width, uint64_t *value, struct where *where, + unsigned width, uint64_t *value, + struct where const *where, enum section_id offset_into, GElf_Sym **symptr); #define PRI_LACK_RELOCATION ": %s seems to lack a relocation.\n" diff --git a/src/dwarflint/where.c b/src/dwarflint/where.c index 9bbd6de03..6e3d5d89c 100644 --- a/src/dwarflint/where.c +++ b/src/dwarflint/where.c @@ -115,7 +115,7 @@ where_fmt (const struct where *wh, char *ptr) ptr = stpcpy (buf, inf->name); if (is_reloc) { - struct where *ref = wh->ref; + struct where const *ref = wh->ref; assert (ref != NULL); if (ref->section == sec_locexpr) { @@ -155,7 +155,7 @@ void where_fmt_chain (const struct where *wh, const char *severity) { if (wh != NULL && show_refs) - for (struct where *it = wh->next; it != NULL; it = it->next) + for (struct where const *it = wh->next; it != NULL; it = it->next) printf ("%s: %s: caused by this reference.\n", severity, where_fmt (it, NULL)); } diff --git a/src/dwarflint/where.h b/src/dwarflint/where.h index 0522c72b4..5155cae7d 100644 --- a/src/dwarflint/where.h +++ b/src/dwarflint/where.h @@ -62,8 +62,9 @@ extern "C" uint64_t addr1; // E.g. a CU offset. uint64_t addr2; // E.g. a DIE address. uint64_t addr3; // E.g. an attribute. - struct where *ref; // Related reference, e.g. an abbrev related to given DIE. - struct where *next; // For forming "caused-by" chains. + struct where const *ref; // Related reference, e.g. an abbrev + // related to given DIE. + struct where const *next; // For forming "caused-by" chains. }; # define WHERE(SECTION, NEXT) \