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
};
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)
{
}
}
-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, ...)
{
}
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
#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
#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. */
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);
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);
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,
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. */
{
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)
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
{
if (!read_ctx_need_data (ctx, 1))
return false;
- *ret = *ctx->ptr++;
+ if (ret != NULL)
+ *ret = *ctx->ptr;
+ ctx->ptr++;
return true;
}
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
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
{
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;
}
{
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;
}
{
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;
}
if (!read_ctx_read_4ubyte (ctx, &v))
return false;
- *ret = v;
+ if (ret != NULL)
+ *ret = v;
return true;
}
};
}
+/* 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)
{
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;
static void
coverage_add (struct coverage *ar, uint64_t begin, uint64_t end)
{
+ assert (ar);
assert (begin <= end);
assert (end <= ar->size);
}
}
+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;
{
assert (hole);
if (a != begin)
- cb (begin, a - 1);
+ cb (begin, a - 1, user);
hole = false;
}
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)
{
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));
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;
/* 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;
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);
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:
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;
}
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,
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:
case DW_FORM_string:
{
- /* XXX check encoding? DW_AT_use_UTF8 */
uint8_t byte;
do
{
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;
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;
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;
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)
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;
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)
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,
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. */
}
next:
- ctx->ptr += size;
+ if (!read_ctx_skip (ctx, size))
+ /* A "can't happen" error. */
+ goto not_enough;
}
return retval;
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. */
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
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",
--- /dev/null
+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)