From: Petr Machata Date: Mon, 23 Nov 2009 18:40:44 +0000 (+0100) Subject: dwarflint: Move a bunch more over to check_debug_loc_range X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=15f124c32e9d0ae5a9aa9d04547f2fc7e8b16c40;p=thirdparty%2Felfutils.git dwarflint: Move a bunch more over to check_debug_loc_range --- diff --git a/src/dwarflint/check_debug_info.cc b/src/dwarflint/check_debug_info.cc index 25ae93a2f..db76d67dd 100644 --- a/src/dwarflint/check_debug_info.cc +++ b/src/dwarflint/check_debug_info.cc @@ -42,6 +42,7 @@ #include "checks-low.hh" #include "pri.hh" #include "config.h" +#include "check_debug_loc_range.hh" namespace { diff --git a/src/dwarflint/check_debug_loc_range.cc b/src/dwarflint/check_debug_loc_range.cc index a09fd7241..1f07843e8 100644 --- a/src/dwarflint/check_debug_loc_range.cc +++ b/src/dwarflint/check_debug_loc_range.cc @@ -39,6 +39,8 @@ #include "low.h" #include "config.h" #include "check_debug_loc_range.hh" +#include "dwarf-opcodes.h" +#include "pri.hh" namespace { @@ -123,6 +125,12 @@ namespace return true; } + struct coverage_map_hole_info + { + struct elf_file *elf; + struct hole_info info; + }; + /* begin is inclusive, end is exclusive. */ bool coverage_map_found_hole (uint64_t begin, uint64_t end, @@ -677,6 +685,314 @@ check_debug_loc::check_debug_loc (dwarflint &lint) throw check_base::failed (); } +namespace +{ + /* Operands are passed back as attribute forms. In particular, + DW_FORM_dataX for X-byte operands, DW_FORM_[us]data for + ULEB128/SLEB128 operands, and DW_FORM_addr for 32b/64b operands. + If the opcode takes no operands, 0 is passed. + + Return value is false if we couldn't determine (i.e. invalid + opcode). + */ + + bool + get_location_opcode_operands (uint8_t opcode, uint8_t *op1, uint8_t *op2) + { + switch (opcode) + { +#define DW_OP_2(OPCODE, OP1, OP2) \ + case OPCODE: *op1 = OP1; *op2 = OP2; return true; +#define DW_OP_1(OPCODE, OP1) DW_OP_2(OPCODE, OP1, 0) +#define DW_OP_0(OPCODE) DW_OP_2(OPCODE, 0, 0) + + DW_OP_OPERANDS + +#undef DEF_DW_OP_2 +#undef DEF_DW_OP_1 +#undef DEF_DW_OP_0 + default: + return false; + }; + } + + /* The value passed back in uint64_t VALUEP may actually be + type-casted signed quantity. WHAT and WHERE describe error + message and context for LEB128 loading. + + If IS_BLOCKP is non-NULL, block values are accepted, and + *IS_BLOCKP is initialized depending on whether FORM is a block + form. For block forms, the value passed back in VALUEP is block + length. */ + bool + read_ctx_read_form (struct read_ctx *ctx, int address_size, uint8_t form, + uint64_t *valuep, struct where *where, const char *what, + bool *is_blockp) + { + if (is_blockp != NULL) + *is_blockp = false; + switch (form) + { + case DW_FORM_addr: + 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 checked_read_sleb128 (ctx, (int64_t *)valuep, where, what); + case DW_FORM_data1: + { + uint8_t v; + if (!read_ctx_read_ubyte (ctx, &v)) + return false; + if (valuep != NULL) + *valuep = v; + return true; + } + case DW_FORM_data2: + { + uint16_t v; + if (!read_ctx_read_2ubyte (ctx, &v)) + return false; + if (valuep != NULL) + *valuep = v; + return true; + } + case DW_FORM_data4: + { + uint32_t v; + if (!read_ctx_read_4ubyte (ctx, &v)) + return false; + if (valuep != NULL) + *valuep = v; + return true; + } + case DW_FORM_data8: + return read_ctx_read_8ubyte (ctx, valuep); + }; + + if (is_blockp != NULL) + { + int dform; + switch (form) + { +#define HANDLE(BFORM, DFORM) \ + case BFORM: \ + dform = DFORM; \ + break + HANDLE (DW_FORM_block, DW_FORM_udata); + HANDLE (DW_FORM_block1, DW_FORM_data1); + HANDLE (DW_FORM_block2, DW_FORM_data2); + HANDLE (DW_FORM_block4, DW_FORM_data4); +#undef HANDLE + default: + return false; + } + + *is_blockp = true; + return read_ctx_read_form (ctx, address_size, dform, + valuep, where, what, NULL) + && read_ctx_skip (ctx, *valuep); + } + + return false; + } + + static enum section_id + reloc_target_loc (uint8_t opcode) + { + switch (opcode) + { + case DW_OP_call2: + case DW_OP_call4: + return sec_info; + + case DW_OP_addr: + return rel_address; + + case DW_OP_call_ref: + assert (!"Can't handle call_ref!"); + }; + + std::cout << "XXX don't know how to handle opcode=" + << pri::locexpr_opcode (opcode) << std::endl; + + return rel_value; + } + + bool + op_read_form (struct elf_file *file, + struct read_ctx *ctx, + struct cu *cu, + uint64_t init_off, + struct relocation_data *reloc, + int opcode, + int form, + uint64_t *valuep, + char const *str, + struct where *where) + { + if (form == 0) + return true; + + bool isblock; + uint64_t off = read_ctx_get_offset (ctx) + init_off; + + if (!read_ctx_read_form (ctx, cu->head->address_size, form, + valuep, where, str, &isblock)) + { + wr_error (*where) + << "opcode \"" << pri::locexpr_opcode (opcode) + << "\": can't read " << str << " (form \"" + << pri::form (form) << "\")." << std::endl; + return false; + } + + /* For non-block forms, allow relocation of the datum. For block + form, allow relocation of block contents, but not the + block length). */ + + struct relocation *rel; + if ((rel = relocation_next (reloc, off, + where, skip_mismatched))) + { + if (!isblock) + relocate_one (file, reloc, rel, + cu->head->address_size, valuep, where, + reloc_target_loc (opcode), NULL); + else + wr_error (where, ": relocation relocates a length field.\n"); + } + if (isblock) + { + uint64_t off_block_end = read_ctx_get_offset (ctx) + init_off - 1; + relocation_next (reloc, off_block_end, where, skip_ok); + } + + return true; + } +} + +bool +check_location_expression (struct elf_file *file, + struct read_ctx *parent_ctx, + struct cu *cu, + uint64_t init_off, + struct relocation_data *reloc, + size_t length, + struct where *wh) +{ + struct read_ctx ctx; + if (!read_ctx_init_sub (&ctx, parent_ctx, parent_ctx->ptr, + parent_ctx->ptr + length)) + { + wr_error (wh, PRI_NOT_ENOUGH, "location expression"); + return false; + } + + struct ref_record oprefs; + WIPE (oprefs); + + struct addr_record opaddrs; + WIPE (opaddrs); + + while (!read_ctx_eof (&ctx)) + { + struct where where = WHERE (sec_locexpr, wh); + uint64_t opcode_off = read_ctx_get_offset (&ctx) + init_off; + where_reset_1 (&where, opcode_off); + addr_record_add (&opaddrs, opcode_off); + + uint8_t opcode; + if (!read_ctx_read_ubyte (&ctx, &opcode)) + { + wr_error (&where, ": can't read opcode.\n"); + break; + } + + uint8_t op1, op2; + if (!get_location_opcode_operands (opcode, &op1, &op2)) + { + wr_error (where) + << "can't decode opcode \"" + << pri::locexpr_opcode (opcode) << "\"." << std::endl; + break; + } + + uint64_t value1, value2; + if (!op_read_form (file, &ctx, cu, init_off, reloc, + opcode, op1, &value1, "1st operand", &where) + || !op_read_form (file, &ctx, cu, init_off, reloc, + opcode, op2, &value2, "2st operand", &where)) + goto out; + + switch (opcode) + { + case DW_OP_bra: + case DW_OP_skip: + { + int16_t skip = (uint16_t)value1; + + if (skip == 0) + wr_message (where, cat (mc_loc, mc_acc_bloat, mc_impact_3)) + << pri::locexpr_opcode (opcode) + << " with skip 0." << std::endl; + else if (skip > 0 && !read_ctx_need_data (&ctx, (size_t)skip)) + wr_error (where) + << pri::locexpr_opcode (opcode) + << " branches out of location expression." << std::endl; + /* Compare with the offset after the two-byte skip value. */ + else if (skip < 0 && ((uint64_t)-skip) > read_ctx_get_offset (&ctx)) + wr_error (where) + << pri::locexpr_opcode (opcode) + << " branches before the beginning of location expression." + << std::endl; + else + { + uint64_t off_after = read_ctx_get_offset (&ctx) + init_off; + ref_record_add (&oprefs, off_after + skip, &where); + } + + break; + } + + case DW_OP_const8u: + case DW_OP_const8s: + if (cu->head->address_size == 4) + wr_error (where) + << pri::locexpr_opcode (opcode) << " on 32-bit machine." + << std::endl; + break; + + default: + if (cu->head->address_size == 4 + && (opcode == DW_OP_constu + || opcode == DW_OP_consts + || opcode == DW_OP_deref_size + || opcode == DW_OP_plus_uconst) + && (value1 > (uint64_t)(uint32_t)-1)) + wr_message (where, cat (mc_loc, mc_acc_bloat, mc_impact_3)) + << pri::locexpr_opcode (opcode) + << " with operand " << pri::hex (value1) + << " on a 32-bit machine." << std::endl; + }; + } + + out: + for (size_t i = 0; i < oprefs.size; ++i) + { + struct ref *ref = oprefs.refs + i; + if (!addr_record_has_addr (&opaddrs, ref->addr)) + wr_error (&ref->who, + ": unresolved reference to opcode at %#" PRIx64 ".\n", + ref->addr); + } + + addr_record_free (&opaddrs); + ref_record_free (&oprefs); + + return true; +} + bool found_hole (uint64_t start, uint64_t length, void *data) { diff --git a/src/dwarflint/check_debug_loc_range.hh b/src/dwarflint/check_debug_loc_range.hh index b5711d887..893b553fc 100644 --- a/src/dwarflint/check_debug_loc_range.hh +++ b/src/dwarflint/check_debug_loc_range.hh @@ -46,4 +46,22 @@ public: explicit check_debug_loc (dwarflint &lint); }; +struct hole_info +{ + enum section_id section; + enum message_category category; + void *data; + unsigned align; +}; + +/* DATA has to be a pointer to an instance of struct hole_info. + DATA->data has to point at d_buf of section in question. */ extern bool found_hole (uint64_t start, uint64_t length, void *data); + +extern bool check_location_expression (struct elf_file *file, + struct read_ctx *parent_ctx, + struct cu *cu, + uint64_t init_off, + struct relocation_data *reloc, + size_t length, + struct where *wh); diff --git a/src/dwarflint/low.c b/src/dwarflint/low.c index 2f0f20235..89e8c60ae 100644 --- a/src/dwarflint/low.c +++ b/src/dwarflint/low.c @@ -47,7 +47,6 @@ #include "low.h" #include "readctx.h" #include "config.h" -#include "dwarf-opcodes.h" #include "tables.h" #define PRI_CU "CU 0x%" PRIx64 @@ -103,86 +102,6 @@ checked_read_sleb128 (struct read_ctx *ctx, int64_t *ret, return st >= 0; } -/* The value passed back in uint64_t VALUEP may actually be - type-casted signed quantity. WHAT and WHERE describe error message - and context for LEB128 loading. - - If IS_BLOCKP is non-NULL, block values are accepted, and *IS_BLOCKP - is initialized depending on whether FORM is a block form. For - block forms, the value passed back in VALUEP is block length. */ -static bool -read_ctx_read_form (struct read_ctx *ctx, int address_size, uint8_t form, - uint64_t *valuep, struct where *where, const char *what, - bool *is_blockp) -{ - if (is_blockp != NULL) - *is_blockp = false; - switch (form) - { - case DW_FORM_addr: - 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 checked_read_sleb128 (ctx, (int64_t *)valuep, where, what); - case DW_FORM_data1: - { - uint8_t v; - if (!read_ctx_read_ubyte (ctx, &v)) - return false; - if (valuep != NULL) - *valuep = v; - return true; - } - case DW_FORM_data2: - { - uint16_t v; - if (!read_ctx_read_2ubyte (ctx, &v)) - return false; - if (valuep != NULL) - *valuep = v; - return true; - } - case DW_FORM_data4: - { - uint32_t v; - if (!read_ctx_read_4ubyte (ctx, &v)) - return false; - if (valuep != NULL) - *valuep = v; - return true; - } - case DW_FORM_data8: - return read_ctx_read_8ubyte (ctx, valuep); - }; - - if (is_blockp != NULL) - { - int dform; - switch (form) - { -#define HANDLE(BFORM, DFORM) \ - case BFORM: \ - dform = DFORM; \ - break - HANDLE (DW_FORM_block, DW_FORM_udata); - HANDLE (DW_FORM_block1, DW_FORM_data1); - HANDLE (DW_FORM_block2, DW_FORM_data2); - HANDLE (DW_FORM_block4, DW_FORM_data4); -#undef HANDLE - default: - return false; - } - - *is_blockp = true; - return read_ctx_read_form (ctx, address_size, dform, - valuep, where, what, NULL) - && read_ctx_skip (ctx, *valuep); - } - - return false; -} - int check_sibling_form (dwarf_version_h ver, uint64_t form) { @@ -280,28 +199,6 @@ check_zero_padding (struct read_ctx *ctx, return true; } -static enum section_id -reloc_target_loc (uint8_t opcode) -{ - switch (opcode) - { - case DW_OP_call2: - case DW_OP_call4: - return sec_info; - - case DW_OP_addr: - return rel_address; - - case DW_OP_call_ref: - assert (!"Can't handle call_ref!"); - }; - - printf ("XXX don't know how to handle opcode=%s\n", - dwarf_locexpr_opcode_string (opcode)); - - return rel_value; -} - bool supported_version (unsigned version, size_t num_supported, struct where *where, ...) @@ -832,202 +729,6 @@ check_pub_structural (struct elf_file *file, return retval; } - -/* Operands are passed back as attribute forms. In particular, - DW_FORM_dataX for X-byte operands, DW_FORM_[us]data for - ULEB128/SLEB128 operands, and DW_FORM_addr for 32b/64b operands. - If the opcode takes no operands, 0 is passed. - - Return value is false if we couldn't determine (i.e. invalid - opcode). - */ -static bool -get_location_opcode_operands (uint8_t opcode, uint8_t *op1, uint8_t *op2) -{ - switch (opcode) - { -#define DW_OP_2(OPCODE, OP1, OP2) \ - case OPCODE: *op1 = OP1; *op2 = OP2; return true; -#define DW_OP_1(OPCODE, OP1) DW_OP_2(OPCODE, OP1, 0) -#define DW_OP_0(OPCODE) DW_OP_2(OPCODE, 0, 0) - - DW_OP_OPERANDS - -#undef DEF_DW_OP_2 -#undef DEF_DW_OP_1 -#undef DEF_DW_OP_0 - default: - return false; - }; -} - -static bool -op_read_form (struct elf_file *file, - struct read_ctx *ctx, - struct cu *cu, - uint64_t init_off, - struct relocation_data *reloc, - int opcode, - int form, - uint64_t *valuep, - char const *str, - struct where *where) -{ - if (form == 0) - return true; - - bool isblock; - uint64_t off = read_ctx_get_offset (ctx) + init_off; - - if (!read_ctx_read_form (ctx, cu->head->address_size, form, - valuep, where, str, &isblock)) - { - wr_error (where, ": opcode \"%s\": can't read %s (form \"%s\").\n", - dwarf_locexpr_opcode_string (opcode), - str, dwarf_form_string (form)); - return false; - } - - /* For non-block forms, allow relocation of the datum. For block - form, allow relocation of block contents, but not the - block length). */ - - struct relocation *rel; - if ((rel = relocation_next (reloc, off, - where, skip_mismatched))) - { - if (!isblock) - relocate_one (file, reloc, rel, - cu->head->address_size, valuep, where, - reloc_target_loc (opcode), NULL); - else - wr_error (where, ": relocation relocates a length field.\n"); - } - if (isblock) - { - uint64_t off_block_end = read_ctx_get_offset (ctx) + init_off - 1; - relocation_next (reloc, off_block_end, where, skip_ok); - } - - return true; -} - -bool -check_location_expression (struct elf_file *file, - struct read_ctx *parent_ctx, - struct cu *cu, - uint64_t init_off, - struct relocation_data *reloc, - size_t length, - struct where *wh) -{ - struct read_ctx ctx; - if (!read_ctx_init_sub (&ctx, parent_ctx, parent_ctx->ptr, - parent_ctx->ptr + length)) - { - wr_error (wh, PRI_NOT_ENOUGH, "location expression"); - return false; - } - - struct ref_record oprefs; - WIPE (oprefs); - - struct addr_record opaddrs; - WIPE (opaddrs); - - while (!read_ctx_eof (&ctx)) - { - struct where where = WHERE (sec_locexpr, wh); - uint64_t opcode_off = read_ctx_get_offset (&ctx) + init_off; - where_reset_1 (&where, opcode_off); - addr_record_add (&opaddrs, opcode_off); - - uint8_t opcode; - if (!read_ctx_read_ubyte (&ctx, &opcode)) - { - wr_error (&where, ": can't read opcode.\n"); - break; - } - - uint8_t op1, op2; - if (!get_location_opcode_operands (opcode, &op1, &op2)) - { - wr_error (&where, ": can't decode opcode \"%s\".\n", - dwarf_locexpr_opcode_string (opcode)); - break; - } - - uint64_t value1, value2; - if (!op_read_form (file, &ctx, cu, init_off, reloc, - opcode, op1, &value1, "1st operand", &where) - || !op_read_form (file, &ctx, cu, init_off, reloc, - opcode, op2, &value2, "2st operand", &where)) - goto out; - - switch (opcode) - { - case DW_OP_bra: - case DW_OP_skip: - { - int16_t skip = (uint16_t)value1; - - if (skip == 0) - wr_message (mc_loc | mc_acc_bloat | mc_impact_3, &where, - ": %s with skip 0.\n", - dwarf_locexpr_opcode_string (opcode)); - else if (skip > 0 && !read_ctx_need_data (&ctx, (size_t)skip)) - wr_error (&where, ": %s branches out of location expression.\n", - dwarf_locexpr_opcode_string (opcode)); - /* Compare with the offset after the two-byte skip value. */ - else if (skip < 0 && ((uint64_t)-skip) > read_ctx_get_offset (&ctx)) - wr_error (&where, - ": %s branches before the beginning of location expression.\n", - dwarf_locexpr_opcode_string (opcode)); - else - { - uint64_t off_after = read_ctx_get_offset (&ctx) + init_off; - ref_record_add (&oprefs, off_after + skip, &where); - } - - break; - } - - case DW_OP_const8u: - case DW_OP_const8s: - if (cu->head->address_size == 4) - wr_error (&where, ": %s on 32-bit machine.\n", - dwarf_locexpr_opcode_string (opcode)); - break; - - default: - if (cu->head->address_size == 4 - && (opcode == DW_OP_constu - || opcode == DW_OP_consts - || opcode == DW_OP_deref_size - || opcode == DW_OP_plus_uconst) - && (value1 > (uint64_t)(uint32_t)-1)) - wr_message (mc_loc | mc_acc_bloat | mc_impact_3, &where, - ": %s with operand %#" PRIx64 " on 32-bit machine.\n", - dwarf_locexpr_opcode_string (opcode), value1); - }; - } - - out: - for (size_t i = 0; i < oprefs.size; ++i) - { - struct ref *ref = oprefs.refs + i; - if (!addr_record_has_addr (&opaddrs, ref->addr)) - wr_error (&ref->who, - ": unresolved reference to opcode at %#" PRIx64 ".\n", - ref->addr); - } - - addr_record_free (&opaddrs); - ref_record_free (&oprefs); - - return true; -} - static GElf_Rela * get_rel_or_rela (Elf_Data *data, int ndx, GElf_Rela *dst, size_t type) diff --git a/src/dwarflint/low.h b/src/dwarflint/low.h index fd6183bed..ee345aa6d 100644 --- a/src/dwarflint/low.h +++ b/src/dwarflint/low.h @@ -156,13 +156,6 @@ extern "C" extern bool check_line_structural (struct elf_file *file, struct sec *sec, struct addr_record *line_tables); - extern bool check_location_expression (struct elf_file *file, - struct read_ctx *parent_ctx, - struct cu *cu, - uint64_t init_off, - struct relocation_data *reloc, - size_t length, - struct where *wh); extern void check_range_relocations (enum message_category cat, struct where *where, struct elf_file *file, @@ -175,28 +168,6 @@ extern "C" extern int check_sibling_form (dwarf_version_h ver, uint64_t form); extern bool is_location_attrib (uint64_t name); - struct hole_info - { - enum section_id section; - enum message_category category; - void *data; - unsigned align; - }; - - /* DATA has to be a pointer to an instance of struct hole_info. - DATA->data has to point at d_buf of section in question. */ - bool found_hole (uint64_t begin, uint64_t end, void *data); - - struct coverage_map_hole_info - { - struct elf_file *elf; - struct hole_info info; - }; - - /* DATA has to be a pointer to an instance of struct hole_info. - DATA->info.data has to be NULL, it is used by the callback. */ - bool coverage_map_found_hole (uint64_t begin, uint64_t end, - struct section_coverage *sco, void *data); bool checked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret, struct where *where, const char *what); bool checked_read_sleb128 (struct read_ctx *ctx, int64_t *ret, diff --git a/src/dwarflint/pri.cc b/src/dwarflint/pri.cc index 741b7a2ab..166428f13 100644 --- a/src/dwarflint/pri.cc +++ b/src/dwarflint/pri.cc @@ -20,6 +20,10 @@ pri::tag::tag (int die_tag) : pribase (dwarf_tag_string (die_tag)) {} +pri::locexpr_opcode::locexpr_opcode (int opcode) + : pribase (dwarf_locexpr_opcode_string (opcode)) +{} + std::ostream & pri::operator << (std::ostream &os, pri::ref const &obj) { diff --git a/src/dwarflint/pri.hh b/src/dwarflint/pri.hh index 5e49e0213..ccdbb1ec4 100644 --- a/src/dwarflint/pri.hh +++ b/src/dwarflint/pri.hh @@ -54,6 +54,12 @@ namespace pri tag (int tag); }; + struct locexpr_opcode + : public pribase + { + locexpr_opcode (int opcode); + }; + class ref { Dwarf_Off off;