diff --git a/framework/dwarf_call_site.cpp b/framework/dwarf_call_site.cpp new file mode 100644 index 0000000..788190e --- /dev/null +++ b/framework/dwarf_call_site.cpp @@ -0,0 +1,188 @@ +/* + * dwarf_call_site.cpp + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#include + +#include "dwarf_call_site.h" +#include "dwarf_utils.h" + +// ----------------------------------------------------------------------------------- +// pst_call_site_param +// ----------------------------------------------------------------------------------- +void param_set_location(pst_call_site_param* param, Dwarf_Op* expr, size_t exprlen) +{ + param->location.setup(¶m->location, expr, exprlen); +} + +void pst_call_site_param_init(pst_call_site_param* param) +{ + // methods + param->set_location = param_set_location; + + // fields + param->param = NULL; + param->name = NULL; + param->value = 0; + pst_dwarf_expr_init(¶m->location); + param->allocated = false; +} + +pst_call_site_param* pst_call_site_param_new() +{ + pst_call_site_param* param = (pst_call_site_param*)allocator.alloc(&allocator, sizeof(pst_call_site_param)); + if(param) { + pst_call_site_param_init(param); + param->allocated = true; + } + + return param; +} + +void pst_call_site_param_fini(pst_call_site_param* param) +{ + if(param->name) { + allocator.free(&allocator, param->name); + param->name = NULL; + } + + if(param->allocated) { + allocator.free(&allocator, param); + } +} + +// ----------------------------------------------------------------------------------- +// pst_call_site +// ----------------------------------------------------------------------------------- +pst_call_site_param* site_add_param(pst_call_site* site) +{ + pst_call_site_param* p = pst_call_site_param_new(); + pst_call_site_param_init(p); + list_add_bottom(&site->params, &p->node); + + return p; +} + +void site_del_param(pst_call_site*, pst_call_site_param* p) +{ + list_del(&p->node); + pst_call_site_param_fini(p); +} + +pst_call_site_param* site_next_param(pst_call_site* site, pst_call_site_param* p) +{ + list_node* n = (p == NULL) ? list_first(&site->params) : list_next(&p->node); + pst_call_site_param* ret = NULL; + if(n) { + ret = list_entry(n, pst_call_site_param, node); + } + + return ret; +} + +pst_call_site_param* site_find_param(pst_call_site* site, pst_dwarf_expr* expr) +{ + for(pst_call_site_param* param = site_next_param(site, NULL); param; param = site_next_param(site, param)) { + if(param->location.is_equal(¶m->location, expr)) { + return param; + } + } + + return NULL; +} + +bool site_handle_dwarf(pst_call_site* site, Dwarf_Die* child) +{ + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + do { + switch(dwarf_tag(child)) + { + case DW_TAG_GNU_call_site_parameter: { + Dwarf_Addr pc; + unw_get_reg(site->ctx->curr_frame, UNW_REG_IP, &pc); + + // expression represent where callee parameter will be stored + pst_call_site_param* param = site->add_param(site); + if(dwarf_hasattr(child, DW_AT_location)) { + // handle location expression here + // determine location of parameter in stack/heap or CPU registers + attr = dwarf_attr(child, DW_AT_location, &attr_mem); + if(!handle_location(site->ctx, attr, ¶m->location, pc, NULL)) { + site->ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", site->ctx->buff); + site->del_param(site, param); + return false; + } + } + + // expression represents call parameter's value + if(dwarf_hasattr(child, DW_AT_GNU_call_site_value)) { + // handle value expression here + attr = dwarf_attr(child, DW_AT_GNU_call_site_value, &attr_mem); + pst_dwarf_expr loc; + pst_dwarf_expr_init(&loc); + if(handle_location(site->ctx, attr, &loc, pc, NULL)) { + param->value = loc.value; + site->ctx->log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_value:\"%s\" ==> 0x%lX", site->ctx->buff, param->value); + } else { + site->ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", site->ctx->buff); + site->del_param(site, param); + return false; + } + } + break; + } + default: + break; + } + } while (dwarf_siblingof (child, child) == 0); + + return true; +} + +void pst_call_site_init(pst_call_site* site, pst_context* c, uint64_t tgt, const char* orn) { + // methods + + + // fields + list_node_init(&site->node); + + site->target = tgt; + if(orn) { + site->origin = strdup(orn); + } else { + site->origin = NULL; + } + site->die = NULL; + list_head_init(&site->params); + site->ctx = c; + site->allocated = false; +} + +pst_call_site* pst_call_site_new(pst_context* c, uint64_t tgt, const char* orn) +{ + pst_call_site* nc = (pst_call_site*)allocator.alloc(&allocator, sizeof(pst_call_site)); + if(nc) { + pst_call_site_init(nc, c, tgt, orn); + nc->allocated = true; + } + + return nc; +} + +void pst_call_site_fini(pst_call_site* site) +{ + if(site->origin) { + allocator.free(&allocator, site->origin); + site->origin = NULL; + } + + if(site->allocated) { + allocator.free(&allocator, site); + } +} + diff --git a/framework/dwarf_call_site.h b/framework/dwarf_call_site.h new file mode 100644 index 0000000..64fb943 --- /dev/null +++ b/framework/dwarf_call_site.h @@ -0,0 +1,63 @@ +/* + * dwarf_call_site.h + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#ifndef FRAMEWORK_DWARF_CALL_SITE_H_ +#define FRAMEWORK_DWARF_CALL_SITE_H_ + +#include +#include + +#include "list_head.h" +#include "context.h" +#include "dwarf_expression.h" + + +// ----------------------------------------------------------------------------------- +// DW_TAG_call_site_parameter +// ----------------------------------------------------------------------------------- +typedef struct pst_call_site_param { + list_node node; // uplink. !!! must be first !!! + + // methods + void (*set_location) (pst_call_site_param* param, Dwarf_Op* expr, size_t exprlen); + + // fields + Dwarf_Die* param; // reference to parameter DIE in callee (DW_AT_call_parameter) + char* name; // name of parameter if present (DW_AT_name) + pst_dwarf_expr location; // DWARF stack containing location expression + uint64_t value; // parameter's value + bool allocated; // whether this object was allocated or not +} pst_call_site_param; + +void pst_call_site_param_init(pst_call_site_param* param); +pst_call_site_param* pst_call_site_param_new(); +void pst_call_site_param_fini(pst_call_site_param* param); + +// ----------------------------------------------------------------------------------- +// DW_TAG_call_site +// ----------------------------------------------------------------------------------- +typedef struct pst_call_site { + list_node node; // uplink. !!! must be first !!! + + // methods + pst_call_site_param* (*add_param) (pst_call_site* site); + void (*del_param) (pst_call_site* site, pst_call_site_param* p); + pst_call_site_param* (*next_param) (pst_call_site* site, pst_call_site_param* p); + pst_call_site_param* (*find_param) (pst_call_site* site, pst_dwarf_expr& expr); + bool (*handle_dwarf) (pst_call_site* site, Dwarf_Die* die); + + // fields + uint64_t target; // pointer to callee function's (it's Low PC + base address) + char* origin; // name of callee function + Dwarf_Die* die; // DIE of function for which this call site has parameters + list_head params; // list of parameters and their values + pst_context* ctx; // execution context + bool allocated; // whether this object was allocated or not +} pst_call_site; + + +#endif /* FRAMEWORK_DWARF_CALL_SITE_H_ */ diff --git a/framework/dwarf_expression.cpp b/framework/dwarf_expression.cpp index d04b285..df71254 100644 --- a/framework/dwarf_expression.cpp +++ b/framework/dwarf_expression.cpp @@ -9,6 +9,8 @@ #include "dwarf_expression.h" #include "common.h" +#include "allocator.h" +#include "context.h" // // DWARF operation @@ -42,7 +44,7 @@ void pst_dwarf_op_fini(pst_dwarf_op* dwop) assert(dwop); if(dwop->allocated) { - allocator.free(dwop); + allocator.free(&allocator, dwop); } else { dwop->operation = 0; dwop->arg1 = 0; diff --git a/framework/dwarf_function.cpp b/framework/dwarf_function.cpp new file mode 100644 index 0000000..70aa6e7 --- /dev/null +++ b/framework/dwarf_function.cpp @@ -0,0 +1,539 @@ +/* + * dwarf_function.cpp + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#include "dwarf_function.h" + + +// ----------------------------------------------------------------------------------- +// pst_function +// ----------------------------------------------------------------------------------- +pst_parameter* __pst_function::add_param() +{ + pst_parameter* p = new pst_parameter(ctx); + params.InsertLast(p); + + return p; +} + +void __pst_function::del_param(pst_parameter* p) +{ + params.Remove(p); + delete p; +} + +void __pst_function::clear() +{ + for(pst_parameter* p = (pst_parameter*)params.First(); p; p = (pst_parameter*)params.First()) { + params.Remove(p); + delete p; + } + + for(pst_call_site* p = (pst_call_site*)call_sites.First(); p; p = (pst_call_site*)call_sites.First()) { + call_sites.Remove(p); + delete p; + } + + mCallStToTarget.Clean(); + mCallStToTarget.Clean(); +} + +pst_parameter* __pst_function::next_param(pst_parameter* p) +{ + pst_parameter* next = NULL; + if(!p) { + next = (pst_parameter*)params.First(); + } else { + next = (pst_parameter*)params.Next(p); + } + + return next; +} + +pst_call_site* __pst_function::add_call_site(uint64_t target, const char* origin) +{ + pst_call_site* st = new pst_call_site(ctx, target, origin); + call_sites.InsertLast(st); + if(target) { + mCallStToTarget.Insert(&target, sizeof(target), st); + } + + if(origin) { + mCallStToOrigin.Insert(origin, strlen(origin), st); + } + + return st; +} +void __pst_function::del_call_site(pst_call_site* st) +{ + call_sites.Remove(st); + if(st->target && mCallStToTarget.Lookup(&st->target, sizeof(st->target))) { + mCallStToTarget.Remove(); + } + + if(st->origin && mCallStToTarget.Lookup(st->origin, strlen(st->origin))) { + mCallStToTarget.Remove(); + } + + delete st; +} +pst_call_site* __pst_function::next_call_site(pst_call_site* st) +{ + pst_call_site* next = NULL; + if(st) { + next = (pst_call_site*)call_sites.First(); + } else { + next = (pst_call_site*)call_sites.Next(st); + } + + return next; +} + +pst_call_site* __pst_function::call_site_by_origin(const char* origin) +{ + return (pst_call_site*)mCallStToOrigin.Lookup(origin, strlen(origin)); +} + +pst_call_site* __pst_function::call_site_by_target(uint64_t target) +{ + return (pst_call_site*)mCallStToTarget.Lookup(&target, sizeof(target)); +} + +pst_call_site* __pst_function::find_call_site(pst_function* callee) +{ + uint64_t start_pc = ctx->base_addr + callee->lowpc; + pst_call_site* cs = (pst_call_site*)mCallStToTarget.Lookup(&start_pc, sizeof(start_pc)); + if(!cs) { + cs = (pst_call_site*)mCallStToOrigin.Lookup(callee->name.c_str(), strlen(callee->name.c_str())); + } + + return cs; +} + +bool __pst_function::print_dwarf() +{ + char* at = NULL; + if(!asprintf(&at, " at %s:%d, %p", file.c_str(), line, (void*)pc)) { + return false; + } + if(at[4] == ':' && at[5] == '-') { + free(at); + if(!asprintf(&at, " at %p", (void*)pc)) { + return false; + } + } + //ctx->print(ctx, "%s:%d: ", file.c_str(), line); + + // handle return parameter and be safe if function haven't parameters (for example, dwar info for function is absent) + pst_parameter* param = next_param(NULL); + if(param && param->is_return) { + // print return value type, function name and start list of parameters + param->print_dwarf(); + ctx->print(ctx, " %s(", name.c_str()); + param = next_param(param); + } else { + ctx->print(ctx, "%s(", name.c_str()); + } + + bool first = true; bool start_variable = false; + for(; param; param = next_param(param)) { + if(param->is_return) { + // print return value type, function name and start list of parameters + param->print_dwarf(); + ctx->print(ctx, " %s(", name.c_str()); + continue; + } + + if(param->is_variable) { + if(!start_variable) { + ctx->print(ctx, ")%s\n", at); + ctx->print(ctx, "{\n"); + start_variable = true; + } + if(param->line) { + ctx->print(ctx, "%04u: ", param->line); + } else { + ctx->print(ctx, " "); + } + param->print_dwarf(); + ctx->print(ctx, ";\n"); + } else { + if(first) { + first = false; + } else { + ctx->print(ctx, ", "); + } + param->print_dwarf(); + } + } + + if(!start_variable) { + ctx->print(ctx, ");%s\n", at); + } else { + ctx->print(ctx, "}\n"); + } + + free(at); + return true; +} + +bool __pst_function::handle_lexical_block(Dwarf_Die* result) +{ + uint64_t lowpc = 0, highpc = 0; const char* origin_name = ""; + dwarf_lowpc(result, &lowpc); + dwarf_highpc(result, &lowpc); + + Dwarf_Attribute attr_mem; + Dwarf_Die origin; + if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { + origin_name = dwarf_diename(&origin); + Dwarf_Die child; + if(dwarf_child (&origin, &child) == 0) { + do { + switch (dwarf_tag (&child)) + { + case DW_TAG_variable: + case DW_TAG_formal_parameter: + ctx->log(SEVERITY_DEBUG, "Abstract origin: %s('%s')", dwarf_diename(&origin), dwarf_diename (&child)); + break; + // Also handle DW_TAG_unspecified_parameters (unknown number of arguments i.e. fun(arg1, ...); + default: + break; + } + } while (dwarf_siblingof (&child, &child) == 0); + } + } + const char* die_name = ""; + if(dwarf_diename(result)) { + die_name = dwarf_diename(result); + } + ctx->log(SEVERITY_DEBUG, "Lexical block with name '%s', tag 0x%X and origin '%s' found. lowpc = 0x%lX, highpc = 0x%lX", die_name, dwarf_tag (result), origin_name, lowpc, highpc); + Dwarf_Die child; + if(dwarf_child (result, &child) == 0) { + do { + switch (dwarf_tag (&child)) { + case DW_TAG_lexical_block: + handle_lexical_block(&child); + break; + case DW_TAG_variable: { + pst_parameter* param = add_param(); + if(!param->handle_dwarf(&child, this)) { + del_param(param); + } + break; + } + case DW_TAG_GNU_call_site: + handle_call_site(&child); + break; + case DW_TAG_inlined_subroutine: + ctx->log(SEVERITY_DEBUG, "Skipping Lexical block tag 'DW_TAG_inlined_subroutine'"); + break; + default: + ctx->log(SEVERITY_DEBUG, "Unknown Lexical block tag 0x%X", dwarf_tag(&child)); + break; + } + }while (dwarf_siblingof (&child, &child) == 0); + } + + return true; +} + +// DW_AT_low_pc should point to the offset from process base address which is actually PC of current function, usually. +// further handle DW_AT_abstract_origin attribute of DW_TAG_GNU_call_site DIE to determine what DIE is referenced by it. +// probably by invoke by: +// Dwarf_Die *scopes; +// int n = dwarf_getscopes_die (funcdie, &scopes); // where 'n' is the number of scopes +// if (n <= 0) -> FAILURE +// see handle_function() in elfutils/tests/funcscopes.c -> handle_function() -> print_vars() +// DW_TAG_GNU_call_site_parameter is defined under child DIE of DW_TAG_GNU_call_site and defines value of subroutine before calling it +// relates to DW_OP_GNU_entry_value() handling in callee function to determine the value of an argument/variable of the callee +// get DIE of return type +bool __pst_function::handle_call_site(Dwarf_Die* result) +{ + Dwarf_Die origin; + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + ctx->log(SEVERITY_DEBUG, "***** DW_TAG_GNU_call_site contents:"); + // reference to DIE which represents callee's parameter if compiler knows where it is at compile time + const char* oname = NULL; + if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { + oname = dwarf_diename(&origin); + ctx->log(SEVERITY_DEBUG, "\tDW_AT_abstract_origin: '%s'", oname); + } + + // The call site may have a DW_AT_call_site_target attribute which is a DWARF expression. For indirect calls or jumps where it is unknown at + // compile time which subprogram will be called the expression computes the address of the subprogram that will be called. + uint64_t target = 0; + if(dwarf_hasattr (result, DW_AT_GNU_call_site_target)) { + attr = dwarf_attr(result, DW_AT_GNU_call_site_target, &attr_mem); + if(attr) { + pst_dwarf_expr expr(ctx->alloc); + if(handle_location(ctx, &attr_mem, expr, pc, this)) { + target = expr.value; + ctx->log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_target: %#lX", target); + } + } + } + + if(target == 0 && oname == NULL) { + ctx->log(SEVERITY_ERROR, "Cannot determine both call-site target and origin"); + return false; + } + + Dwarf_Die child; + if(dwarf_child (result, &child) == 0) { + pst_call_site* st = add_call_site(target, oname); + if(!st->handle_dwarf(&child)) { + del_call_site(st); + return false; + } + } + + return true; +} + +bool __pst_function::handle_dwarf(Dwarf_Die* d) +{ + die = d; + get_frame(); + + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + + // get list of offsets from process base address of continuous memory ranges where function's code resides +// if(dwarf_haspc(d, pc)) { + dwarf_lowpc(d, &lowpc); + dwarf_highpc(d, &highpc); +// } else { +// ctx->log(SEVERITY_ERROR, "Function's '%s' DIE hasn't definitions of memory offsets of function's code", dwarf_diename(d)); +// return false; +// } + + unw_proc_info_t info; + unw_get_proc_info(&cursor, &info); + ctx->clean_print(ctx); + + ctx->log(SEVERITY_INFO, "Function %s(...): LOW_PC = %#lX, HIGH_PC = %#lX, offset from base address: 0x%lX, START_PC = 0x%lX, offset from start of function: 0x%lX", + dwarf_diename(d), lowpc, highpc, pc - ctx->base_addr, info.start_ip, info.start_ip - ctx->base_addr); + ctx->print_registers(ctx, 0x0, 0x10); + ctx->log(SEVERITY_INFO, "Function %s(...): CFA: %#lX %s", dwarf_diename(d), parent ? parent->sp : 0, ctx->buff); + ctx->log(SEVERITY_INFO, "Function %s(...): %s", dwarf_diename(d), ctx->buff); + + // determine function's stack frame base + attr = dwarf_attr(die, DW_AT_frame_base, &attr_mem); + if(attr) { + if(dwarf_hasform(attr, DW_FORM_exprloc)) { + Dwarf_Op *expr; + size_t exprlen; + if (dwarf_getlocation (attr, &expr, &exprlen) == 0) { + ctx->print_expr(ctx, expr, exprlen, attr); + pst_decl(pst_dwarf_stack, stack, ctx); + if(stack.calc(&stack, expr, exprlen, attr, this)) { + uint64_t value; + if(stack.get_value(&stack, &value)) { + ctx->log(SEVERITY_DEBUG, "DW_AT_framebase expression: \"%s\" ==> 0x%lX", ctx->buff, value); + } else { + ctx->log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_framebase expression: %s", ctx->buff); + } + } else { + ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_framebase expression: %s", ctx->buff); + } + pst_dwarf_stack_fini(&stack); + } else { + ctx->log(SEVERITY_WARNING, "Unknown attribute form = 0x%X, code = 0x%X", attr->form, attr->code); + } + } + } + + // Get reference to return attribute type of the function + // may be to use dwfl_module_return_value_location() instead + pst_parameter* ret_p = add_param(); ret_p->is_return = true; + attr = dwarf_attr(die, DW_AT_type, &attr_mem); + if(attr) { + if(!ret_p->handle_type(attr)) { + ctx->log(SEVERITY_ERROR, "Failed to handle return parameter type for function %s(...)", name.c_str()); + del_param(ret_p); + } + } else { + ret_p->add_type("void", 0); + } + + // handle and save additionally these attributes: + // 1. string of DW_AT_linkage_name (mangled name of program if any) + // A debugging information entry may have a DW_AT_linkage_name attribute + // whose value is a null-terminated string containing the object file linkage name + // associated with the corresponding entity. + + // 2. flag DW_AT_external attribute + // A DW_AT_external attribute, which is a flag, if the name of a variable is + // visible outside of its enclosing compilation unit. + + // 3. A subroutine entry may contain a DW_AT_main_subprogram attribute which is + // a flag whose presence indicates that the subroutine has been identified as the + // starting function of the program. If more than one subprogram contains this flag, + // any one of them may be the starting subroutine of the program. + + + Dwarf_Die result; + if(dwarf_child(die, &result) != 0) + return false; + + // went through parameters and local variables of the function + do { + switch (dwarf_tag(&result)) { + case DW_TAG_formal_parameter: + case DW_TAG_variable: { + pst_parameter* param = add_param(); + if(!param->handle_dwarf(&result, this)) { + del_param(param); + } + + break; + } + case DW_TAG_GNU_call_site: + handle_call_site(&result); + break; + + // case DW_TAG_inlined_subroutine: + // /* Recurse further down */ + // HandleFunction(&result); + // break; + case DW_TAG_lexical_block: { + handle_lexical_block(&result); + break; + } + // Also handle: + // DW_TAG_unspecified_parameters (unknown number of arguments i.e. fun(arg1, ...); + // DW_AT_inline + default: + ctx->log(SEVERITY_DEBUG, "Unknown TAG of function: 0x%X", dwarf_tag(&result)); + break; + } + } while(dwarf_siblingof(&result, &result) == 0); + + return true; +} + +bool __pst_function::unwind(Dwarf_Addr addr) +{ + pc = addr; + + Dwfl_Line *dwline = dwfl_getsrc(ctx->dwfl, addr); + if(dwline != NULL) { + Dwarf_Addr addr; + const char* filename = dwfl_lineinfo (dwline, &addr, &line, NULL, NULL, NULL); + if(filename) { + const char* str = strrchr(filename, '/'); + if(str && *str != 0) { + str++; + } else { + str = filename; + } + file = str; + ctx->print(ctx, "%s:%d", str, line); + } else { + ctx->print(ctx, "%p", (void*)addr); + } + } else { + ctx->print(ctx, "%p", (void*)addr); + } + + const char* addrname = dwfl_module_addrname(ctx->module, addr); + char* demangle_name = NULL; + if(addrname) { + int status; + demangle_name = abi::__cxa_demangle(addrname, NULL, NULL, &status); + char* function_name = NULL; + if(asprintf(&function_name, "%s%s", demangle_name ? demangle_name : addrname, demangle_name ? "" : "()") == -1) { + ctx->log(SEVERITY_ERROR, "Failed to allocate memory"); + return false; + } + ctx->print(ctx, " --> %s", function_name); + + char* str = strchr(function_name, '('); + if(str) { + *str = 0; + } + name = function_name; + free(function_name); + } + + if(demangle_name) { + free(demangle_name); + } + + return true; +} + +bool __pst_function::get_frame() +{ + // get CFI (Call Frame Information) for current module + // from handle_cfi() + Dwarf_Addr mod_bias = 0; + Dwarf_CFI* cfi = dwfl_module_eh_cfi(ctx->module, &mod_bias); // rty .eh_cfi first + if(!cfi) { // then try .debug_fame second + cfi = dwfl_module_dwarf_cfi(ctx->module, &mod_bias); + } + if(!cfi) { + ctx->log(SEVERITY_ERROR, "Cannot find CFI for module"); + return false; + } + + // get frame of CFI for address + int result = dwarf_cfi_addrframe (cfi, pc - mod_bias, &frame); + if (result != 0) { + ctx->log(SEVERITY_ERROR, "Failed to find CFI frame for module"); + return false; + } + + // setup context to match frame + ctx->frame = frame; + + // get return register and PC range for function + Dwarf_Addr start = pc; + Dwarf_Addr end = pc; + bool signalp; + int ra_regno = dwarf_frame_info (frame, &start, &end, &signalp); + if(ra_regno >= 0) { + start += mod_bias; + end += mod_bias; + reginfo info; info.regno = ra_regno; + dwfl_module_register_names(ctx->module, regname_callback, &info); + ctx->log(SEVERITY_INFO, "Function %s(...): '.eh/debug frame' info: PC range: => [%#" PRIx64 ", %#" PRIx64 "], return register: %s, in_signal = %s", + name.c_str(), start, end, info.regname, signalp ? "true" : "false"); + } else { + ctx->log(SEVERITY_WARNING, "Return address register info unavailable (%s)", dwarf_errmsg(0)); + } + + // finally get CFA (Canonical Frame Address) + // Point cfa_ops to dummy to match print_detail expectations. + // (nops == 0 && cfa_ops != NULL => "undefined") + Dwarf_Op dummy; + Dwarf_Op *cfa_ops = &dummy; + size_t cfa_nops; + if(dwarf_frame_cfa(frame, &cfa_ops, &cfa_nops)) { + ctx->log(SEVERITY_ERROR, "Failed to get CFA for frame"); + return false; + } + + ctx->print_expr(ctx, cfa_ops, cfa_nops, NULL); + pst_decl(pst_dwarf_stack, stack, ctx); + if(stack.calc(&stack, cfa_ops, cfa_nops, NULL, this) && stack.get_value(&stack, &cfa)) { + //ctx.sp = v; + ctx->log(SEVERITY_INFO, "Function %s(...): CFA expression: %s ==> %#lX", name.c_str(), ctx->buff, cfa); + + // setup context to match CFA for frame + ctx->cfa = cfa; + } else { + ctx->log(SEVERITY_ERROR, "Failed to calculate CFA expression"); + } + + pst_dwarf_stack_fini(&stack); + + return true; +} + diff --git a/framework/dwarf_function.h b/framework/dwarf_function.h new file mode 100644 index 0000000..5e37251 --- /dev/null +++ b/framework/dwarf_function.h @@ -0,0 +1,84 @@ +/* + * dwarf_function.h + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#ifndef FRAMEWORK_DWARF_FUNCTION_H_ +#define FRAMEWORK_DWARF_FUNCTION_H_ + +#include "context.h" +#include "dwarf_expression.h" +#include "list_head.h" +#include "dwarf_call_site.h" +#include "dwarf_parameter.h" + +// ----------------------------------------------------------------------------------- +// pst_function +// ----------------------------------------------------------------------------------- +typedef struct __pst_function : public SC_ListNode { + __pst_function(pst_context* _ctx, __pst_function* _parent) : ctx(_ctx) { + pc = 0; + line = -1; + die = NULL; + lowpc = 0; + highpc = 0; + memcpy(&cursor, _ctx->curr_frame, sizeof(cursor)); + parent = _parent; + sp = 0; + cfa = 0; + frame = NULL; + } + + ~__pst_function() { + clear(); + if(frame) { + free(frame); + } + } + + void clear(); + pst_parameter* add_param(); + void del_param(pst_parameter* p); + pst_parameter* next_param(pst_parameter* p); + + pst_call_site* add_call_site(uint64_t target, const char* origin); + void del_call_site(pst_call_site* st); + pst_call_site* next_call_site(pst_call_site* st); + pst_call_site* call_site_by_origin(const char* origin); + pst_call_site* call_site_by_target(uint64_t target); + + bool unwind(Dwarf_Addr addr); + bool handle_dwarf(Dwarf_Die* d); + bool print_dwarf(); + bool handle_lexical_block(Dwarf_Die* result); + bool handle_call_site(Dwarf_Die* result); + pst_call_site* find_call_site(__pst_function* callee); + + bool get_frame(); + + Dwarf_Addr lowpc; // offset to start of the function against base address + Dwarf_Addr highpc; // offset to the next address after the end of the function against base address + Dwarf_Die* die; // DWARF DIE containing definition of the function + std::string name; // function's name + SC_ListHead params; // function's parameters + + // call-site + SC_ListHead call_sites; // Call-Site definitions + SC_Dict mCallStToTarget; // map pointer to caller to call-site + SC_Dict mCallStToOrigin; // map caller name to call-site + + unw_word_t pc; // address between lowpc & highpc (plus base address offset). actually, currently executed command + unw_word_t sp; // SP register in function's frame + unw_word_t cfa; // CFA (Canonical Frame Adress) of the function + unw_cursor_t cursor; // copy of stack state of the function + int line; // line in code where function is defined + std::string file; // file name (DWARF Compilation Unit) where function is defined + __pst_function* parent; // parent function in call trace (caller) + Dwarf_Frame* frame; // function's stack frame + pst_context* ctx; // context of unwinding +} pst_function; + + +#endif /* FRAMEWORK_DWARF_FUNCTION_H_ */ diff --git a/framework/dwarf_parameter.cpp b/framework/dwarf_parameter.cpp index 3595f53..23b58f5 100644 --- a/framework/dwarf_parameter.cpp +++ b/framework/dwarf_parameter.cpp @@ -6,8 +6,10 @@ */ #include +#include #include "dwarf_parameter.h" +#include "dwarf_utils.h" // // pst_type @@ -173,7 +175,7 @@ pst_type* param_next_type(pst_parameter* param, pst_type* t) return ret; } -bool param_handle_dwarf(pst_parameter* param, Dwarf_Die* result, __pst_function* fun) +bool param_handle_dwarf(pst_parameter* param, Dwarf_Die* result, pst_function* fun) { param->die = result; diff --git a/framework/dwarf_parameter.h b/framework/dwarf_parameter.h index a5faa8d..ec934c4 100644 --- a/framework/dwarf_parameter.h +++ b/framework/dwarf_parameter.h @@ -35,7 +35,7 @@ typedef struct pst_parameter{ void (*clear) (pst_parameter* param); pst_type* (*add_type) (pst_parameter* param, const char* name, int type); pst_type* (*next_type) (pst_parameter* param, pst_type* t); - bool (*handle_dwarf) (pst_parameter* param, Dwarf_Die* d, __pst_function* fun); + bool (*handle_dwarf) (pst_parameter* param, Dwarf_Die* d, pst_function* fun); bool (*handle_type) (pst_parameter* param, Dwarf_Attribute* attr); bool (*print_dwarf) (pst_parameter* param); diff --git a/framework/dwarf_utils.cpp b/framework/dwarf_utils.cpp new file mode 100644 index 0000000..42291ef --- /dev/null +++ b/framework/dwarf_utils.cpp @@ -0,0 +1,65 @@ +/* + * dwarf_utils.cpp + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#include + +#include "dwarf_utils.h" +#include "dwarf_stack.h" + +int regname_callback (void *arg, int regno, const char *setname, const char *prefix, const char *regname, int bits, int type) +{ + reginfo* info = (reginfo*)arg; + if(info->regno == regno) { + snprintf(info->regname, sizeof(info->regname), "%s %s%s", setname, prefix, regname); + } + + return 0; +} + +bool handle_location(pst_context* ctx, Dwarf_Attribute* attr, pst_dwarf_expr* loc, Dwarf_Addr pc, pst_function* fun = NULL) +{ + ctx->clean_print(ctx); + Dwarf_Addr offset = pc - ctx->base_addr; + + pst_decl(pst_dwarf_stack, stack, ctx); + Dwarf_Op *expr; + size_t exprlen; + bool ret = false; + if(dwarf_hasform(attr, DW_FORM_exprloc)) { + // Location expression (exprloc class of location in DWARF terms) + if(dwarf_getlocation(attr, &expr, &exprlen) == 0) { + loc->setup(expr, exprlen); + ctx->print_expr(ctx, expr, exprlen, attr); + ret = stack.calc(&stack, expr, exprlen, attr, fun); + } + } else if(dwarf_hasform(attr, DW_FORM_sec_offset)) { + // Location list (loclist class of location in DWARF terms) + Dwarf_Addr base, start, end; + ptrdiff_t off = 0; + + // handle list of possible locations of parameter + for(int i = 0; (off = dwarf_getlocations (attr, off, &base, &start, &end, &expr, &exprlen)) > 0; ++i) { + ctx->print_expr(ctx, expr, exprlen, attr); + if(offset >= start && offset <= end) { + loc->setup(expr, exprlen); + // actual location, try to calculate Location expression + ret = stack.calc(&stack, expr, exprlen, attr, fun); + } else { + // Location skipped due to don't match current PC offset + ctx->log(SEVERITY_DEBUG, "Skip Location list expression: [%d] (low_offset: 0x%" PRIx64 ", high_offset: 0x%" PRIx64 "), \"%s\"", i, start, end, ctx->buff); + } + } + + } else { + ctx->log(SEVERITY_WARNING, "Unknown location attribute form = 0x%X, code = 0x%X, ", attr->form, attr->code); + } + + pst_dwarf_stack_fini(&stack); + + return ret; +} + diff --git a/framework/dwarf_utils.h b/framework/dwarf_utils.h new file mode 100644 index 0000000..b70a5d9 --- /dev/null +++ b/framework/dwarf_utils.h @@ -0,0 +1,29 @@ +/* + * dwarf_utils.h + * + * Created on: Feb 1, 2020 + * Author: nnosov + */ + +#ifndef FRAMEWORK_DWARF_UTILS_H_ +#define FRAMEWORK_DWARF_UTILS_H_ + +#include + +#include "dwarf_expression.h" +#include "context.h" +#include "sysutils.h" + +typedef struct __reginfo { + __reginfo() { + regname[0] = 0; + regno = -1; + } + char regname[32]; + int regno; +} reginfo; + +int regname_callback (void *arg, int regno, const char *setname, const char *prefix, const char *regname, int bits, int type); +bool handle_location(pst_context* ctx, Dwarf_Attribute* attr, pst_dwarf_expr* loc, Dwarf_Addr pc, pst_function* fun); + +#endif /* FRAMEWORK_DWARF_UTILS_H_ */ diff --git a/framework/sysutils.cpp b/framework/sysutils.cpp index 495a513..4e689ca 100644 --- a/framework/sysutils.cpp +++ b/framework/sysutils.cpp @@ -34,687 +34,6 @@ // dwarf_getattrs() allows to enumerate all DIE attributes // dwarf_getfuncs() allows to enumerate functions within CU -typedef struct __reginfo { - __reginfo() { - regname[0] = 0; - regno = -1; - } - char regname[32]; - int regno; -} reginfo; - -int regname_callback (void *arg, int regno, const char *setname, const char *prefix, const char *regname, int bits, int type) -{ - reginfo* info = (reginfo*)arg; - if(info->regno == regno) { - snprintf(info->regname, sizeof(info->regname), "%s %s%s", setname, prefix, regname); - } - - return 0; -} - -bool handle_location(pst_context* ctx, Dwarf_Attribute* attr, pst_dwarf_expr* loc, Dwarf_Addr pc, __pst_function* fun = NULL) -{ - ctx->clean_print(ctx); - Dwarf_Addr offset = pc - ctx->base_addr; - - pst_decl(pst_dwarf_stack, stack, ctx); - Dwarf_Op *expr; - size_t exprlen; - bool ret = false; - if(dwarf_hasform(attr, DW_FORM_exprloc)) { - // Location expression (exprloc class of location in DWARF terms) - if(dwarf_getlocation(attr, &expr, &exprlen) == 0) { - loc->setup(expr, exprlen); - ctx->print_expr(ctx, expr, exprlen, attr); - ret = stack.calc(&stack, expr, exprlen, attr, fun); - } - } else if(dwarf_hasform(attr, DW_FORM_sec_offset)) { - // Location list (loclist class of location in DWARF terms) - Dwarf_Addr base, start, end; - ptrdiff_t off = 0; - - // handle list of possible locations of parameter - for(int i = 0; (off = dwarf_getlocations (attr, off, &base, &start, &end, &expr, &exprlen)) > 0; ++i) { - ctx->print_expr(ctx, expr, exprlen, attr); - if(offset >= start && offset <= end) { - loc->setup(expr, exprlen); - // actual location, try to calculate Location expression - ret = stack.calc(&stack, expr, exprlen, attr, fun); - } else { - // Location skipped due to don't match current PC offset - ctx->log(SEVERITY_DEBUG, "Skip Location list expression: [%d] (low_offset: 0x%" PRIx64 ", high_offset: 0x%" PRIx64 "), \"%s\"", i, start, end, ctx->buff); - } - } - - } else { - ctx->log(SEVERITY_WARNING, "Unknown location attribute form = 0x%X, code = 0x%X, ", attr->form, attr->code); - } - - pst_dwarf_stack_fini(&stack); - - return ret; -} - - -void __pst_call_site_param::set_location(Dwarf_Op* expr, size_t exprlen) -{ - location.setup(expr, exprlen); -} - -pst_call_site_param* __pst_call_site::add_param() -{ - pst_call_site_param* p = new pst_call_site_param(ctx); - params.InsertLast(p); - - return p; -} - -void __pst_call_site::del_param(pst_call_site_param* p) -{ - params.Remove(p); - - delete p; -} - -pst_call_site_param* __pst_call_site::next_param(pst_call_site_param* p) -{ - pst_call_site_param* param = NULL; - if(p == NULL) { - param = (pst_call_site_param*)params.First(); - } else { - param = (pst_call_site_param*)params.Next(p); - } - - return param; -} - -pst_call_site_param* __pst_call_site::find_param(pst_dwarf_expr& expr) -{ - for(pst_call_site_param* param = next_param(NULL); param; param = next_param(param)) { - if(param->location == expr) { - return param; - } - } - - return NULL; -} - -bool __pst_call_site::handle_dwarf(Dwarf_Die* child) -{ - Dwarf_Attribute attr_mem; - Dwarf_Attribute* attr; - - do { - switch(dwarf_tag(child)) - { - case DW_TAG_GNU_call_site_parameter: { - Dwarf_Addr pc; - unw_get_reg(ctx->curr_frame, UNW_REG_IP, &pc); - - // expression represent where callee parameter will be stored - pst_call_site_param* param = add_param(); - if(dwarf_hasattr(child, DW_AT_location)) { - // handle location expression here - // determine location of parameter in stack/heap or CPU registers - attr = dwarf_attr(child, DW_AT_location, &attr_mem); - if(!handle_location(ctx, attr, param->location, pc, NULL)) { - ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", ctx->buff); - del_param(param); - return false; - } - } - - // expression represents call parameter's value - if(dwarf_hasattr(child, DW_AT_GNU_call_site_value)) { - // handle value expression here - attr = dwarf_attr(child, DW_AT_GNU_call_site_value, &attr_mem); - pst_dwarf_expr loc(ctx->alloc); - if(handle_location(ctx, attr, loc, pc, NULL)) { - param->value = loc.value; - ctx->log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_value:\"%s\" ==> 0x%lX", ctx->buff, param->value); - } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", ctx->buff); - del_param(param); - return false; - } - } - break; - } - default: - break; - } - } while (dwarf_siblingof (child, child) == 0); - - return true; -} - -pst_parameter* __pst_function::add_param() -{ - pst_parameter* p = new pst_parameter(ctx); - params.InsertLast(p); - - return p; -} - -void __pst_function::del_param(pst_parameter* p) -{ - params.Remove(p); - delete p; -} - -void __pst_function::clear() -{ - for(pst_parameter* p = (pst_parameter*)params.First(); p; p = (pst_parameter*)params.First()) { - params.Remove(p); - delete p; - } - - for(pst_call_site* p = (pst_call_site*)call_sites.First(); p; p = (pst_call_site*)call_sites.First()) { - call_sites.Remove(p); - delete p; - } - - mCallStToTarget.Clean(); - mCallStToTarget.Clean(); -} - -pst_parameter* __pst_function::next_param(pst_parameter* p) -{ - pst_parameter* next = NULL; - if(!p) { - next = (pst_parameter*)params.First(); - } else { - next = (pst_parameter*)params.Next(p); - } - - return next; -} - -pst_call_site* __pst_function::add_call_site(uint64_t target, const char* origin) -{ - pst_call_site* st = new pst_call_site(ctx, target, origin); - call_sites.InsertLast(st); - if(target) { - mCallStToTarget.Insert(&target, sizeof(target), st); - } - - if(origin) { - mCallStToOrigin.Insert(origin, strlen(origin), st); - } - - return st; -} -void __pst_function::del_call_site(pst_call_site* st) -{ - call_sites.Remove(st); - if(st->target && mCallStToTarget.Lookup(&st->target, sizeof(st->target))) { - mCallStToTarget.Remove(); - } - - if(st->origin && mCallStToTarget.Lookup(st->origin, strlen(st->origin))) { - mCallStToTarget.Remove(); - } - - delete st; -} -pst_call_site* __pst_function::next_call_site(pst_call_site* st) -{ - pst_call_site* next = NULL; - if(st) { - next = (pst_call_site*)call_sites.First(); - } else { - next = (pst_call_site*)call_sites.Next(st); - } - - return next; -} - -pst_call_site* __pst_function::call_site_by_origin(const char* origin) -{ - return (pst_call_site*)mCallStToOrigin.Lookup(origin, strlen(origin)); -} - -pst_call_site* __pst_function::call_site_by_target(uint64_t target) -{ - return (pst_call_site*)mCallStToTarget.Lookup(&target, sizeof(target)); -} - -pst_call_site* __pst_function::find_call_site(pst_function* callee) -{ - uint64_t start_pc = ctx->base_addr + callee->lowpc; - pst_call_site* cs = (pst_call_site*)mCallStToTarget.Lookup(&start_pc, sizeof(start_pc)); - if(!cs) { - cs = (pst_call_site*)mCallStToOrigin.Lookup(callee->name.c_str(), strlen(callee->name.c_str())); - } - - return cs; -} - -bool __pst_function::print_dwarf() -{ - char* at = NULL; - if(!asprintf(&at, " at %s:%d, %p", file.c_str(), line, (void*)pc)) { - return false; - } - if(at[4] == ':' && at[5] == '-') { - free(at); - if(!asprintf(&at, " at %p", (void*)pc)) { - return false; - } - } - //ctx->print(ctx, "%s:%d: ", file.c_str(), line); - - // handle return parameter and be safe if function haven't parameters (for example, dwar info for function is absent) - pst_parameter* param = next_param(NULL); - if(param && param->is_return) { - // print return value type, function name and start list of parameters - param->print_dwarf(); - ctx->print(ctx, " %s(", name.c_str()); - param = next_param(param); - } else { - ctx->print(ctx, "%s(", name.c_str()); - } - - bool first = true; bool start_variable = false; - for(; param; param = next_param(param)) { - if(param->is_return) { - // print return value type, function name and start list of parameters - param->print_dwarf(); - ctx->print(ctx, " %s(", name.c_str()); - continue; - } - - if(param->is_variable) { - if(!start_variable) { - ctx->print(ctx, ")%s\n", at); - ctx->print(ctx, "{\n"); - start_variable = true; - } - if(param->line) { - ctx->print(ctx, "%04u: ", param->line); - } else { - ctx->print(ctx, " "); - } - param->print_dwarf(); - ctx->print(ctx, ";\n"); - } else { - if(first) { - first = false; - } else { - ctx->print(ctx, ", "); - } - param->print_dwarf(); - } - } - - if(!start_variable) { - ctx->print(ctx, ");%s\n", at); - } else { - ctx->print(ctx, "}\n"); - } - - free(at); - return true; -} - -bool __pst_function::handle_lexical_block(Dwarf_Die* result) -{ - uint64_t lowpc = 0, highpc = 0; const char* origin_name = ""; - dwarf_lowpc(result, &lowpc); - dwarf_highpc(result, &lowpc); - - Dwarf_Attribute attr_mem; - Dwarf_Die origin; - if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { - origin_name = dwarf_diename(&origin); - Dwarf_Die child; - if(dwarf_child (&origin, &child) == 0) { - do { - switch (dwarf_tag (&child)) - { - case DW_TAG_variable: - case DW_TAG_formal_parameter: - ctx->log(SEVERITY_DEBUG, "Abstract origin: %s('%s')", dwarf_diename(&origin), dwarf_diename (&child)); - break; - // Also handle DW_TAG_unspecified_parameters (unknown number of arguments i.e. fun(arg1, ...); - default: - break; - } - } while (dwarf_siblingof (&child, &child) == 0); - } - } - const char* die_name = ""; - if(dwarf_diename(result)) { - die_name = dwarf_diename(result); - } - ctx->log(SEVERITY_DEBUG, "Lexical block with name '%s', tag 0x%X and origin '%s' found. lowpc = 0x%lX, highpc = 0x%lX", die_name, dwarf_tag (result), origin_name, lowpc, highpc); - Dwarf_Die child; - if(dwarf_child (result, &child) == 0) { - do { - switch (dwarf_tag (&child)) { - case DW_TAG_lexical_block: - handle_lexical_block(&child); - break; - case DW_TAG_variable: { - pst_parameter* param = add_param(); - if(!param->handle_dwarf(&child, this)) { - del_param(param); - } - break; - } - case DW_TAG_GNU_call_site: - handle_call_site(&child); - break; - case DW_TAG_inlined_subroutine: - ctx->log(SEVERITY_DEBUG, "Skipping Lexical block tag 'DW_TAG_inlined_subroutine'"); - break; - default: - ctx->log(SEVERITY_DEBUG, "Unknown Lexical block tag 0x%X", dwarf_tag(&child)); - break; - } - }while (dwarf_siblingof (&child, &child) == 0); - } - - return true; -} - -// DW_AT_low_pc should point to the offset from process base address which is actually PC of current function, usually. -// further handle DW_AT_abstract_origin attribute of DW_TAG_GNU_call_site DIE to determine what DIE is referenced by it. -// probably by invoke by: -// Dwarf_Die *scopes; -// int n = dwarf_getscopes_die (funcdie, &scopes); // where 'n' is the number of scopes -// if (n <= 0) -> FAILURE -// see handle_function() in elfutils/tests/funcscopes.c -> handle_function() -> print_vars() -// DW_TAG_GNU_call_site_parameter is defined under child DIE of DW_TAG_GNU_call_site and defines value of subroutine before calling it -// relates to DW_OP_GNU_entry_value() handling in callee function to determine the value of an argument/variable of the callee -// get DIE of return type -bool __pst_function::handle_call_site(Dwarf_Die* result) -{ - Dwarf_Die origin; - Dwarf_Attribute attr_mem; - Dwarf_Attribute* attr; - - ctx->log(SEVERITY_DEBUG, "***** DW_TAG_GNU_call_site contents:"); - // reference to DIE which represents callee's parameter if compiler knows where it is at compile time - const char* oname = NULL; - if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { - oname = dwarf_diename(&origin); - ctx->log(SEVERITY_DEBUG, "\tDW_AT_abstract_origin: '%s'", oname); - } - - // The call site may have a DW_AT_call_site_target attribute which is a DWARF expression. For indirect calls or jumps where it is unknown at - // compile time which subprogram will be called the expression computes the address of the subprogram that will be called. - uint64_t target = 0; - if(dwarf_hasattr (result, DW_AT_GNU_call_site_target)) { - attr = dwarf_attr(result, DW_AT_GNU_call_site_target, &attr_mem); - if(attr) { - pst_dwarf_expr expr(ctx->alloc); - if(handle_location(ctx, &attr_mem, expr, pc, this)) { - target = expr.value; - ctx->log(SEVERITY_DEBUG, "\tDW_AT_GNU_call_site_target: %#lX", target); - } - } - } - - if(target == 0 && oname == NULL) { - ctx->log(SEVERITY_ERROR, "Cannot determine both call-site target and origin"); - return false; - } - - Dwarf_Die child; - if(dwarf_child (result, &child) == 0) { - pst_call_site* st = add_call_site(target, oname); - if(!st->handle_dwarf(&child)) { - del_call_site(st); - return false; - } - } - - return true; -} - -bool __pst_function::handle_dwarf(Dwarf_Die* d) -{ - die = d; - get_frame(); - - Dwarf_Attribute attr_mem; - Dwarf_Attribute* attr; - - // get list of offsets from process base address of continuous memory ranges where function's code resides -// if(dwarf_haspc(d, pc)) { - dwarf_lowpc(d, &lowpc); - dwarf_highpc(d, &highpc); -// } else { -// ctx->log(SEVERITY_ERROR, "Function's '%s' DIE hasn't definitions of memory offsets of function's code", dwarf_diename(d)); -// return false; -// } - - unw_proc_info_t info; - unw_get_proc_info(&cursor, &info); - ctx->clean_print(ctx); - - ctx->log(SEVERITY_INFO, "Function %s(...): LOW_PC = %#lX, HIGH_PC = %#lX, offset from base address: 0x%lX, START_PC = 0x%lX, offset from start of function: 0x%lX", - dwarf_diename(d), lowpc, highpc, pc - ctx->base_addr, info.start_ip, info.start_ip - ctx->base_addr); - ctx->print_registers(ctx, 0x0, 0x10); - ctx->log(SEVERITY_INFO, "Function %s(...): CFA: %#lX %s", dwarf_diename(d), parent ? parent->sp : 0, ctx->buff); - ctx->log(SEVERITY_INFO, "Function %s(...): %s", dwarf_diename(d), ctx->buff); - - // determine function's stack frame base - attr = dwarf_attr(die, DW_AT_frame_base, &attr_mem); - if(attr) { - if(dwarf_hasform(attr, DW_FORM_exprloc)) { - Dwarf_Op *expr; - size_t exprlen; - if (dwarf_getlocation (attr, &expr, &exprlen) == 0) { - ctx->print_expr(ctx, expr, exprlen, attr); - pst_decl(pst_dwarf_stack, stack, ctx); - if(stack.calc(&stack, expr, exprlen, attr, this)) { - uint64_t value; - if(stack.get_value(&stack, &value)) { - ctx->log(SEVERITY_DEBUG, "DW_AT_framebase expression: \"%s\" ==> 0x%lX", ctx->buff, value); - } else { - ctx->log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_framebase expression: %s", ctx->buff); - } - } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_framebase expression: %s", ctx->buff); - } - pst_dwarf_stack_fini(&stack); - } else { - ctx->log(SEVERITY_WARNING, "Unknown attribute form = 0x%X, code = 0x%X", attr->form, attr->code); - } - } - } - - // Get reference to return attribute type of the function - // may be to use dwfl_module_return_value_location() instead - pst_parameter* ret_p = add_param(); ret_p->is_return = true; - attr = dwarf_attr(die, DW_AT_type, &attr_mem); - if(attr) { - if(!ret_p->handle_type(attr)) { - ctx->log(SEVERITY_ERROR, "Failed to handle return parameter type for function %s(...)", name.c_str()); - del_param(ret_p); - } - } else { - ret_p->add_type("void", 0); - } - - // handle and save additionally these attributes: - // 1. string of DW_AT_linkage_name (mangled name of program if any) - // A debugging information entry may have a DW_AT_linkage_name attribute - // whose value is a null-terminated string containing the object file linkage name - // associated with the corresponding entity. - - // 2. flag DW_AT_external attribute - // A DW_AT_external attribute, which is a flag, if the name of a variable is - // visible outside of its enclosing compilation unit. - - // 3. A subroutine entry may contain a DW_AT_main_subprogram attribute which is - // a flag whose presence indicates that the subroutine has been identified as the - // starting function of the program. If more than one subprogram contains this flag, - // any one of them may be the starting subroutine of the program. - - - Dwarf_Die result; - if(dwarf_child(die, &result) != 0) - return false; - - // went through parameters and local variables of the function - do { - switch (dwarf_tag(&result)) { - case DW_TAG_formal_parameter: - case DW_TAG_variable: { - pst_parameter* param = add_param(); - if(!param->handle_dwarf(&result, this)) { - del_param(param); - } - - break; - } - case DW_TAG_GNU_call_site: - handle_call_site(&result); - break; - - // case DW_TAG_inlined_subroutine: - // /* Recurse further down */ - // HandleFunction(&result); - // break; - case DW_TAG_lexical_block: { - handle_lexical_block(&result); - break; - } - // Also handle: - // DW_TAG_unspecified_parameters (unknown number of arguments i.e. fun(arg1, ...); - // DW_AT_inline - default: - ctx->log(SEVERITY_DEBUG, "Unknown TAG of function: 0x%X", dwarf_tag(&result)); - break; - } - } while(dwarf_siblingof(&result, &result) == 0); - - return true; -} - -bool __pst_function::unwind(Dwarf_Addr addr) -{ - pc = addr; - - Dwfl_Line *dwline = dwfl_getsrc(ctx->dwfl, addr); - if(dwline != NULL) { - Dwarf_Addr addr; - const char* filename = dwfl_lineinfo (dwline, &addr, &line, NULL, NULL, NULL); - if(filename) { - const char* str = strrchr(filename, '/'); - if(str && *str != 0) { - str++; - } else { - str = filename; - } - file = str; - ctx->print(ctx, "%s:%d", str, line); - } else { - ctx->print(ctx, "%p", (void*)addr); - } - } else { - ctx->print(ctx, "%p", (void*)addr); - } - - const char* addrname = dwfl_module_addrname(ctx->module, addr); - char* demangle_name = NULL; - if(addrname) { - int status; - demangle_name = abi::__cxa_demangle(addrname, NULL, NULL, &status); - char* function_name = NULL; - if(asprintf(&function_name, "%s%s", demangle_name ? demangle_name : addrname, demangle_name ? "" : "()") == -1) { - ctx->log(SEVERITY_ERROR, "Failed to allocate memory"); - return false; - } - ctx->print(ctx, " --> %s", function_name); - - char* str = strchr(function_name, '('); - if(str) { - *str = 0; - } - name = function_name; - free(function_name); - } - - if(demangle_name) { - free(demangle_name); - } - - return true; -} - -bool __pst_function::get_frame() -{ - // get CFI (Call Frame Information) for current module - // from handle_cfi() - Dwarf_Addr mod_bias = 0; - Dwarf_CFI* cfi = dwfl_module_eh_cfi(ctx->module, &mod_bias); // rty .eh_cfi first - if(!cfi) { // then try .debug_fame second - cfi = dwfl_module_dwarf_cfi(ctx->module, &mod_bias); - } - if(!cfi) { - ctx->log(SEVERITY_ERROR, "Cannot find CFI for module"); - return false; - } - - // get frame of CFI for address - int result = dwarf_cfi_addrframe (cfi, pc - mod_bias, &frame); - if (result != 0) { - ctx->log(SEVERITY_ERROR, "Failed to find CFI frame for module"); - return false; - } - - // setup context to match frame - ctx->frame = frame; - - // get return register and PC range for function - Dwarf_Addr start = pc; - Dwarf_Addr end = pc; - bool signalp; - int ra_regno = dwarf_frame_info (frame, &start, &end, &signalp); - if(ra_regno >= 0) { - start += mod_bias; - end += mod_bias; - reginfo info; info.regno = ra_regno; - dwfl_module_register_names(ctx->module, regname_callback, &info); - ctx->log(SEVERITY_INFO, "Function %s(...): '.eh/debug frame' info: PC range: => [%#" PRIx64 ", %#" PRIx64 "], return register: %s, in_signal = %s", - name.c_str(), start, end, info.regname, signalp ? "true" : "false"); - } else { - ctx->log(SEVERITY_WARNING, "Return address register info unavailable (%s)", dwarf_errmsg(0)); - } - - // finally get CFA (Canonical Frame Address) - // Point cfa_ops to dummy to match print_detail expectations. - // (nops == 0 && cfa_ops != NULL => "undefined") - Dwarf_Op dummy; - Dwarf_Op *cfa_ops = &dummy; - size_t cfa_nops; - if(dwarf_frame_cfa(frame, &cfa_ops, &cfa_nops)) { - ctx->log(SEVERITY_ERROR, "Failed to get CFA for frame"); - return false; - } - - ctx->print_expr(ctx, cfa_ops, cfa_nops, NULL); - pst_decl(pst_dwarf_stack, stack, ctx); - if(stack.calc(&stack, cfa_ops, cfa_nops, NULL, this) && stack.get_value(&stack, &cfa)) { - //ctx.sp = v; - ctx->log(SEVERITY_INFO, "Function %s(...): CFA expression: %s ==> %#lX", name.c_str(), ctx->buff, cfa); - - // setup context to match CFA for frame - ctx->cfa = cfa; - } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate CFA expression"); - } - - pst_dwarf_stack_fini(&stack); - - return true; -} - bool __pst_handler::get_dwarf_function(pst_function* fun) { Dwarf_Addr mod_cu = 0; diff --git a/framework/sysutils.h b/framework/sysutils.h index 5261e1c..9139420 100644 --- a/framework/sysutils.h +++ b/framework/sysutils.h @@ -29,125 +29,8 @@ #include "list_head.h" #include "dwarf_expression.h" #include "dwarf_parameter.h" - - -// DW_TAG_call_site_parameter -typedef struct __pst_call_site_param : public SC_ListNode { - __pst_call_site_param(pst_context* ctx) { - param = NULL; name = NULL; value = 0; - pst_dwarf_expr_init(&location, ctx->alloc); - } - - ~__pst_call_site_param() { - if(name) { - free(name); name = NULL; - } - } - - void set_location(Dwarf_Op* expr, size_t exprlen); - - Dwarf_Die* param; // reference to parameter DIE in callee (DW_AT_call_parameter) - char* name; // name of parameter if present (DW_AT_name) - pst_dwarf_expr location; // DWARF stack containing location expression - uint64_t value; // parameter's value -} pst_call_site_param; - -// DW_TAG_call_site -typedef struct __pst_call_site : public SC_ListNode { - __pst_call_site(pst_context* c, uint64_t tgt, const char* orn) { - target = tgt; - if(orn) { - origin = strdup(orn); - } else { - origin = NULL; - } - die = NULL; - ctx = c; - } - - ~__pst_call_site() { - if(origin) { - free(origin); - origin = NULL; - } - } - - pst_call_site_param* add_param(); - void del_param(pst_call_site_param* p); - pst_call_site_param* next_param(pst_call_site_param* p); - pst_call_site_param* find_param(pst_dwarf_expr& expr); - - bool handle_dwarf(Dwarf_Die* die); - - uint64_t target; // pointer to callee function's (it's Low PC + base address) - char* origin; // name of callee function - Dwarf_Die* die; // DIE of function for which this call site has parameters - SC_ListHead params; // list of parameters and their values - pst_context* ctx; -} pst_call_site; - -typedef struct __pst_function : public SC_ListNode { - __pst_function(pst_context* _ctx, __pst_function* _parent) : ctx(_ctx) { - pc = 0; - line = -1; - die = NULL; - lowpc = 0; - highpc = 0; - memcpy(&cursor, _ctx->curr_frame, sizeof(cursor)); - parent = _parent; - sp = 0; - cfa = 0; - frame = NULL; - } - - ~__pst_function() { - clear(); - if(frame) { - free(frame); - } - } - - void clear(); - pst_parameter* add_param(); - void del_param(pst_parameter* p); - pst_parameter* next_param(pst_parameter* p); - - pst_call_site* add_call_site(uint64_t target, const char* origin); - void del_call_site(pst_call_site* st); - pst_call_site* next_call_site(pst_call_site* st); - pst_call_site* call_site_by_origin(const char* origin); - pst_call_site* call_site_by_target(uint64_t target); - - bool unwind(Dwarf_Addr addr); - bool handle_dwarf(Dwarf_Die* d); - bool print_dwarf(); - bool handle_lexical_block(Dwarf_Die* result); - bool handle_call_site(Dwarf_Die* result); - pst_call_site* find_call_site(__pst_function* callee); - - bool get_frame(); - - Dwarf_Addr lowpc; // offset to start of the function against base address - Dwarf_Addr highpc; // offset to the next address after the end of the function against base address - Dwarf_Die* die; // DWARF DIE containing definition of the function - std::string name; // function's name - SC_ListHead params; // function's parameters - - // call-site - SC_ListHead call_sites; // Call-Site definitions - SC_Dict mCallStToTarget; // map pointer to caller to call-site - SC_Dict mCallStToOrigin; // map caller name to call-site - - unw_word_t pc; // address between lowpc & highpc (plus base address offset). actually, currently executed command - unw_word_t sp; // SP register in function's frame - unw_word_t cfa; // CFA (Canonical Frame Adress) of the function - unw_cursor_t cursor; // copy of stack state of the function - int line; // line in code where function is defined - std::string file; // file name (DWARF Compilation Unit) where function is defined - __pst_function* parent; // parent function in call trace (caller) - Dwarf_Frame* frame; // function's stack frame - pst_context* ctx; // context of unwinding -} pst_function; +#include "dwarf_utils.h" +#include "dwarf_function.h" typedef struct __pst_handler { __pst_handler(ucontext_t* hctx) @@ -170,7 +53,7 @@ typedef struct __pst_handler { } void clear(); - pst_function* add_function(__pst_function* fun); + pst_function* add_function(pst_function* fun); void del_function(pst_function* f); pst_function* next_function(pst_function* f); pst_function* last_function();