#define PRI_DIE "DIE 0x%" PRIx64
#define PRI_NOT_ENOUGH ": not enough data for %s.\n"
+struct relocation_data
+{
+ Elf_Data *data; /* May be NULL if there are no associated
+ relocation data. */
+ size_t type; /* SHT_REL or SHT_RELA. */
+ size_t count; /* How many relocation entries are there? */
+ size_t index; /* Current index. */
+};
+
struct section_data
{
enum section_id sec;
- Elf_Data *secdata;
- Elf_Data *reldata; /* May be NULL if there are no associated
- relocation data. */
- size_t reltype; /* SHT_REL or SHT_RELA. */
+ Elf_Data *data;
+ struct relocation_data rel;
};
/* Functions and data structures related to bounds-checked
/* Functions for checking of structural integrity. */
-static struct cu *check_debug_info_structural (Dwarf *dwarf,
+static struct cu *check_debug_info_structural (Dwarf *dwarf, Ebl *ebl,
struct section_data *data,
struct abbrev_table *abbrev_chain);
struct read_ctx ctx;
#define DEF_SECDATA(VAR, SEC) \
- struct section_data VAR = {SEC, NULL, NULL, 0}
+ struct section_data VAR = {SEC, NULL, {NULL, 0, 0, 0}}
DEF_SECDATA (abbrev_data, sec_abbrev);
DEF_SECDATA (aranges_data, sec_aranges);
struct section_data *secdata = find_secdata (scnname);
if (secdata != NULL)
{
- if (secdata->secdata == NULL)
+ if (secdata->data == NULL)
{
- secdata->secdata = elf_getdata (scn, NULL);
- if (secdata->secdata == NULL)
+ secdata->data = elf_getdata (scn, NULL);
+ if (secdata->data == NULL)
wr_error (NULL, "data-less section %s.\n", scnname);
}
else
if (relocated_secdata != NULL)
{
- if (relocated_secdata->reldata != NULL)
+ if (relocated_secdata->rel.data != NULL)
wr_error (NULL,
"Several relocation sections for debug section %s\n.",
relocated_scnname);
else
{
- relocated_secdata->reldata = elf_getdata (scn, NULL);
- if (relocated_secdata->reldata == NULL)
+ relocated_secdata->rel.data = elf_getdata (scn, NULL);
+ if (relocated_secdata->rel.data == NULL)
wr_error (NULL,
"Data-less relocation section %s.\n", scnname);
- relocated_secdata->reltype = shdr->sh_type;
+ relocated_secdata->rel.type = shdr->sh_type;
}
if (reloc_symtab == NULL)
/* Check relocation sections that we've got. */
for (size_t i = 0; i < sizeof (secinfo) / sizeof (*secinfo); ++i)
- if (secinfo[i].dataptr->reldata != NULL)
+ if (secinfo[i].dataptr->rel.data != NULL)
{
- if (secinfo[i].dataptr->secdata == NULL)
+ if (secinfo[i].dataptr->data == NULL)
{
- secinfo[i].dataptr->secdata = NULL;
+ secinfo[i].dataptr->data = NULL;
wr_error (&WHERE (secinfo[i].dataptr->sec, NULL),
": the ELF contains relocation section for this data-less section.\n");
}
else if (!check_relocation_section_structural (dwarf,
secinfo[i].dataptr,
ebl, elf_64))
- secinfo[i].dataptr->reldata = NULL;
+ secinfo[i].dataptr->rel.data = NULL;
}
skip_rel:;
/* If we got Dwarf pointer, debug_abbrev and debug_info are present
inside the file. But let's be paranoid. */
struct abbrev_table *abbrev_chain = NULL;
- if (likely (abbrev_data.secdata != NULL))
+ if (likely (abbrev_data.data != NULL))
{
- read_ctx_init (&ctx, dwarf, abbrev_data.secdata);
+ read_ctx_init (&ctx, dwarf, abbrev_data.data);
abbrev_chain = abbrev_table_load (&ctx);
}
else if (!tolerate_nodebug)
if (abbrev_chain != NULL)
{
/* Same as above... */
- if (info_data.secdata != NULL)
- cu_chain = check_debug_info_structural (dwarf, &info_data,
+ if (info_data.data != NULL)
+ cu_chain = check_debug_info_structural (dwarf, ebl, &info_data,
abbrev_chain);
else if (!tolerate_nodebug)
/* Hard error, not a message. We can't debug without this. */
}
bool ranges_sound;
- if (ranges_data.secdata != NULL && cu_chain != NULL)
+ if (ranges_data.data != NULL && cu_chain != NULL)
ranges_sound = check_loc_or_range_structural (dwarf, &ranges_data, cu_chain);
else
ranges_sound = false;
- if (loc_data.secdata != NULL && cu_chain != NULL)
+ if (loc_data.data != NULL && cu_chain != NULL)
check_loc_or_range_structural (dwarf, &loc_data, cu_chain);
- if (aranges_data.secdata != NULL)
+ if (aranges_data.data != NULL)
{
- read_ctx_init (&ctx, dwarf, aranges_data.secdata);
+ read_ctx_init (&ctx, dwarf, aranges_data.data);
if (check_aranges_structural (&ctx, cu_chain)
&& ranges_sound)
check_matching_ranges (dwarf);
}
- if (pubnames_data.secdata != NULL)
+ if (pubnames_data.data != NULL)
check_pub_structural (dwarf, &pubnames_data, cu_chain);
else
wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf,
&WHERE (sec_pubnames, NULL), ": data not found.\n");
- if (pubtypes_data.secdata != NULL)
+ if (pubtypes_data.data != NULL)
check_pub_structural (dwarf, &pubtypes_data, cu_chain);
else
wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf | mc_pubtypes,
return true;
}
+static bool
+check_reloc (struct relocation_data *reloc, Ebl *ebl,
+ uint64_t offset, unsigned width,
+ struct where *where)
+{
+ if (reloc == NULL)
+ return true;
+
+ bool retval = true;
+ while (reloc->index < reloc->count)
+ {
+ GElf_Rela rela_mem, *rela
+ = gelf_getrela (reloc->data, reloc->index, &rela_mem);
+
+ /* This relocation entry is ahead of us. */
+ if (rela->r_offset > offset)
+ break;
+
+ reloc->index++;
+
+ /* Hit! */
+ if (rela->r_offset == offset)
+ {
+ int rtype = GELF_R_TYPE (rela->r_info);
+ Elf_Type type = ebl_reloc_simple_type (ebl, rtype);
+
+ unsigned rel_width;
+ switch (type)
+ {
+ case ELF_T_BYTE:
+ rel_width = 1;
+ break;
+
+ case ELF_T_HALF:
+ rel_width = 2;
+ break;
+
+ case ELF_T_WORD:
+ case ELF_T_SWORD:
+ rel_width = 4;
+ break;
+
+ case ELF_T_XWORD:
+ case ELF_T_SXWORD:
+ rel_width = 8;
+ break;
+
+ default:
+ /* Even though it is an error, it's an error that was
+ already diagnosed, so don't make the result false. */
+ return retval;
+ };
+
+ if (rel_width != width)
+ {
+ /* XXX here we want both relocation and data reference
+ in where. */
+ wr_error (where,
+ ": %d-byte relocation relocates %d-byte datum.\n",
+ rel_width, width);
+ return false;
+ }
+
+ break;
+ }
+
+ /* XXX make 'where' point to reloc. Currently it's done so
+ that it points to the first place with offset greater than
+ the current relocation cursor. */
+ retval = false;
+ wr_error (where,
+ ": mismatched relocation of %#" PRIx64 ".\n",
+ rela->r_offset);
+ }
+
+ return retval;
+}
+
/*
Returns:
-1 in case of error
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 relocation_data *reloc,
+ Ebl *ebl)
{
bool got_die = false;
uint64_t sibling_addr = 0;
assert (!(check_locptr && check_rangeptr));
+ uint64_t offset = read_ctx_get_offset (ctx) + cu->offset;
+ //printf ("Datum @%#" PRIx64 ", form %s.\n", offset, dwarf_form_string (form));
switch (form)
{
case DW_FORM_strp:
{
uint64_t addr;
+ check_reloc (reloc, ebl, offset, dwarf_64 ? 8 : 4, &where);
if (!read_ctx_read_offset (ctx, dwarf_64, &addr))
{
cant_read:
case DW_FORM_ref_addr:
{
uint64_t addr;
+ check_reloc (reloc, ebl, offset, dwarf_64 ? 8 : 4, &where);
if (!read_ctx_read_offset (ctx, addr_64, &addr))
goto cant_read;
case DW_FORM_data1:
case DW_FORM_ref1:
{
+ if (form == DW_FORM_data1)
+ check_reloc (reloc, ebl, offset, 1, &where);
+
uint8_t value;
if (!read_ctx_read_ubyte (ctx, &value))
goto cant_read;
case DW_FORM_data2:
case DW_FORM_ref2:
{
+ if (form == DW_FORM_data2)
+ check_reloc (reloc, ebl, offset, 2, &where);
+
uint16_t value;
if (!read_ctx_read_2ubyte (ctx, &value))
goto cant_read;
case DW_FORM_data4:
case DW_FORM_ref4:
{
+ if (form == DW_FORM_data4)
+ check_reloc (reloc, ebl, offset, 4, &where);
+
uint32_t value;
if (!read_ctx_read_4ubyte (ctx, &value))
goto cant_read;
case DW_FORM_data8:
case DW_FORM_ref8:
{
+ if (form == DW_FORM_data8)
+ check_reloc (reloc, ebl, offset, 8, &where);
+
uint64_t value;
if (!read_ctx_read_8ubyte (ctx, &value))
goto cant_read;
int st = read_die_chain (ctx, cu, abbrevs, strings,
dwarf_64, addr_64,
die_refs, die_loc_refs,
- strings_coverage);
+ strings_coverage, reloc, ebl);
if (st == -1)
return -1;
else if (st == 0)
Elf_Data *strings,
bool dwarf_64,
struct ref_record *die_refs,
- struct coverage *strings_coverage)
+ struct coverage *strings_coverage,
+ struct relocation_data *reloc,
+ Ebl *ebl)
{
uint16_t version;
uint64_t abbrev_offset;
uint8_t address_size;
+ bool retval = true;
/* Version. */
if (!read_ctx_read_version (ctx, dwarf_64, &version, &cu->where))
return false;
/* Abbrev offset. */
+ check_reloc (reloc, ebl, read_ctx_get_offset (ctx),
+ dwarf_64 ? 8 : 4, &cu->where);
if (!read_ctx_read_offset (ctx, dwarf_64, &abbrev_offset))
{
wr_error (&cu->where, ": can't read abbrev offset.\n");
struct ref_record die_loc_refs;
memset (&die_loc_refs, 0, sizeof (die_loc_refs));
- bool retval = true;
if (read_die_chain (ctx, cu, abbrevs, strings,
dwarf_64, address_size == 8,
- die_refs, &die_loc_refs,
- strings_coverage) >= 0)
+ die_refs, &die_loc_refs, strings_coverage,
+ reloc->data != NULL ? reloc : NULL, ebl) >= 0)
{
for (size_t i = 0; i < abbrevs->size; ++i)
if (!abbrevs->abbr[i].used)
}
static struct cu *
-check_debug_info_structural (Dwarf *dwarf,
+check_debug_info_structural (Dwarf *dwarf, Ebl *ebl,
struct section_data *data,
struct abbrev_table *abbrev_chain)
{
struct read_ctx ctx;
- read_ctx_init (&ctx, dwarf, data->secdata);
+ read_ctx_init (&ctx, dwarf, data->data);
Elf_Data *strings = dwarf->sectiondata[IDX_debug_str];
struct ref_record die_refs;
strings_coverage = &strings_coverage_mem;
}
+ struct relocation_data *reloc = data->rel.data != NULL ? &data->rel : NULL;
while (!read_ctx_eof (&ctx))
{
const unsigned char *cu_begin = ctx.ptr;
cu_ctx.ptr = ctx.ptr;
if (!check_cu_structural (&cu_ctx, cur, abbrev_chain,
- strings,
- dwarf_64, &die_refs,
- strings_coverage))
+ strings, dwarf_64, &die_refs,
+ strings_coverage, reloc, ebl))
{
success = false;
break;
struct cu *cu_chain)
{
struct read_ctx ctx;
- read_ctx_init (&ctx, dwarf, data->secdata);
+ read_ctx_init (&ctx, dwarf, data->data);
bool retval = true;
while (!read_ctx_eof (&ctx))
and ending addresses are equal has no effect. */
wr_message (cat | mc_acc_bloat | mc_impact_3, &where,
": entry covers no range.\n");
- /* Skip coverage analysis if we have errors or have no base. */
- else if (base < (uint64_t)-2 && retval)
+ /* Skip coverage analysis if we have errors or have no base
+ (or just don't do coverage analysis at all). */
+ else if (base < (uint64_t)-2 && retval && coverage_map != NULL)
{
uint64_t address = begin_addr + base;
uint64_t length = end_addr - begin_addr;
assert (cu_chain != NULL);
struct read_ctx ctx;
- read_ctx_init (&ctx, dwarf, data->secdata);
+ read_ctx_init (&ctx, dwarf, data->data);
bool retval = true;
Ebl *ebl,
bool elf_64)
{
- assert (data->reltype == SHT_REL || data->reltype == SHT_RELA);
+ assert (data->rel.type == SHT_REL || data->rel.type == SHT_RELA);
bool retval = true;
struct read_ctx ctx;
- read_ctx_init (&ctx, dwarf, data->secdata);
+ read_ctx_init (&ctx, dwarf, data->data);
- bool is_rela = data->reltype == SHT_RELA;
+ bool is_rela = data->rel.type == SHT_RELA;
size_t entrysize
= elf_64
? (is_rela ? sizeof (Elf64_Rela) : sizeof (Elf64_Rel))
: (is_rela ? sizeof (Elf32_Rela) : sizeof (Elf32_Rel));
- unsigned entries = data->reldata->d_size / entrysize;
+ data->rel.count = data->rel.data->d_size / entrysize;
struct where parent = WHERE (data->sec, NULL);
struct where where = WHERE (is_rela ? sec_rela : sec_rel, NULL);
where.ref = &parent;
- for (unsigned i = 0; i < entries; ++i)
+ for (unsigned i = 0; i < data->rel.count; ++i)
{
where_reset_1 (&where, i);
GElf_Rela rela_mem = {0, 0, 0};
GElf_Rela *rela = is_rela
- ? gelf_getrela (data->reldata, i, &rela_mem)
- : (GElf_Rela *)gelf_getrel (data->reldata, i, (GElf_Rel *)(&rela_mem));
+ ? gelf_getrela (data->rel.data, i, &rela_mem)
+ : (GElf_Rela *)gelf_getrel (data->rel.data, i, (GElf_Rel *)(&rela_mem));
if (rela == NULL)
{
wr_error (&where, ": couldn't read relocation.\n");
int compar##BITS (const void *a, const void *b) \
{ \
typedef Elf##BITS##_Rela rel_t; \
- return ((rel_t *)b)->r_offset - ((rel_t *)a)->r_offset; \
+ return ((rel_t *)a)->r_offset - ((rel_t *)b)->r_offset; \
}
COMPAR (32);
COMPAR (64);
#undef COMPAR
- qsort (data->reldata->d_buf, entries, entrysize,
+ qsort (data->rel.data->d_buf, data->rel.count, entrysize,
elf_64 ? &compar64 : &compar32);
}