static void coverage_free (struct coverage *ar);
+/* Functions and data structures for CU handling. */
+
+struct cu
+{
+ uint64_t offset;
+ struct addr_record die_addrs; // Addresses where DIEs begin in this CU.
+ struct addr_record die_refs; // DIE references into other CUs from this CU.
+ struct cu *next;
+};
+
+static void cu_free (struct cu *cu_chain);
+static struct cu *cu_find_cu (struct cu *cu_chain, uint64_t offset);
+
+
/* Functions for checking of structural integrity. */
-static bool check_debug_info_structural (struct read_ctx *ctx,
- struct abbrev_table *abbrev_chain,
- struct addr_record *cu_addrs,
- Elf_Data *strings);
-static int read_die_chain (struct read_ctx *ctx, uint64_t cu_off,
+static struct cu *check_debug_info_structural (struct read_ctx *ctx,
+ struct abbrev_table *abbrev_chain,
+ Elf_Data *strings);
+static int read_die_chain (struct read_ctx *ctx,
+ struct cu *cu,
struct abbrev_table *abbrevs, Elf_Data *strings,
bool dwarf_64, bool addr_64,
- struct addr_record *die_addrs,
struct addr_record *die_refs,
struct addr_record *die_loc_refs,
struct coverage *strings_coverage);
-static bool check_cu_structural (struct read_ctx *ctx, uint64_t cu_off,
+static bool check_cu_structural (struct read_ctx *ctx,
+ struct cu *const cu,
struct abbrev_table *abbrev_chain,
Elf_Data *strings, bool dwarf_64,
- struct addr_record *die_addrs,
struct addr_record *die_refs,
struct coverage *strings_coverage);
static bool check_aranges_structural (struct read_ctx *ctx,
- struct addr_record *cu_addrs);
+ struct cu *cu_chain);
static void
else if (!tolerate_nodebug)
ERROR (".debug_abbrev data not found.");
- /* Set up container for storing CU addresses. Don't do that if we
- either have no data to fill that container from (abbrev and
- info), or have no data that need validation against that
- container (aranges). */
- struct addr_record cu_addrs_mem;
- struct addr_record *cu_addrs = NULL;
- if (abbrev_data != NULL
- && info_data != NULL
- && aranges_data != NULL)
- {
- cu_addrs = &cu_addrs_mem;
- memset (cu_addrs, 0, sizeof (*cu_addrs));
- }
+ struct cu *cu_chain = NULL;
if (abbrev_chain != NULL)
{
if (info_data != NULL)
{
read_ctx_init (&ctx, dwarf, info_data);
- check_debug_info_structural (&ctx, abbrev_chain, cu_addrs, str_data);
+ cu_chain = check_debug_info_structural (&ctx, abbrev_chain, str_data);
}
else if (!tolerate_nodebug)
ERROR (".debug_info or .debug_str data not found.");
if (aranges_data != NULL)
{
read_ctx_init (&ctx, dwarf, aranges_data);
- check_aranges_structural (&ctx, cu_addrs);
+ check_aranges_structural (&ctx, cu_chain);
}
else
/* Even though this is an error (XXX ?), it doesn't prevent us
sections. */
ERROR (".debug_aranges data not found.");
- addr_record_free (cu_addrs);
+ cu_free (cu_chain);
abbrev_table_free (abbrev_chain);
}
static bool
addr_record_has_addr (struct addr_record *ar, uint64_t addr)
{
+ if (ar->size == 0
+ || addr < ar->addrs[0]
+ || addr > ar->addrs[ar->size - 1])
+ return false;
+
size_t a = addr_record_find_addr (ar, addr);
return a < ar->size && ar->addrs[a] == addr;
}
free (ar->buf);
}
-static bool
-check_addr_record_addr (struct addr_record *ar, uint64_t addr)
+
+static void
+cu_free (struct cu *cu_chain)
{
- bool retval = addr_record_has_addr (ar, addr);
- if (!retval)
- /* XXX Write where it happened, ideally which DIE has the reference. */
- ERROR ("Unresolved DIE reference to " PRI_DIE ".\n", addr);
- return retval;
+ for (struct cu *it = cu_chain; it != NULL; )
+ {
+ addr_record_free (&it->die_addrs);
+
+ struct cu *temp = it;
+ it = it->next;
+ free (temp);
+ }
}
+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->offset == offset)
+ return it;
+ return NULL;
+}
+
+
static bool
-check_die_references (struct addr_record *die_addrs,
+check_die_references (struct cu *cu,
struct addr_record *die_refs)
{
bool retval = true;
for (size_t i = 0; i < die_refs->size; ++i)
{
- uint64_t addr = die_refs->addrs[i];
- if (!check_addr_record_addr (die_addrs, addr))
- retval = false;
+ uint64_t ref_addr = die_refs->addrs[i];
+ if (!addr_record_has_addr (&cu->die_addrs, ref_addr))
+ {
+ /* XXX Write which DIE has the reference. */
+ ERROR (PRI_D_INFO PRI_CU
+ ": unresolved DIE reference to " PRI_DIE ".\n",
+ cu->offset, ref_addr);
+ retval = false;
+ }
}
return retval;
}
+static bool
+check_global_die_references (struct cu *cu_chain)
+{
+ bool retval = true;
+ for (struct cu *it = cu_chain; it != NULL; it = it->next)
+ for (size_t i = 0; i < it->die_refs.size; ++i)
+ {
+ uint64_t ref_addr = it->die_refs.addrs[i];
+ struct cu *ref_cu = NULL;
+ for (struct cu *jt = cu_chain; jt != NULL; jt = jt->next)
+ if (addr_record_has_addr (&jt->die_addrs, ref_addr))
+ {
+ ref_cu = jt;
+ break;
+ }
+
+ /* XXX Write which DIE has the reference. */
+ if (ref_cu == NULL)
+ {
+ ERROR (PRI_D_INFO PRI_CU
+ ": unresolved (global) DIE reference to " PRI_DIE ".\n",
+ it->offset, ref_addr);
+ retval = false;
+ }
+ else if (ref_cu == it)
+ /* XXX Turn to MESSAGE. */
+ WARNING (PRI_D_INFO PRI_CU
+ ": local reference to " PRI_DIE " formed as global.\n",
+ it->offset, ref_addr);
+ }
+
+ return retval;
+}
+
#define READ_SIZE_EXTRA(CTX, SIZE32, SIZEP, DWARF_64P, FMT, ARGS...) \
({ \
__label__ out; \
_retval; \
})
-static bool
+static struct cu *
check_debug_info_structural (struct read_ctx *ctx,
struct abbrev_table *abbrev_chain,
- struct addr_record *cu_addrs,
Elf_Data *strings)
{
- struct addr_record die_addrs_mem;
- struct addr_record *die_addrs = &die_addrs_mem;
- memset (die_addrs, 0, sizeof (*die_addrs));
+ struct addr_record die_refs;
+ memset (&die_refs, 0, sizeof (die_refs));
- struct addr_record die_refs_mem;
- struct addr_record *die_refs = &die_refs_mem;
- memset (die_refs, 0, sizeof (*die_refs));
+ struct cu *cu_chain = NULL;
- bool recording = true;
- bool retval = true;
+ bool success = true;
struct coverage strings_coverage_mem;
struct coverage *strings_coverage = NULL;
const unsigned char *cu_begin = ctx->ptr;
uint64_t cu_off = read_ctx_get_offset (ctx);
- if (cu_addrs != NULL)
- addr_record_add (cu_addrs, cu_off);
+ struct cu *cur = xcalloc (1, sizeof (*cur));
+ cur->offset = cu_off;
+ cur->next = cu_chain;
+ cu_chain = cur;
uint32_t size32;
uint64_t size;
if (!read_ctx_read_4ubyte (ctx, &size32))
{
ERROR (PRI_D_INFO PRI_CU ": can't read CU length.\n", cu_off);
- retval = false;
+ success = false;
break;
}
if (size32 == 0 && CHECK_ZERO_PADDING (ctx, mc_die_other,
if (!READ_SIZE_EXTRA (ctx, size32, &size, &dwarf_64,
PRI_D_INFO PRI_CU, cu_off))
{
- retval = false;
+ success = false;
break;
}
ERROR (PRI_D_INFO PRI_CU ": section doesn't have enough data"
" to read CU of size %" PRIx64 ".\n", cu_off, size);
ctx->ptr = ctx->end;
- retval = false;
+ success = false;
break;
}
{
ERROR (PRI_D_INFO PRI_CU ": claimed length of %" PRIx64
" doesn't even cover CU header.\n", cu_off, size);
- retval = false;
+ success = false;
break;
}
else
read_ctx_init_sub (&cu_ctx, ctx->dbg, ctx->data, cu_begin, cu_end);
cu_ctx.ptr = ctx->ptr;
- if (!check_cu_structural (&cu_ctx, cu_off, abbrev_chain, strings,
- dwarf_64, die_addrs, die_refs,
- strings_coverage))
+ if (!check_cu_structural (&cu_ctx, cur, abbrev_chain, strings,
+ dwarf_64, &die_refs, strings_coverage))
{
- retval = false;
+ success = false;
break;
}
if (cu_ctx.ptr != cu_ctx.end
}
// Only check this if above we have been successful.
- if (retval && ctx->ptr != ctx->end)
+ if (success && ctx->ptr != ctx->end)
MESSAGE (mc_die_other | mc_impact_4,
".debug_info: CU lengths don't exactly match Elf_Data contents.");
- bool references_sound = true;
- if (recording)
- {
- if (!check_die_references (die_addrs, die_refs))
- references_sound = false;
- addr_record_free (&die_addrs_mem);
- addr_record_free (&die_refs_mem);
- }
+ bool references_sound = check_global_die_references (cu_chain);
+ addr_record_free (&die_refs);
if (strings_coverage != NULL)
{
MESSAGE_PADDING_N0 (mc_strings, ".debug_str", begin, end);
}
- if (retval)
+ if (success)
coverage_find_holes (strings_coverage, hole);
coverage_free (strings_coverage);
}
- return retval && references_sound;
+ if (!success || !references_sound)
+ {
+ cu_free (cu_chain);
+ cu_chain = NULL;
+ }
+
+ return cu_chain;
}
+1 in case some dies were actually loaded
*/
static int
-read_die_chain (struct read_ctx *ctx, uint64_t cu_off,
+read_die_chain (struct read_ctx *ctx,
+ struct cu *cu,
struct abbrev_table *abbrevs, Elf_Data *strings,
bool dwarf_64, bool addr_64,
- struct addr_record *die_addrs,
struct addr_record *die_refs,
struct addr_record *die_loc_refs,
struct coverage *strings_coverage)
die_off = read_ctx_get_offset (ctx);
if (!CHECKED_READ_ULEB128 (ctx, &abbr_code,
PRI_D_INFO PRI_CU_DIE, "abbrev code",
- cu_off, die_off))
+ cu->offset, die_off))
return -1;
/* Check sibling value advertised last time through the loop. */
if (abbr_code == 0)
ERROR (PRI_D_INFO PRI_CU_DIE
": is the last sibling in chain, but has a DW_AT_sibling attribute.\n",
- cu_off, prev_die_off);
+ cu->offset, prev_die_off);
else if (sibling_addr != die_off)
ERROR (PRI_D_INFO PRI_CU_DIE
": This DIE should have had its sibling at 0x%"
PRIx64 ", but it's at 0x%" PRIx64 " instead.\n",
- cu_off, prev_die_off, sibling_addr, die_off);
+ cu->offset, prev_die_off, sibling_addr, die_off);
sibling_addr = 0;
}
else if (prev_abbrev != NULL && prev_abbrev->has_children)
MESSAGE (mc_die_siblings | mc_acc_suboptimal | mc_impact_4,
PRI_D_INFO PRI_CU_DIE
": This DIE had children, but no DW_AT_sibling attribute.\n",
- cu_off, prev_die_off);
+ cu->offset, prev_die_off);
/* The section ended. */
if (read_ctx_eof (ctx) || abbr_code == 0)
if (abbr_code != 0)
ERROR (PRI_D_INFO PRI_CU
": DIE chain at %p not terminated with DIE with zero abbrev code.\n",
- cu_off, begin);
+ cu->offset, begin);
break;
}
{
ERROR (PRI_D_INFO PRI_CU_DIE ": abbrev section at 0x%" PRIx64
" doesn't contain code %" PRIu64 ".\n",
- cu_off, die_off, abbrevs->offset, abbr_code);
+ cu->offset, die_off, abbrevs->offset, abbr_code);
return -1;
}
abbrev->used = true;
- if (die_addrs != NULL)
- addr_record_add (die_addrs, cu_off + die_off);
+ addr_record_add (&cu->die_addrs, cu->offset + die_off);
/* Attribute values. */
for (struct abbrev_attrib *it = abbrev->attribs;
void record_ref (uint64_t addr, bool local)
{
- struct addr_record *record = die_refs;
+ struct addr_record *record = &cu->die_refs;
if (local)
{
assert (ctx->end > ctx->begin);
{
ERROR (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
": Invalid reference outside the CU: 0x%" PRIx64 ".\n",
- cu_off, die_off, abbrev->code, it->offset, addr);
+ cu->offset, die_off, abbrev->code, it->offset, addr);
return;
}
- addr += cu_off;
+ /* Address holds a CU-local reference, so add CU
+ offset to turn it into section offset. */
+ addr += cu->offset;
record = die_loc_refs;
}
- if (die_refs != NULL)
+ if (record != NULL)
addr_record_add (record, addr);
}
if (!CHECKED_READ_ULEB128 (ctx, &value,
PRI_D_INFO PRI_CU_DIE_ABBR_ATTR,
"indirect attribute form",
- cu_off, die_off, abbrev->code,
+ cu->offset, die_off, abbrev->code,
it->offset))
return -1;
{
ERROR (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
": invalid indirect form 0x%" PRIx64 ".\n",
- cu_off, die_off, abbrev->code, it->offset, value);
+ cu->offset, die_off, abbrev->code, it->offset, value);
return -1;
}
form = value;
MESSAGE (mc_die_siblings | mc_impact_2,
PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
": DW_AT_sibling attribute with (indirect) form DW_FORM_ref_addr.\n",
- cu_off, die_off, abbrev->code, it->offset);
+ cu->offset, die_off, abbrev->code, it->offset);
break;
case -2:
ERROR (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
": DW_AT_sibling attribute with non-reference (indirect) form %s.\n",
- cu_off, die_off, abbrev->code, it->offset,
+ cu->offset, die_off, abbrev->code, it->offset,
dwarf_form_string (value));
};
}
cant_read:
ERROR (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
": can't read attribute value.\n",
- cu_off, die_off, abbrev->code, it->offset);
+ cu->offset, die_off, abbrev->code, it->offset);
return -1;
}
if (strings == NULL)
ERROR (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
": strp attribute, but no .debug_str section.\n",
- cu_off, die_off, abbrev->code, it->offset);
+ cu->offset, die_off, abbrev->code, it->offset);
else if (addr >= strings->d_size)
ERROR (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
": Invalid offset outside .debug_str: 0x%" PRIx64 ".",
- cu_off, die_off, abbrev->code, it->offset, addr);
+ cu->offset, die_off, abbrev->code, it->offset, addr);
else
{
/* Record used part of .debug_str. */
if (!CHECKED_READ_ULEB128 (ctx, &value,
PRI_D_INFO PRI_CU_DIE_ABBR_ATTR,
"attribute value",
- cu_off, die_off, abbrev->code,
+ cu->offset, die_off, abbrev->code,
it->offset))
return -1;
if (!CHECKED_READ_SLEB128 (ctx, &value,
PRI_D_INFO PRI_CU_DIE_ABBR_ATTR,
"attribute value",
- cu_off, die_off, abbrev->code,
+ cu->offset, die_off, abbrev->code,
it->offset))
return -1;
break;
if (!CHECKED_READ_ULEB128 (ctx, &length,
PRI_D_INFO PRI_CU_DIE_ABBR_ATTR,
"attribute value",
- cu_off, die_off, abbrev->code,
+ cu->offset, die_off, abbrev->code,
it->offset))
return -1;
}
case DW_FORM_indirect:
ERROR (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
": Indirect form is again indirect.\n",
- cu_off, die_off, abbrev->code, it->offset);
+ cu->offset, die_off, abbrev->code, it->offset);
return -1;
default:
ERROR (PRI_D_INFO PRI_CU_DIE_ABBR_ATTR
": Internal error: unhandled form 0x%x\n",
- cu_off, die_off, abbrev->code, it->offset, it->form);
+ cu->offset, die_off, abbrev->code, it->offset, it->form);
}
}
if (abbrev->has_children)
{
- int st = read_die_chain (ctx, cu_off, abbrevs, strings,
+ int st = read_die_chain (ctx, cu, abbrevs, strings,
dwarf_64, addr_64,
- die_addrs, die_refs, die_loc_refs,
+ die_refs, die_loc_refs,
strings_coverage);
if (st == -1)
return -1;
MESSAGE (mc_die_children | mc_acc_suboptimal | mc_impact_3,
PRI_D_INFO PRI_CU_DIE
": Abbrev has_children, but the chain was empty.\n",
- cu_off, die_off);
+ cu->offset, die_off);
}
}
ERROR (PRI_D_INFO PRI_CU_DIE
": This DIE should have had its sibling at 0x%"
PRIx64 ", but the DIE chain ended.\n",
- cu_off, prev_die_off, sibling_addr);
+ cu->offset, prev_die_off, sibling_addr);
return got_die ? 1 : 0;
}
})
static bool
-check_cu_structural (struct read_ctx *ctx, uint64_t cu_off,
+check_cu_structural (struct read_ctx *ctx,
+ struct cu *const cu,
struct abbrev_table *abbrev_chain,
Elf_Data *strings, bool dwarf_64,
- struct addr_record *die_addrs,
struct addr_record *die_refs,
struct coverage *strings_coverage)
{
uint8_t address_size;
/* Version. */
- if (!READ_VERSION (ctx, dwarf_64, &version, PRI_D_INFO PRI_CU, cu_off))
+ if (!READ_VERSION (ctx, dwarf_64, &version, PRI_D_INFO PRI_CU, cu->offset))
return false;
/* Abbrev offset. */
if (!read_ctx_read_offset (ctx, dwarf_64, &abbrev_offset))
{
- ERROR (PRI_D_INFO PRI_CU ": can't read abbrev offset.\n", cu_off);
+ ERROR (PRI_D_INFO PRI_CU ": can't read abbrev offset.\n", cu->offset);
return false;
}
/* Address size. */
if (!read_ctx_read_ubyte (ctx, &address_size))
{
- ERROR (PRI_D_INFO PRI_CU ": can't read address size.\n", cu_off);
+ ERROR (PRI_D_INFO PRI_CU ": can't read address size.\n", cu->offset);
return false;
}
if (address_size != 4 && address_size != 8)
{
ERROR (PRI_D_INFO PRI_CU
": Invalid address size: %d (only 4 or 8 allowed).\n",
- cu_off, address_size);
+ cu->offset, address_size);
return false;
}
{
ERROR (PRI_D_INFO PRI_CU
": Couldn't find abbrev section with offset 0x%" PRIx64 ".\n",
- cu_off, abbrev_offset);
+ cu->offset, abbrev_offset);
return false;
}
- struct addr_record die_loc_refs_mem;
- struct addr_record *die_loc_refs = NULL;
- if (die_addrs != NULL)
- {
- die_loc_refs = &die_loc_refs_mem;
- memset (die_loc_refs, 0, sizeof (*die_loc_refs));
- }
+ struct addr_record die_loc_refs;
+ memset (&die_loc_refs, 0, sizeof (die_loc_refs));
bool retval = true;
- if (read_die_chain (ctx, cu_off, abbrevs, strings,
+ if (read_die_chain (ctx, cu, abbrevs, strings,
dwarf_64, address_size == 8,
- die_addrs, die_refs, die_loc_refs,
+ die_refs, &die_loc_refs,
strings_coverage) >= 0)
{
for (size_t i = 0; i < abbrevs->size; ++i)
if (!abbrevs->abbr[i].used)
- ERROR (PRI_D_INFO PRI_CU ": Abbreviation with code %"
- PRIu64 " is never used.\n",
- cu_off, abbrevs->abbr[i].code);
+ MESSAGE (mc_impact_3 | mc_acc_bloat | mc_abbrevs,
+ PRI_D_INFO PRI_CU ": Abbreviation with code %"
+ PRIu64 " is never used.\n",
+ cu->offset, abbrevs->abbr[i].code);
- if (!check_die_references (die_addrs, die_loc_refs))
+ if (!check_die_references (cu, &die_loc_refs))
retval = false;
}
else
retval = false;
- if (die_loc_refs)
- addr_record_free (die_loc_refs);
-
+ addr_record_free (&die_loc_refs);
return retval;
}
static bool
check_aranges_structural (struct read_ctx *ctx,
- struct addr_record *cu_addrs)
+ struct cu *cu_chain)
{
while (!read_ctx_eof (ctx))
{
": can't read debug info offset.\n", atab_off);
return false;
}
- if (cu_addrs != NULL && !addr_record_has_addr (cu_addrs, cu_off))
+ if (cu_find_cu (cu_chain, cu_off) == NULL)
ERROR (PRI_D_ARANGES PRI_ARANGETAB
": invalid reference to " PRI_CU ".\n", atab_off, cu_off);
atab_off, cu_off, tuple_off);
return false;
}
- /* XXX find out how to validate address and length. */
if (address == 0 && length == 0)
break;
+
+ /* Address and length can be validated on high level. */
}
}