Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for compressed DWARF .zdebug_* sections #3819

Merged
merged 7 commits into from
Sep 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 70 additions & 29 deletions librz/analysis/dwarf_process.c
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,7 @@ static RzBaseType *RzBaseType_from_die(Context *ctx, const RzBinDwarfDie *die) {
goto err;
}
break;
case DW_TAG_unspecified_type:
case DW_TAG_base_type:
RzBaseType_NEW_CHECKED(btype, RZ_BASE_TYPE_KIND_ATOMIC);
break;
Expand Down Expand Up @@ -723,13 +724,12 @@ static RzBaseType *RzBaseType_from_die(Context *ctx, const RzBinDwarfDie *die) {
rz_pvector_foreach (btypes, it) {
RzBaseType *b = *it;
if (RzBaseType_eq(btype, b)) {
goto skip_btype;
goto ok;
}
}
rz_pvector_push(btypes, btype);
}

skip_btype:
ok:
return btype;
err:
rz_type_base_type_free(btype);
Expand Down Expand Up @@ -794,6 +794,28 @@ static RzType *type_parse_from_die_internal(
return type_parse_from_offset_internal(ctx, attr->reference, size, visited);
}

static void RzType_from_base_type(RzType *t, RzBaseType *b) {
rz_return_if_fail(t && b);
t->kind = RZ_TYPE_KIND_IDENTIFIER;
free(t->identifier.name);
t->identifier.name = rz_str_new(b->name);
switch (b->kind) {
case RZ_BASE_TYPE_KIND_STRUCT:
t->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_STRUCT;
break;
case RZ_BASE_TYPE_KIND_UNION:
t->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_UNION;
break;
case RZ_BASE_TYPE_KIND_ENUM:
t->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_ENUM;
break;
case RZ_BASE_TYPE_KIND_TYPEDEF:
case RZ_BASE_TYPE_KIND_ATOMIC:
t->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_UNSPECIFIED;
break;
}
}

