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 \
--- /dev/null
+/* 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
+ <http://www.openinventionnetwork.com>. */
+
+// xxx drop as soon as not necessary
+#define __STDC_FORMAT_MACROS
+#define PRI_DIE "DIE 0x%" PRIx64
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cassert>
+
+#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 <cu_head>
+ 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 <cu_head> 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_head> cu_headers = read_info_headers (file, sec);
+ struct read_ctx ctx;
+ read_ctx_init (&ctx, sec->data, file->other_byte_order);
+ for (std::vector <cu_head>::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<cu>::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_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<cu>::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))
class check
: public check_base
{
-public:
+private:
+ template <class X>
+ friend X *dwarflint::check ();
static void const *key ()
{
return reinterpret_cast <void const *> (&key);
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
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;
}
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)
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;
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;
#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)
}
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;
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. */
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. */
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));
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));
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",
}
relocate = rel_require;
- width = cu->offset_size;
+ width = cu->head->offset_size;
break;
case DW_FORM_string:
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:
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;
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))
return true;
}
-static bool
+bool
check_cu_structural (struct elf_file *file,
struct read_ctx *ctx,
struct cu *const cu,
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;
}
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;
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,
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)
{
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)
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;
}
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);
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
{
};
// 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,
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.
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];
}
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)
{
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,
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,
__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,
#include <cassert>
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);
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;
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);
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)
};
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);
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"
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)
{
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));
}
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) \