From dcfbcd0952dec710e208086243584077c0d6e422 Mon Sep 17 00:00:00 2001 From: Nikolai Nosov Date: Fri, 17 Jan 2020 09:44:59 +0400 Subject: [PATCH] implemented base support of DWARF lexical block and call site --- framework/sysutils.cpp | 229 +++++++++++++++++++++++++++-------------- framework/sysutils.h | 2 + 2 files changed, 154 insertions(+), 77 deletions(-) diff --git a/framework/sysutils.cpp b/framework/sysutils.cpp index 17200ac..b3b4aad 100644 --- a/framework/sysutils.cpp +++ b/framework/sysutils.cpp @@ -37,7 +37,7 @@ typedef struct __reginfo { regname[0] = 0; regno = -1; } - char regname[1024]; + char regname[32]; int regno; } reginfo; @@ -57,7 +57,7 @@ bool handle_location(pst_context* ctx, Dwarf_Attribute* attr, dwarf_stack& stack Dwarf_Addr offset = pc - ctx->base_addr; if(dwarf_hasform(attr, DW_FORM_exprloc)) { - // Location expression (exprloc form of location in DWARF terms) + // Location expression (exprloc class of location in DWARF terms) Dwarf_Op *expr; size_t exprlen; if(dwarf_getlocation(attr, &expr, &exprlen) == 0) { @@ -67,7 +67,7 @@ bool handle_location(pst_context* ctx, Dwarf_Attribute* attr, dwarf_stack& stack } } } else if(dwarf_hasform(attr, DW_FORM_sec_offset)) { - // Location list (loclist form of location in DWARF terms) + // Location list (loclist class of location in DWARF terms) Dwarf_Addr base, start, end; ptrdiff_t off = 0; Dwarf_Op *expr; @@ -77,7 +77,7 @@ bool handle_location(pst_context* ctx, Dwarf_Attribute* attr, dwarf_stack& stack for(int i = 0; (off = dwarf_getlocations (attr, off, &base, &start, &end, &expr, &exprlen)) > 0; ++i) { ctx->print_expr_block (expr, exprlen, str, strsize, attr); if(offset >= start && offset <= end) { - // actual location, try to calculate Location expression and retrieve value of parameter + // actual location, try to calculate Location expression if(stack.calc_expression(expr, exprlen, attr)) { return true; } @@ -296,7 +296,6 @@ bool __pst_parameter::handle_dwarf(Dwarf_Die* result) handle_type(attr); } - dwarf_stack stack(ctx); 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); @@ -304,7 +303,7 @@ bool __pst_parameter::handle_dwarf(Dwarf_Die* result) unw_get_reg(&ctx->curr_frame, UNW_REG_IP, &pc); dwarf_stack stack(ctx); - char str[1024]; + char str[1024]; str[0] = 0; if(handle_location(ctx, attr, stack, pc, str, sizeof(str))) { if(stack.get_value(value)) { ctx->log(SEVERITY_DEBUG, "Parameter Location: %s ==> 0x%lX", str, value); @@ -450,6 +449,146 @@ bool __pst_function::print_dwarf() 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; + 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)) { + 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; + + if(dwarf_hasattr (result, DW_AT_abstract_origin) && dwarf_formref_die (dwarf_attr (result, DW_AT_abstract_origin, &attr_mem), &origin) != NULL) { + 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; + default: + break; + } + } while (dwarf_siblingof (&child, &child) == 0); + } + } + Dwarf_Die child; + if(dwarf_child (result, &child) == 0) { + do { + switch (dwarf_tag (&child)) + { + case DW_TAG_GNU_call_site_parameter: { + Dwarf_Addr pc; + unw_get_reg(&cursor, UNW_REG_IP, &pc); + + dwarf_stack stack(ctx); char str[1024]; uint64_t value; + 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, stack, pc, str, sizeof(str))) { + if(stack.get_value(value)) { + ctx->log(SEVERITY_DEBUG, "DW_TAG_GNU_call_site_parameter Location: \"%s\" ==> 0x%lX", str, value); + } else { + ctx->log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_location expression: %s", str); + } + } else { + ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", str); + } + } + 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); + if(handle_location(ctx, attr, stack, pc, str, sizeof(str))) { + if(stack.get_value(value)) { + ctx->log(SEVERITY_DEBUG, "DW_AT_GNU_call_site_value Location:\"%s\" ==> 0x%lX", str, value); + } else { + ctx->log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_location expression: %s", str); + } + } else { + ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", str); + } + } + break; + } + default: + break; + } + } while (dwarf_siblingof (&child, &child) == 0); + } + + return true; +} + bool __pst_function::handle_dwarf(Dwarf_Die* d) { die = d; @@ -532,84 +671,19 @@ bool __pst_function::handle_dwarf(Dwarf_Die* d) break; } case DW_TAG_GNU_call_site: - // 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 - 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) { - 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; - default: - break; - } - } while (dwarf_siblingof (&child, &child) == 0); - } - } - Dwarf_Die child; - if(dwarf_child (&result, &child) == 0) { - do { - switch (dwarf_tag (&child)) - { - case DW_TAG_GNU_call_site_parameter: { - Dwarf_Addr pc; - unw_get_reg(&cursor, UNW_REG_IP, &pc); - - dwarf_stack stack(ctx); char str[1024]; uint64_t value; - 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, stack, pc, str, sizeof(str))) { - if(stack.get_value(value)) { - ctx->log(SEVERITY_DEBUG, "DW_TAG_GNU_call_site_parameter Location: \"%s\" ==> 0x%lX", str, value); - } else { - ctx->log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_location expression: %s", str); - } - } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", str); - } - } - 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); - if(handle_location(ctx, attr, stack, pc, str, sizeof(str))) { - if(stack.get_value(value)) { - ctx->log(SEVERITY_DEBUG, "DW_AT_GNU_call_site_value Location:\"%s\" ==> 0x%lX", str, value); - } else { - ctx->log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_location expression: %s", str); - } - } else { - ctx->log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", str); - } - } - break; - } - default: - break; - } - } while (dwarf_siblingof (&child, &child) == 0); - } + 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; + } default: + ctx->log(SEVERITY_DEBUG, "Unknown TAG of function: 0x%X", dwarf_tag(&result)); break; } } while(dwarf_siblingof(&result, &result) == 0); @@ -798,6 +872,7 @@ bool __pst_handler::handle_dwarf() for(pst_function* fun = next_function(NULL); fun; fun = next_function(fun)) { dladdr((void*)(fun->pc), &info); ctx.base_addr = (uint64_t)info.dli_fbase; + ctx.log(SEVERITY_DEBUG, "Function %s(...): module name: %s, base address: %p", fun->name.c_str(), info.dli_fname, info.dli_fbase); ctx.curr_frame = fun->cursor; if(fun->parent) { diff --git a/framework/sysutils.h b/framework/sysutils.h index d95b267..f477372 100644 --- a/framework/sysutils.h +++ b/framework/sysutils.h @@ -104,6 +104,8 @@ typedef struct __pst_function : public SC_ListNode { bool unwind(Dwfl* dwfl, Dwfl_Module* module, 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); Dwarf_Addr lowpc; // start of the function Dwarf_Addr highpc; // next address after the end of the function