#include "low.h"
#include "checks-low.hh"
#include "pri.hh"
+#include "config.h"
namespace
{
return message_accept (&warning_criteria, cat);
}
+ bool
+ check_die_references (struct cu *cu,
+ struct ref_record *die_refs)
+ {
+ bool retval = true;
+ for (size_t i = 0; i < die_refs->size; ++i)
+ {
+ struct ref *ref = die_refs->refs + i;
+ if (!addr_record_has_addr (&cu->die_addrs, ref->addr))
+ {
+ wr_error (&ref->who,
+ ": unresolved reference to " PRI_DIE ".\n", ref->addr);
+ retval = false;
+ }
+ }
+ return retval;
+ }
+
bool
check_global_die_references (struct cu *cu_chain)
{
std::vector <cu_head>
read_info_headers (struct elf_file *file,
- struct sec *sec)
+ struct sec *sec,
+ struct relocation_data *reloc)
{
struct read_ctx ctx;
read_ctx_init (&ctx, sec->data, file->other_byte_order);
std::vector <cu_head> ret;
while (!read_ctx_eof (&ctx))
{
+ std::cout << "head " << read_ctx_get_offset (&ctx)
+ << ' ' << pri::hex (read_ctx_get_offset (&ctx)) << std::endl;
const unsigned char *cu_begin = ctx.ptr;
struct where where = WHERE (sec_info, NULL);
where_reset_1 (&where, read_ctx_get_offset (&ctx));
&& check_zero_padding (&ctx, cat (mc_info, mc_header), &where))
break;
- /* CU length. */
+ /* CU length. In DWARF 2, (uint32_t)-1 is simply a CU of that
+ length. In DWARF 3+ that's an escape for 64bit length.
+ Unfortunately to read CU version, we have to get through
+ this field. So we just assume that (uint32_t)-1 is an
+ escape in all cases. */
uint32_t size32;
if (!read_ctx_read_4ubyte (&ctx, &size32))
{
&& check_zero_padding (&ctx, cat (mc_info, mc_header), &where))
break;
- if (!read_size_extra (&ctx, size32, &head.size,
+ Dwarf_Off cu_size;
+ if (!read_size_extra (&ctx, size32, &cu_size,
&head.offset_size, &where))
throw check_base::failed ();
- if (!read_ctx_need_data (&ctx, head.size))
+ if (!read_ctx_need_data (&ctx, cu_size))
{
wr_error (where)
<< "section doesn't have enough data to read CU of size "
- << head.size << '.' << std::endl;
+ << cu_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)
+ /* CU size captures the size from the end of the length field
+ to the end of the CU. */
+ const unsigned char *cu_end = ctx.ptr + cu_size;
+
+ /* Version. */
+ uint16_t version;
+ if (!read_ctx_read_2ubyte (&ctx, &version))
{
- wr_error (where)
- << "claimed length of " << head.size
- << " doesn't even cover CU head." << std::endl;
+ wr_error (head.where) << "can't read version." << std::endl;
+ throw check_base::failed ();
+ }
+ if (get_dwarf_version (version) == NULL)
+ {
+ wr_error (head.where) << "unsupported CU version "
+ << version << '.' << std::endl;
throw check_base::failed ();
}
+ if (version == 2 && 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 (head.where) << "invalid 64-bit unit in DWARF 2 format.\n";
+ head.version = version;
+
+ /* Abbrev offset. */
+ uint64_t ctx_offset = read_ctx_get_offset (&ctx) + head.offset;
+ if (!read_ctx_read_offset (&ctx, head.offset_size == 8,
+ &head.abbrev_offset))
+ {
+ wr_error (head.where) << "can't read abbrev offset." << std::endl;
+ throw check_base::failed ();
+ }
+
+ struct relocation *rel
+ = relocation_next (reloc, ctx_offset, &head.where, skip_ok);
+ if (rel != NULL)
+ {
+ relocate_one (file, reloc, rel, head.offset_size,
+ &head.abbrev_offset, &head.where, sec_abbrev, NULL);
+ rel->invalid = true; // mark as invalid so it's skipped
+ // next time we pass by this
+ }
+ else if (file->ehdr.e_type == ET_REL)
+ wr_message (head.where, cat (mc_impact_2, mc_info, mc_reloc))
+ << pri::lacks_relocation ("abbrev offset") << std::endl;
+
+ /* Address size. */
+ if (!read_address_size (file, &ctx, &head.address_size, &head.where))
+ 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.
+ head.head_size = ctx.ptr - cu_begin; // Length of the headers itself.
+ head.total_size = cu_end - cu_begin; // Length including headers field.
+ head.size = head.total_size - head.head_size;
if (!read_ctx_skip (&ctx, head.size))
{
return ret;
}
+ 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)
+ {
+ if (dump_die_offsets)
+ fprintf (stderr, "%s: CU starts\n", where_fmt (&cu->head->where, NULL));
+ bool retval = true;
+
+ dwarf_version_h ver = get_dwarf_version (cu->head->version);
+ assert (ver != NULL);
+
+ /* Look up Abbrev table for this CU. */
+ struct abbrev_table *abbrevs = abbrev_chain;
+ for (; abbrevs != NULL; abbrevs = abbrevs->next)
+ if (abbrevs->offset == cu->head->abbrev_offset)
+ break;
+
+ if (abbrevs == NULL)
+ {
+ wr_error (&cu->head->where,
+ ": couldn't find abbrev section with offset %" PRId64 ".\n",
+ cu->head->abbrev_offset);
+ return false;
+ }
+
+ abbrevs->used = true;
+
+ /* Read DIEs. */
+ struct ref_record local_die_refs;
+ WIPE (local_die_refs);
+
+ 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,
+ cu_coverage) < 0)
+ {
+ abbrevs->skip_check = true;
+ retval = false;
+ }
+ else if (!check_die_references (cu, &local_die_refs))
+ retval = false;
+
+ ref_record_free (&local_die_refs);
+ return retval;
+ }
+
struct cu *
check_info_structural (struct elf_file *file,
struct sec *sec,
}
struct relocation_data *reloc = sec->rel.size > 0 ? &sec->rel : NULL;
+ // xxx temporary static xxx
+ static std::vector <cu_head> cu_headers = read_info_headers (file, sec, reloc);
+ if (reloc != NULL)
+ relocation_reset (reloc);
- 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;
+ std::cout << "read " << pri::hex (head.offset) << std::endl;
where const &where = head.where;
struct cu *cur = (cu *)xcalloc (1, sizeof (*cur));
cur->head = &head;
type-casted int64_t. WHAT and WHERE describe error message and
context for LEB128 loading. */
static bool
-read_ctx_read_form (struct read_ctx *ctx, struct cu *cu, uint8_t form,
+read_ctx_read_form (struct read_ctx *ctx, int address_size, uint8_t form,
uint64_t *valuep, struct where *where, const char *what)
{
switch (form)
{
case DW_FORM_addr:
- return read_ctx_read_offset (ctx, cu->address_size == 8, valuep);
+ return read_ctx_read_offset (ctx, address_size == 8, valuep);
case DW_FORM_udata:
return checked_read_uleb128 (ctx, valuep, where, what);
case DW_FORM_sdata:
return NULL;
}
-
-static bool
-check_die_references (struct cu *cu,
- struct ref_record *die_refs)
-{
- bool retval = true;
- for (size_t i = 0; i < die_refs->size; ++i)
- {
- struct ref *ref = die_refs->refs + i;
- if (!addr_record_has_addr (&cu->die_addrs, ref->addr))
- {
- wr_error (&ref->who,
- ": unresolved reference to " PRI_DIE ".\n", ref->addr);
- retval = false;
- }
- }
- return retval;
-}
-
bool
read_size_extra (struct read_ctx *ctx, uint32_t size32, uint64_t *sizep,
int *offset_sizep, struct where *wh)
terminating zero die.
+1 in case some dies were actually loaded
*/
-static int
+int
read_die_chain (dwarf_version_h ver,
struct elf_file *file,
struct read_ctx *ctx,
/* Callback for rangeptr values. */
void check_rangeptr (uint64_t value, struct where *who)
{
- if ((value % cu->address_size) != 0)
+ if ((value % cu->head->address_size) != 0)
wr_message (mc_ranges | mc_impact_2, who,
": rangeptr value %#" PRIx64
" not aligned to CU address size.\n", value);
value_check_cb = check_die_ref_global;
width = cu->head->offset_size;
- if (cu->version == 2)
+ if (cu->head->version == 2)
case DW_FORM_addr:
- width = cu->address_size;
+ width = cu->head->address_size;
if (!read_ctx_read_offset (ctx, width == 8, &value))
goto cant_read;
return got_die ? 1 : 0;
}
-static bool
-read_address_size (struct elf_file *file,
+bool
+read_address_size (bool elf_64,
struct read_ctx *ctx,
- uint8_t *address_sizep,
+ int *address_sizep,
struct where const *where)
{
uint8_t address_size;
wr_error (where,
": invalid address size: %d (only 4 or 8 allowed).\n",
address_size);
- address_size = file->addr_64 ? 8 : 4;
+ address_size = elf_64 ? 8 : 4;
}
- else if ((address_size == 8) != file->addr_64)
+ else if ((address_size == 8) != elf_64)
/* Keep going, we may still be able to parse it. */
wr_error (where,
": CU reports address size of %d in %d-bit ELF.\n",
- address_size, file->addr_64 ? 64 : 32);
+ address_size, elf_64 ? 64 : 32);
*address_sizep = address_size;
return true;
}
-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)
-{
- if (dump_die_offsets)
- 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->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->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->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->head->offset;
- if (!read_ctx_read_offset (ctx, cu->head->offset_size == 8, &abbrev_offset))
- {
- wr_error (&cu->head->where, ": can't read abbrev offset.\n");
- return false;
- }
-
- struct relocation *rel
- = relocation_next (reloc, ctx_offset, &cu->head->where, skip_mismatched);
- if (rel != 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->head->where,
- PRI_LACK_RELOCATION, "abbrev offset");
-
- /* Address size. */
- {
- uint8_t address_size;
- if (!read_address_size (file, ctx, &address_size, &cu->head->where))
- return false;
- cu->address_size = address_size;
- }
-
- /* Look up Abbrev table for this CU. */
- struct abbrev_table *abbrevs = abbrev_chain;
- for (; abbrevs != NULL; abbrevs = abbrevs->next)
- if (abbrevs->offset == abbrev_offset)
- break;
-
- if (abbrevs == NULL)
- {
- wr_error (&cu->head->where,
- ": couldn't find abbrev section with offset %" PRId64 ".\n",
- abbrev_offset);
- return false;
- }
-
- abbrevs->used = true;
-
- /* Read DIEs. */
- struct ref_record local_die_refs;
- WIPE (local_die_refs);
-
- 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,
- cu_coverage) < 0)
- {
- abbrevs->skip_check = true;
- retval = false;
- }
- else if (!check_die_references (cu, &local_die_refs))
- retval = false;
-
- ref_record_free (&local_die_refs);
- return retval;
-}
-
static struct coverage_map *
coverage_map_alloc_XA (struct elf_file *elf, bool allow_overlap)
{
}
/* Address size. */
- uint8_t address_size;
+ int address_size;
if (!read_address_size (file, &sub_ctx, &address_size, &where))
{
retval = false;
{ \
uint64_t _off = read_ctx_get_offset (&ctx) + init_off; \
uint64_t *_ptr = (PTR); \
- if (!read_ctx_read_form (&ctx, cu, (OP), \
+ if (!read_ctx_read_form (&ctx, cu->head->address_size, (OP), \
_ptr, &where, STR " operand")) \
{ \
wr_error (&where, ": opcode \"%s\"" \
if ((_rel = relocation_next (reloc, _off, \
&where, skip_mismatched))) \
relocate_one (file, reloc, _rel, \
- cu->address_size, _ptr, &where, \
+ cu->head->address_size, _ptr, &where, \
reloc_target_loc (opcode), NULL); \
} \
} while (0)
case DW_OP_const8u:
case DW_OP_const8s:
- if (cu->address_size == 4)
+ if (cu->head->address_size == 4)
wr_error (&where, ": %s on 32-bit machine.\n",
dwarf_locexpr_opcode_string (opcode));
break;
default:
- if (cu->address_size == 4
+ if (cu->head->address_size == 4
&& (opcode == DW_OP_constu
|| opcode == DW_OP_consts
|| opcode == DW_OP_deref_size
retval = false;
}
- uint64_t escape = cu->address_size == 8
+ uint64_t escape = cu->head->address_size == 8
? (uint64_t)-1 : (uint64_t)(uint32_t)-1;
bool overlap = false;
GElf_Sym begin_symbol_mem, *begin_symbol = &begin_symbol_mem;
bool begin_relocated = false;
if (!overlap
- && coverage_is_overlap (coverage, begin_off, cu->address_size))
+ && coverage_is_overlap (coverage, begin_off, cu->head->address_size))
HAVE_OVERLAP;
- if (!read_ctx_read_offset (&ctx, cu->address_size == 8, &begin_addr))
+ if (!read_ctx_read_offset (&ctx, cu->head->address_size == 8, &begin_addr))
{
wr_error (&where, ": can't read address range beginning.\n");
return false;
&where, skip_mismatched)))
{
begin_relocated = true;
- relocate_one (file, &sec->rel, rel, cu->address_size,
+ relocate_one (file, &sec->rel, rel, cu->head->address_size,
&begin_addr, &where, rel_value, &begin_symbol);
}
GElf_Sym end_symbol_mem, *end_symbol = &end_symbol_mem;
bool end_relocated = false;
if (!overlap
- && coverage_is_overlap (coverage, end_off, cu->address_size))
+ && coverage_is_overlap (coverage, end_off, cu->head->address_size))
HAVE_OVERLAP;
- if (!read_ctx_read_offset (&ctx, cu->address_size == 8, &end_addr))
+ if (!read_ctx_read_offset (&ctx, cu->head->address_size == 8, &end_addr))
{
wr_error (&where, ": can't read address range ending.\n");
return false;
&where, skip_mismatched)))
{
end_relocated = true;
- relocate_one (file, &sec->rel, rel, cu->address_size,
+ relocate_one (file, &sec->rel, rel, cu->head->address_size,
&end_addr, &where, rel_value, &end_symbol);
if (begin_addr != escape)
{
coverage_find_holes (&coverage, 0, ctx.data->d_size, found_hole,
&((struct hole_info)
{sec->id, cat, ctx.data->d_buf,
- cu_chain->address_size}));
+ cu_chain->head->address_size}));
if (coverage_map)
coverage_map_find_holes (coverage_map, &coverage_map_found_hole,