/**
* \brief Recursively parses type entry of a certain offset and saves type size into *size
*
Expand Down Expand Up @@ -830,7 +852,8 @@ static RZ_OWN RzType *type_parse_from_offset_internal(
// this should be recursive search for the type until you find base/user defined type
case DW_TAG_pointer_type:
case DW_TAG_reference_type: // C++ references are just pointers to us
case DW_TAG_rvalue_reference_type: {
case DW_TAG_rvalue_reference_type:
case DW_TAG_ptr_to_member_type: {
RzType *pointee = type_parse_from_die_internal(ctx, die, true, size, visited);
if (!pointee) {
goto end;
Expand All @@ -849,12 +872,18 @@ static RZ_OWN RzType *type_parse_from_offset_internal(
case DW_TAG_structure_type:
case DW_TAG_enumeration_type:
case DW_TAG_union_type:
case DW_TAG_class_type: {
case DW_TAG_class_type:
case DW_TAG_unspecified_type: {
const char *const_name = die_name_const(die);
type = RZ_NEW0(RzType);
if (!type) {
goto end;
}
RzBaseType *ref = ht_up_find(ctx->analysis->debug_info->base_type_by_offset, offset, NULL);
if (ref) {
RzType_from_base_type(type, ref);
break;
}
RzBaseTypeKind k = RZ_BASE_TYPE_KIND_STRUCT;
switch (die->tag) {
case DW_TAG_structure_type:
Expand All @@ -870,6 +899,7 @@ static RZ_OWN RzType *type_parse_from_offset_internal(
type->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_ENUM;
k = RZ_BASE_TYPE_KIND_ENUM;
break;
case DW_TAG_unspecified_type:
default:
type->identifier.kind = RZ_TYPE_IDENTIFIER_KIND_UNSPECIFIED;
break;
Expand Down Expand Up @@ -932,13 +962,14 @@ static RZ_OWN RzType *type_parse_from_offset_internal(
break;
}

if (type) {
RzType *copy = rz_type_clone(type);
if (!ht_up_insert(ctx->analysis->debug_info->type_by_offset, offset, copy)) {
RZ_LOG_ERROR("Failed to insert type [%s] into debug_info->type_by_offset\n", rz_type_as_string(ctx->analysis->typedb, type));
rz_type_free(copy);
}
RzType *copy = type ? rz_type_clone(type) : NULL;
if (copy && ht_up_insert(ctx->analysis->debug_info->type_by_offset, offset, copy)) {
RZ_LOG_DEBUG("Insert RzType [%s] into type_by_offset\n", rz_type_as_string(ctx->analysis->typedb, type));
} else {
RZ_LOG_ERROR("Failed to insert RzType [0x%" PFMT64x "] into type_by_offset\n", offset);
rz_type_free(copy);
}

end:
set_u_delete(visited, offset);
return type;
Expand Down Expand Up @@ -999,7 +1030,10 @@ static RzType *type_parse_from_abstract_origin(Context *ctx, ut64 offset, char *
* \brief Parses structured entry into *result RzTypeStructMember
* https://www.dwarfstd.org/doc/DWARF4.pdf#page=102
*/
static RzTypeStructMember *struct_member_parse(Context *ctx, RzBinDwarfDie *die, RzTypeStructMember *result) {
static RzTypeStructMember *struct_member_parse(
Context *ctx,
RzBinDwarfDie *die,
RzTypeStructMember *result) {
rz_return_val_if_fail(result, NULL);
char *name = NULL;
RzType *type = NULL;
Expand Down Expand Up @@ -1086,8 +1120,9 @@ static bool struct_union_children_parse(
rz_pvector_foreach (children, it) {
RzBinDwarfDie *child_die = *it;
// we take only direct descendats of the structure
// can be also DW_TAG_suprogram for class methods or tag for templates
if (child_die->tag != DW_TAG_member) {
if (!(child_die->depth == die->depth + 1 &&
child_die->tag == DW_TAG_member)) {
parse_die(ctx, child_die);
continue;
}
RzTypeStructMember member = { 0 };
Expand Down Expand Up @@ -1160,7 +1195,9 @@ static bool enum_children_parse(
void **it;
rz_pvector_foreach (children, it) {
RzBinDwarfDie *child_die = *it;
if (child_die->tag != DW_TAG_enumerator) {
if (!(child_die->depth == die->depth + 1 &&
child_die->tag == DW_TAG_enumerator)) {
parse_die(ctx, child_die);
continue;
}
RzTypeEnumCase cas = { 0 };
Expand Down Expand Up @@ -1212,8 +1249,8 @@ static void function_apply_specification(Context *ctx, const RzBinDwarfDie *die,
}
}

static void log_block(Context *ctx, const RzBinDwarfBlock *block, ut64 offset, const RzBinDwarfRange *range) {
char *expr_str = rz_bin_dwarf_expression_to_string(&ctx->dw->encoding, block);
static void RzBinDwarfBlock_log(Context *ctx, const RzBinDwarfBlock *block, ut64 offset, const RzBinDwarfRange *range) {
char *expr_str = rz_bin_dwarf_expression_to_string(&ctx->dw->encoding, block, ctx->dw->loc->big_endian);
if (RZ_STR_ISNOTEMPTY(expr_str)) {
if (!range) {
RZ_LOG_VERBOSE("Location parse failed: 0x%" PFMT64x " [%s]\n", offset, expr_str);
Expand Down Expand Up @@ -1261,7 +1298,7 @@ static RzBinDwarfLocation *location_list_parse(
}
entry->location = rz_bin_dwarf_location_from_block(entry->expression, ctx->dw, ctx->unit, fn);
if (!entry->location) {
log_block(ctx, entry->expression, loclist->offset, entry->range);
RzBinDwarfBlock_log(ctx, entry->expression, loclist->offset, entry->range);
entry->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_DECODE_ERROR);
continue;
}
Expand Down Expand Up @@ -1295,7 +1332,7 @@ static RzBinDwarfLocation *location_from_block(
RZ_LOG_ERROR("Location parse failed: 0x%" PFMT64x " %s\n", offset, msg);
return RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_DECODE_ERROR);
err_eval:
log_block(ctx, block, offset, NULL);
RzBinDwarfBlock_log(ctx, block, offset, NULL);
return RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_DECODE_ERROR);
empty_loc:
return RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_EMPTY);
Expand All @@ -1312,7 +1349,7 @@ static RzBinDwarfLocation *location_parse(
ut64 offset = attr->reference;
RzBinDwarfLocList *loclist = ht_up_find(ctx->dw->loc->loclist_by_offset, offset, NULL);
if (!loclist) { /* for some reason offset isn't there, wrong parsing or malformed dwarf */
if (!rz_bin_dwarf_loclist_table_parse_at(ctx->dw->loc, &ctx->unit->hdr.encoding, offset)) {
if (!rz_bin_dwarf_loclist_table_parse_at(ctx->dw->loc, ctx->unit, offset)) {
goto err_find;
}
loclist = ht_up_find(ctx->dw->loc->loclist_by_offset, offset, NULL);
Expand All @@ -1338,7 +1375,13 @@ static RzBinDwarfLocation *location_parse(
return NULL;
}

static bool function_var_parse(Context *ctx, RzAnalysisDwarfFunction *f, const RzBinDwarfDie *fn_die, RzAnalysisDwarfVariable *v, const RzBinDwarfDie *var_die, bool *has_unspecified_parameters) {
static bool function_var_parse(
Context *ctx,
RzAnalysisDwarfFunction *f,
const RzBinDwarfDie *fn_die,
RzAnalysisDwarfVariable *v,
const RzBinDwarfDie *var_die,
bool *has_unspecified_parameters) {
v->offset = var_die->offset;
switch (var_die->tag) {
case DW_TAG_formal_parameter:
Expand Down Expand Up @@ -1389,14 +1432,12 @@ static bool function_var_parse(Context *ctx, RzAnalysisDwarfFunction *f, const R
}
}

if (!v->name) {
v->name = anonymous_name("var", v->offset);
}
if (!has_location) {
v->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_EMPTY);
} else if (!v->location) {
v->location = RzBinDwarfLocation_with_kind(RzBinDwarfLocationKind_DECODE_ERROR);
}

v->prefer_name = select_name(NULL, v->link_name, v->name, ctx->unit->language);
if (!v->prefer_name) {
v->prefer_name = v->name = anonymous_name("var", var_die->offset);
Expand Down Expand Up @@ -1428,15 +1469,14 @@ static bool function_children_parse(Context *ctx, const RzBinDwarfDie *die, RzCa
callable->has_unspecified_parameters = true;
goto err;
}
if (!(v.location && v.type)) {
RZ_LOG_ERROR("DWARF function variable parse failed "
"%s f.addr=0x%" PFMT64x " f.offset=0x%" PFMT64x " [0x%" PFMT64x "]\n",
fn->prefer_name, fn->low_pc, die->offset, child_die->offset);
if (!v.type) {
RZ_LOG_ERROR("DWARF function %s variable %s failed\n",
fn->prefer_name, v.prefer_name);
goto err;
}
if (v.kind == RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER) {
RzCallableArg *arg = rz_type_callable_arg_new(
ctx->analysis->typedb, v.prefer_name ? v.prefer_name : "", rz_type_clone(v.type));
ctx->analysis->typedb, v.prefer_name, rz_type_clone(v.type));
rz_type_callable_arg_add(callable, arg);
}
rz_vector_push(&fn->variables, &v);
Expand Down Expand Up @@ -1582,6 +1622,7 @@ static void parse_die(Context *ctx, RzBinDwarfDie *die) {
case DW_TAG_class_type:
case DW_TAG_enumeration_type:
case DW_TAG_typedef:
case DW_TAG_unspecified_type:
case DW_TAG_base_type: {
RzBaseType_from_die(ctx, die);
break;
Expand Down
9 changes: 3 additions & 6 deletions librz/bin/dwarf/addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@

RZ_IPI bool DebugAddr_get_address(const RzBinDwarfDebugAddr *self, ut64 *address,
ut8 address_size, bool big_endian, ut64 base, ut64 index) {
RzBuffer *buffer = rz_buf_new_with_buf(self->buffer);
RzBuffer *buffer = self->buffer;
RET_FALSE_IF_FAIL(buffer);
rz_buf_seek(buffer, (st64)base, RZ_BUF_CUR);
rz_buf_seek(buffer, (st64)(index * address_size), RZ_BUF_CUR);
ut64 addr = 0;
UX_OR_RET_FALSE(address_size, addr);
*address = addr;
rz_buf_seek(buffer, (st64)(base + (index * address_size)), RZ_BUF_SET);
UX_OR_RET_FALSE(address_size, *address);
return true;
}

Expand Down
4 changes: 2 additions & 2 deletions librz/bin/dwarf/attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ RZ_IPI bool RzBinDwarfAttr_parse(RzBuffer *buffer, RzBinDwarfAttr *value, DwAttr
return false;
}

bool big_endian = opt->encoding.big_endian;
bool big_endian = opt->big_endian;

// http://www.dwarfstd.org/doc/DWARF4.pdf#page=161&zoom=100,0,560
switch (value->form) {
Expand Down Expand Up @@ -107,7 +107,7 @@ RZ_IPI bool RzBinDwarfAttr_parse(RzBuffer *buffer, RzBinDwarfAttr *value, DwAttr
break;
case DW_FORM_block: // variable length ULEB128
value->kind = DW_AT_KIND_BLOCK;
ULE128_OR_RET_NULL(value->block.length);
ULE128_OR_RET_FALSE(value->block.length);
RET_FALSE_IF_FAIL(buf_read_block(buffer, &value->block));
break;
case DW_FORM_flag:
Expand Down
22 changes: 14 additions & 8 deletions librz/bin/dwarf/dwarf.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ RZ_IPI RzBinSection *get_section(RzBinFile *binfile, const char *sn) {
return NULL;
}

typedef struct {
ut8 gch_magic[4]; /* [ 'Z', 'L', 'I', 'B'] */
ut8 gch_size[8]; /* unaligned 64-bit ELFDATAMSB integer */
} Chdr_GNU;

RZ_IPI RzBuffer *get_section_buf(RzBinFile *binfile, const char *sect_name) {
rz_return_val_if_fail(binfile && sect_name, NULL);
RzBinSection *section = get_section(binfile, sect_name);
Expand All @@ -37,14 +42,14 @@ RZ_IPI RzBuffer *get_section_buf(RzBinFile *binfile, const char *sect_name) {
return NULL;
}
ut64 len = RZ_MIN(section->size, binfile->size - section->paddr);
if (!(section->flags & SHF_COMPRESSED)) {
bool is_zlib_gnu = rz_str_startswith(section->name, ".zdebug");
if (!(section->flags & SHF_COMPRESSED || is_zlib_gnu)) {
return rz_buf_new_slice(binfile->buf, section->paddr, len);
}

bool is_64bit = binfile->o->info->bits == 64;
bool bigendian = bf_bigendian(binfile);
ut64 Elf_Chdr_size = is_64bit ? sizeof(Elf64_Chdr) : sizeof(Elf32_Chdr);
if (len < Elf_Chdr_size) {
ut64 Chdr_size = is_zlib_gnu ? sizeof(Chdr_GNU) : (is_64bit ? sizeof(Elf64_Chdr) : sizeof(Elf32_Chdr));
if (len < Chdr_size) {
RZ_LOG_ERROR("corrupted compressed section header\n");
return NULL;
}
Expand All @@ -57,10 +62,12 @@ RZ_IPI RzBuffer *get_section_buf(RzBinFile *binfile, const char *sect_name) {
if (rz_buf_read_at(binfile->buf, section->paddr, sh_buf, len) != len) {
goto err;
}
ut32 ch_type = rz_read_at_ble32(sh_buf, 0, bigendian);
bool bigendian = bf_bigendian(binfile);
ut32 ch_type = is_zlib_gnu ? ELFCOMPRESS_ZLIB
: rz_read_at_ble32(sh_buf, 0, bigendian);

const ut8 *src = sh_buf + Elf_Chdr_size;
ut64 src_len = len - Elf_Chdr_size;
const ut8 *src = sh_buf + Chdr_size;
ut64 src_len = len - Chdr_size;
ut64 uncompressed_len = 0;
ut8 *uncompressed = NULL;
RZ_LOG_VERBOSE("Section %s is compressed\n", section->name);
Expand Down Expand Up @@ -111,7 +118,6 @@ RZ_IPI bool RzBinDwarfEncoding_from_file(RzBinDwarfEncoding *encoding, RzBinFile
}
RzBinInfo *binfo = bf->o && bf->o->info ? bf->o->info : NULL;
encoding->address_size = binfo->bits ? binfo->bits / 8 : 4;
encoding->big_endian = binfo->big_endian;
return true;
}

Expand Down
10 changes: 8 additions & 2 deletions librz/bin/dwarf/dwarf_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ typedef struct {
};
RzBinDwarfDebugStr *debug_str;
RzBinDwarfEncoding encoding;
bool big_endian;
} DwAttrOption;

typedef RzBinDwarfValue Value;
Expand Down Expand Up @@ -65,13 +66,18 @@ RZ_IPI RzBinDwarfDebugAddr *DebugAddr_from_file(RzBinFile *bf);

/// range

RZ_IPI bool Range_parse(RzBinDwarfRange *self, RzBuffer *buffer, RzBinDwarfEncoding *encoding);
RZ_IPI bool Range_parse(RzBinDwarfRange *self, RzBuffer *buffer, bool big_endian, ut8 address_size);
RZ_IPI bool Range_is_end(RzBinDwarfRange *self);
RZ_IPI bool Range_is_base_address(RzBinDwarfRange *self, ut8 address_size);
RZ_IPI void Range_add_base_address(RzBinDwarfRange *self, ut64 base_address, ut8 address_size);
RZ_IPI void Range_free(RzBinDwarfRange *self);

RZ_IPI bool RzBinDwarfRawRngListEntry_parse(RzBinDwarfRawRngListEntry *out, RzBuffer *buffer, RzBinDwarfEncoding *encoding, RzBinDwarfRngListsFormat format);
RZ_IPI bool RzBinDwarfRawRngListEntry_parse(
RzBinDwarfRawRngListEntry *out,
RzBuffer *buffer,
bool big_endian,
RzBinDwarfEncoding *encoding,
RzBinDwarfRngListsFormat format);
RZ_IPI void RzBinDwarfRngListTable_free(RzBinDwarfRngListTable *self);

/// value
Expand Down
Loading