/* * dwarf_parameter.cpp * * Created on: Feb 1, 2020 * Author: nnosov */ #include #include "dwarf_parameter.h" // // pst_type // void pst_type_init(pst_type* t, const char* name, uint32_t type) { list_node_init(&t->node); t->name = pst_dup(&allocator, name); t->type = type; t->allocated = false; } pst_type* pst_type_new(const char* name, uint32_t type) { pst_type* nt = (pst_type*)allocator.alloc(&allocator, sizeof(pst_type)); if(nt) { pst_type_init(nt, name, type); nt->allocated = true; } return nt; } void pst_type_fini(pst_type* t) { allocator.free(&allocator, t->name); if(t->allocated) { allocator.free(&allocator, t); } } // // pst_parameter // bool param_print_dwarf(pst_parameter* param) { if(list_count(¶m->types)) { if(!param->is_return) { if(param->has_value) { param->ctx->print(param->ctx, "%s %s = 0x%lX", param->next_type(NULL)->name, param->name, param->location.value); } else { param->ctx->print(param->ctx, "%s %s = ", param->next_type(NULL)->name, param->name); } } else { param->ctx->print(param->ctx, "%s", param->next_type(NULL)->name); } } else { if(param->has_value) { param->ctx->print(param->ctx, "%s = 0x%lX", param->name, param->location.value); } else { param->ctx->print(param->ctx, "%s = ", param->name); } } return true; } bool param_handle_type(pst_parameter* param, Dwarf_Attribute* base) { Dwarf_Attribute attr_mem; Dwarf_Attribute* attr; // get DIE of return type Dwarf_Die ret_die; if(!dwarf_formref_die(base, &ret_die)) { logger.log(SEVERITY_ERROR, "Failed to get parameter DIE"); return false; } switch (dwarf_tag(&ret_die)) { case DW_TAG_base_type: { // get Size attribute and it's value param->size = 0; attr = dwarf_attr(&ret_die, DW_AT_byte_size, &attr_mem); if(attr) { dwarf_formudata(attr, ¶m->size); } logger.log(SEVERITY_DEBUG, "base type '%s'(%lu)", dwarf_diename(&ret_die), param->size); param->add_type(dwarf_diename(&ret_die), DW_TAG_base_type); param->type = DW_TAG_base_type; attr = dwarf_attr(&ret_die, DW_AT_encoding, &attr_mem); if(attr) { param->enc_type = 0; dwarf_formudata(attr, ¶m->enc_type); } break; } case DW_TAG_array_type: logger.log(SEVERITY_DEBUG, "array type"); param->add_type("[]", DW_TAG_array_type); break; case DW_TAG_structure_type: logger.log(SEVERITY_DEBUG, "structure type"); param->add_type("struct", DW_TAG_structure_type); break; case DW_TAG_union_type: logger.log(SEVERITY_DEBUG, "union type"); param->add_type("union", DW_TAG_union_type); break; case DW_TAG_class_type: logger.log(SEVERITY_DEBUG, "class type"); param->add_type("class", DW_TAG_class_type); break; case DW_TAG_pointer_type: logger.log(SEVERITY_DEBUG, "pointer type"); param->add_type("*", DW_TAG_pointer_type); break; case DW_TAG_enumeration_type: logger.log(SEVERITY_DEBUG, "enumeration type"); param->add_type("enum", DW_TAG_enumeration_type); break; case DW_TAG_const_type: logger.log(SEVERITY_DEBUG, "constant type"); param->add_type("const", DW_TAG_const_type); break; case DW_TAG_subroutine_type: logger.log(SEVERITY_DEBUG, "Skipping subroutine type"); break; case DW_TAG_typedef: logger.log(SEVERITY_DEBUG, "typedef '%s' type", dwarf_diename(&ret_die)); param->add_type(dwarf_diename(&ret_die), DW_TAG_typedef); break; default: logger.log(SEVERITY_WARNING, "Unknown 0x%X tag type", dwarf_tag(&ret_die)); break; } attr = dwarf_attr(&ret_die, DW_AT_type, &attr_mem); if(attr) { return param->handle_type(attr); } return true; } pst_type* param_add_type(pst_parameter* param, const char* name, int type) { pst_alloc(pst_type, t, name, type); list_add_bottom(¶m->types, &t->node); return t; } void param_clear(pst_parameter* param) { pst_type* t = NULL; struct list_node *pos, *tn; list_for_each_entry_safe(t, pos, tn, ¶m->types, node) { list_del(&t->node); pst_type_fini(t); } } pst_type* param_next_type(pst_parameter* param, pst_type* t) { list_node* n = (t == NULL) ? list_first(¶m->types) : list_next(&t->node); pst_type* ret = NULL; if(n) { ret = list_entry(n, pst_type, node); } return ret; } bool param_handle_dwarf(pst_parameter* param, Dwarf_Die* result, __pst_function* fun) { param->die = result; Dwarf_Attribute attr_mem; Dwarf_Attribute* attr; param->name = pst_dup(&allocator, dwarf_diename(result)); param->is_variable = (dwarf_tag(result) == DW_TAG_variable); dwarf_decl_line(result, (int*)¶m->line); // Get reference to attribute type of the parameter/variable attr = dwarf_attr(result, DW_AT_type, &attr_mem); logger.log(SEVERITY_DEBUG, "---> Handle '%s' %s", param->name, dwarf_tag(result) == DW_TAG_formal_parameter ? "parameter" : "variable"); if(attr) { param->handle_type(param, attr); } if(dwarf_hasattr(result, DW_AT_location)) { // determine location of parameter in stack/heap or CPU registers attr = dwarf_attr(result, DW_AT_location, &attr_mem); Dwarf_Addr pc; unw_get_reg(param->ctx->curr_frame, UNW_REG_IP, &pc); if(handle_location(param->ctx, attr, ¶m->location, pc, fun)) { param->has_value = true; } else { logger.log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", param->ctx->buff); return false; } } else if(dwarf_hasattr(result, DW_AT_const_value)) { // no locations definitions, value is constant, known by DWARF directly attr = dwarf_attr(result, DW_AT_const_value, &attr_mem); switch (dwarf_whatform(attr)) { case DW_FORM_string: // do nothing for now logger.log(SEVERITY_WARNING, "Const value form DW_FORM_string value = %s.", dwarf_formstring(attr)); break; case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: case DW_FORM_data8: dwarf_formudata(attr, ¶m->location.value); param->has_value = true; break; case DW_FORM_sdata: dwarf_formsdata(attr, (int64_t*)¶m->location.value); param->has_value = true; break; case DW_FORM_udata: dwarf_formudata(attr, ¶m->location.value); param->has_value = true; break; } if(param->has_value) { logger.log(SEVERITY_DEBUG, "Parameter constant value: 0x%lX", param->location.value); } } // Additionally handle these attributes: // 1. DW_AT_default_value to get information about default value for DW_TAG_formal_parameter type of function // A DW_AT_default_value attribute for a formal parameter entry. The value of // this attribute may be a constant, or a reference to the debugging information // entry for a variable, or a reference to a debugging information entry containing a DWARF procedure // 2. DW_AT_variable_parameter // A DW_AT_variable_parameter attribute, which is a flag, if a formal // parameter entry represents a parameter whose value in the calling function // may be modified by the callee. The absence of this attribute implies that the // parameter’s value in the calling function cannot be modified by the callee. // 3. DW_AT_abstract_origin // In place of these omitted attributes, each concrete inlined instance entry has a DW_AT_abstract_origin attribute that may be used to obtain the // missing information (indirectly) from the associated abstract instance entry. The value of the abstract origin attribute is a reference to the associated abstract // instance entry. return true; } void pst_parameter_init(pst_parameter*param, pst_context* ctx) { // methods param->clear = param_clear; param->add_type = param_add_type; param->next_type = param_next_type; param->handle_dwarf = param_handle_dwarf; param->handle_type = param_handle_type; param->print_dwarf = param_print_dwarf; // fields list_node_init(¶m->node); param->die = NULL; param->name = NULL; param->line = 0; param->size = 0; param->type = 0; param->enc_type = 0; list_head_init(¶m->types); param->is_return = false; param->is_variable = false; param->has_value = false; param->ctx = ctx; pst_dwarf_expr_init(¶m->location); param->allocated = false; } pst_parameter* pst_parameter_new(pst_context* ctx) { pst_parameter* param = (pst_parameter*)allocator.alloc(&allocator, sizeof(pst_parameter)); if(param) { pst_parameter_init(param, ctx); param->allocated = true; } return param; } void pst_parameter_fini(pst_parameter* param) { param->clear(); if(param->name) { allocator.free(param->name); } if(param->allocated) { allocator.free(&allocator, param); } }