From b3a10859aa63d030c9e457f1f5ddabb1cb2c1b9d Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Thu, 15 Jan 2009 18:02:27 +0100 Subject: [PATCH] Implement validation of .debug_loc --- src/ChangeLog | 5 + src/dwarflint.c | 727 ++++++++++++++++++++++++++++++++++++++------- src/expr_opcodes.h | 152 ++++++++++ 3 files changed, 773 insertions(+), 111 deletions(-) create mode 100644 src/expr_opcodes.h diff --git a/src/ChangeLog b/src/ChangeLog index 3bfa90494..72005bc80 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,8 @@ +2009-01-15 Petr Machata + + * dwarflint.c: Implement validation of .debug_loc and references + from .debug_info to .debug_loc. + 2009-01-14 Petr Machata * dwarfstrings.h (dwarf_locexpr_opcode_string): New. diff --git a/src/dwarflint.c b/src/dwarflint.c index fa5ef8372..9474409a3 100644 --- a/src/dwarflint.c +++ b/src/dwarflint.c @@ -127,8 +127,9 @@ enum message_category mc_aranges = 0x20000, // address ranges table mc_elf = 0x40000, // ELF structure, e.g. missing optional sections mc_pubtables = 0x80000, // table of public names/types - mc_other = 0x100000, // messages unrelated to any of the above - mc_pubtypes = 0x200000, // .debug_pubtypes presence + mc_pubtypes = 0x100000, // .debug_pubtypes presence + mc_loc = 0x200000, // messages related to .debug_loc + mc_other = 0x400000, // messages unrelated to any of the above mc_all = 0xffffff00, // all areas }; @@ -158,14 +159,35 @@ check_category (enum message_category cat) static char fmterr[] = "(fmt error)"; -static void -verror (const char *format, va_list ap) +static void __attribute__ ((format (printf, 2, 4))) +vverror (const char *fmt1, const char *fmt2, va_list ap1, ...) { fputs ("error: ", stdout); - vprintf (format, ap); + vprintf (fmt1, ap1); + + va_list ap2; + va_start (ap2, ap1); + vprintf (fmt2, ap2); + va_end (ap2); + ++error_count; } +static void +verror (const char *format, va_list ap) +{ + vverror (format, "%s", ap, ""); +} + +static void __attribute__ ((format (printf, 1, 2))) +wr_error (const char *format, ...) +{ + va_list ap; + va_start (ap, format); + verror (format, ap); + va_end (ap); +} + static void vwarning (const char *format, va_list ap) { @@ -186,15 +208,6 @@ vmessage (enum message_category category, const char *format, va_list ap) } } -static void __attribute__ ((format (printf, 1, 2))) -wr_error (const char *format, ...) -{ - va_list ap; - va_start (ap, format); - verror (format, ap); - va_end (ap); -} - static void __attribute__ ((format (printf, 1, 2))) wr_warning (const char *format, ...) { @@ -214,43 +227,49 @@ message (enum message_category category, const char *format, ...) } static void -format_padding_message (enum message_category category, - uint64_t start, uint64_t end, - char *kind, const char *format, va_list ap) +vfmessage (enum message_category category, + const char *fmt1, const char *fmt2, va_list ap1, ...) { if (!accept_message (&warning_criteria, category)) return; - char *buf; - if (vasprintf (&buf, format, ap) < 0) - buf = NULL; + char *buf1, *buf2; + if (vasprintf (&buf1, fmt1, ap1) < 0) + buf1 = NULL; - message (category, - "%s: 0x%" PRIx64 "..0x%" PRIx64 - ": %s.\n", - buf ?: fmterr, start, end, kind); - free (buf); + va_list ap2; + va_start (ap2, ap1); + if (vasprintf (&buf2, fmt2, ap2) < 0) + buf2 = NULL; + message (category, "%s%s", buf1 ?: fmterr, buf2 ?: fmterr); + va_end (ap2); + + free (buf1); + free (buf2); } static void -format_leb128_message (int st, const char *format, char *what, va_list ap) +format_padding_message (enum message_category category, + uint64_t start, uint64_t end, + char *kind, const char *format, va_list ap) +{ + vfmessage (category, format, + ": 0x%" PRIx64 "..0x%" PRIx64 ": %s.\n", + ap, start, end, kind); +} + +static void +format_leb128_message (int st, const char *format, const char *what, va_list ap) { enum message_category category = mc_leb128 | mc_acc_bloat | mc_impact_3; if (st == 0 || (st > 0 && !accept_message (&warning_criteria, category))) return; - char *buf; - if (vasprintf (&buf, format, ap) < 0) - buf = NULL; - if (st < 0) - wr_error ("%s: can't read %s.\n", buf ?: fmterr, what); + vverror (format, ": can't read %s.\n", ap, what ?: ""); else if (st > 0) - message (category, - "%s: unnecessarily long encoding of %s.\n", - buf ?: fmterr, what); - - free (buf); + vfmessage (category, format, + ": unnecessarily long encoding of %s.\n", ap, what ?: ""); } static void @@ -436,6 +455,8 @@ parse_opt (int key, char *arg __attribute__ ((unused)), #define PRI_D_PUBNAMES ".debug_pubnames: " #define PRI_D_PUBTYPES ".debug_pubtypes: " #define PRI_D_STR ".debug_str: " +#define PRI_D_LOC ".debug_loc: " +#define PRI_LOCEXPR "location expression: " #define PRI_CU "CU 0x%" PRIx64 #define PRI_DIE "DIE 0x%" PRIx64 @@ -453,6 +474,8 @@ parse_opt (int key, char *arg __attribute__ ((unused)), #define PRI_PUBSET_CU PRI_PUBSET " (for " PRI_CU ")" #define PRI_PUBSET_CU_RECORD PRI_PUBSET_CU ", " PRI_RECORD +#define PRI_NOT_ENOUGH ": not enough data for %s.\n" +#define PRI_CAUSE ": caused by this reference.\n" /* Functions and data structures related to bounds-checked reading. */ @@ -469,8 +492,8 @@ struct read_ctx static void read_ctx_init (struct read_ctx *ctx, Dwarf *dbg, Elf_Data *data); -static void read_ctx_init_sub (struct read_ctx *ctx, Dwarf *dbg, - Elf_Data *data, +static bool read_ctx_init_sub (struct read_ctx *ctx, + struct read_ctx *parent, const unsigned char *begin, const unsigned char *end); static off64_t read_ctx_get_offset (struct read_ctx *ctx); @@ -585,10 +608,20 @@ struct coverage coverage_emt_type *buf; }; +struct hole_info +{ + const char *section; + enum message_category category; + void *d_buf; +}; + static void coverage_init (struct coverage *ar, uint64_t size); static void coverage_add (struct coverage *ar, uint64_t begin, uint64_t end); +static bool coverage_is_covered (struct coverage *ar, uint64_t address); static void coverage_find_holes (struct coverage *ar, - void (*cb)(uint64_t begin, uint64_t end)); + void (*cb)(uint64_t begin, uint64_t end, void *data), + void *data); +static void found_hole (uint64_t begin, uint64_t end, void *data); static void coverage_free (struct coverage *ar); @@ -611,13 +644,17 @@ static struct cu *cu_find_cu (struct cu *cu_chain, uint64_t offset); static struct cu *check_debug_info_structural (struct read_ctx *ctx, struct abbrev_table *abbrev_chain, - Elf_Data *strings); + Elf_Data *strings, + Elf_Data *loc); static bool check_cu_structural (struct read_ctx *ctx, struct cu *const cu, struct abbrev_table *abbrev_chain, - Elf_Data *strings, bool dwarf_64, + Elf_Data *strings, + Elf_Data *loc, + bool dwarf_64, struct ref_record *die_refs, - struct coverage *strings_coverage); + struct coverage *strings_coverage, + struct coverage *loc_coverage); static bool check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain); static bool check_pub_structural (struct read_ctx *ctx, @@ -640,6 +677,7 @@ process_file (int fd __attribute__((unused)), Elf_Data *info_data = dwarf->sectiondata[IDX_debug_info]; Elf_Data *aranges_data = dwarf->sectiondata[IDX_debug_aranges]; Elf_Data *pubnames_data = dwarf->sectiondata[IDX_debug_pubnames]; + Elf_Data *loc_data = dwarf->sectiondata[IDX_debug_loc]; /* Obtaining pubtypes is a bit complicated, because GNU toolchain doesn't emit it, and libdw doesn't account for it. */ @@ -689,14 +727,15 @@ process_file (int fd __attribute__((unused)), { Elf_Data *str_data = dwarf->sectiondata[IDX_debug_str]; /* Same as above... */ - if (info_data != NULL && str_data != NULL) + if (info_data != NULL) { read_ctx_init (&ctx, dwarf, info_data); - cu_chain = check_debug_info_structural (&ctx, abbrev_chain, str_data); + cu_chain = check_debug_info_structural (&ctx, abbrev_chain, + str_data, loc_data); } else if (!tolerate_nodebug) /* Hard error, not a message. We can't debug without this. */ - wr_error (".debug_info or .debug_str data not found.\n"); + wr_error (".debug_info data not found.\n"); } if (aranges_data != NULL) @@ -736,21 +775,30 @@ read_ctx_init (struct read_ctx *ctx, Dwarf *dbg, Elf_Data *data) if (data == NULL) abort (); - read_ctx_init_sub (ctx, dbg, data, data->d_buf, data->d_buf + data->d_size); + ctx->dbg = dbg; + ctx->data = data; + ctx->begin = data->d_buf; + ctx->end = data->d_buf + data->d_size; + ctx->ptr = data->d_buf; } -static void -read_ctx_init_sub (struct read_ctx *ctx, Dwarf *dbg, Elf_Data *data, +static bool +read_ctx_init_sub (struct read_ctx *ctx, struct read_ctx *parent, const unsigned char *begin, const unsigned char *end) { - if (data == NULL) + if (parent == NULL) abort (); - ctx->dbg = dbg; - ctx->data = data; + if (begin < parent->begin + || end > parent->end) + return false; + + ctx->dbg = parent->dbg; + ctx->data = parent->data; ctx->begin = begin; ctx->end = end; ctx->ptr = begin; + return true; } static off64_t @@ -771,7 +819,9 @@ read_ctx_read_ubyte (struct read_ctx *ctx, unsigned char *ret) { if (!read_ctx_need_data (ctx, 1)) return false; - *ret = *ctx->ptr++; + if (ret != NULL) + *ret = *ctx->ptr; + ctx->ptr++; return true; } @@ -799,20 +849,29 @@ read_ctx_read_uleb128 (struct read_ctx *ctx, uint64_t *ret) break; } - *ret = result; + if (ret != NULL) + *ret = result; return zero_tail ? 1 : 0; } +static bool +vchecked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret, + const char *format, const char *what, va_list ap) +{ + int st = read_ctx_read_uleb128 (ctx, ret); + format_leb128_message (st, format, what, ap); + return st >= 0; +} + static bool __attribute__ ((format (printf, 3, 5))) checked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret, - char *format, char *what, ...) + const char *format, const char *what, ...) { - int st = read_ctx_read_uleb128 (ctx, ret); va_list ap; va_start (ap, what); - format_leb128_message (st, format, what, ap); + bool retval = vchecked_read_uleb128 (ctx, ret, format, what, ap); va_end (ap); - return st >= 0; + return retval; } static int @@ -846,20 +905,29 @@ read_ctx_read_sleb128 (struct read_ctx *ctx, int64_t *ret) return -1; } - *ret = result; + if (ret != NULL) + *ret = result; return zero_tail ? 1 : 0; } +static bool +vchecked_read_sleb128 (struct read_ctx *ctx, int64_t *ret, + const char *format, const char *what, va_list ap) +{ + int st = read_ctx_read_sleb128 (ctx, ret); + format_leb128_message (st, format, what, ap); + return st >= 0; +} + static bool __attribute__ ((format (printf, 3, 5))) checked_read_sleb128 (struct read_ctx *ctx, int64_t *ret, - char *format, char *what, ...) + const char *format, const char *what, ...) { - int st = read_ctx_read_sleb128 (ctx, ret); va_list ap; va_start (ap, what); - format_leb128_message (st, format, what, ap); + bool retval = vchecked_read_sleb128 (ctx, ret, format, what, ap); va_end (ap); - return st >= 0; + return retval; } static bool @@ -867,7 +935,9 @@ read_ctx_read_2ubyte (struct read_ctx *ctx, uint16_t *ret) { if (!read_ctx_need_data (ctx, 2)) return false; - *ret = read_2ubyte_unaligned_inc (ctx->dbg, ctx->ptr); + uint16_t val = read_2ubyte_unaligned_inc (ctx->dbg, ctx->ptr); + if (ret != NULL) + *ret = val; return true; } @@ -876,7 +946,9 @@ read_ctx_read_4ubyte (struct read_ctx *ctx, uint32_t *ret) { if (!read_ctx_need_data (ctx, 4)) return false; - *ret = read_4ubyte_unaligned_inc (ctx->dbg, ctx->ptr); + uint32_t val = read_4ubyte_unaligned_inc (ctx->dbg, ctx->ptr); + if (ret != NULL) + *ret = val; return true; } @@ -885,7 +957,9 @@ read_ctx_read_8ubyte (struct read_ctx *ctx, uint64_t *ret) { if (!read_ctx_need_data (ctx, 8)) return false; - *ret = read_8ubyte_unaligned_inc (ctx->dbg, ctx->ptr); + uint64_t val = read_8ubyte_unaligned_inc (ctx->dbg, ctx->ptr); + if (ret != NULL) + *ret = val; return true; } @@ -899,7 +973,8 @@ read_ctx_read_offset (struct read_ctx *ctx, bool dwarf64, uint64_t *ret) if (!read_ctx_read_4ubyte (ctx, &v)) return false; - *ret = v; + if (ret != NULL) + *ret = v; return true; } @@ -976,6 +1051,76 @@ check_sibling_form (uint64_t form) }; } +/* Check that given form may in fact be valid in some CU. */ +static bool +check_abbrev_location_form (uint64_t form) +{ + switch (form) + { + case DW_FORM_indirect: + + /* loclistptr */ + case DW_FORM_data4: + case DW_FORM_data8: + + /* block */ + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + case DW_FORM_block: + return true; + + default: + return false; + }; +} + +/* Check that given form is in fact valid in concrete CU. Return 0 if + it's absolutely invalid, -1 if it's invalid in the given context, 1 + if it's valid loclistptr, 2 if it's valid block. */ +static int +check_CU_location_form (uint64_t form, bool dwarf_64) +{ + switch (form) + { + /* loclistptr */ + case DW_FORM_data4: + if (dwarf_64) + return -1; + return 1; + + case DW_FORM_data8: + if (!dwarf_64) + return -1; + return 1; + + /* block */ + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + case DW_FORM_block: + return 2; + + default: + return 0; + }; +} + +static bool +is_location_attrib (uint64_t name) +{ + switch (name) + { + case DW_AT_location: + case DW_AT_frame_base: + case DW_AT_data_location: + case DW_AT_data_member_location: + return true; + default: + return false; + } +} + static struct abbrev_table * abbrev_table_load (struct read_ctx *ctx) { @@ -1152,10 +1297,18 @@ abbrev_table_load (struct read_ctx *ctx) case -2: wr_error (PRI_D_ABBREV PRI_ABBR_ATTR - ": DW_AT_sibling attribute with non-reference form %s.\n", + ": DW_AT_sibling attribute with non-reference form \"%s\".\n", abbr_off, attr_off, dwarf_form_string (attrib_form)); }; } + /* Similar for DW_AT_location. */ + else if (is_location_attrib (attrib_name)) + { + if (!check_abbrev_location_form (attrib_form)) + wr_error (PRI_D_ABBREV PRI_ABBR_ATTR + ": location attribute with invalid form \"%s\".\n", + abbr_off, attr_off, dwarf_form_string (attrib_form)); + } acur->name = attrib_name; acur->form = attrib_form; @@ -1309,6 +1462,7 @@ coverage_init (struct coverage *ar, uint64_t size) static void coverage_add (struct coverage *ar, uint64_t begin, uint64_t end) { + assert (ar); assert (begin <= end); assert (end <= ar->size); @@ -1331,9 +1485,31 @@ coverage_add (struct coverage *ar, uint64_t begin, uint64_t end) } } +static bool +coverage_is_covered (struct coverage *ar, uint64_t address) +{ + assert (ar); + assert (address <= ar->size); + + uint64_t bi = address / coverage_emt_bits; + uint8_t bb = address % coverage_emt_bits; + coverage_emt_type bm = (coverage_emt_type)1 << (coverage_emt_bits - 1 - bb); + return !!(ar->buf[bi] & bm); +} + +static bool +coverage_pristine (struct coverage *ar, uint64_t begin, uint64_t length) +{ + for (uint64_t i = 0; i < length; ++i) + if (coverage_is_covered (ar, begin + i)) + return false; + return true; +} + static void coverage_find_holes (struct coverage *ar, - void (*cb)(uint64_t begin, uint64_t end)) + void (*cb)(uint64_t begin, uint64_t end, void *user), + void *user) { bool hole; uint64_t begin = 0; @@ -1348,7 +1524,7 @@ coverage_find_holes (struct coverage *ar, { assert (hole); if (a != begin) - cb (begin, a - 1); + cb (begin, a - 1, user); hole = false; } @@ -1381,6 +1557,26 @@ coverage_find_holes (struct coverage *ar, hole_end (ar->size); } +static void +found_hole (uint64_t begin, uint64_t end, void *data) +{ + struct hole_info *info = (struct hole_info *)data; + bool all_zeroes = true; + for (uint64_t i = begin; i <= end; ++i) + if (((char*)info->d_buf)[i] != 0) + { + all_zeroes = false; + break; + } + + if (all_zeroes) + message_padding_0 (info->category, begin, end, info->section); + else + /* XXX: This is actually lying in case that the unreferenced + portion is composed of sequences of zeroes and non-zeroes. */ + message_padding_n0 (info->category, begin, end, info->section); +} + static void coverage_free (struct coverage *ar) { @@ -1532,7 +1728,8 @@ check_zero_padding (struct read_ctx *ctx, static struct cu * check_debug_info_structural (struct read_ctx *ctx, struct abbrev_table *abbrev_chain, - Elf_Data *strings) + Elf_Data *strings, + Elf_Data *loc) { struct ref_record die_refs; memset (&die_refs, 0, sizeof (die_refs)); @@ -1541,14 +1738,20 @@ check_debug_info_structural (struct read_ctx *ctx, bool success = true; - struct coverage strings_coverage_mem; - struct coverage *strings_coverage = NULL; + struct coverage strings_coverage_mem, *strings_coverage = NULL; if (strings != NULL && check_category (mc_strings)) { coverage_init (&strings_coverage_mem, strings->d_size); strings_coverage = &strings_coverage_mem; } + struct coverage loc_coverage_mem, *loc_coverage = NULL; + if (loc != NULL && check_category (mc_loc)) + { + coverage_init (&loc_coverage_mem, loc->d_size); + loc_coverage = &loc_coverage_mem; + } + while (!read_ctx_eof (ctx)) { const unsigned char *cu_begin = ctx->ptr; @@ -1614,11 +1817,17 @@ check_debug_info_structural (struct read_ctx *ctx, /* Make CU context begin just before the CU length, so that DIE offsets are computed correctly. */ struct read_ctx cu_ctx; - read_ctx_init_sub (&cu_ctx, ctx->dbg, ctx->data, cu_begin, cu_end); + if (!read_ctx_init_sub (&cu_ctx, ctx, cu_begin, cu_end)) + { + wr_error (PRI_D_INFO PRI_CU PRI_NOT_ENOUGH, cu_off, "next CU"); + success = false; + break; + } cu_ctx.ptr = ctx->ptr; - if (!check_cu_structural (&cu_ctx, cur, abbrev_chain, strings, - dwarf_64, &die_refs, strings_coverage)) + if (!check_cu_structural (&cu_ctx, cur, abbrev_chain, strings, loc, + dwarf_64, &die_refs, + strings_coverage, loc_coverage)) { success = false; break; @@ -1644,29 +1853,22 @@ check_debug_info_structural (struct read_ctx *ctx, if (strings_coverage != NULL) { - void hole (uint64_t begin, uint64_t end) - { - bool all_zeroes = true; - for (uint64_t i = begin; i <= end; ++i) - if (((char*)strings->d_buf)[i] != 0) - { - all_zeroes = false; - break; - } - - if (all_zeroes) - message_padding_0 (mc_strings, begin, end, PRI_D_STR); - else - /* XXX: This is actually lying in case that the unreferenced - portion is composed of sequences of zeroes and non-zeroes. */ - message_padding_n0 (mc_strings, begin, end, PRI_D_STR); - } - if (success) - coverage_find_holes (strings_coverage, hole); + coverage_find_holes (strings_coverage, found_hole, + &((struct hole_info) + {PRI_D_STR, mc_strings, strings->d_buf})); coverage_free (strings_coverage); } + if (loc_coverage != NULL) + { + if (success) + coverage_find_holes (loc_coverage, found_hole, + &((struct hole_info) + {PRI_D_LOC, mc_loc, loc->d_buf})); + coverage_free (loc_coverage); + } + if (!success || !references_sound) { cu_free (cu_chain); @@ -1676,6 +1878,223 @@ check_debug_info_structural (struct read_ctx *ctx, return cu_chain; } +/* 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 DEF_DW_OP(OPCODE, OP1, OP2) \ + case OPCODE: *op1 = OP1; *op2 = OP2; return true; +# include "expr_opcodes.h" +#undef DEF_DW_OP + default: + return false; + }; +} + +static bool +skip_form (struct read_ctx *ctx, bool addr_64, uint8_t form, ...) +{ + /* XXX fix messages in this function. */ + va_list ap; + va_start (ap, form); + switch (form) + { + case DW_FORM_addr: + return read_ctx_read_offset (ctx, addr_64, NULL); + case DW_FORM_udata: + return vchecked_read_uleb128 (ctx, NULL, "", NULL, ap); // here + case DW_FORM_sdata: + return vchecked_read_sleb128 (ctx, NULL, "", NULL, ap); // here + case DW_FORM_data1: + return read_ctx_read_ubyte (ctx, NULL); + case DW_FORM_data2: + return read_ctx_read_2ubyte (ctx, NULL); + case DW_FORM_data4: + return read_ctx_read_4ubyte (ctx, NULL); + case DW_FORM_data8: + return read_ctx_read_8ubyte (ctx, NULL); + }; + + return false; +} + +/* If it returns false, an error has been written, and the caller + should provide "at this point in file"-type message. */ +static bool +check_location_expression (struct read_ctx *ctx, bool addr_64) +{ + while (!read_ctx_eof (ctx)) + { + uint64_t op_off = read_ctx_get_offset (ctx); + uint8_t opcode; + if (!read_ctx_read_ubyte (ctx, &opcode)) + { + wr_error (PRI_LOCEXPR PRI_RECORD ": can't read opcode.\n", op_off); + return false; + } + + uint8_t op1, op2; + if (!get_location_opcode_operands (opcode, &op1, &op2)) + { + wr_error (PRI_LOCEXPR PRI_RECORD ": can't decode opcode \"%s\".\n", + op_off, dwarf_locexpr_opcode_string (opcode)); + return false; + } + +#define SKIP_FORM(OP, STR) \ + if (OP != 0 && !skip_form (ctx, addr_64, OP)) \ + { \ + wr_error (PRI_LOCEXPR PRI_RECORD ": opcode \"%s\"" \ + ": can't read " STR " operand (form \"%s\").\n", \ + op_off, dwarf_locexpr_opcode_string (opcode), \ + dwarf_form_string (OP)); \ + return false; \ + } + + SKIP_FORM (op1, "1st"); + SKIP_FORM (op2, "2nd"); +#undef SKIP_FORM + } + + return true; +} + +/* If it returns false, an error has been written, and the caller + should provide "at this point in file"-type message. */ +static bool +check_x_location_expression (Dwarf *dbg, Elf_Data *loc, + struct coverage *loc_coverage, + uint64_t addr, bool addr_64) +{ + if (loc == NULL || loc_coverage == NULL) + return true; + + struct read_ctx ctx; + read_ctx_init (&ctx, dbg, loc); + if (!read_ctx_skip (&ctx, addr)) + { + wr_error ("invalid reference outside " PRI_D_LOC + "0x%" PRIx64 ", size only 0x%" PRIx64 ".\n", + addr, loc->d_size); + return false; + } + + if (coverage_is_covered (loc_coverage, addr)) + /* Already checked this. + XXX check this properly: keep a list of region beginnings. */ + return true; + + uint64_t escape = addr_64 ? (uint64_t)-1 : (uint64_t)(uint32_t)-1; + + bool retval = true; + bool overlap = false; + while (!read_ctx_eof (&ctx)) + { + uint64_t off = read_ctx_get_offset (&ctx); + +#define HAVE_OVERLAP \ + do { \ + wr_error (PRI_D_LOC PRI_RECORD \ + ": range definitions overlap.\n", off); \ + retval = false; \ + overlap = true; \ + } while (0) + + /* begin address */ + uint64_t begin_addr; + if (!overlap + && !coverage_pristine (loc_coverage, + read_ctx_get_offset (&ctx), + addr_64 ? 8 : 4)) + HAVE_OVERLAP; + + if (!read_ctx_read_offset (&ctx, addr_64, &begin_addr)) + { + wr_error (PRI_D_LOC PRI_RECORD + ": can't read address range beginning.\n", off); + return false; + } + + /* end address */ + uint64_t end_addr; + if (!overlap + && !coverage_pristine (loc_coverage, + read_ctx_get_offset (&ctx), + addr_64 ? 8 : 4)) + HAVE_OVERLAP; + + if (!read_ctx_read_offset (&ctx, addr_64, &end_addr)) + { + wr_error (PRI_D_LOC PRI_RECORD + ": can't read address range ending.\n", off); + return false; + } + + bool done = begin_addr == 0 && end_addr == 0; + + if (!done && begin_addr != escape) + { + /* location expression length */ + uint16_t len; + if (!overlap + && !coverage_pristine (loc_coverage, + read_ctx_get_offset (&ctx), 2)) + HAVE_OVERLAP; + + if (!read_ctx_read_2ubyte (&ctx, &len)) + { + wr_error (PRI_D_LOC PRI_RECORD + ": can't read length of location expression.\n", off); + return false; + } + + /* location expression itself */ + struct read_ctx expr_ctx; + if (!read_ctx_init_sub (&expr_ctx, &ctx, ctx.ptr, ctx.ptr + len)) + { + not_enough: + wr_error (PRI_D_LOC PRI_RECORD PRI_NOT_ENOUGH, + off, "location expression"); + return false; + } + + uint64_t expr_start = read_ctx_get_offset (&ctx); + if (!check_location_expression (&expr_ctx, addr_64)) + { + wr_error (PRI_D_LOC PRI_RECORD PRI_NOT_ENOUGH, + off, "location expression"); + retval = false; + } + uint64_t expr_end = read_ctx_get_offset (&ctx); + if (!overlap + && !coverage_pristine (loc_coverage, + expr_start, expr_end - expr_start)) + HAVE_OVERLAP; + + if (!read_ctx_skip (&ctx, len)) + /* "can't happen" */ + goto not_enough; + } +#undef HAVE_OVERLAP + + uint64_t fin_off = read_ctx_get_offset (&ctx); + coverage_add (loc_coverage, off, fin_off - 1); + if (done) + break; + } + + return retval; +} + /* Returns: @@ -1687,11 +2106,14 @@ check_debug_info_structural (struct read_ctx *ctx, static int read_die_chain (struct read_ctx *ctx, struct cu *cu, - struct abbrev_table *abbrevs, Elf_Data *strings, + struct abbrev_table *abbrevs, + Elf_Data *strings, + Elf_Data *loc, bool dwarf_64, bool addr_64, struct ref_record *die_refs, struct ref_record *die_loc_refs, - struct coverage *strings_coverage) + struct coverage *strings_coverage, + struct coverage *loc_coverage) { bool got_die = false; const unsigned char *begin = ctx->ptr; @@ -1789,7 +2211,8 @@ read_die_chain (struct read_ctx *ctx, } uint8_t form = it->form; - if (form == DW_FORM_indirect) + bool indirect = form == DW_FORM_indirect; + if (indirect) { uint64_t value; if (!checked_read_uleb128 (ctx, &value, @@ -1820,12 +2243,41 @@ read_die_chain (struct read_ctx *ctx, case -2: wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR - ": DW_AT_sibling attribute with non-reference (indirect) form %s.\n", + ": DW_AT_sibling attribute with non-reference (indirect) form \"%s\".\n", cu->offset, die_off, abbrev->code, it->offset, dwarf_form_string (value)); }; } + bool check_locptr = false; + bool locptr_64 = dwarf_64; + if (is_location_attrib (it->name)) + { + switch (check_CU_location_form (form, dwarf_64)) + { + case 0: /* absolutely invalid */ + /* Only print error if it's indirect. Otherwise we + gave diagnostic during abbrev loading. */ + if (indirect) + wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR + ": location attribute with invalid (indirect) form \"%s\".\n", + cu->offset, die_off, abbrev->code, it->offset, + dwarf_form_string (form)); + break; + + case -1: /* locptr invalid in this context */ + wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR + ": location attribute with form \"%s\" in %d-bit CU.\n", + cu->offset, die_off, abbrev->code, it->offset, + dwarf_form_string (form), (dwarf_64 ? 64 : 32)); + locptr_64 = !locptr_64; + + /* fall-through */ + case 1: /* locptr */ + check_locptr = true; + }; + } + switch (form) { case DW_FORM_strp: @@ -1863,7 +2315,6 @@ read_die_chain (struct read_ctx *ctx, case DW_FORM_string: { - /* XXX check encoding? DW_AT_use_UTF8 */ uint8_t byte; do { @@ -1944,6 +2395,14 @@ read_die_chain (struct read_ctx *ctx, if (it->name == DW_AT_sibling) sibling_addr = value; + else if (check_locptr && !locptr_64) + { + if (!check_x_location_expression (ctx->dbg, loc, + loc_coverage, value, + addr_64)) + wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR PRI_CAUSE, + cu->offset, die_off, abbrev->code, it->offset); + } else if (it->form == DW_FORM_ref4) record_ref (value, die_off, true); break; @@ -1958,6 +2417,15 @@ read_die_chain (struct read_ctx *ctx, if (it->name == DW_AT_sibling) sibling_addr = value; + else if (check_locptr && locptr_64) + { + if (!check_x_location_expression (ctx->dbg, loc, + loc_coverage, value, + addr_64)) + wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR PRI_CAUSE, + cu->offset, die_off, abbrev->code, + it->offset); + } else if (it->form == DW_FORM_ref8) record_ref (value, die_off, true); break; @@ -2005,6 +2473,23 @@ read_die_chain (struct read_ctx *ctx, else if (!read_ctx_read_var (ctx, width, &length)) goto cant_read; + if (is_location_attrib (it->name)) + { + struct read_ctx sub_ctx; + if (!read_ctx_init_sub (&sub_ctx, ctx, ctx->ptr, + ctx->ptr + length)) + { + wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR PRI_NOT_ENOUGH, + cu->offset, die_off, abbrev->code, it->offset, + "location expression"); + return -1; + } + + if (!check_location_expression (&sub_ctx, addr_64)) + wr_error (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR PRI_CAUSE, + cu->offset, die_off, abbrev->code, it->offset); + } + if (!read_ctx_skip (ctx, length)) goto cant_read; @@ -2026,10 +2511,11 @@ read_die_chain (struct read_ctx *ctx, if (abbrev->has_children) { - int st = read_die_chain (ctx, cu, abbrevs, strings, + int st = read_die_chain (ctx, cu, abbrevs, strings, loc, dwarf_64, addr_64, die_refs, die_loc_refs, - strings_coverage); + strings_coverage, + loc_coverage); if (st == -1) return -1; else if (st == 0) @@ -2096,9 +2582,12 @@ static bool check_cu_structural (struct read_ctx *ctx, struct cu *const cu, struct abbrev_table *abbrev_chain, - Elf_Data *strings, bool dwarf_64, + Elf_Data *strings, + Elf_Data *loc, + bool dwarf_64, struct ref_record *die_refs, - struct coverage *strings_coverage) + struct coverage *strings_coverage, + struct coverage *loc_coverage) { uint16_t version; uint64_t abbrev_offset; @@ -2146,10 +2635,11 @@ check_cu_structural (struct read_ctx *ctx, memset (&die_loc_refs, 0, sizeof (die_loc_refs)); bool retval = true; - if (read_die_chain (ctx, cu, abbrevs, strings, + if (read_die_chain (ctx, cu, abbrevs, strings, loc, dwarf_64, address_size == 8, die_refs, &die_loc_refs, - strings_coverage) >= 0) + strings_coverage, + loc_coverage) >= 0) { for (size_t i = 0; i < abbrevs->size; ++i) if (!abbrevs->abbr[i].used) @@ -2186,7 +2676,7 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain) if (!read_ctx_read_4ubyte (ctx, &size32)) { wr_error (PRI_D_ARANGES PRI_ARANGETAB - ": can't read unit length.\n", atab_off); + ": can't read table length.\n", atab_off); return false; } if (!read_size_extra (ctx, size32, &size, &dwarf_64, @@ -2195,7 +2685,14 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain) struct read_ctx sub_ctx; const unsigned char *atab_end = ctx->ptr + size; - read_ctx_init_sub (&sub_ctx, ctx->dbg, ctx->data, atab_begin, atab_end); + if (!read_ctx_init_sub (&sub_ctx, ctx, atab_begin, atab_end)) + { + not_enough: + wr_error (PRI_D_ARANGES PRI_ARANGETAB PRI_NOT_ENOUGH, + atab_off, "next table"); + return false; + } + sub_ctx.ptr = ctx->ptr; /* Version. */ @@ -2329,7 +2826,9 @@ check_aranges_structural (struct read_ctx *ctx, struct cu *cu_chain) } next: - ctx->ptr += size; + if (!read_ctx_skip (ctx, size)) + /* A "can't happen" error. */ + goto not_enough; } return retval; @@ -2362,7 +2861,12 @@ check_pub_structural (struct read_ctx *ctx, struct cu *cu_chain, struct read_ctx sub_ctx; const unsigned char *set_end = ctx->ptr + size; - read_ctx_init_sub (&sub_ctx, ctx->dbg, ctx->data, set_begin, set_end); + if (!read_ctx_init_sub (&sub_ctx, ctx, set_begin, set_end)) + { + wr_error ("%s" PRI_PUBSET PRI_NOT_ENOUGH, + secname, set_off, "next set"); + return false; + } sub_ctx.ptr = ctx->ptr; /* Version. */ @@ -2405,7 +2909,7 @@ check_pub_structural (struct read_ctx *ctx, struct cu *cu_chain, retval = false; goto next; } - if (cu_len != cu->length) + if (cu_chain != NULL && cu_len != cu->length) { wr_error ("%s" PRI_PUBSET_CU ": the set covers length %" PRId64 @@ -2435,7 +2939,8 @@ check_pub_structural (struct read_ctx *ctx, struct cu *cu_chain, if (offset == 0) break; - if (!addr_record_has_addr (&cu->die_addrs, offset + cu->offset)) + if (cu_chain != NULL + && !addr_record_has_addr (&cu->die_addrs, offset + cu->offset)) { wr_error ("%s" PRI_PUBSET_CU_RECORD ": unresolved reference to " PRI_DIE ".\n", diff --git a/src/expr_opcodes.h b/src/expr_opcodes.h new file mode 100644 index 000000000..8f5da8a82 --- /dev/null +++ b/src/expr_opcodes.h @@ -0,0 +1,152 @@ +DEF_DW_OP (DW_OP_addr, DW_FORM_addr, 0) +DEF_DW_OP (DW_OP_deref, DW_FORM_data1, 0) +DEF_DW_OP (DW_OP_const1u, DW_FORM_data1, 0) +DEF_DW_OP (DW_OP_const1s, DW_FORM_data1, 0) +DEF_DW_OP (DW_OP_const2u, DW_FORM_data2, 0) +DEF_DW_OP (DW_OP_const2s, DW_FORM_data2, 0) +DEF_DW_OP (DW_OP_const4u, DW_FORM_data4, 0) +DEF_DW_OP (DW_OP_const4s, DW_FORM_data4, 0) +DEF_DW_OP (DW_OP_const8u, DW_FORM_data8, 0) +DEF_DW_OP (DW_OP_const8s, DW_FORM_data8, 0) +DEF_DW_OP (DW_OP_constu, DW_FORM_udata, 0) +DEF_DW_OP (DW_OP_consts, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_dup, 0, 0) +DEF_DW_OP (DW_OP_drop, 0, 0) +DEF_DW_OP (DW_OP_over, 0, 0) +DEF_DW_OP (DW_OP_pick, DW_FORM_data1, 0) +DEF_DW_OP (DW_OP_swap, 0, 0) +DEF_DW_OP (DW_OP_rot, 0, 0) +DEF_DW_OP (DW_OP_xderef, 0, 0) +DEF_DW_OP (DW_OP_abs, 0, 0) +DEF_DW_OP (DW_OP_and, 0, 0) +DEF_DW_OP (DW_OP_div, 0, 0) +DEF_DW_OP (DW_OP_minus, 0, 0) +DEF_DW_OP (DW_OP_mod, 0, 0) +DEF_DW_OP (DW_OP_mul, 0, 0) +DEF_DW_OP (DW_OP_neg, 0, 0) +DEF_DW_OP (DW_OP_not, 0, 0) +DEF_DW_OP (DW_OP_or, 0, 0) +DEF_DW_OP (DW_OP_plus, 0, 0) +DEF_DW_OP (DW_OP_plus_uconst, DW_FORM_udata, 0) +DEF_DW_OP (DW_OP_shl, 0, 0) +DEF_DW_OP (DW_OP_shr, 0, 0) +DEF_DW_OP (DW_OP_shra, 0, 0) +DEF_DW_OP (DW_OP_xor, 0, 0) +DEF_DW_OP (DW_OP_bra, DW_FORM_data2, 0) +DEF_DW_OP (DW_OP_eq, 0, 0) +DEF_DW_OP (DW_OP_ge, 0, 0) +DEF_DW_OP (DW_OP_gt, 0, 0) +DEF_DW_OP (DW_OP_le, 0, 0) +DEF_DW_OP (DW_OP_lt, 0, 0) +DEF_DW_OP (DW_OP_ne, 0, 0) +DEF_DW_OP (DW_OP_skip, DW_FORM_data2, 0) +DEF_DW_OP (DW_OP_lit0, 0, 0) +DEF_DW_OP (DW_OP_lit1, 0, 0) +DEF_DW_OP (DW_OP_lit2, 0, 0) +DEF_DW_OP (DW_OP_lit3, 0, 0) +DEF_DW_OP (DW_OP_lit4, 0, 0) +DEF_DW_OP (DW_OP_lit5, 0, 0) +DEF_DW_OP (DW_OP_lit6, 0, 0) +DEF_DW_OP (DW_OP_lit7, 0, 0) +DEF_DW_OP (DW_OP_lit8, 0, 0) +DEF_DW_OP (DW_OP_lit9, 0, 0) +DEF_DW_OP (DW_OP_lit10, 0, 0) +DEF_DW_OP (DW_OP_lit11, 0, 0) +DEF_DW_OP (DW_OP_lit12, 0, 0) +DEF_DW_OP (DW_OP_lit13, 0, 0) +DEF_DW_OP (DW_OP_lit14, 0, 0) +DEF_DW_OP (DW_OP_lit15, 0, 0) +DEF_DW_OP (DW_OP_lit16, 0, 0) +DEF_DW_OP (DW_OP_lit17, 0, 0) +DEF_DW_OP (DW_OP_lit18, 0, 0) +DEF_DW_OP (DW_OP_lit19, 0, 0) +DEF_DW_OP (DW_OP_lit20, 0, 0) +DEF_DW_OP (DW_OP_lit21, 0, 0) +DEF_DW_OP (DW_OP_lit22, 0, 0) +DEF_DW_OP (DW_OP_lit23, 0, 0) +DEF_DW_OP (DW_OP_lit24, 0, 0) +DEF_DW_OP (DW_OP_lit25, 0, 0) +DEF_DW_OP (DW_OP_lit26, 0, 0) +DEF_DW_OP (DW_OP_lit27, 0, 0) +DEF_DW_OP (DW_OP_lit28, 0, 0) +DEF_DW_OP (DW_OP_lit29, 0, 0) +DEF_DW_OP (DW_OP_lit30, 0, 0) +DEF_DW_OP (DW_OP_lit31, 0, 0) +DEF_DW_OP (DW_OP_reg0, 0, 0) +DEF_DW_OP (DW_OP_reg1, 0, 0) +DEF_DW_OP (DW_OP_reg2, 0, 0) +DEF_DW_OP (DW_OP_reg3, 0, 0) +DEF_DW_OP (DW_OP_reg4, 0, 0) +DEF_DW_OP (DW_OP_reg5, 0, 0) +DEF_DW_OP (DW_OP_reg6, 0, 0) +DEF_DW_OP (DW_OP_reg7, 0, 0) +DEF_DW_OP (DW_OP_reg8, 0, 0) +DEF_DW_OP (DW_OP_reg9, 0, 0) +DEF_DW_OP (DW_OP_reg10, 0, 0) +DEF_DW_OP (DW_OP_reg11, 0, 0) +DEF_DW_OP (DW_OP_reg12, 0, 0) +DEF_DW_OP (DW_OP_reg13, 0, 0) +DEF_DW_OP (DW_OP_reg14, 0, 0) +DEF_DW_OP (DW_OP_reg15, 0, 0) +DEF_DW_OP (DW_OP_reg16, 0, 0) +DEF_DW_OP (DW_OP_reg17, 0, 0) +DEF_DW_OP (DW_OP_reg18, 0, 0) +DEF_DW_OP (DW_OP_reg19, 0, 0) +DEF_DW_OP (DW_OP_reg20, 0, 0) +DEF_DW_OP (DW_OP_reg21, 0, 0) +DEF_DW_OP (DW_OP_reg22, 0, 0) +DEF_DW_OP (DW_OP_reg23, 0, 0) +DEF_DW_OP (DW_OP_reg24, 0, 0) +DEF_DW_OP (DW_OP_reg25, 0, 0) +DEF_DW_OP (DW_OP_reg26, 0, 0) +DEF_DW_OP (DW_OP_reg27, 0, 0) +DEF_DW_OP (DW_OP_reg28, 0, 0) +DEF_DW_OP (DW_OP_reg29, 0, 0) +DEF_DW_OP (DW_OP_reg30, 0, 0) +DEF_DW_OP (DW_OP_reg31, 0, 0) +DEF_DW_OP (DW_OP_breg0, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg1, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg2, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg3, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg4, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg5, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg6, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg7, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg8, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg9, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg10, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg11, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg12, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg13, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg14, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg15, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg16, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg17, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg18, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg19, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg20, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg21, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg22, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg23, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg24, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg25, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg26, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg27, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg28, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg29, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg30, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_breg31, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_regx, DW_FORM_udata, 0) +DEF_DW_OP (DW_OP_fbreg, DW_FORM_sdata, 0) +DEF_DW_OP (DW_OP_bregx, DW_FORM_udata, DW_FORM_sdata) +DEF_DW_OP (DW_OP_piece, DW_FORM_udata, 0) +DEF_DW_OP (DW_OP_deref_size, DW_FORM_data1, 0) +DEF_DW_OP (DW_OP_xderef_size, DW_FORM_data1, 0) +DEF_DW_OP (DW_OP_nop, 0, 0) +DEF_DW_OP (DW_OP_push_object_address, 0, 0) +DEF_DW_OP (DW_OP_call2, DW_FORM_data2, 0) +DEF_DW_OP (DW_OP_call4, DW_FORM_data4, 0) +DEF_DW_OP (DW_OP_call_ref, DW_FORM_addr, 0) +DEF_DW_OP (DW_OP_form_tls_address, 0, 0) +DEF_DW_OP (DW_OP_call_frame_cfa, 0, 0) +DEF_DW_OP (DW_OP_bit_piece, DW_FORM_udata, DW_FORM_udata) -- 2.47.3