#define PRI_DIE "DIE 0x%" PRIx64
#define PRI_NOT_ENOUGH ": not enough data for %s.\n"
+struct elf_file
+{
+ Dwarf *dwarf;
+ Ebl *ebl;
+
+ enum section_id *sec;
+ size_t size;
+ size_t alloc;
+};
+
struct relocation_data
{
+ struct elf_file *elf;
Elf_Data *data; /* May be NULL if there are no associated
relocation data. */
Elf_Data *symdata; /* Symbol table associated with this
struct section_data *data,
struct cu *cu_chain);
-static void check_location_expression (struct read_ctx *ctx,
+static bool check_location_expression (struct read_ctx *ctx,
+ struct relocation_data *reloc,
+ size_t length,
struct where *wh,
bool addr_64);
[sec_str] = {".debug_str", "offset", "%#"PRIx64,
NULL, NULL, NULL, NULL},
+ [sec_line] = {".debug_line", NULL, NULL, NULL, NULL, NULL, NULL},
+
[sec_loc] = {".debug_loc", "loclist", "%#"PRIx64,
"offset", "%#"PRIx64, NULL, NULL},
- [sec_locexpr] = {"location expression", "offset", "%#"PRIx64,
- NULL, NULL, NULL, NULL},
+ [sec_mac] = {".debug_mac", NULL, NULL, NULL, NULL, NULL, NULL},
[sec_ranges] = {".debug_ranges", "rangelist", "%#"PRIx64,
"offset", "%#"PRIx64, NULL, NULL},
+ [sec_locexpr] = {"location expression", "offset", "%#"PRIx64,
+ NULL, NULL, NULL, NULL},
+
[sec_rel] = {".rel", "relocation", "%"PRId64,
"offset", "%#"PRIx64, NULL, NULL},
[sec_rela] = {".rela", "relocation", "%"PRId64,
"offset", "%#"PRIx64, NULL, NULL},
+ [sec_text] = {".debug_text", NULL, NULL, NULL, NULL, NULL, NULL},
};
assert (wh->section < sizeof (section_names) / sizeof (*section_names));
struct read_ctx ctx;
+ Ebl *ebl = ebl_openbackend (dwarf->elf);
+ struct elf_file elf = {dwarf, ebl, NULL, 0, 0};
+
#define DEF_SECDATA(VAR, SEC) \
- struct section_data VAR = {SEC, NULL, {NULL, NULL, 0, 0, 0}}
+ struct section_data VAR = {SEC, NULL, {&elf, NULL, NULL, 0, 0, 0}}
DEF_SECDATA (abbrev_data, sec_abbrev);
DEF_SECDATA (aranges_data, sec_aranges);
DEF_SECDATA (info_data, sec_info);
+ DEF_SECDATA (line_data, sec_line);
DEF_SECDATA (loc_data, sec_loc);
DEF_SECDATA (pubnames_data, sec_pubnames);
DEF_SECDATA (pubtypes_data, sec_pubtypes);
{
const char *name;
struct section_data *dataptr;
+ enum section_id sec;
};
struct secinfo secinfo[] = {
- {".debug_abbrev", &abbrev_data},
- {".debug_aranges", &aranges_data},
- {".debug_info", &info_data},
- {".debug_loc", &loc_data},
- {".debug_pubnames", &pubnames_data},
- {".debug_pubtypes", &pubtypes_data},
- {".debug_ranges", &ranges_data},
+#define DEF_SECINFO(SEC) {".debug_" #SEC, &SEC##_data, sec_##SEC}
+#define DEF_NOINFO(SEC) {".debug_" #SEC, NULL, sec_##SEC}
+ DEF_SECINFO (abbrev),
+ DEF_SECINFO (aranges),
+ DEF_SECINFO (info),
+ DEF_SECINFO (line),
+ DEF_SECINFO (loc),
+ DEF_SECINFO (pubnames),
+ DEF_SECINFO (pubtypes),
+ DEF_SECINFO (ranges),
+ DEF_NOINFO (str),
+ {".text", NULL, sec_text}
+#undef DEF_NOINFO
+#undef DEF_SECINFO
};
Elf_Scn *reloc_symtab = NULL;
- struct section_data *find_secdata (const char *secname)
+ struct secinfo *find_secentry (const char *secname)
{
for (size_t i = 0; i < sizeof (secinfo) / sizeof (*secinfo); ++i)
if (strcmp (secinfo[i].name, secname) == 0)
- return secinfo[i].dataptr;
+ return secinfo + i;
return NULL;
}
+ struct section_data *find_secdata (const char *secname)
+ {
+ struct secinfo *info = find_secentry (secname);
+ if (info != NULL)
+ return info->dataptr;
+ else
+ return NULL;
+ }
+
/* Now find all necessary debuginfo sections and associated
relocation sections. */
Elf_Scn *scn = NULL;
GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (dwarf->elf, &ehdr_mem);
- Ebl *ebl = ebl_openbackend (dwarf->elf);
if (ehdr == NULL || ebl == NULL)
goto invalid_elf;
bool elf_64 = ehdr->e_ident[EI_CLASS] == ELFCLASS64;
+
+ /* Section 0 is special, skip it. */
+ REALLOC (&elf, sec);
+ elf.sec[elf.size++] = sec_invalid;
+
while ((scn = elf_nextscn (dwarf->elf, scn)) != NULL)
{
GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
if (scnname == NULL)
goto invalid_elf;
- struct section_data *secdata = find_secdata (scnname);
+ struct secinfo *secentry = find_secentry (scnname);
+ struct section_data *secdata = secentry != NULL ? secentry->dataptr : NULL;
+
+ REALLOC (&elf, sec);
+ elf.sec[elf.size++] = secentry != NULL ? secentry->sec : sec_invalid;
+
if (secdata != NULL)
{
if (secdata->data == NULL)
/* Check relocation sections that we've got. */
for (size_t i = 0; i < sizeof (secinfo) / sizeof (*secinfo); ++i)
- if (secinfo[i].dataptr->rel.data != NULL)
+ if (secinfo[i].dataptr != NULL
+ && secinfo[i].dataptr->rel.data != NULL)
{
if (secinfo[i].dataptr->data == NULL)
{
abbrev_table_free (abbrev_chain);
if (ebl != NULL)
ebl_closebackend (ebl);
+ free (elf.sec);
}
static void
}
static GElf_Rela *
-next_relocation (struct relocation_data *reloc, uint64_t offset,
- GElf_Rela *rela_mem, struct where *where)
+relocation_next (struct relocation_data *reloc, uint64_t offset,
+ GElf_Rela *rela_mem, struct where *where,
+ bool skip_is_legal)
{
if (reloc == NULL)
return NULL;
reloc->index++;
- if (rela->r_offset != offset)
+ if (rela->r_offset < offset)
{
- wr_error (&reloc_where,
- ": relocation of %#" PRIx64 " is mismatched.\n",
- rela->r_offset);
+ if (!skip_is_legal)
+ wr_error (&reloc_where,
+ ": relocation of %#" PRIx64 " is mismatched.\n",
+ rela->r_offset);
continue;
}
return NULL;
}
+static void
+relocation_skip (struct relocation_data *reloc, uint64_t offset,
+ struct where *where, bool skip_is_legal)
+{
+ GElf_Rela rela_mem;
+ relocation_next (reloc, offset, &rela_mem, where, skip_is_legal);
+}
+
static void
relocate_one (struct relocation_data *reloc, GElf_Rela *rela,
Elf *elf, Ebl *ebl, unsigned width,
return;
}
- uint64_t nv = rela->r_addend + symbol->st_value;
- /*
- wr_warning (where,
- ": relocation %#lx st_shndx %d: %#lx->%#lx.\n",
- rela->r_offset, symbol->st_shndx, *value, nv);
- */
-
+ *value = rela->r_addend + symbol->st_value;
uint64_t section_index = symbol->st_shndx;
/* It's target value, not section offset. */
": associated section #%" PRId64 " isn't SHF_ALLOC.\n",
section_index);
}
- *value = nv;
}
- /* XXX If symtab[symndx].st_shndx does not match the
- expected debug section's index, complain */
+ else
+ {
+ enum section_id id;
+ /* If symtab[symndx].st_shndx does not match the expected
+ debug section's index, complain. */
+ if (section_index >= reloc->elf->size)
+ wr_error (&reloc_where,
+ ": invalid associated section #%" PRId64 ".\n",
+ section_index);
+ else if ((id = reloc->elf->sec[section_index]) != offset_into)
+ {
+ char *wh1 = strdup (where_fmt (&WHERE (id, NULL), NULL));
+ char *wh2 = strdup (where_fmt (&WHERE (offset_into, NULL), NULL));
+ wr_error (&reloc_where,
+ ": relocation references section %s, but %s was expected.\n",
+ wh1, wh2);
+ free (wh2);
+ free (wh1);
+ }
+ }
}
}
return -1;
}
- if ((rela = next_relocation (reloc, offset, &rela_mem, &where)))
+ if ((rela = relocation_next (reloc, offset, &rela_mem,
+ &where, false)))
relocate_one (reloc, rela, elf, ebl, dwarf_64 ? 8 : 4,
&addr, &where, sec_str);
if (!read_ctx_read_offset (ctx, addr_64, &addr))
goto cant_read;
- if ((rela = next_relocation (reloc, offset, &rela_mem, &where)))
- relocate_one (reloc, rela, elf, ebl, dwarf_64 ? 8 : 4,
+ if ((rela = relocation_next (reloc, offset, &rela_mem,
+ &where, false)))
+ relocate_one (reloc, rela, elf, ebl, addr_64 ? 8 : 4,
&addr, &where, reloc_target (form, it));
if (form == DW_FORM_ref_addr)
goto cant_read;
uint64_t value = raw_value;
- if ((rela = next_relocation (reloc, offset, &rela_mem, &where)))
+ if ((rela = relocation_next (reloc, offset, &rela_mem,
+ &where, false)))
relocate_one (reloc, rela, elf, ebl, 1,
&value, &where, reloc_target (form, it));
goto cant_read;
uint64_t value = raw_value;
- if ((rela = next_relocation (reloc, offset, &rela_mem, &where)))
+ if ((rela = relocation_next (reloc, offset, &rela_mem,
+ &where, false)))
relocate_one (reloc, rela, elf, ebl, 2,
&value, &where, reloc_target (form, it));
goto cant_read;
uint64_t value = raw_value;
- if ((rela = next_relocation (reloc, offset, &rela_mem, &where)))
+ if ((rela = relocation_next (reloc, offset, &rela_mem,
+ &where, false)))
relocate_one (reloc, rela, elf, ebl, 4,
&value, &where, reloc_target (form, it));
uint64_t value;
if (!read_ctx_read_8ubyte (ctx, &value))
goto cant_read;
- if ((rela = next_relocation (reloc, offset, &rela_mem, &where)))
+ if ((rela = relocation_next (reloc, offset, &rela_mem,
+ &where, false)))
relocate_one (reloc, rela, elf, ebl, 8,
&value, &where, reloc_target (form, it));
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 (&where, PRI_NOT_ENOUGH, "location expression");
- return -1;
- }
-
- check_location_expression (&sub_ctx, &where, addr_64);
+ if (!check_location_expression (ctx, reloc, length,
+ &where, addr_64))
+ return -1;
}
+ else
+ relocation_skip (reloc,
+ read_ctx_get_offset (ctx) + length,
+ &where, false);
if (!read_ctx_skip (ctx, length))
goto cant_read;
return false;
}
GElf_Rela rela_mem, *rela;
- if ((rela = next_relocation (reloc, offset, &rela_mem, &cu->where)))
+ if ((rela = relocation_next (reloc, offset, &rela_mem,
+ &cu->where, false)))
relocate_one (reloc, rela, ctx->dbg->elf, ebl, dwarf_64 ? 8 : 4,
&abbrev_offset, &cu->where, sec_abbrev);
};
}
-static void
-check_location_expression (struct read_ctx *ctx, struct where *wh, bool addr_64)
+static bool
+check_location_expression (struct read_ctx *parent_ctx,
+ struct relocation_data *reloc,
+ size_t length,
+ struct where *wh,
+ bool addr_64)
{
+ struct read_ctx ctx;
+ if (!read_ctx_init_sub (&ctx, parent_ctx, parent_ctx->ptr,
+ parent_ctx->ptr + length))
+ {
+ wr_error (wh, PRI_NOT_ENOUGH, "location expression");
+ return false;
+ }
+
+ /* XXX Just skip relocations for now. */
+ if (reloc != NULL)
+ relocation_skip (reloc,
+ read_ctx_get_offset (parent_ctx) + length,
+ wh, true);
+
struct ref_record oprefs;
memset (&oprefs, 0, sizeof (oprefs));
struct addr_record opaddrs;
memset (&opaddrs, 0, sizeof (opaddrs));
- while (!read_ctx_eof (ctx))
+ while (!read_ctx_eof (&ctx))
{
struct where where = WHERE (sec_locexpr, wh);
- uint64_t opcode_off = read_ctx_get_offset (ctx);
+ uint64_t opcode_off = read_ctx_get_offset (&ctx);
where_reset_1 (&where, opcode_off);
addr_record_add (&opaddrs, opcode_off);
uint8_t opcode;
- if (!read_ctx_read_ubyte (ctx, &opcode))
+ if (!read_ctx_read_ubyte (&ctx, &opcode))
{
wr_error (&where, ": can't read opcode.\n");
break;
#define READ_FORM(OP, STR, PTR) \
do { \
if (OP != 0 \
- && !read_ctx_read_form (ctx, addr_64, (OP), \
+ && !read_ctx_read_form (&ctx, addr_64, (OP), \
PTR, &where, STR " operand")) \
{ \
wr_error (&where, ": opcode \"%s\"" \
wr_message (mc_loc | mc_acc_bloat | mc_impact_3, &where,
": %s with skip 0.\n",
dwarf_locexpr_opcode_string (opcode));
- else if (skip > 0 && !read_ctx_need_data (ctx, (size_t)skip))
+ else if (skip > 0 && !read_ctx_need_data (&ctx, (size_t)skip))
wr_error (&where, ": %s branches out of location expression.\n",
dwarf_locexpr_opcode_string (opcode));
/* Compare with the offset after the two-byte skip value. */
- else if (skip < 0 && ((uint64_t)-skip) > read_ctx_get_offset (ctx))
+ else if (skip < 0 && ((uint64_t)-skip) > read_ctx_get_offset (&ctx))
wr_error (&where,
": %s branches before the beginning of location expression.\n",
dwarf_locexpr_opcode_string (opcode));
addr_record_free (&opaddrs);
ref_record_free (&oprefs);
+
+ return true;
}
static bool
}
/* 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 (&where, PRI_NOT_ENOUGH, "location expression");
- return false;
- }
-
uint64_t expr_start = read_ctx_get_offset (ctx);
- check_location_expression (&expr_ctx, &where, addr_64);
+ if (!check_location_expression (ctx, NULL, len, &where, addr_64))
+ return false;
uint64_t expr_end = read_ctx_get_offset (ctx);
if (!overlap
&& !coverage_pristine (coverage,
HAVE_OVERLAP;
if (!read_ctx_skip (ctx, len))
- /* "can't happen" */
- goto not_enough;
+ {
+ /* "can't happen" */
+ wr_error (&where, PRI_NOT_ENOUGH, "location expression");
+ return false;
+ }
}
}
else