From 8f7fed23dcf889aee4f2ea226cecac53ff3bcd09 Mon Sep 17 00:00:00 2001 From: Nikolai Nosov Date: Thu, 9 Jan 2020 18:49:45 +0400 Subject: [PATCH] global refactoring: use libunwind to get stack frame, get rid on process base address, better DWARF DW_OP_XXX handling, initial implementation of parameter's values retrieval --- Makefile | 2 +- framework/common.cpp | 105 ++++++ framework/common.h | 13 + framework/sysutils.cpp | 715 ++++++++++++++++++++++++----------------- framework/sysutils.h | 109 ++++++- main.cpp | 9 +- 6 files changed, 652 insertions(+), 301 deletions(-) create mode 100644 framework/common.cpp create mode 100644 framework/common.h diff --git a/Makefile b/Makefile index 8284b80..5f799d9 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ SRC = $(shell find . -name '*.cpp') #OBJ = $(patsubst %.cpp,%.o,$(addprefix $(BUILD_DIR)/,$(notdir $(SRC)))) OBJ = $(BUILD_DIR)/main.o -LIBS = -L./ -lpthread -lpq -lpqtypes -luuid -lrabbitmq -ljson-c -lcrypto -ldl -lcurl -ldw -lxml2 -lmicrohttpd -larchive -lmagic +LIBS = -L./ -lpthread -lpq -lpqtypes -luuid -lrabbitmq -ljson-c -lcrypto -ldl -lcurl -ldw -lxml2 -lmicrohttpd -larchive -lmagic -lunwind -lunwind-x86_64 ATR_LIBS = ./build/libframework.a INCS = -I./ \ diff --git a/framework/common.cpp b/framework/common.cpp new file mode 100644 index 0000000..7c1e58d --- /dev/null +++ b/framework/common.cpp @@ -0,0 +1,105 @@ +/* + * common.cpp + * + * Created on: Jan 8, 2020 + * Author: nnosov + */ + +#include +#include + +#include "common.h" + +dwarf_op_map dw_op[] = { + {0x23, 0x0, 0, "DW_OP_plus_uconst"}, + // Register location descriptions. From DWARF 5, section 2.6.1.1.3: + // Register location descriptions describe an object (or a piece of an object) that resides in a register. + // A register location description must stand alone as the entire description of an object or a piece of an object. + {0x50, 0x0, "RAX", "DW_OP_reg0"}, + {0x51, 0x1, "RDX", "DW_OP_reg1"}, + {0x52, 0x2, "RCX", "DW_OP_reg2"}, + {0x53, 0x3, "RBX", "DW_OP_reg3"}, + {0x54, 0x4, "RSI", "DW_OP_reg4"}, + {0x55, 0x5, "RDI", "DW_OP_reg5"}, + {0x56, 0x6, "RBP", "DW_OP_reg6"}, + {0x57, 0x7, "RSP", "DW_OP_reg7"}, + {0x58, 0x8, "R8", "DW_OP_reg8"}, + {0x59, 0x9, "R9", "DW_OP_reg9"}, + {0x5A, 0xA, "R10", "DW_OP_reg10"}, + {0x5B, 0xB, "R11", "DW_OP_reg11"}, + {0x5C, 0xC, "R12", "DW_OP_reg12"}, + {0x5D, 0xD, "R13", "DW_OP_reg13"}, + {0x5E, 0xE, "R14", "DW_OP_reg14"}, + {0x5F, 0xF, "R15", "DW_OP_reg15"}, + {0x60, 0x10, "RIP", "DW_OP_reg16"}, + + // Register values. DWARF5, section 2.5.1.2 + // Register values are used to describe an object (or a piece of an object) that is located in memory at an address that is contained in + // a register (possibly offset by some constant) + {0x70, 0x0, "RAX", "DW_OP_breg0"}, + {0x71, 0x1, "RDX", "DW_OP_breg1"}, + {0x72, 0x2, "RCX", "DW_OP_breg2"}, + {0x73, 0x3, "RBX", "DW_OP_breg3"}, + {0x74, 0x4, "RSI", "DW_OP_breg4"}, + {0x75, 0x5, "RDI", "DW_OP_breg5"}, + {0x76, 0x6, "RBP", "DW_OP_breg6"}, + {0x77, 0x7, "RSP", "DW_OP_breg7"}, + {0x78, 0x8, "R8", "DW_OP_breg8"}, + {0x79, 0x9, "R9", "DW_OP_breg9"}, + {0x7A, 0xA, "R10", "DW_OP_breg10"}, + {0x7B, 0xB, "R11", "DW_OP_breg11"}, + {0x7C, 0xC, "R12", "DW_OP_breg12"}, + {0x7D, 0xD, "R13", "DW_OP_breg13"}, + {0x7E, 0xE, "R14", "DW_OP_breg14"}, + {0x7F, 0xF, "R15", "DW_OP_breg15"}, + {0x80, 0x10, "RIP", "DW_OP_breg16"}, + + // The DW_OP_fbreg operation provides a signed LEB128 offset from the address specified by the location description in the DW_AT_frame_base + // attribute of the current function. This is typically a stack pointer register plus or minus some offset + {0x91, -1, "", "DW_OP_fbreg"}, + {0x92, -1, "", "DW_OP_bregx"}, + {0x9C, -1, "", "DW_OP_call_frame_cfa"}, + // DWARF5, Section 2.6.1.1.4: + // he DW_OP_stack_value operation specifies that the object does not exist in memory but its value is nonetheless known and is at the top of the DWARF + // expression stack. In this form of location description, the DWARF expression represents the actual value of the object, rather than its location. + // The DW_OP_stack_value operation terminates the expression. + {0x9F, -1, "", "DW_OP_stack_value"}, + // + {0xF3, -1, "", "DW_OP_GNU_entry_value"}, +}; + +const dwarf_op_map* find_op_map(int op) +{ + for(uint32_t i = 0; i < sizeof(dw_op) / sizeof(dwarf_op_map); ++i) { + if(dw_op[i].op_num == op) { + return &dw_op[i]; + } + } + + return NULL; +} + +int32_t decode_sleb128(uint8_t *sleb128) +{ + int32_t num = 0, shift = 0, size = 0; + do { + num |= ((*sleb128 & 0x7f) << shift); + shift += 7; + size += 8; + } while(*sleb128++ & 0x80); + if((shift < size) && (*(--sleb128) & 0x40)) { + num |= - (1 << shift); + } + return num; +} + +uint32_t decode_uleb128(uint8_t *uleb128) +{ + uint32_t num = 0, shift = 0; + do { + num |= ((*uleb128 & 0x7f) << shift); + shift += 7; + } while(*uleb128++ & 0x80); + return num; +} + diff --git a/framework/common.h b/framework/common.h new file mode 100644 index 0000000..05895e5 --- /dev/null +++ b/framework/common.h @@ -0,0 +1,13 @@ +#pragma once + + +typedef struct __dwarf_op_map { + int op_num; // DWARF Operation DW_OP_XXX + int regno; // platform-dependent register number if present + const char* regname; // register name if regno specified + const char* op_name; // string representation of an operation +} dwarf_op_map; + +const dwarf_op_map* find_op_map(int op); +int32_t decode_sleb128(uint8_t *sleb128); +uint32_t decode_uleb128(uint8_t *uleb128); diff --git a/framework/sysutils.cpp b/framework/sysutils.cpp index 55cddd7..6209b3c 100644 --- a/framework/sysutils.cpp +++ b/framework/sysutils.cpp @@ -20,28 +20,51 @@ extern SC_LogBase* logger; -char __stack_trace[8192]; +#define USEI_LIBUNWIND +#ifdef USEI_LIBUNWIND +#include +#endif -Dwarf_Frame* frame = 0; -Dwarf_Frame* prev_frame = 0; -Dwfl_Module* module; +#include "common.h" + +bool __pst_context::print(const char* fmt, ...) +{ + bool nret = true; + + va_list args; + va_start(args, fmt); + int size = sizeof(buff) - offset; + int ret = vsnprintf(buff + offset, size, fmt, args); + if(ret >= size || ret < 0) { + nret = false; + } + offset += ret; + va_end(args); + + return nret; +} + +void __pst_context::log(SC_LogSeverity severity, const char* fmt, ...) +{ + uint32_t str_len = 0; + char str[PATH_MAX]; str[0] = 0; + va_list args; + va_start(args, fmt); + str_len += vsnprintf(str + str_len, sizeof(str) - str_len, fmt, args); + va_end(args); + + logger->Log(severity, "%s", str); +} // dwfl_addrsegment() possibly can be used to check address validity // dwarf_getattrs() allows to enumerate all DIE attributes // dwarf_getfuncs() allows to enumerate functions within CU -static void print_detail (int result, const Dwarf_Op *ops, size_t nops, Dwarf_Addr bias, const char* prefix); - bool is_location_form(int form) { - if (form == DW_FORM_block1 || - form == DW_FORM_block2 || - form == DW_FORM_block4 || - form == DW_FORM_block || - form == DW_FORM_data4 || - form == DW_FORM_data8 || - form == DW_FORM_sec_offset) { + if (form == DW_FORM_block1 || form == DW_FORM_block2 || form == DW_FORM_block4 || form == DW_FORM_block || + form == DW_FORM_data4 || form == DW_FORM_data8 || form == DW_FORM_sec_offset) { return true; } return false; @@ -83,6 +106,7 @@ int regname_callback (void *arg, int regno, const char *setname, const char *pre return 0; } +/* int reginfo_callback (void *arg, int regno, const char *setname, const char *prefix, const char *regname, int bits, int type) { reginfo* info = (reginfo*)arg; @@ -114,56 +138,9 @@ int reginfo_callback (void *arg, int regno, const char *setname, const char *pre return 0; } +*/ -void HandleType(Dwarf_Attribute* param, bool is_return = false) -{ - Dwarf_Attribute attr_mem; - Dwarf_Attribute* attr; - - // get DIE of return type - Dwarf_Die ret_die; - - if(dwarf_formref_die(param, &ret_die)) { - switch (dwarf_tag(&ret_die)) { - case DW_TAG_base_type: { - // get Size attribute and it's value - Dwarf_Word size = 0; - attr = dwarf_attr(&ret_die, DW_AT_byte_size, &attr_mem); - if(attr) { - dwarf_formudata(attr, &size); - } - logger->Log(SEVERITY_INFO, "base type '%s'(%lu)", dwarf_diename(&ret_die), size); - break; - } - case DW_TAG_array_type: - logger->Log(SEVERITY_INFO, "array type"); - break; - case DW_TAG_pointer_type: - logger->Log(SEVERITY_INFO, "pointer type"); - break; - case DW_TAG_enumeration_type: - logger->Log(SEVERITY_INFO, "enumeration type"); - break; - case DW_TAG_const_type: - logger->Log(SEVERITY_INFO, "constant type"); - break; - case DW_TAG_subroutine_type: - logger->Log(SEVERITY_INFO, "subroutine type"); - break; - case DW_TAG_typedef: - logger->Log(SEVERITY_INFO, "typedef '%s' type", dwarf_diename(&ret_die)); - break; - default: - logger->Log(SEVERITY_INFO, "Unknown 0x%X tag type", dwarf_tag(&ret_die)); - break; - } - attr = dwarf_attr(&ret_die, DW_AT_type, &attr_mem); - if(attr) { - HandleType(attr); - } - } -} - +/* void print_framereg(int regno) { Dwarf_Op ops_mem[3]; @@ -187,31 +164,145 @@ void print_framereg(int regno) logger->Log(SEVERITY_DEBUG, "\t[%d] operation: 0x%hhX, operand1: 0x%lX, operand2: 0x%lx, offset: 0x%lX", i, ops[i].atom, ops[i].number, ops[i].number2, ops[i].offset); } } +*/ -void -print_expr_block (Dwarf_Op *exprs, int len, char* buff, uint32_t buff_size) +uint32_t __pst_context::print_expr_block (Dwarf_Op *exprs, int len, char* buff, uint32_t buff_size, Dwarf_Attribute* attr) { uint32_t offset = 0; for (int i = 0; i < len; i++) { //printf ("%s", (i + 1 < len ? ", " : "")); - offset += snprintf(buff + offset, buff_size - offset, "0x%hhX(0x%lX, 0x%lx) ", exprs[i].atom, exprs[i].number, exprs[i].number2); - if(exprs[i].atom >= DW_OP_reg0 && exprs[i].atom <= DW_OP_bregx) { - print_framereg(exprs[i].atom); + const dwarf_op_map* map = find_op_map(exprs[i].atom); + if(map) { + if(map->op_num >= DW_OP_breg0 && map->op_num <= DW_OP_breg16) { + int32_t off = decode_sleb128((unsigned char*)&exprs[i].number); + + unw_word_t ptr = 0; + unw_get_reg(&cursor, map->regno, &ptr); + //ptr += off; + + offset += snprintf(buff + offset, buff_size - offset, "%s(*%s%s%d) reg_value: 0x%lX ", map->op_name, map->regname, off >=0 ? "+" : "", off, ptr); + } else if(map->op_num >= DW_OP_reg0 && map->op_num <= DW_OP_reg16) { + unw_word_t value = 0; + unw_get_reg(&cursor, map->regno, &value); + offset += snprintf(buff + offset, buff_size - offset, "%s(*%s) value: 0x%lX", map->op_name, map->regname, value); + } else if(map->op_num == DW_OP_GNU_entry_value) { + uint32_t value = decode_uleb128((unsigned char*)&exprs[i].number); + offset += snprintf(buff + offset, buff_size - offset, "%s(%u, ", map->op_name, value); + Dwarf_Attribute attr_mem; + if(!dwarf_getlocation_attr(attr, exprs, &attr_mem)) { + Dwarf_Op *expr; + size_t exprlen; + if (dwarf_getlocation(&attr_mem, &expr, &exprlen) == 0) { + offset += print_expr_block (expr, exprlen, buff + offset, buff_size - offset, attr); + offset += snprintf(buff + offset, buff_size - offset, ") "); + } else { + log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr location"); + } + } else { + log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr expression"); + } + } else if(map->op_num == DW_OP_stack_value) { + offset += snprintf(buff + offset, buff_size - offset, "%s", map->op_name); + } else if(map->op_num == DW_OP_plus_uconst) { + uint32_t value = decode_uleb128((unsigned char*)&exprs[i].number); + offset += snprintf(buff + offset, buff_size - offset, "%s(+%u) ", map->op_name, value); + } else if(map->op_num == DW_OP_bregx) { + uint32_t regno = decode_uleb128((unsigned char*)&exprs[i].number); + int32_t off = decode_sleb128((unsigned char*)&exprs[i].number2); + + unw_word_t ptr = 0; + unw_get_reg(&cursor, map->regno, &ptr); + //ptr += off; + + offset += snprintf(buff + offset, buff_size - offset, "%s(%s%s%d) reg_value = 0x%lX", map->op_name, unw_regname(regno), off >= 0 ? "+" : "", off, ptr); + } else { + offset += snprintf(buff + offset, buff_size - offset, "%s(0x%lX, 0x%lx) ", map->op_name, exprs[i].number, exprs[i].number2); + } + } else { + offset += snprintf(buff + offset, buff_size - offset, "0x%hhX(0x%lX, 0x%lx) ", exprs[i].atom, exprs[i].number, exprs[i].number2); } +// if(exprs[i].atom >= DW_OP_reg0 && exprs[i].atom <= DW_OP_bregx) { +// print_framereg(exprs[i].atom); +// } } + return offset; } -void HandleParameter(Dwarf_Die* result) + +bool __pst_parameter::handle_type(Dwarf_Attribute* param, bool is_return) { Dwarf_Attribute attr_mem; Dwarf_Attribute* attr; + // get DIE of return type + Dwarf_Die ret_die; + + if(!dwarf_formref_die(param, &ret_die)) { + function->ctx->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 + Dwarf_Word size = 0; + attr = dwarf_attr(&ret_die, DW_AT_byte_size, &attr_mem); + if(attr) { + dwarf_formudata(attr, &size); + } + function->ctx->log(SEVERITY_INFO, "base type '%s'(%lu)", dwarf_diename(&ret_die), size); + types.push_back(dwarf_diename(&ret_die)); + break; + } + case DW_TAG_array_type: + logger->Log(SEVERITY_INFO, "array type"); + types.push_back("[]"); + break; + case DW_TAG_pointer_type: + logger->Log(SEVERITY_INFO, "pointer type"); + types.push_back("*"); + break; + case DW_TAG_enumeration_type: + logger->Log(SEVERITY_INFO, "enumeration type"); + types.push_back("enum"); + break; + case DW_TAG_const_type: + logger->Log(SEVERITY_INFO, "constant type"); + types.push_back("const"); + break; + case DW_TAG_subroutine_type: + logger->Log(SEVERITY_INFO, "subroutine type"); + break; + case DW_TAG_typedef: + logger->Log(SEVERITY_INFO, "typedef '%s' type", dwarf_diename(&ret_die)); + types.push_back(dwarf_diename(&ret_die)); + break; + default: + logger->Log(SEVERITY_INFO, "Unknown 0x%X tag type", dwarf_tag(&ret_die)); + break; + } + + attr = dwarf_attr(&ret_die, DW_AT_type, &attr_mem); + if(attr) { + return handle_type(attr); + } + + return true; +} + +bool __pst_parameter::handle_dwarf(Dwarf_Die* result) +{ + die = result; + + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; + // Get reference to attribute type of the parameter/variable attr = dwarf_attr(result, DW_AT_type, &attr_mem); - logger->Log(SEVERITY_INFO, "Handle '%s' %s", dwarf_diename(result), dwarf_tag(result) == DW_TAG_formal_parameter ? "parameter" : "variable"); + function->ctx->log(SEVERITY_INFO, "Handle '%s' %s", dwarf_diename(result), dwarf_tag(result) == DW_TAG_formal_parameter ? "parameter" : "variable"); if(attr) { - HandleType(attr); + handle_type(attr); } // determine location of parameter in stack/heap or CPU registers @@ -222,209 +313,265 @@ void HandleParameter(Dwarf_Die* result) size_t exprlen; if (dwarf_getlocation(attr, &expr, &exprlen) == 0) { char str[1024]; str[0] = 0; - print_expr_block (expr, exprlen, str, sizeof(str)); - logger->Log(SEVERITY_DEBUG, "Found DW_AT_location expression: %s", str); - if(expr[0].atom >= DW_OP_reg0 && expr[0].atom <= DW_OP_bregx) { - print_framereg(expr[0].atom); - } + function->ctx->print_expr_block (expr, exprlen, str, sizeof(str), attr); + function->ctx->log(SEVERITY_DEBUG, "Found DW_AT_location expression: %s", str); +// if(expr[0].atom >= DW_OP_reg0 && expr[0].atom <= DW_OP_bregx) { +// print_framereg(expr[0].atom); +// } } } else if(dwarf_hasform(attr, DW_FORM_sec_offset)) { Dwarf_Addr base, start, end; ptrdiff_t off = 0; Dwarf_Op *expr; size_t exprlen; - Dwarf_Word value; - int ret = dwarf_formudata(attr, &value); - if(ret < 0) { - logger->Log(SEVERITY_ERROR, "Cannot get DW_AT_location offset"); - } + for(int i = 0; (off = dwarf_getlocations (attr, off, &base, &start, &end, &expr, &exprlen)) > 0; ++i) { char str[1024]; str[0] = 0; - print_expr_block (expr, exprlen, str, sizeof(str)); - logger->Log(SEVERITY_DEBUG, "[%d] low_offset: %" PRIx64 ", high_offset: %" PRIx64 ", .debug_locations section offset: 0x%lX ==> %s", i, start, end, value, str); + function->ctx->print_expr_block (expr, exprlen, str, sizeof(str), attr); + function->ctx->log(SEVERITY_DEBUG, "[%d] low_offset: 0x%" PRIx64 ", high_offset: 0x%" PRIx64 " ==> %s", i, start, end, str); } } else { - logger->Log(SEVERITY_WARNING, "Unknown attribute form = 0x%X, code = 0x%X, ", attr->form, attr->code); + function->ctx->log(SEVERITY_WARNING, "Unknown attribute form = 0x%X, code = 0x%X, ", attr->form, attr->code); } - } + + return true; } -void HandleFunction(Dwarf_Die* func, const char* fname) +bool __pst_function::handle_dwarf(Dwarf_Die* d) { - if(!strcmp(fname, dwarf_diename(func))) { - logger->Log(SEVERITY_INFO, "Found function in debug info. DWARF tag: 0x%X, name = %s(...)", dwarf_tag(func), dwarf_diename(func)); + die = d; - Dwarf_Attribute attr_mem; - Dwarf_Attribute* attr; + Dwarf_Attribute attr_mem; + Dwarf_Attribute* attr; - // determine function's stack frame base - attr = dwarf_attr(func, 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) { - char str[1024]; str[0] = 0; - print_expr_block (expr, exprlen, str, sizeof(str)); - logger->Log(SEVERITY_DEBUG, "Found DW_AT_framebase expression: %s", str); - //print_detail(0, expr, exprlen, 0, "\tLocation "); - } else { - logger->Log(SEVERITY_WARNING, "Unknown attribute form = 0x%X, code = 0x%X", attr->form, attr->code); - } + // get offset to function in memory against base address where process executed + dwarf_lowpc(d, &lowpc); + Dwarf_Addr highpc; + dwarf_highpc(d, &highpc); + + unw_proc_info_t info; + unw_get_proc_info(&ctx->cursor, &info); + + logger->Log(SEVERITY_INFO, "Found function in debug info. name = %s(...), PC = 0x%lX, LOW_PC = 0x%lX, HIGH_PC = 0x%lX, offset from base address: 0x%lX, info start = 0x%lX, offset from info start: 0x%lX", + dwarf_diename(d), pc, lowpc, highpc, pc - ctx->base_addr, info.start_ip, info.start_ip - ctx->base_addr); + + // 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) { + char str[1024]; str[0] = 0; + ctx->print_expr_block (expr, exprlen, str, sizeof(str), attr); + ctx->log(SEVERITY_DEBUG, "Found DW_AT_framebase expression: %s", str); + } 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 - attr = dwarf_attr(func, DW_AT_type, &attr_mem); - if(attr) { - logger->Log(SEVERITY_INFO, "Handle return parameter"); - HandleType(attr, true); - } else { - logger->Log(SEVERITY_DEBUG, "return attr name = 'void(0)'"); - } - - Dwarf_Die result; - if (dwarf_child(func, &result) != 0) - return; - - // went through parameters and local variables of the function - do { - switch (dwarf_tag(&result)) { - case DW_TAG_formal_parameter: - case DW_TAG_variable: - HandleParameter(&result); - break; -// case DW_TAG_inlined_subroutine: -// /* Recurse further down */ -// HandleFunction(&result, dwarf_diename(&result)); -// break; - default: - break; - } - } while(dwarf_siblingof(&result, &result) == 0); } - return; -} -static void print_detail (int result, const Dwarf_Op *ops, size_t nops, Dwarf_Addr bias, const char* prefix) -{ - printf("\t%s ", prefix); - if (result < 0) { - printf("indeterminate (%s)\n", dwarf_errmsg (-1)); - } else if (nops == 0) { - printf("%s\n", ops == NULL ? "same_value" : "undefined"); + // Get reference to return attribute type of the function + // may be to use dwfl_module_return_value_location() instead + pst_parameter ret_p(this); ret_p.is_return = true; + attr = dwarf_attr(die, DW_AT_type, &attr_mem); + if(attr) { + ctx->log(SEVERITY_INFO, "Handle return parameter"); + if(ret_p.handle_type(attr, true)) { + params.push_back(ret_p); + } } else { - printf("%s expression:", result == 0 ? "location" : "value"); - for (size_t i = 0; i < nops; ++i) { - printf (" 0x%X (offset: 0x%lX)", ops[i].atom, ops[i].offset); - if (ops[i].number2 == 0) { - if (ops[i].atom == DW_OP_addr) { - printf ("(%#" PRIx64 ")", ops[i].number + bias); - } else if (ops[i].number != 0) { - printf ("(%" PRIx64 ")", ops[i].number); - } - } - else { - printf ("(%" PRIx64 ",%" PRIx64 ")", ops[i].number, ops[i].number2); - } - } - puts(""); + ret_p.types.push_back("void"); + params.push_back(ret_p); + ctx->log(SEVERITY_DEBUG, "return attr name = 'void(0)'"); } + + Dwarf_Die result; + if(dwarf_child(die, &result) != 0) + return false; + + // went through parameters and local variables of the function + do { + pst_parameter param(this); + + switch (dwarf_tag(&result)) { + case DW_TAG_formal_parameter: + case DW_TAG_variable: + if(param.handle_dwarf(&result)) { + params.push_back(param); + } + break; + // case DW_TAG_inlined_subroutine: + // /* Recurse further down */ + // HandleFunction(&result, dwarf_diename(&result)); + // break; + default: + break; + } + } while(dwarf_siblingof(&result, &result) == 0); + + return true; } -void HandleCompilationUnit(Dwfl_Module* module, Dwarf_Addr addr, const char* fname) +bool __pst_function::unwind(Dwfl* dwfl, Dwfl_Module* module, Dwarf_Addr addr) { - Dwarf_Addr mod_cu = 0; - // get CU(Compilation Unit) debug definition - Dwarf_Die* cdie = dwfl_module_addrdie(module, addr, &mod_cu); - logger->Log(SEVERITY_DEBUG, "Function bias in module is 0x%lX", mod_cu); - //Dwarf_Die* cdie = dwfl_addrdie(dwfl, addr, &mod_bias); - if(!cdie) { - logger->Log(SEVERITY_INFO, "Failed to find DWARF DIE for address %X", addr); - return; - } + pc = addr; + Dwfl_Line *line = dwfl_getsrc(dwfl, addr); + if(line != NULL) { + int nline; + Dwarf_Addr addr; + const char* filename = dwfl_lineinfo (line, &addr, &nline, NULL, NULL, NULL); + if(filename) { + const char* str = strrchr(filename, '/'); + if(str && *str != 0) { + str++; + } else { + str = filename; + } + ctx->print("%s:%d", str, nline); + } else { + ctx->print("%p", (void*)addr); + } + } else { + ctx->print("%p", (void*)addr); + } + + const char* addrname = dwfl_module_addrname(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(" --> %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_context::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(module, &mod_bias); //Dwarf_CFI* cfi = dwfl_module_dwarf_cfi(module, &mod_bias); - if(cfi) { - // get frame of CFI for address - logger->Log(SEVERITY_INFO, "Found CFI for module %s", dwarf_diename(cdie)); - if(frame) { - prev_frame = frame; - } - int result = dwarf_cfi_addrframe (cfi, addr - mod_bias, &frame); - if (result == 0) { - // get frame information - logger->Log(SEVERITY_INFO, "Found CFI frame for module %s", dwarf_diename(cdie)); - Dwarf_Addr start = addr; - Dwarf_Addr end = addr; - bool signalp; - int ra_regno = dwarf_frame_info (frame, &start, &end, &signalp); - if(ra_regno >= 0) { - start += mod_bias; - end += mod_bias; - } - logger->Log(SEVERITY_DEBUG, "Per '.eh_frame' info has %#" PRIx64 " => [%#" PRIx64 ", %#" PRIx64 "] in_signal = %s", addr, start, end, signalp ? "true" : "false"); - if (ra_regno < 0) - logger->Log(SEVERITY_DEBUG, "return address register unavailable (%s)", dwarf_errmsg (0)); - else { - reginfo info; info.regno = ra_regno; - dwfl_module_register_names(module, regname_callback, &info); - logger->Log(SEVERITY_DEBUG, "return address in reg%u%s ==> %s", ra_regno, signalp ? " (signal frame)" : "", info.regname); - } + if(!cfi) { + log(SEVERITY_INFO, "Cannot find CFI for module"); + return false; + } - // 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; - result = dwarf_frame_cfa(frame, &cfa_ops, &cfa_nops); - char str[1024]; str[0] = 0; - print_expr_block (cfa_ops, cfa_nops, str, sizeof(str)); - logger->Log(SEVERITY_INFO, "Found CFA expression: %s", str); - //print_detail (result, cfa_ops, cfa_nops, mod_bias, "\tCFA "); + // get frame of CFI for address + int result = dwarf_cfi_addrframe (cfi, addr - mod_bias, &frame); + if (result == 0) { + // get frame information + log(SEVERITY_INFO, "Found CFI frame for module"); + Dwarf_Addr start = addr; + Dwarf_Addr end = addr; + bool signalp; + int ra_regno = dwarf_frame_info (frame, &start, &end, &signalp); + if(ra_regno >= 0) { + start += mod_bias; + end += mod_bias; + } + log(SEVERITY_DEBUG, "Per '.eh_frame' info has %#" PRIx64 " => [%#" PRIx64 ", %#" PRIx64 "] in_signal = %s", addr, start, end, signalp ? "true" : "false"); + if (ra_regno < 0) + log(SEVERITY_DEBUG, "return address register unavailable (%s)", dwarf_errmsg(0)); + else { + reginfo info; info.regno = ra_regno; + dwfl_module_register_names(module, regname_callback, &info); + log(SEVERITY_DEBUG, "return address in reg%u%s ==> %s", ra_regno, signalp ? " (signal frame)" : "", info.regname); } + // 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; + result = dwarf_frame_cfa(frame, &cfa_ops, &cfa_nops); + char str[1024]; str[0] = 0; + print_expr_block (cfa_ops, cfa_nops, str, sizeof(str)); + log(SEVERITY_INFO, "Found CFA expression: %s", str); + //print_detail (result, cfa_ops, cfa_nops, mod_bias, "\tCFA "); + } + + return true; +} + +bool __pst_context::get_dwarf_function(pst_function& fun) +{ + Dwarf_Addr mod_cu = 0; + // get CU(Compilation Unit) debug definition + Dwarf_Die* cdie = dwfl_module_addrdie(module, addr, &mod_cu); + //Dwarf_Die* cdie = dwfl_addrdie(dwfl, addr, &mod_bias); + if(!cdie) { + logger->Log(SEVERITY_INFO, "Failed to find DWARF DIE for address %X", addr); + return false; } if(dwarf_tag(cdie) != DW_TAG_compile_unit) { logger->Log(SEVERITY_DEBUG, "Skipping non-cu die. DWARF tag: 0x%X, name = %s", dwarf_tag(cdie), dwarf_diename(cdie)); - return; + return false; } - logger->Log(SEVERITY_DEBUG, "Enumerating Compilation Unit '%s' to lookup for function %s()", dwarf_diename(cdie), fname); - Dwarf_Die result; + Dwarf_Die result; if(dwarf_child(cdie, &result)) { - logger->Log(SEVERITY_INFO, "No child DIE found for CU"); - return; + logger->Log(SEVERITY_ERROR, "No child DIE found for CU %s", dwarf_diename(cdie)); + return false; } + bool nret = false; + do { - switch (dwarf_tag(&result)) { - case DW_TAG_subprogram: - case DW_TAG_entry_point: - case DW_TAG_inlined_subroutine: - HandleFunction(&result, fname); - break; - default: - //logger->Log(SEVERITY_INFO, "Unknown tag 0x%X for die '%s'", dwarf_tag(&result), dwarf_diename(&result)); - break; + int tag = dwarf_tag(&result); + if(tag == DW_TAG_subprogram || tag == DW_TAG_entry_point || tag == DW_TAG_inlined_subroutine) { + if(!strcmp(fun.name.c_str(), dwarf_diename(&result))) { + return fun.handle_dwarf(&result); + } } } while(dwarf_siblingof(&result, &result) == 0); + + return nret; } -const char* LibdwTraceCallStack(ucontext_t* uctx) +void __pst_context::dwarf_print() +{ + +} + +char *debuginfo_path = NULL; +Dwfl_Callbacks callbacks = { + .find_elf = dwfl_linux_proc_find_elf, + .find_debuginfo = dwfl_standard_find_debuginfo, + .section_address = dwfl_offline_section_address, + .debuginfo_path = &debuginfo_path, +}; + +#include + +bool __pst_context::unwind() { - void * caller_address = 0; #ifdef REG_RIP // x86_64 - caller_address = (void *) uctx->uc_mcontext.gregs[REG_RIP]; + caller = (void *) hcontext->uc_mcontext.gregs[REG_RIP]; #elif defined(REG_EIP) // x86_32 caller_address = (void *) uctx->uc_mcontext.gregs[REG_EIP]); #elif defined(__arm__) @@ -440,84 +587,76 @@ const char* LibdwTraceCallStack(ucontext_t* uctx) #else # error "unknown architecture!" #endif - void * array[50]; - int size = backtrace(array, 50); + + handle = dlopen(NULL, RTLD_NOW); + Dl_info info; + dladdr(caller, &info); + base_addr = (uint64_t)info.dli_fbase; + logger->Log(SEVERITY_INFO, "Process address information: dlopen handle: %p, base address: %p, object name: %s, symbol name: %s", handle, info.dli_fbase, info.dli_fname, info.dli_sname); + int skipped = 0; +#ifndef USEI_LIBUNWIND + int size = 0; + void * array[50]; + size = backtrace(array, 50); for(int i = 0; i < size && array[i] != caller_address; ++i, ++skipped); - __stack_trace[0] = 0; uint32_t offset = 0; - offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "Stack trace(caller = %p. Total stack frames: %d, skipped: %d):\n", - caller_address, size, skipped); + print("Stack trace(caller = %p. Total stack frames: %d, skipped: %d):\n", caller_address, size, skipped); +#else + unw_word_t pc; - char *debuginfo_path = NULL; - Dwfl_Callbacks callbacks = { - .find_elf = dwfl_linux_proc_find_elf, - .find_debuginfo = dwfl_standard_find_debuginfo, - .section_address = dwfl_offline_section_address, - .debuginfo_path = &debuginfo_path, - }; + unw_getcontext(&context); + unw_init_local(&cursor, &context); + while (unw_step(&cursor) > 0) { + unw_get_reg(&cursor, UNW_REG_IP, &pc); + if(pc == (uint64_t)caller) { + break; + } else { + ++skipped; + } + } + print("Stack trace(caller = %p. Skipped stack frames: %d):\n", caller, skipped); +#endif - Dwfl* dwfl = dwfl_begin(&callbacks); + dwfl = dwfl_begin(&callbacks); if(dwfl == NULL) { - offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "Failed to initialize libdw session for parse stack frames"); - return __stack_trace; + print("Failed to initialize libdw session for parse stack frames"); + return false; } if(dwfl_linux_proc_report(dwfl, getpid()) != 0 || dwfl_report_end(dwfl, NULL, NULL) !=0) { - offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "Failed to parse debug section of executable"); - return __stack_trace; + print("Failed to parse debug section of executable"); + return false; } +#ifndef USEI_LIBUNWIND for (int i = skipped, idx = 0; i < size ; ++i, ++idx) { - offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "[%-2d] ", idx); + print("[%-2d] ", idx); + Dwarf_Addr addr = (uintptr_t)array[i]; +#else + for (int i = skipped, idx = 0; true ; ++i, ++idx) { + unw_get_reg(&cursor, UNW_REG_IP, &pc); + addr = pc; + print("[%-2d] ", idx); +#endif + module = dwfl_addrmodule(dwfl, addr); + get_frame(); + pst_function fun(this); + if(fun.unwind(dwfl, module, pc)) { + if(get_dwarf_function(fun)) { + functions.push_back(fun); + } + } + print("\n"); +#ifdef USEI_LIBUNWIND + if(unw_step(&cursor) <= 0) { + break; + } +#endif + } - Dwarf_Addr addr = (uintptr_t)array[i]; - Dwfl_Line *line = dwfl_getsrc(dwfl, addr); - if(line != NULL) { - int nline; - Dwarf_Addr addr; - const char* filename = dwfl_lineinfo (line, &addr, &nline, NULL, NULL, NULL); - if(filename) { - const char* str = strrchr(filename, '/'); - if(str && *str != 0) { - str++; - } else { - str = filename; - } - offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "%s:%d", str, nline); - } else { - offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "%p", array[i]); - } - } else { - offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "%p", array[i]); - } +// for(int op = DW_OP_breg0; op <= DW_OP_breg16; op++ ) { +// printf("{0x%X, 0x%X, \"%s\"},\n", op, op - DW_OP_breg0, unw_regname(op - DW_OP_breg0)); +// } - module = dwfl_addrmodule(dwfl, addr); - const char* addrname = dwfl_module_addrname(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) { - logger->Log(SEVERITY_ERROR, "Failed to allocate memory"); - return __stack_trace; - } - offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, " --> %s", function_name); - - char* str = strchr(function_name, '('); - if(str) { - *str = 0; - } - HandleCompilationUnit(module, addr, function_name); - free(function_name); - } - - if(demangle_name) { - free(demangle_name); - } - - offset += snprintf(__stack_trace + offset, sizeof(__stack_trace) - offset, "\n"); - } - - return __stack_trace; + return true; } diff --git a/framework/sysutils.h b/framework/sysutils.h index ce9ac05..84df3c7 100644 --- a/framework/sysutils.h +++ b/framework/sysutils.h @@ -14,17 +14,110 @@ #include #include #include +#include #include #include - -using std::string; - -// stack trace string buffer -extern char __stack_trace[8192]; - -// libdw-based trace call stack implementation -const char* LibdwTraceCallStack(ucontext_t* uctx); +#include +#include +#include char* GetExecutableName(char* path, uint32_t size); +typedef struct __pst_function pst_function; + +typedef struct __pst_parameter { + __pst_parameter(pst_function* fun) : function(fun) + { + die = NULL; + size = 0; + type = 0; + loc = NULL; + is_return = false; + } + + bool handle_dwarf(Dwarf_Die* d); + bool handle_type(Dwarf_Attribute* param, bool is_return = false); + + Dwarf_Die* die; // DWARF DIE containing parameter's definition + std::string name; // parameter's name + Dwarf_Word size; // size of parameter in bytes + int type; // base type of parameter in DW_TAG_XXX types enumeration + std::vector types; // list of parameters definitions i.e. 'typedef', 'uint32_t' + void* loc; // pointer to location of parameter's value + bool is_return; // whether this parameter is return value of the function + pst_function* function; +} pst_parameter; + +typedef struct __pst_context pst_context; + +typedef struct __pst_function { + __pst_function(pst_context* ctx) : ctx(ctx) + { + pc = 0; + line = -1; + die = NULL; + lowpc = 0; + } + + bool unwind(Dwfl* dwfl, Dwfl_Module* module, Dwarf_Addr addr); + bool handle_dwarf(Dwarf_Die* d); + + Dwarf_Addr lowpc; + Dwarf_Die* die; // DWARF DIE containing definition of the function + std::string name; // function's name + std::vector params; // array of function's parameters + unw_word_t pc; // pointer to the start of the function + int line; // line in code where function is defined + std::string file; // file name (DWARF Compilation Unit) where is function defined + pst_context* ctx; +} pst_function; + +typedef struct __pst_context { + __pst_context(ucontext_t* hctx) : hcontext(hctx) + { + caller = NULL; + module = NULL; + dwfl = NULL; + offset = 0; + buff[0] = 0; + frame = NULL; + addr = 0; + base_addr = 0; + handle = 0; + } + + ~__pst_context() + { + if(handle) { + dlclose(handle); + } + } + + + bool print(const char* fmt, ...); + void dwarf_print(); + uint32_t print_expr_block(Dwarf_Op *exprs, int len, char* buff, uint32_t buff_size, Dwarf_Attribute* attr = 0); + void log(SC_LogSeverity severity, const char*fmt, ...); + bool unwind(); + bool get_frame(); + bool get_dwarf_function(pst_function& fun); + + ucontext_t* hcontext;// context of signal handler + unw_context_t context;// context of stack trace + unw_cursor_t cursor; // currently examined frame of context + void* handle; // process handle + Dwarf_Addr addr; // address of currently processed function + Dwarf_Addr base_addr;// base address where process loaded + Dwfl* dwfl; // DWARF context + Dwfl_Module* module; // currently processed CU + Dwarf_Frame* frame; // currently processed stack frame + void* caller; // pointer to the function which requested to unwind stack + std::vector functions;// array of functions in stack frame + char buff[8192]; // stack trace buffer + uint32_t offset; // offset in the 'buff' +} pst_context; + +// libdw-based trace call stack implementation +bool LibdwTraceCallStack(pst_context& ctx); + #endif /* SC_SYSUTILS_H_ */ diff --git a/main.cpp b/main.cpp index a9ae941..d170265 100644 --- a/main.cpp +++ b/main.cpp @@ -63,14 +63,15 @@ void FatalSignalHandler(int sig, siginfo_t* info, void* context) fatal_error_in_progress = 1; logger->Log(SEVERITY_ERROR, "%s signal handled", strsignal(sig)); - const char* trace = 0; + bool trace = false; + pst_context ctx((ucontext_t*)context); if((context != 0) && (sig == SIGSEGV || sig == SIGABRT || sig == SIGBUS || sig == SIGFPE)) { - trace = LibdwTraceCallStack((ucontext_t*)context); + trace = ctx.unwind(); } if(trace) { - logger->Log(SEVERITY_DEBUG, "%s", trace); + logger->Log(SEVERITY_DEBUG, "%s", ctx.buff); } else { logger->Log(SEVERITY_ERROR, "No stack trace obtained"); } @@ -193,7 +194,7 @@ int main(int argc, char* argv[]) SetSignalHandler(SigusrHandler); - Fun1(1, DEF_2, 3); + Fun1(1, DEF_2, 5); return 0; }