From: Petr Machata Date: Tue, 21 Sep 2010 19:02:16 +0000 (+0200) Subject: dwarflint: Move check_aranges_structural to check_debug_aranges X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=511faebc9d6d0c050f43d2d9e8f44edaf1084587;p=thirdparty%2Felfutils.git dwarflint: Move check_aranges_structural to check_debug_aranges --- diff --git a/dwarflint/check_debug_aranges.cc b/dwarflint/check_debug_aranges.cc index 9918030f8..3eacb462e 100644 --- a/dwarflint/check_debug_aranges.cc +++ b/dwarflint/check_debug_aranges.cc @@ -1,5 +1,5 @@ /* Low-level checking of .debug_aranges. - Copyright (C) 2009 Red Hat, Inc. + Copyright (C) 2009, 2010 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -23,10 +23,15 @@ Network licensing program, please visit www.openinventionnetwork.com . */ +// xxx drop as soon as not necessary +#define __STDC_FORMAT_MACROS + #ifdef HAVE_CONFIG_H # include #endif +#include + #include "low.h" #include "sections.hh" #include "check_debug_aranges.hh" @@ -56,6 +61,347 @@ check_debug_aranges::descriptor () return &cd; } +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->head->offset == offset) + return it; + return NULL; +} + +#define PRI_CU "CU 0x%" PRIx64 + +namespace +{ + struct hole_user + { + elf_file *elf; + section_id id; + char const *what; + + hole_user (elf_file *a_elf, section_id a_id, + char const *a_what) + : elf (a_elf) + , id (a_id) + , what (a_what) + {} + }; +} + +static bool +hole (uint64_t start, uint64_t length, void *user) +{ + /* We need to check alignment vs. the covered section. Find + where the hole lies. */ + ::hole_user &info = *static_cast< ::hole_user *> (user); + struct elf_file *elf = info.elf; + struct sec *sec = NULL; + for (size_t i = 1; i < elf->size; ++i) + { + struct sec *it = elf->sec + i; + GElf_Shdr *shdr = &it->shdr; + Elf64_Addr s_end = shdr->sh_addr + shdr->sh_size; + if (start >= shdr->sh_addr && start + length < s_end) + { + sec = it; + /* Simply assume the first section that the hole + intersects. */ + break; + } + } + + if (sec == NULL + || !necessary_alignment (start, length, sec->shdr.sh_addralign)) + { + char buf[128]; + where where = WHERE (info.id, NULL); + wr_message (mc_aranges | mc_impact_3, &where, + ": addresses %s are covered with CUs, but not with %s.\n", + range_fmt (buf, sizeof buf, start, start + length), + info.what); + } + + if (sec == NULL) + wr_error (NULL, "Couldn't find the section containing the above hole.\n"); + + return true; +} + +static void +compare_coverage (struct elf_file *file, + struct coverage *coverage, struct coverage *other, + enum section_id id, char const *what) +{ + struct coverage *cov = coverage_clone (coverage); + coverage_remove_all (cov, other); + + hole_user info (file, id, what); + coverage_find_ranges (cov, hole, &info); + coverage_free (cov); + free (cov); +} + +inline static void +aranges_coverage_add (struct coverage *aranges_coverage, + uint64_t begin, uint64_t length, + struct where *where) +{ + if (coverage_is_overlap (aranges_coverage, begin, length)) + { + char buf[128]; + /* Not a show stopper, this shouldn't derail high-level. */ + wr_message (mc_aranges | mc_impact_2 | mc_error, where, + ": the range %s overlaps with another one.\n", + range_fmt (buf, sizeof buf, begin, begin + length)); + } + + coverage_add (aranges_coverage, begin, length); +} + +/* 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 +check_aranges_structural (struct elf_file *file, + struct sec *sec, + struct cu *cu_chain, + struct coverage *coverage) +{ + struct read_ctx ctx; + read_ctx_init (&ctx, sec->data, file->other_byte_order); + + bool retval = true; + + struct coverage *aranges_coverage + = coverage != NULL + ? (struct coverage *)calloc (1, sizeof (struct coverage)) + : NULL; + + while (!read_ctx_eof (&ctx)) + { + struct where where = WHERE (sec_aranges, NULL); + where_reset_1 (&where, read_ctx_get_offset (&ctx)); + const unsigned char *atab_begin = ctx.ptr; + + /* Size. */ + uint32_t size32; + uint64_t size; + int offset_size; + if (!read_ctx_read_4ubyte (&ctx, &size32)) + { + wr_error (&where, ": can't read table length.\n"); + return false; + } + if (!read_size_extra (&ctx, size32, &size, &offset_size, &where)) + return false; + + struct read_ctx sub_ctx; + const unsigned char *atab_end = ctx.ptr + size; + if (false) + { + next: + if (!read_ctx_skip (&ctx, size)) + /* A "can't happen" error. */ + goto not_enough; + continue; + } + if (!read_ctx_init_sub (&sub_ctx, &ctx, atab_begin, atab_end)) + { + not_enough: + wr_error (&where, PRI_NOT_ENOUGH, "next table"); + return false; + } + + sub_ctx.ptr = ctx.ptr; + + /* Version. */ + uint16_t version; + if (!read_ctx_read_2ubyte (&sub_ctx, &version)) + { + wr_error (&where, ": can't read version.\n"); + retval = false; + goto next; + } + if (!supported_version (version, 1, &where, 2)) + { + retval = false; + goto next; + } + + /* CU offset. */ + uint64_t cu_offset; + uint64_t ctx_offset = sub_ctx.ptr - ctx.begin; + if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &cu_offset)) + { + wr_error (&where, ": can't read debug info offset.\n"); + retval = false; + goto next; + } + + struct relocation *rel; + if ((rel = relocation_next (&sec->rel, ctx_offset, + &where, skip_mismatched))) + relocate_one (file, &sec->rel, rel, offset_size, + &cu_offset, &where, sec_info, NULL); + else if (file->ehdr.e_type == ET_REL) + wr_message (mc_impact_2 | mc_aranges | mc_reloc | mc_header, &where, + PRI_LACK_RELOCATION, "debug info offset"); + + struct cu *cu = NULL; + if (cu_chain != NULL && (cu = cu_find_cu (cu_chain, cu_offset)) == NULL) + wr_error (&where, ": unresolved reference to " PRI_CU ".\n", cu_offset); + + struct where where_cudie; + if (cu != NULL) + { + where_cudie = WHERE (sec_info, NULL); + where_reset_1 (&where_cudie, cu->cudie_offset); + where.ref = &where_cudie; + where_cudie.formatting = wf_cudie; + if (cu->has_arange) + wr_message (mc_impact_2 | mc_aranges | mc_header, &where, + ": there has already been arange section for this CU.\n"); + else + cu->has_arange = true; + } + + /* Address size. */ + int address_size; + if (!read_address_size (&sub_ctx, file->addr_64, &address_size, &where)) + { + retval = false; + goto next; + } + + /* Segment size. */ + uint8_t segment_size; + if (!read_ctx_read_ubyte (&sub_ctx, &segment_size)) + { + wr_error (&where, ": can't read unit segment size.\n"); + retval = false; + goto next; + } + if (segment_size != 0) + { + wr_warning (&where, ": dwarflint can't handle segment_size != 0.\n"); + retval = false; + goto next; + } + + + /* 7.20: The first tuple following the header in each set begins + at an offset that is a multiple of the size of a single tuple + (that is, twice the size of an address). The header is + padded, if necessary, to the appropriate boundary. */ + const uint8_t tuple_size = 2 * address_size; + uint64_t off = read_ctx_get_offset (&sub_ctx); + if ((off % tuple_size) != 0) + { + uint64_t noff = ((off / tuple_size) + 1) * tuple_size; + for (uint64_t i = off; i < noff; ++i) + { + uint8_t c; + if (!read_ctx_read_ubyte (&sub_ctx, &c)) + { + wr_error (&where, + ": section ends after the header, " + "but before the first entry.\n"); + retval = false; + goto next; + } + if (c != 0) + wr_message (mc_impact_2 | mc_aranges | mc_header, &where, + ": non-zero byte at 0x%" PRIx64 + " in padding before the first entry.\n", + read_ctx_get_offset (&sub_ctx)); + } + } + assert ((read_ctx_get_offset (&sub_ctx) % tuple_size) == 0); + + while (!read_ctx_eof (&sub_ctx)) + { + /* We would like to report aranges the same way that readelf + does. But readelf uses index of the arange in the array + as returned by dwarf_getaranges, which sorts the aranges + beforehand. We don't want to disturb the memory this + way, the better to catch structural errors accurately. + So report arange offset instead. If this becomes a + problem, we will achieve this by two-pass analysis. */ + where_reset_2 (&where, read_ctx_get_offset (&sub_ctx)); + + /* Record address. */ + uint64_t address; + ctx_offset = sub_ctx.ptr - ctx.begin; + bool address_relocated = false; + if (!read_ctx_read_var (&sub_ctx, address_size, &address)) + { + wr_error (&where, ": can't read address field.\n"); + retval = false; + goto next; + } + + if ((rel = relocation_next (&sec->rel, ctx_offset, + &where, skip_mismatched))) + { + address_relocated = true; + relocate_one (file, &sec->rel, rel, address_size, + &address, &where, rel_address, NULL); + } + else if (file->ehdr.e_type == ET_REL && address != 0) + wr_message (mc_impact_2 | mc_aranges | mc_reloc, &where, + PRI_LACK_RELOCATION, "address field"); + + /* Record length. */ + uint64_t length; + if (!read_ctx_read_var (&sub_ctx, address_size, &length)) + { + wr_error (&where, ": can't read length field.\n"); + retval = false; + goto next; + } + + if (address == 0 && length == 0 && !address_relocated) + break; + + if (length == 0) + /* DWARF 3 spec, 6.1.2 Lookup by Address: Each descriptor + is a pair consisting of the beginning address [...], + followed by the _non-zero_ length of that range. */ + wr_error (&where, ": zero-length address range.\n"); + /* Skip coverage analysis if we have errors. */ + else if (retval && aranges_coverage != NULL) + aranges_coverage_add (aranges_coverage, address, length, &where); + } + + if (sub_ctx.ptr != sub_ctx.end) + { + uint64_t start, end; + struct where wh = WHERE (where.section, NULL); + if (read_check_zero_padding (&sub_ctx, &start, &end)) + wr_message_padding_0 (mc_aranges, &wh, start, end); + else + { + wr_message_padding_n0 (mc_aranges | mc_error, &wh, + start, start + size); + retval = false; + } + } + + goto next; + } + + if (aranges_coverage != NULL) + { + compare_coverage (file, coverage, aranges_coverage, + sec_aranges, "aranges"); + coverage_free (aranges_coverage); + free (aranges_coverage); + } + + return retval; +} + check_debug_aranges::check_debug_aranges (checkstack &stack, dwarflint &lint) : _m_sec_aranges (lint.check (stack, _m_sec_aranges)) , _m_info (lint.toplev_check (stack, _m_info)) diff --git a/dwarflint/low.c b/dwarflint/low.c index d49819a5d..85aafaa89 100644 --- a/dwarflint/low.c +++ b/dwarflint/low.c @@ -48,10 +48,6 @@ #include "readctx.h" #include "tables.h" -#define PRI_CU "CU 0x%" PRIx64 - -static struct cu *cu_find_cu (struct cu *cu_chain, uint64_t offset); - bool address_aligned (uint64_t addr, uint64_t align) { @@ -125,15 +121,6 @@ is_location_attrib (uint64_t name) } } -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->head->offset == offset) - return it; - return NULL; -} - bool supported_version (unsigned version, size_t num_supported, struct where *where, ...) @@ -176,305 +163,6 @@ check_range_relocations (enum message_category cat, file->sec[end_symbol->st_shndx].name); } -static void -compare_coverage (struct elf_file *file, - struct coverage *coverage, struct coverage *other, - enum section_id id, char const *what) -{ - struct coverage *cov = coverage_clone (coverage); - coverage_remove_all (cov, other); - - bool hole (uint64_t start, uint64_t length, void *user) - { - /* We need to check alignment vs. the covered section. Find - where the hole lies. */ - struct elf_file *elf = user; - struct sec *sec = NULL; - for (size_t i = 1; i < elf->size; ++i) - { - struct sec *it = elf->sec + i; - GElf_Shdr *shdr = &it->shdr; - Elf64_Addr s_end = shdr->sh_addr + shdr->sh_size; - if (start >= shdr->sh_addr && start + length < s_end) - { - sec = it; - /* Simply assume the first section that the hole - intersects. */ - break; - } - } - - if (sec == NULL - || !necessary_alignment (start, length, sec->shdr.sh_addralign)) - { - char buf[128]; - wr_message (mc_aranges | mc_impact_3, &WHERE (id, NULL), - ": addresses %s are covered with CUs, but not with %s.\n", - range_fmt (buf, sizeof buf, start, start + length), what); - } - - if (sec == NULL) - wr_error (NULL, "Couldn't find the section containing the above hole.\n"); - - return true; - } - - coverage_find_ranges (cov, &hole, file); - coverage_free (cov); - free (cov); -} - -/* 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. */ -bool -check_aranges_structural (struct elf_file *file, - struct sec *sec, - struct cu *cu_chain, - struct coverage *coverage) -{ - struct read_ctx ctx; - read_ctx_init (&ctx, sec->data, file->other_byte_order); - - bool retval = true; - - struct coverage *aranges_coverage - = coverage != NULL ? calloc (1, sizeof (struct coverage)) : NULL; - - while (!read_ctx_eof (&ctx)) - { - struct where where = WHERE (sec_aranges, NULL); - where_reset_1 (&where, read_ctx_get_offset (&ctx)); - const unsigned char *atab_begin = ctx.ptr; - - inline void aranges_coverage_add (uint64_t begin, uint64_t length) - { - if (coverage_is_overlap (aranges_coverage, begin, length)) - { - char buf[128]; - /* Not a show stopper, this shouldn't derail high-level. */ - wr_message (mc_aranges | mc_impact_2 | mc_error, &where, - ": the range %s overlaps with another one.\n", - range_fmt (buf, sizeof buf, begin, begin + length)); - } - - coverage_add (aranges_coverage, begin, length); - } - - /* Size. */ - uint32_t size32; - uint64_t size; - int offset_size; - if (!read_ctx_read_4ubyte (&ctx, &size32)) - { - wr_error (&where, ": can't read table length.\n"); - return false; - } - if (!read_size_extra (&ctx, size32, &size, &offset_size, &where)) - return false; - - struct read_ctx sub_ctx; - const unsigned char *atab_end = ctx.ptr + size; - if (!read_ctx_init_sub (&sub_ctx, &ctx, atab_begin, atab_end)) - { - not_enough: - wr_error (&where, PRI_NOT_ENOUGH, "next table"); - return false; - } - - sub_ctx.ptr = ctx.ptr; - - /* Version. */ - uint16_t version; - if (!read_ctx_read_2ubyte (&sub_ctx, &version)) - { - wr_error (&where, ": can't read version.\n"); - retval = false; - goto next; - } - if (!supported_version (version, 1, &where, 2)) - { - retval = false; - goto next; - } - - /* CU offset. */ - uint64_t cu_offset; - uint64_t ctx_offset = sub_ctx.ptr - ctx.begin; - if (!read_ctx_read_offset (&sub_ctx, offset_size == 8, &cu_offset)) - { - wr_error (&where, ": can't read debug info offset.\n"); - retval = false; - goto next; - } - - struct relocation *rel; - if ((rel = relocation_next (&sec->rel, ctx_offset, - &where, skip_mismatched))) - relocate_one (file, &sec->rel, rel, offset_size, - &cu_offset, &where, sec_info, NULL); - else if (file->ehdr.e_type == ET_REL) - wr_message (mc_impact_2 | mc_aranges | mc_reloc | mc_header, &where, - PRI_LACK_RELOCATION, "debug info offset"); - - struct cu *cu = NULL; - if (cu_chain != NULL && (cu = cu_find_cu (cu_chain, cu_offset)) == NULL) - wr_error (&where, ": unresolved reference to " PRI_CU ".\n", cu_offset); - - struct where where_cudie; - if (cu != NULL) - { - where_cudie = WHERE (sec_info, NULL); - where_reset_1 (&where_cudie, cu->cudie_offset); - where.ref = &where_cudie; - where_cudie.formatting = wf_cudie; - if (cu->has_arange) - wr_message (mc_impact_2 | mc_aranges | mc_header, &where, - ": there has already been arange section for this CU.\n"); - else - cu->has_arange = true; - } - - /* Address size. */ - int address_size; - if (!read_address_size (&sub_ctx, file->addr_64, &address_size, &where)) - { - retval = false; - goto next; - } - - /* Segment size. */ - uint8_t segment_size; - if (!read_ctx_read_ubyte (&sub_ctx, &segment_size)) - { - wr_error (&where, ": can't read unit segment size.\n"); - retval = false; - goto next; - } - if (segment_size != 0) - { - wr_warning (&where, ": dwarflint can't handle segment_size != 0.\n"); - retval = false; - goto next; - } - - - /* 7.20: The first tuple following the header in each set begins - at an offset that is a multiple of the size of a single tuple - (that is, twice the size of an address). The header is - padded, if necessary, to the appropriate boundary. */ - const uint8_t tuple_size = 2 * address_size; - uint64_t off = read_ctx_get_offset (&sub_ctx); - if ((off % tuple_size) != 0) - { - uint64_t noff = ((off / tuple_size) + 1) * tuple_size; - for (uint64_t i = off; i < noff; ++i) - { - uint8_t c; - if (!read_ctx_read_ubyte (&sub_ctx, &c)) - { - wr_error (&where, - ": section ends after the header, " - "but before the first entry.\n"); - retval = false; - goto next; - } - if (c != 0) - wr_message (mc_impact_2 | mc_aranges | mc_header, &where, - ": non-zero byte at 0x%" PRIx64 - " in padding before the first entry.\n", - read_ctx_get_offset (&sub_ctx)); - } - } - assert ((read_ctx_get_offset (&sub_ctx) % tuple_size) == 0); - - while (!read_ctx_eof (&sub_ctx)) - { - /* We would like to report aranges the same way that readelf - does. But readelf uses index of the arange in the array - as returned by dwarf_getaranges, which sorts the aranges - beforehand. We don't want to disturb the memory this - way, the better to catch structural errors accurately. - So report arange offset instead. If this becomes a - problem, we will achieve this by two-pass analysis. */ - where_reset_2 (&where, read_ctx_get_offset (&sub_ctx)); - - /* Record address. */ - uint64_t address; - ctx_offset = sub_ctx.ptr - ctx.begin; - bool address_relocated = false; - if (!read_ctx_read_var (&sub_ctx, address_size, &address)) - { - wr_error (&where, ": can't read address field.\n"); - retval = false; - goto next; - } - - if ((rel = relocation_next (&sec->rel, ctx_offset, - &where, skip_mismatched))) - { - address_relocated = true; - relocate_one (file, &sec->rel, rel, address_size, - &address, &where, rel_address, NULL); - } - else if (file->ehdr.e_type == ET_REL && address != 0) - wr_message (mc_impact_2 | mc_aranges | mc_reloc, &where, - PRI_LACK_RELOCATION, "address field"); - - /* Record length. */ - uint64_t length; - if (!read_ctx_read_var (&sub_ctx, address_size, &length)) - { - wr_error (&where, ": can't read length field.\n"); - retval = false; - goto next; - } - - if (address == 0 && length == 0 && !address_relocated) - break; - - if (length == 0) - /* DWARF 3 spec, 6.1.2 Lookup by Address: Each descriptor - is a pair consisting of the beginning address [...], - followed by the _non-zero_ length of that range. */ - wr_error (&where, ": zero-length address range.\n"); - /* Skip coverage analysis if we have errors. */ - else if (retval && aranges_coverage != NULL) - aranges_coverage_add (address, length); - } - - if (sub_ctx.ptr != sub_ctx.end) - { - uint64_t start, end; - if (read_check_zero_padding (&sub_ctx, &start, &end)) - wr_message_padding_0 (mc_aranges, &WHERE (where.section, NULL), - start, end); - else - { - wr_message_padding_n0 (mc_aranges | mc_error, - &WHERE (where.section, NULL), - start, start + size); - retval = false; - } - } - - next: - if (!read_ctx_skip (&ctx, size)) - /* A "can't happen" error. */ - goto not_enough; - } - - if (aranges_coverage != NULL) - { - compare_coverage (file, coverage, aranges_coverage, - sec_aranges, "aranges"); - coverage_free (aranges_coverage); - free (aranges_coverage); - } - - return retval; -} - static GElf_Rela * get_rel_or_rela (Elf_Data *data, int ndx, GElf_Rela *dst, size_t type) diff --git a/dwarflint/low.h b/dwarflint/low.h index 42361f8d0..fa4af1eda 100644 --- a/dwarflint/low.h +++ b/dwarflint/low.h @@ -122,13 +122,6 @@ extern "C" // xxx low-level check entry points, will go away struct cu; - extern bool check_aranges_structural (struct elf_file *file, - struct sec *sec, - struct cu *cu_chain, - struct coverage *coverage); - extern bool check_line_structural (struct elf_file *file, - struct sec *sec, - struct addr_record *line_tables); extern void check_range_relocations (enum message_category cat, struct where *where, struct elf_file const *file,