code hierarchy refactoring
This commit is contained in:
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* dwarf_call_site.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include "../src/dwarf/dwarf_call_site.h"
|
||||
|
||||
#include <dwarf.h>
|
||||
#include <elfutils/libdw.h>
|
||||
|
||||
#include "../src/dwarf/dwarf_function.h"
|
||||
#include "../src/dwarf/dwarf_utils.h"
|
||||
#include "../src/dwarf/dwarf_utils.h"
|
||||
#include "../utils/hash_multimap.h"
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// pst_call_site_param
|
||||
// -----------------------------------------------------------------------------------
|
||||
void pst_call_site_param_init(pst_call_site_param* param)
|
||||
{
|
||||
// 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_alloc(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) {
|
||||
pst_free(param->name);
|
||||
param->name = NULL;
|
||||
}
|
||||
|
||||
if(param->allocated) {
|
||||
pst_free(param);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// pst_call_site
|
||||
// -----------------------------------------------------------------------------------
|
||||
pst_call_site_param* 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 del_param(pst_call_site*, pst_call_site_param* p)
|
||||
{
|
||||
list_del(&p->node);
|
||||
pst_call_site_param_fini(p);
|
||||
}
|
||||
|
||||
pst_call_site_param* 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* pst_call_site_find(pst_call_site* site, pst_dwarf_expr* expr)
|
||||
{
|
||||
for(pst_call_site_param* param = next_param(site, NULL); param; param = next_param(site, param)) {
|
||||
if(pst_dwarf_expr_equal(¶m->location, expr)) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool call_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 = add_param(site);
|
||||
if(dwarf_hasattr(child, DW_AT_location)) {
|
||||
// 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)) {
|
||||
pst_log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", site->ctx->buff);
|
||||
del_param(site, param);
|
||||
return false;
|
||||
}
|
||||
pst_log(SEVERITY_DEBUG, " DW_AT_location: %s", site->ctx->buff);
|
||||
}
|
||||
|
||||
// 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_decl0(pst_dwarf_expr, loc);
|
||||
if(handle_location(site->ctx, attr, &loc, pc, NULL)) {
|
||||
param->value = loc.value;
|
||||
pst_dwarf_expr_fini(&loc);
|
||||
pst_log(SEVERITY_DEBUG, " DW_AT_GNU_call_site_value:\"%s\" ==> 0x%lX", site->ctx->buff, param->value);
|
||||
} else {
|
||||
pst_log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", site->ctx->buff);
|
||||
del_param(site, param);
|
||||
pst_dwarf_expr_fini(&loc);
|
||||
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)
|
||||
{
|
||||
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_alloc(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) {
|
||||
pst_free(site->origin);
|
||||
site->origin = NULL;
|
||||
}
|
||||
|
||||
if(site->allocated) {
|
||||
pst_free(site);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// pst_call_site_storage
|
||||
// -----------------------------------------------------------------------------------
|
||||
|
||||
// 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_call_site_storage_handle_dwarf(pst_call_site_storage* storage, Dwarf_Die* result, pst_function* info)
|
||||
{
|
||||
Dwarf_Die origin;
|
||||
Dwarf_Attribute attr_mem;
|
||||
Dwarf_Attribute* attr;
|
||||
|
||||
pst_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);
|
||||
pst_log(SEVERITY_DEBUG, "DW_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_decl0(pst_dwarf_expr, expr);
|
||||
if(handle_location(storage->ctx, &attr_mem, &expr, info->pc, info)) {
|
||||
target = expr.value;
|
||||
pst_log(SEVERITY_DEBUG, "DW_AT_GNU_call_site_target: %#lX", target);
|
||||
}
|
||||
pst_dwarf_expr_fini(&expr);
|
||||
}
|
||||
}
|
||||
|
||||
if(target == 0 && oname == NULL) {
|
||||
pst_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 = pst_call_site_storage_add(storage, target, oname);
|
||||
|
||||
st->tail_call = (dwarf_hasattr(result, DW_AT_call_tail_call) != 0);
|
||||
|
||||
// if DW_AT_low_pc attribute is specified, then it's value is actually PC in caller's frame (address of invocation of callee)
|
||||
if(dwarf_hasattr (result, DW_AT_low_pc) && dwarf_attr(result, DW_AT_low_pc, &attr_mem)) {
|
||||
if(!dwarf_formaddr(&attr_mem, &st->call_pc)) {
|
||||
pst_log(SEVERITY_DEBUG, "DW_AT_low_pc: %#lX", st->call_pc);
|
||||
}
|
||||
}
|
||||
|
||||
if(!call_site_handle_dwarf(st, &child)) {
|
||||
pst_call_site_storage_del(storage, st);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pst_call_site* storage_call_site_by_origin(pst_call_site_storage* storage, const char* origin)
|
||||
{
|
||||
pst_call_site* ret = NULL;
|
||||
hash_node* node = hash_find(&storage->cs_to_origin, origin, strlen(origin));
|
||||
if(node) {
|
||||
ret = hash_entry(node, pst_call_site, org_node);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
pst_call_site* storage_call_site_by_target(pst_call_site_storage* storage, uint64_t target)
|
||||
{
|
||||
pst_call_site* ret = NULL;
|
||||
hash_node* node = hash_find(&storage->cs_to_target, (char*)&target, sizeof(target));
|
||||
if(node) {
|
||||
ret = hash_entry(node, pst_call_site, tgt_node);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
pst_call_site* pst_call_site_storage_add(pst_call_site_storage* storage, uint64_t target, const char* origin)
|
||||
{
|
||||
pst_new(pst_call_site, st, storage->ctx, target, origin);
|
||||
list_add_bottom(&storage->call_sites, &st->node);
|
||||
|
||||
if(target) {
|
||||
hash_add(&storage->cs_to_target, &st->tgt_node, &target, sizeof(target));
|
||||
} else if(origin) {
|
||||
hash_add(&storage->cs_to_origin, &st->org_node, origin, strlen(origin));
|
||||
}
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
void pst_call_site_storage_del(pst_call_site_storage* storage, pst_call_site* st)
|
||||
{
|
||||
hash_node* node = NULL;
|
||||
list_del(&st->node);
|
||||
|
||||
if(st->target) {
|
||||
node = hash_find(&storage->cs_to_target, &st->target, sizeof(st->target));
|
||||
} else if(st->origin) {
|
||||
node = hash_find(&storage->cs_to_origin, st->origin, strlen(st->origin));
|
||||
}
|
||||
|
||||
if(node) {
|
||||
hash_del(node);
|
||||
}
|
||||
|
||||
pst_free(st);
|
||||
}
|
||||
|
||||
pst_call_site* pst_call_site_storage_find(pst_call_site_storage* storage, pst_function* callee)
|
||||
{
|
||||
uint64_t start_pc = storage->ctx->base_addr + callee->lowpc;
|
||||
pst_call_site* cs = storage_call_site_by_target(storage, start_pc);
|
||||
if(!cs) {
|
||||
cs = storage_call_site_by_origin(storage, callee->name);
|
||||
}
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
void pst_call_site_storage_init(pst_call_site_storage* storage, pst_context* ctx)
|
||||
{
|
||||
storage->ctx = ctx;
|
||||
list_head_init(&storage->call_sites);
|
||||
hash_head_init(&storage->cs_to_target);
|
||||
hash_head_init(&storage->cs_to_origin);
|
||||
storage->allocated = false;
|
||||
}
|
||||
|
||||
pst_call_site_storage* pst_call_site_storage_new(pst_context* ctx)
|
||||
{
|
||||
pst_call_site_storage* ns = pst_alloc(pst_call_site_storage);
|
||||
if(ns) {
|
||||
pst_call_site_storage_init(ns, ctx);
|
||||
ns->allocated = true;
|
||||
}
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
void pst_call_site_storage_fini(pst_call_site_storage* storage)
|
||||
{
|
||||
pst_call_site* site = NULL;
|
||||
struct list_node *pos, *tn;
|
||||
list_for_each_entry_safe(site, pos, tn, &storage->call_sites, node) {
|
||||
list_del(&site->node);
|
||||
pst_call_site_fini(site);
|
||||
}
|
||||
|
||||
if(storage->allocated) {
|
||||
pst_free(storage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* dwarf_call_site.h
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#ifndef SRC_DWARF_DWARF_CALL_SITE_H_
|
||||
#define SRC_DWARF_DWARF_CALL_SITE_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <elfutils/libdw.h>
|
||||
|
||||
#include "../src/dwarf/dwarf_expression.h"
|
||||
#include "../utils/hash_multimap.h"
|
||||
#include "../utils/list_head.h"
|
||||
#include "context.h"
|
||||
|
||||
typedef struct pst_callee_info {
|
||||
Dwarf_Addr target;
|
||||
char* origin;
|
||||
} pst_callee_info;
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// DW_TAG_call_site_parameter
|
||||
// -----------------------------------------------------------------------------------
|
||||
typedef struct pst_call_site_param {
|
||||
list_node node; // uplink. !!! must be first !!!
|
||||
|
||||
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 to list of call-sites
|
||||
hash_node tgt_node; // uplink to call-site by node dictionary
|
||||
hash_node org_node; // uplink to call-site by origin dictionary
|
||||
|
||||
uint64_t target; // pointer to callee function (it's Low PC + base address)
|
||||
char* origin; // name of callee function
|
||||
Dwarf_Addr call_pc; // address of 'call' instruction to callee inside of caller
|
||||
bool tail_call; // whether this a tail-call (jump) or normal call 'call'
|
||||
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;
|
||||
void pst_call_site_init(pst_call_site* site, pst_context* context, uint64_t target, const char* origin);
|
||||
pst_call_site* pst_call_site_new(pst_context* context, uint64_t target, const char* origin);
|
||||
void pst_call_site_fini(pst_call_site* site);
|
||||
|
||||
pst_call_site_param* pst_call_site_find(pst_call_site* site, pst_dwarf_expr* expr);
|
||||
bool pst_call_site_handle_dwarf(pst_call_site* site, Dwarf_Die* child);
|
||||
|
||||
typedef struct __pst_function pst_function;
|
||||
// -----------------------------------------------------------------------------------
|
||||
// storage for all of function's call sites
|
||||
// -----------------------------------------------------------------------------------
|
||||
typedef struct pst_call_site_storage {
|
||||
pst_context* ctx;
|
||||
list_head call_sites; // Call-Site definitions
|
||||
hash_head cs_to_target; // map pointer to caller to call-site
|
||||
hash_head cs_to_origin; // map caller name to call-site
|
||||
bool allocated; // whether this object was allocated or not
|
||||
} pst_call_site_storage;
|
||||
|
||||
void pst_call_site_storage_init(pst_call_site_storage* storage, pst_context* ctx);
|
||||
pst_call_site_storage* pst_call_site_storage_new(pst_context* ctx);
|
||||
void pst_call_site_storage_fini(pst_call_site_storage* storage);
|
||||
|
||||
bool pst_call_site_storage_handle_dwarf(pst_call_site_storage* storage, Dwarf_Die* result, pst_function* info);
|
||||
pst_call_site* pst_call_site_storage_find(pst_call_site_storage* storage, pst_function* callee);
|
||||
pst_call_site* pst_call_site_storage_add(pst_call_site_storage* storage, uint64_t target, const char* origin);
|
||||
void pst_call_site_storage_del(pst_call_site_storage* storage, pst_call_site* st);
|
||||
void pst_call_site_storage_del(pst_call_site_storage* storage, pst_call_site* st);
|
||||
|
||||
|
||||
#endif /* SRC_DWARF_DWARF_CALL_SITE_H_ */
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* dwarf_expression.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include "../src/dwarf/dwarf_expression.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "../utils/allocator.h"
|
||||
#include "common.h"
|
||||
#include "context.h"
|
||||
|
||||
//
|
||||
// DWARF operation
|
||||
//
|
||||
void pst_dwarf_op_init(pst_dwarf_op* dwop, uint8_t op, uint64_t a1, uint64_t a2)
|
||||
{
|
||||
//pst_assert(dwop);
|
||||
|
||||
list_node_init(&dwop->node);
|
||||
dwop->operation = op;
|
||||
dwop->arg1 = a1;
|
||||
dwop->arg2 = a2;
|
||||
dwop->allocated = false;
|
||||
}
|
||||
|
||||
pst_dwarf_op* pst_dwarf_op_new(uint8_t op, uint64_t a1, uint64_t a2)
|
||||
{
|
||||
pst_dwarf_op* nop = pst_alloc(pst_dwarf_op);
|
||||
if(nop) {
|
||||
pst_dwarf_op_init(nop, op, a1, a2);
|
||||
nop->allocated = true;
|
||||
}
|
||||
|
||||
return nop;
|
||||
}
|
||||
|
||||
void pst_dwarf_op_fini(pst_dwarf_op* dwop)
|
||||
{
|
||||
assert(dwop);
|
||||
|
||||
if(dwop->allocated) {
|
||||
pst_free(dwop);
|
||||
} else {
|
||||
dwop->operation = 0;
|
||||
dwop->arg1 = 0;
|
||||
dwop->arg2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// pst_dwarf_expr
|
||||
//
|
||||
pst_dwarf_op* add_op(pst_dwarf_expr* expr, uint8_t operation, uint64_t arg1, uint64_t arg2)
|
||||
{
|
||||
pst_new(pst_dwarf_op, op, operation, arg1, arg2);
|
||||
list_add_bottom(&expr->operations, &op->node);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
pst_dwarf_op* next_op(pst_dwarf_expr* expr, pst_dwarf_op* op)
|
||||
{
|
||||
struct list_node* n = (op == NULL) ? list_first(&expr->operations) : list_next(&op->node);
|
||||
|
||||
pst_dwarf_op* ret = NULL;
|
||||
if(n) {
|
||||
ret = list_entry(n, pst_dwarf_op, node);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void clean(pst_dwarf_expr* expr)
|
||||
{
|
||||
pst_dwarf_op* op = NULL;
|
||||
struct list_node *pos, *tn;
|
||||
list_for_each_entry_safe(op, pos, tn, &expr->operations, node) {
|
||||
list_del(&op->node);
|
||||
pst_dwarf_op_fini(op);
|
||||
}
|
||||
}
|
||||
|
||||
bool pst_dwarf_expr_print_op(pst_dwarf_expr* expr, const char* fmt, ...)
|
||||
{
|
||||
bool nret = true;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int size = sizeof(expr->buff) - expr->offset;
|
||||
int ret = vsnprintf(expr->buff + expr->offset, size, fmt, args);
|
||||
if(ret >= size || ret < 0) {
|
||||
nret = false;
|
||||
}
|
||||
expr->offset += ret;
|
||||
va_end(args);
|
||||
|
||||
return nret;
|
||||
}
|
||||
|
||||
void pst_dwarf_expr_set_value(pst_dwarf_expr* expr, uint64_t v)
|
||||
{
|
||||
expr->has_value = true;
|
||||
expr->value = v;
|
||||
}
|
||||
|
||||
bool pst_dwarf_expr_equal(pst_dwarf_expr* lhs, pst_dwarf_expr* rhs)
|
||||
{
|
||||
if(list_count(&lhs->operations) != list_count(&rhs->operations)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pst_dwarf_op* lop = next_op(lhs, NULL);
|
||||
pst_dwarf_op* rop = next_op(rhs, NULL);
|
||||
while(lop && rop) {
|
||||
if(lop->operation == rop->operation && lop->arg1 == rop->arg1 && lop->arg2 == rop->arg2) {
|
||||
return true;
|
||||
}
|
||||
lop = next_op(lhs, lop);
|
||||
rop = next_op(rhs, rop);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void pst_dwarf_expr_setup(pst_dwarf_expr* expr, Dwarf_Op* exprs, size_t exprlen)
|
||||
{
|
||||
clean(expr);
|
||||
for(size_t i = 0; i < exprlen; ++i) {
|
||||
add_op(expr, exprs[i].atom, exprs[i].number, exprs[i].number2);
|
||||
}
|
||||
}
|
||||
|
||||
void pst_dwarf_expr_init(pst_dwarf_expr* expr)
|
||||
{
|
||||
list_head_init(&expr->operations);
|
||||
expr->has_value = false;
|
||||
expr->value = 0;
|
||||
|
||||
expr->buff[0] = 0;
|
||||
expr->offset = 0;
|
||||
expr->allocated = false;
|
||||
}
|
||||
|
||||
pst_dwarf_expr* pst_dwarf_expr_new()
|
||||
{
|
||||
pst_dwarf_expr* ne = pst_alloc(pst_dwarf_expr);
|
||||
if(ne) {
|
||||
pst_dwarf_expr_init(ne);
|
||||
ne->allocated = true;
|
||||
}
|
||||
|
||||
return ne;
|
||||
}
|
||||
|
||||
void pst_dwarf_expr_fini(pst_dwarf_expr* expr)
|
||||
{
|
||||
clean(expr);
|
||||
if(expr->allocated) {
|
||||
pst_free(expr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* dwarf_expression.h
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#ifndef SRC_DWARF_DWARF_EXPRESSION_H_
|
||||
#define SRC_DWARF_DWARF_EXPRESSION_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
#include "../utils/allocator.h"
|
||||
#include "../utils/list_head.h"
|
||||
|
||||
// DWARF operation (represents our own DW_OP_XXX)
|
||||
typedef struct {
|
||||
list_node node; //uplink
|
||||
|
||||
uint8_t operation;
|
||||
uint64_t arg1;
|
||||
uint64_t arg2;
|
||||
bool allocated;
|
||||
} pst_dwarf_op;
|
||||
void pst_dwarf_op_init(pst_dwarf_op* dwop, uint8_t op, uint64_t a1, uint64_t a2);
|
||||
pst_dwarf_op* pst_dwarf_op_new(uint8_t op, uint64_t a1, uint64_t a2);
|
||||
void pst_dwarf_op_fini(pst_dwarf_op* dwop);
|
||||
|
||||
|
||||
// DWARF expression
|
||||
typedef struct pst_dwarf_expr {
|
||||
list_head operations;
|
||||
bool has_value;
|
||||
uint64_t value;
|
||||
char buff[512];
|
||||
uint16_t offset;
|
||||
bool allocated;
|
||||
} pst_dwarf_expr;
|
||||
void pst_dwarf_expr_init(pst_dwarf_expr* expr);
|
||||
pst_dwarf_expr* pst_dwarf_expr_new();
|
||||
void pst_dwarf_expr_fini(pst_dwarf_expr* expr);
|
||||
|
||||
void pst_dwarf_expr_setup(pst_dwarf_expr* expr, Dwarf_Op* exprs, size_t exprlen);
|
||||
bool pst_dwarf_expr_equal(pst_dwarf_expr* lhs, pst_dwarf_expr* rhs);
|
||||
bool pst_dwarf_expr_print_op(pst_dwarf_expr* expr, const char* fmt, ...);
|
||||
void pst_dwarf_expr_set_value(pst_dwarf_expr* expr, uint64_t v);
|
||||
|
||||
#endif /* SRC_DWARF_DWARF_EXPRESSION_H_ */
|
||||
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* dwarf_function.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include "../src/dwarf/dwarf_function.h"
|
||||
|
||||
#include <dwarf.h>
|
||||
#include <stdlib.h>
|
||||
#include <cxxabi.h>
|
||||
|
||||
#include "../src/dwarf/dwarf_stack.h"
|
||||
#include "../src/dwarf/dwarf_utils.h"
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// pst_function
|
||||
// -----------------------------------------------------------------------------------
|
||||
bool get_frame(pst_function* fn)
|
||||
{
|
||||
// get CFI (Call Frame Information) for current module
|
||||
// from handle_cfi()
|
||||
Dwarf_Addr mod_bias = 0;
|
||||
Dwarf_CFI* cfi = dwfl_module_eh_cfi(fn->ctx->module, &mod_bias); // rty .eh_cfi first
|
||||
if(!cfi) { // then try .debug_fame second
|
||||
cfi = dwfl_module_dwarf_cfi(fn->ctx->module, &mod_bias);
|
||||
}
|
||||
if(!cfi) {
|
||||
pst_log(SEVERITY_ERROR, "Cannot find CFI for module");
|
||||
return false;
|
||||
}
|
||||
|
||||
// get frame of CFI for address
|
||||
int result = dwarf_cfi_addrframe (cfi, fn->pc - mod_bias, &fn->frame);
|
||||
if (result != 0) {
|
||||
pst_log(SEVERITY_ERROR, "Failed to find CFI frame for module");
|
||||
return false;
|
||||
}
|
||||
|
||||
// setup context to match frame
|
||||
fn->ctx->frame = fn->frame;
|
||||
|
||||
// get return register and PC range for function
|
||||
Dwarf_Addr start = fn->pc;
|
||||
Dwarf_Addr end = fn->pc;
|
||||
bool signalp;
|
||||
int ra_regno = dwarf_frame_info (fn->frame, &start, &end, &signalp);
|
||||
if(ra_regno >= 0) {
|
||||
start += mod_bias;
|
||||
end += mod_bias;
|
||||
reginfo info; info.regno = ra_regno;
|
||||
dwfl_module_register_names(fn->ctx->module, regname_callback, &info);
|
||||
pst_log(SEVERITY_INFO, "Function %s(...): '.eh/debug frame' info: PC range: => [%#" PRIx64 ", %#" PRIx64 "], return register: %s, in_signal = %s",
|
||||
fn->name, start, end, info.regname, signalp ? "true" : "false");
|
||||
} else {
|
||||
pst_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(fn->frame, &cfa_ops, &cfa_nops)) {
|
||||
pst_log(SEVERITY_ERROR, "Failed to get CFA for frame");
|
||||
return false;
|
||||
}
|
||||
|
||||
fn->ctx->print_expr(fn->ctx, cfa_ops, cfa_nops, NULL);
|
||||
|
||||
pst_decl(pst_dwarf_stack, stack, fn->ctx);
|
||||
if(pst_dwarf_stack_calc(&stack, cfa_ops, cfa_nops, NULL, NULL) && pst_dwarf_stack_get_value(&stack, &fn->cfa)) {
|
||||
pst_log(SEVERITY_INFO, "Function %s(...): CFA expression: %s ==> %#lX", fn->name, fn->ctx->buff, fn->cfa);
|
||||
|
||||
// setup context to match CFA for frame
|
||||
fn->ctx->cfa = fn->cfa;
|
||||
} else {
|
||||
pst_log(SEVERITY_ERROR, "Failed to calculate CFA expression");
|
||||
}
|
||||
|
||||
pst_dwarf_stack_fini(&stack);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pst_parameter* add_param(pst_function* fn)
|
||||
{
|
||||
pst_new(pst_parameter, p, fn->ctx);
|
||||
list_add_bottom(&fn->params, &p->node);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void del_param(pst_parameter* p)
|
||||
{
|
||||
list_del(&p->node);
|
||||
pst_free(p);
|
||||
}
|
||||
|
||||
void clear(pst_function* fn)
|
||||
{
|
||||
pst_parameter* param = NULL;
|
||||
struct list_node *pos, *tn;
|
||||
list_for_each_entry_safe(param, pos, tn, &fn->params, node) {
|
||||
list_del(¶m->node);
|
||||
pst_parameter_fini(param);
|
||||
}
|
||||
|
||||
pst_call_site_storage_fini(&fn->call_sites);
|
||||
}
|
||||
|
||||
pst_parameter* next_param(pst_function* fn, pst_parameter* p)
|
||||
{
|
||||
struct list_node* n = (p == NULL) ? list_first(&fn->params) : list_next(&p->node);
|
||||
|
||||
pst_parameter* ret = NULL;
|
||||
if(n) {
|
||||
ret = list_entry(n, pst_parameter, node);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool handle_lexical_block(pst_function* fn, 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:
|
||||
pst_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);
|
||||
}
|
||||
pst_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(fn, &child);
|
||||
break;
|
||||
case DW_TAG_variable: {
|
||||
pst_parameter* param = add_param(fn);
|
||||
if(!pst_parameter_handle_dwarf(param, &child, fn)) {
|
||||
del_param(param);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DW_TAG_GNU_call_site:
|
||||
pst_call_site_storage_handle_dwarf(&fn->call_sites, &child, fn);
|
||||
break;
|
||||
case DW_TAG_inlined_subroutine:
|
||||
pst_log(SEVERITY_DEBUG, "Skipping Lexical block tag 'DW_TAG_inlined_subroutine'");
|
||||
break;
|
||||
default:
|
||||
pst_log(SEVERITY_DEBUG, "Unknown Lexical block tag 0x%X", dwarf_tag(&child));
|
||||
break;
|
||||
}
|
||||
}while (dwarf_siblingof (&child, &child) == 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pst_function_print_dwarf(pst_function* fn)
|
||||
{
|
||||
char* at = NULL;
|
||||
if(!asprintf(&at, " at %s:%d, %p", fn->file, fn->line, (void*)fn->pc)) {
|
||||
return false;
|
||||
}
|
||||
if(at[4] == ':' && at[5] == '-') {
|
||||
free(at);
|
||||
if(!asprintf(&at, " at %p", (void*)fn->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(fn, NULL);
|
||||
if(param && param->is_return) {
|
||||
// print return value type, function name and start list of parameters
|
||||
pst_parameter_print_dwarf(param);
|
||||
fn->ctx->print(fn->ctx, " %s(", fn->name);
|
||||
param = next_param(fn, param);
|
||||
} else {
|
||||
fn->ctx->print(fn->ctx, "%s(", fn->name);
|
||||
}
|
||||
|
||||
bool first = true; bool start_variable = false;
|
||||
for(; param; param = next_param(fn, param)) {
|
||||
if(param->is_return) {
|
||||
// print return value type, function name and start list of parameters
|
||||
pst_parameter_print_dwarf(param);
|
||||
fn->ctx->print(fn->ctx, " %s(", fn->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(param->is_variable) {
|
||||
if(!start_variable) {
|
||||
fn->ctx->print(fn->ctx, ")%s\n", at);
|
||||
fn->ctx->print(fn->ctx, "{\n");
|
||||
start_variable = true;
|
||||
}
|
||||
if(param->line) {
|
||||
fn->ctx->print(fn->ctx, "%04u: ", param->line);
|
||||
} else {
|
||||
fn->ctx->print(fn->ctx, " ");
|
||||
}
|
||||
pst_parameter_print_dwarf(param);
|
||||
fn->ctx->print(fn->ctx, ";\n");
|
||||
} else {
|
||||
if(first) {
|
||||
first = false;
|
||||
} else {
|
||||
fn->ctx->print(fn->ctx, ", ");
|
||||
}
|
||||
pst_parameter_print_dwarf(param);
|
||||
}
|
||||
}
|
||||
|
||||
if(!start_variable) {
|
||||
fn->ctx->print(fn->ctx, ");%s\n", at);
|
||||
} else {
|
||||
fn->ctx->print(fn->ctx, "}\n");
|
||||
}
|
||||
|
||||
free(at);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pst_function_handle_dwarf(pst_function * fn, Dwarf_Die* d)
|
||||
{
|
||||
fn->die = d;
|
||||
get_frame(fn);
|
||||
|
||||
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, &fn->lowpc);
|
||||
dwarf_highpc(d, &fn->highpc);
|
||||
// } else {
|
||||
// pst_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(&fn->cursor, &info);
|
||||
fn->ctx->clean_print(fn->ctx);
|
||||
|
||||
pst_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), fn->lowpc, fn->highpc, fn->pc - fn->ctx->base_addr, info.start_ip, info.start_ip - fn->ctx->base_addr);
|
||||
fn->ctx->print_registers(fn->ctx, 0x0, 0x10);
|
||||
pst_log(SEVERITY_INFO, "Function %s(...): CFA: %#lX %s", dwarf_diename(d), fn->parent ? fn->parent->sp : 0, fn->ctx->buff);
|
||||
pst_log(SEVERITY_INFO, "Function %s(...): %s", dwarf_diename(d), fn->ctx->buff);
|
||||
|
||||
// determine function's stack frame base
|
||||
attr = dwarf_attr(fn->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) {
|
||||
fn->ctx->print_expr(fn->ctx, expr, exprlen, attr);
|
||||
pst_decl(pst_dwarf_stack, stack, fn->ctx);
|
||||
if(pst_dwarf_stack_calc(&stack, expr, exprlen, attr, fn)) {
|
||||
uint64_t value;
|
||||
if(pst_dwarf_stack_get_value(&stack, &value)) {
|
||||
pst_log(SEVERITY_DEBUG, "DW_AT_framebase expression: \"%s\" ==> 0x%lX", fn->ctx->buff, value);
|
||||
} else {
|
||||
pst_log(SEVERITY_ERROR, "Failed to get value of calculated DW_AT_framebase expression: %s", fn->ctx->buff);
|
||||
}
|
||||
} else {
|
||||
pst_log(SEVERITY_ERROR, "Failed to calculate DW_AT_framebase expression: %s", fn->ctx->buff);
|
||||
}
|
||||
pst_dwarf_stack_fini(&stack);
|
||||
} else {
|
||||
pst_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(fn); ret_p->is_return = true;
|
||||
attr = dwarf_attr(fn->die, DW_AT_type, &attr_mem);
|
||||
if(attr) {
|
||||
if(!pst_parameter_handle_type(ret_p, attr)) {
|
||||
pst_log(SEVERITY_ERROR, "Failed to handle return parameter type for function %s(...)", fn->name);
|
||||
del_param(ret_p);
|
||||
}
|
||||
} else {
|
||||
pst_parameter_add_type(ret_p, "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(fn->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(fn);
|
||||
if(!pst_parameter_handle_dwarf(param, &result, fn)) {
|
||||
del_param(param);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DW_TAG_GNU_call_site:
|
||||
pst_call_site_storage_handle_dwarf(&fn->call_sites, &result, fn);
|
||||
break;
|
||||
|
||||
// case DW_TAG_inlined_subroutine:
|
||||
// /* Recurse further down */
|
||||
// HandleFunction(&result);
|
||||
// break;
|
||||
case DW_TAG_lexical_block: {
|
||||
handle_lexical_block(fn, &result);
|
||||
break;
|
||||
}
|
||||
// Also handle:
|
||||
// DW_TAG_unspecified_parameters (unknown number of arguments i.e. fun(arg1, ...);
|
||||
// DW_AT_inline
|
||||
default:
|
||||
pst_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(pst_function* fn, Dwarf_Addr addr)
|
||||
{
|
||||
fn->pc = addr;
|
||||
|
||||
Dwfl_Line *dwline = dwfl_getsrc(fn->ctx->dwfl, addr);
|
||||
if(dwline != NULL) {
|
||||
Dwarf_Addr addr;
|
||||
const char* filename = dwfl_lineinfo (dwline, &addr, &fn->line, NULL, NULL, NULL);
|
||||
if(filename) {
|
||||
const char* str = strrchr(filename, '/');
|
||||
if(str && *str != 0) {
|
||||
str++;
|
||||
} else {
|
||||
str = filename;
|
||||
}
|
||||
fn->file = pst_strdup(str);
|
||||
fn->ctx->print(fn->ctx, "%s:%d", str, fn->line);
|
||||
} else {
|
||||
fn->ctx->print(fn->ctx, "%p", (void*)addr);
|
||||
}
|
||||
} else {
|
||||
fn->ctx->print(fn->ctx, "%p", (void*)addr);
|
||||
}
|
||||
|
||||
const char* addrname = dwfl_module_addrname(fn->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) {
|
||||
pst_log(SEVERITY_ERROR, "Failed to allocate memory");
|
||||
return false;
|
||||
}
|
||||
fn->ctx->print(fn->ctx, " --> %s", function_name);
|
||||
|
||||
char* str = strchr(function_name, '(');
|
||||
if(str) {
|
||||
*str = 0;
|
||||
}
|
||||
fn->name = pst_strdup(function_name);
|
||||
free(function_name);
|
||||
}
|
||||
|
||||
if(demangle_name) {
|
||||
free(demangle_name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pst_function_init(pst_function* fn, pst_context* _ctx, __pst_function* _parent)
|
||||
{
|
||||
list_node_init(&fn->node);
|
||||
|
||||
// fields
|
||||
fn->lowpc = 0;
|
||||
fn->highpc = 0;
|
||||
fn->pc = 0;
|
||||
fn->die = NULL;
|
||||
fn->name = NULL;
|
||||
list_head_init(&fn->params);
|
||||
pst_call_site_storage_init(&fn->call_sites, _ctx);
|
||||
|
||||
fn->sp = 0;
|
||||
fn->cfa = 0;
|
||||
memcpy(&fn->cursor, _ctx->curr_frame, sizeof(fn->cursor));
|
||||
fn->line = -1;
|
||||
fn->file = NULL;
|
||||
fn->parent = _parent;
|
||||
fn->frame = NULL;
|
||||
fn->ctx = _ctx;
|
||||
fn->allocated = false;
|
||||
}
|
||||
|
||||
pst_function* pst_function_new(pst_context* _ctx, __pst_function* _parent)
|
||||
{
|
||||
pst_function* fn = pst_alloc(pst_function);
|
||||
if(fn) {
|
||||
pst_function_init(fn, _ctx, _parent);
|
||||
fn->allocated = true;
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
void pst_function_fini(pst_function* fn)
|
||||
{
|
||||
clear(fn);
|
||||
|
||||
if(fn->frame) {
|
||||
// use free here because it was allocate out of our control by libdw
|
||||
free(fn->frame);
|
||||
}
|
||||
|
||||
if(fn->name) {
|
||||
pst_free(fn->name);
|
||||
}
|
||||
|
||||
if(fn->file) {
|
||||
pst_free(fn->file);
|
||||
}
|
||||
|
||||
if(fn->allocated) {
|
||||
pst_free(fn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* dwarf_function.h
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#ifndef SRC_DWARF_DWARF_FUNCTION_H_
|
||||
#define SRC_DWARF_DWARF_FUNCTION_H_
|
||||
|
||||
#include "../src/dwarf/dwarf_call_site.h"
|
||||
#include "../src/dwarf/dwarf_expression.h"
|
||||
#include "../src/dwarf/dwarf_parameter.h"
|
||||
#include "../utils/list_head.h"
|
||||
#include "context.h"
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// pst_function
|
||||
// -----------------------------------------------------------------------------------
|
||||
typedef struct __pst_function {
|
||||
list_node node;
|
||||
|
||||
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
|
||||
unw_word_t pc; // address between LowPC & HighPC (plus base address offset). actually, currently executed command
|
||||
Dwarf_Die* die; // DWARF DIE containing definition of the function
|
||||
char* name; // function's name
|
||||
list_head params; // function's parameters
|
||||
pst_call_site_storage call_sites;
|
||||
|
||||
unw_word_t sp; // SP register in function's frame
|
||||
unw_word_t cfa; // CFA (Canonical Frame Address) of the function
|
||||
unw_cursor_t cursor; // copy of stack state of the function
|
||||
int line; // line in code where function is defined
|
||||
char* 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
|
||||
bool allocated; // whether this object was allocated or not
|
||||
} pst_function;
|
||||
void pst_function_init(pst_function* fn, pst_context* _ctx, __pst_function* _parent);
|
||||
pst_function* pst_function_new(pst_context* _ctx, __pst_function* _parent);
|
||||
void pst_function_fini(pst_function* fn);
|
||||
|
||||
bool pst_function_unwind(pst_function* fn, Dwarf_Addr addr);
|
||||
bool pst_function_handle_dwarf(pst_function * fn, Dwarf_Die* d);
|
||||
bool pst_function_print_dwarf(pst_function* fn);
|
||||
|
||||
#endif /* SRC_DWARF_DWARF_FUNCTION_H_ */
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* sysutils.cpp
|
||||
*
|
||||
* Created on: Dec 28, 2019
|
||||
* Author: nnosov
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <dwarf.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include <cxxabi.h>
|
||||
#include <execinfo.h>
|
||||
#include <inttypes.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "dwarf_handler.h"
|
||||
|
||||
|
||||
#define USE_LIBUNWIND
|
||||
|
||||
#ifdef USE_LIBUNWIND
|
||||
#include <libunwind.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "utils/log.h"
|
||||
#include "utils/allocator.h"
|
||||
#include "dwarf/dwarf_operations.h"
|
||||
#include "dwarf/dwarf_stack.h"
|
||||
#include "dwarf/dwarf_function.h"
|
||||
|
||||
// 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
|
||||
|
||||
bool get_dwarf_function(pst_handler* h, pst_function* fun)
|
||||
{
|
||||
Dwarf_Addr mod_cu = 0;
|
||||
// get CU(Compilation Unit) debug definition
|
||||
h->ctx.module = dwfl_addrmodule(h->ctx.dwfl, fun->pc);
|
||||
Dwarf_Die* cdie = dwfl_module_addrdie(h->ctx.module, fun->pc, &mod_cu);
|
||||
//Dwarf_Die* cdie = dwfl_addrdie(dwfl, addr, &mod_bias);
|
||||
if(!cdie) {
|
||||
pst_log(SEVERITY_INFO, "Failed to find DWARF DIE for address %X", fun->pc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(dwarf_tag(cdie) != DW_TAG_compile_unit) {
|
||||
pst_log(SEVERITY_DEBUG, "Skipping non-cu die. DWARF tag: 0x%X, name = %s", dwarf_tag(cdie), dwarf_diename(cdie));
|
||||
return false;
|
||||
}
|
||||
|
||||
Dwarf_Die result;
|
||||
if(dwarf_child(cdie, &result)) {
|
||||
pst_log(SEVERITY_ERROR, "No child DIE found for CU %s", dwarf_diename(cdie));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nret = false;
|
||||
|
||||
do {
|
||||
int tag = dwarf_tag(&result);
|
||||
if(tag == DW_TAG_subprogram || tag == DW_TAG_entry_point || tag == DW_TAG_inlined_subroutine) {
|
||||
//ctx.log(SEVERITY_DEBUG, "function die name %s", dwarf_diename(&result));
|
||||
if(!strcmp(fun->name, dwarf_diename(&result))) {
|
||||
return pst_function_handle_dwarf(fun, &result);
|
||||
}
|
||||
}
|
||||
} while(dwarf_siblingof(&result, &result) == 0);
|
||||
|
||||
return nret;
|
||||
}
|
||||
|
||||
pst_function* add_function(pst_handler* h, pst_function* parent)
|
||||
{
|
||||
pst_new(pst_function, fn, &h->ctx, parent);
|
||||
list_add_bottom(&h->functions, &fn->node);
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
void del_function(pst_function* fn)
|
||||
{
|
||||
list_del(&fn->node);
|
||||
pst_function_fini(fn);
|
||||
}
|
||||
|
||||
void clear(pst_handler* h)
|
||||
{
|
||||
pst_function* fn = NULL;
|
||||
struct list_node *pos, *tn;
|
||||
list_for_each_entry_safe(fn, pos, tn, &h->functions, node) {
|
||||
list_del(&fn->node);
|
||||
pst_function_fini(fn);
|
||||
}
|
||||
}
|
||||
|
||||
pst_function* next_function(pst_handler* h, pst_function* fn)
|
||||
{
|
||||
list_node* n = (fn == NULL) ? list_first(&h->functions) : list_next(&fn->node);
|
||||
pst_function* ret = NULL;
|
||||
if(n) {
|
||||
ret = list_entry(n, pst_function, node);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
pst_function* prev_function(pst_handler* h, pst_function* fn)
|
||||
{
|
||||
list_node* n = (fn == NULL) ? list_last(&h->functions) : list_prev(&fn->node);
|
||||
pst_function* ret = NULL;
|
||||
if(n) {
|
||||
ret = list_entry(n, pst_function, node);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
pst_function* last_function(pst_handler* h)
|
||||
{
|
||||
list_node* n = list_last(&h->functions);
|
||||
if(n) {
|
||||
return list_entry(n, pst_function, node);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool pst_handler_handle_dwarf(pst_handler* h)
|
||||
{
|
||||
h->ctx.clean_print(&h->ctx);
|
||||
Dl_info info;
|
||||
|
||||
//for(pst_function* fun = next_function(NULL); fun; fun = next_function(fun)) {
|
||||
for(pst_function* fun = last_function(h); fun; fun = prev_function(h, fun)) {
|
||||
dladdr((void*)(fun->pc), &info);
|
||||
pst_log(SEVERITY_INFO, "Function %s(...): module name: %s, base address: %p, CFA: %#lX", fun->name, info.dli_fname, info.dli_fbase, fun->parent ? fun->parent->sp : 0);
|
||||
|
||||
// setup context
|
||||
h->ctx.module = dwfl_addrmodule(h->ctx.dwfl, fun->pc);
|
||||
h->ctx.base_addr = (uint64_t)info.dli_fbase;
|
||||
h->ctx.curr_frame = &fun->cursor;
|
||||
h->ctx.sp = fun->sp;
|
||||
h->ctx.cfa = fun->cfa;
|
||||
h->ctx.curr_frame = &fun->cursor;
|
||||
|
||||
get_dwarf_function(h, fun);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pst_handler_print_dwarf(pst_handler* h)
|
||||
{
|
||||
h->ctx.clean_print(&h->ctx);
|
||||
h->ctx.print(&h->ctx, "DWARF-based stack trace information:\n");
|
||||
uint32_t idx = 0;
|
||||
for(pst_function* fn = next_function(h, NULL); fn; fn = next_function(h, fn)) {
|
||||
h->ctx.print(&h->ctx, "[%-2u] ", idx); idx++;
|
||||
pst_function_print_dwarf(fn);
|
||||
h->ctx.print(&h->ctx, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
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 <dlfcn.h>
|
||||
|
||||
bool pst_handler_unwind(pst_handler* h)
|
||||
{
|
||||
h->ctx.clean_print(&h->ctx);
|
||||
void* caller; // pointer to the function which requested to unwind stack
|
||||
|
||||
#ifdef REG_RIP // x86_64
|
||||
caller = (void *) h->ctx.hcontext->uc_mcontext.gregs[REG_RIP];
|
||||
h->ctx.sp = h->ctx.hcontext->uc_mcontext.gregs[REG_RSP];
|
||||
pst_log(SEVERITY_DEBUG, "Original caller's SP: %#lX", h->ctx.sp);
|
||||
#elif defined(REG_EIP) // x86_32
|
||||
caller_address = (void *) uctx->uc_mcontext.gregs[REG_EIP]);
|
||||
#elif defined(__arm__)
|
||||
caller_address = (void *) uctx->uc_mcontext.arm_pc);
|
||||
#elif defined(__aarch64__)
|
||||
caller_address = (void *) uctx->uc_mcontext.pc);
|
||||
#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__)
|
||||
caller_address = (void *) uctx->uc_mcontext.regs->nip);
|
||||
#elif defined(__s390x__)
|
||||
caller_address = (void *) uctx->uc_mcontext.psw.addr);
|
||||
#elif defined(__APPLE__) && defined(__x86_64__)
|
||||
caller_address = (void *) uctx->uc_mcontext->__ss.__rip);
|
||||
#else
|
||||
# error "unknown architecture!"
|
||||
#endif
|
||||
|
||||
//handle = dlopen(NULL, RTLD_NOW);
|
||||
Dl_info info;
|
||||
dladdr(caller, &info);
|
||||
h->ctx.base_addr = (uint64_t)info.dli_fbase;
|
||||
pst_log(SEVERITY_INFO, "Process address information: PC address: %p, base address: %p, object name: %s", caller, info.dli_fbase, info.dli_fname);
|
||||
|
||||
h->ctx.dwfl = dwfl_begin(&callbacks);
|
||||
if(h->ctx.dwfl == NULL) {
|
||||
h->ctx.print(&h->ctx, "Failed to initialize libdw session for parse stack frames");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(dwfl_linux_proc_report(h->ctx.dwfl, getpid()) != 0 || dwfl_report_end(h->ctx.dwfl, NULL, NULL) !=0) {
|
||||
h->ctx.print(&h->ctx, "Failed to parse debug section of executable");
|
||||
return false;
|
||||
}
|
||||
|
||||
h->ctx.print(&h->ctx, "Stack trace: caller = %p\n", caller);
|
||||
|
||||
unw_getcontext(&h->ctx.context);
|
||||
unw_init_local(&h->ctx.cursor, &h->ctx.context);
|
||||
h->ctx.curr_frame = &h->ctx.cursor;
|
||||
|
||||
Dwarf_Addr addr; // address of currently processed function
|
||||
for(int i = 0, skip = 1; unw_step(h->ctx.curr_frame) > 0; ++i) {
|
||||
if(unw_get_reg(h->ctx.curr_frame, UNW_REG_IP, &addr)) {
|
||||
pst_log(SEVERITY_DEBUG, "Failed to get IP value");
|
||||
continue;
|
||||
}
|
||||
unw_word_t sp;
|
||||
if(unw_get_reg(h->ctx.curr_frame, UNW_REG_SP, &sp)) {
|
||||
pst_log(SEVERITY_DEBUG, "Failed to get SP value");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(addr == (uint64_t)caller) {
|
||||
skip = 0;
|
||||
} else if(skip) {
|
||||
pst_log(SEVERITY_DEBUG, "Skipping frame #%d: PC = %#lX, SP = %#lX", i, addr, sp);
|
||||
continue;
|
||||
}
|
||||
|
||||
h->ctx.print(&h->ctx, "[%-2d] ", i);
|
||||
h->ctx.module = dwfl_addrmodule(h->ctx.dwfl, addr);
|
||||
pst_function* last = last_function(h);
|
||||
pst_function* fn = add_function(h, NULL);
|
||||
fn->sp = sp;
|
||||
pst_log(SEVERITY_DEBUG, "Analyze frame #%d: PC = %#lX, SP = %#lX", i, addr, sp);
|
||||
if(!pst_function_unwind(fn, addr)) {
|
||||
del_function(fn);
|
||||
} else {
|
||||
if(last) {
|
||||
last->parent = fn;
|
||||
}
|
||||
//get_frame(fun);
|
||||
}
|
||||
h->ctx.print(&h->ctx, "\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pst_handler_init(pst_handler* h, ucontext_t* hctx)
|
||||
{
|
||||
pst_context_init(&h->ctx, hctx);
|
||||
list_head_init(&h->functions);
|
||||
}
|
||||
|
||||
void pst_handler_fini(pst_handler* h)
|
||||
{
|
||||
clear(h);
|
||||
pst_context_fini(&h->ctx);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* sysutils.h
|
||||
*
|
||||
* Created on: Dec 28, 2019
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#ifndef PST_HANDLER_H_
|
||||
#define PST_HANDLER_H_
|
||||
|
||||
//system
|
||||
#include <ucontext.h>
|
||||
|
||||
#include "utils/list_head.h"
|
||||
#include "common.h"
|
||||
#include "context.h"
|
||||
|
||||
typedef struct pst_context pst_context;
|
||||
|
||||
typedef struct pst_handler {
|
||||
pst_context ctx; // context of unwinding
|
||||
list_head functions; // list of functions in stack frame
|
||||
} pst_handler;
|
||||
|
||||
void pst_handler_init(pst_handler* h, ucontext_t* hctx);
|
||||
void pst_handler_fini(pst_handler* h);
|
||||
|
||||
bool pst_handler_handle_dwarf(pst_handler* h);
|
||||
void pst_handler_print_dwarf(pst_handler* h);
|
||||
bool pst_handler_unwind(pst_handler* h);
|
||||
|
||||
#endif /* PST_HANDLER_H_ */
|
||||
@@ -0,0 +1,839 @@
|
||||
/*
|
||||
* dwarf_operations.cpp
|
||||
*
|
||||
* Created on: Jan 11, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include "../src/dwarf/dwarf_operations.h"
|
||||
|
||||
#include <dwarf.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "registers.h"
|
||||
|
||||
// not implemented operations
|
||||
bool dw_op_notimpl(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_addr operation has a single operand that encodes a machine
|
||||
// address and whose size is the size of an address on the target machine.
|
||||
bool dw_op_addr(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_stack_push(stack, &op1, sizeof(op1), DWARF_TYPE_GENERIC);
|
||||
return true;
|
||||
}
|
||||
|
||||
// The DW_OP_deref_size operation behaves like the DW_OP_deref operation. In the DW_OP_deref_size operation, however, the size
|
||||
// in bytes of the data retrieved from the dereferenced address is specified by the single operand. This operand is a 1-byte unsigned integral constant
|
||||
// whose value may not be larger than the size of the generic type. The data
|
||||
// retrieved is zero extended to the size of an address on the target machine
|
||||
// before being pushed onto the expression stack.
|
||||
|
||||
bool dw_op_deref_size(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value = pst_dwarf_stack_pop(stack);
|
||||
if(value) {
|
||||
uint64_t addr = value->value.uint64;
|
||||
uint64_t res = 0;
|
||||
switch(op1) {
|
||||
case 1:
|
||||
res = *((uint8_t*)addr);
|
||||
break;
|
||||
case 2:
|
||||
res = *((uint16_t*)addr);
|
||||
break;
|
||||
case 4:
|
||||
res = *((uint32_t*)addr);
|
||||
break;
|
||||
case 8:
|
||||
res = *((uint64_t*)addr);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_GENERIC);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_ OP_deref operation pops the top stack entry and treats it as an address.
|
||||
// The popped value must have an integral type. The value retrieved from that address is pushed, and has the generic type.
|
||||
// The size of the data retrieved from the dereferenced address is the size of an address on the target machine.
|
||||
bool dw_op_deref(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
return dw_op_deref_size(stack, map, 8, op2);
|
||||
}
|
||||
|
||||
// DW_OP_const1u, DW_OP_const2u, DW_OP_const4u, DW_OP_const8u. The single operand of a DW_OP_const<n>u operation provides a 1, 2, 4, or 8-byte unsigned integer constant, respectively.
|
||||
// These operations push a value with the generic type
|
||||
bool dw_op_const_x_u(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
uint8_t size = 0;
|
||||
pst_dwarf_value_type type = DWARF_TYPE_UNSIGNED;
|
||||
switch (map->op_num) {
|
||||
case DW_OP_const1u:
|
||||
size = 1;
|
||||
type = DWARF_TYPE_CHAR;
|
||||
break;
|
||||
case DW_OP_const2u:
|
||||
size = 2;
|
||||
type = DWARF_TYPE_SHORT;
|
||||
break;
|
||||
case DW_OP_const4u:
|
||||
size = 4;
|
||||
type = DWARF_TYPE_INT;
|
||||
break;
|
||||
case DW_OP_const8u:
|
||||
size = 8;
|
||||
type = DWARF_TYPE_LONG;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
pst_dwarf_stack_push(stack, &op1, size, type | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// DW_OP_const1s, DW_OP_const2s, DW_OP_const4s, DW_OP_const8s. The single operand of a DW_OP_const<n>s operation provides a 1, 2, 4, or 8-byte signed integer constant, respectively.
|
||||
// These operations push a value with the generic type
|
||||
bool dw_op_const_x_s(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
uint8_t size; int64_t v;
|
||||
pst_dwarf_value_type type = DWARF_TYPE_SIGNED;
|
||||
switch (map->op_num) {
|
||||
case DW_OP_const1s:
|
||||
v = (int8_t)op1;
|
||||
size = sizeof(int8_t);
|
||||
type = DWARF_TYPE_CHAR;
|
||||
break;
|
||||
case DW_OP_const2s:
|
||||
v = (int16_t)op1;
|
||||
size = sizeof(int16_t);
|
||||
type = DWARF_TYPE_SHORT;
|
||||
break;
|
||||
case DW_OP_const4s:
|
||||
v = (int32_t)op1;
|
||||
size = sizeof(int32_t);
|
||||
type = DWARF_TYPE_INT;
|
||||
break;
|
||||
case DW_OP_const8s:
|
||||
v = (int64_t)op1;
|
||||
size = sizeof(int64_t);
|
||||
type = DWARF_TYPE_LONG;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
pst_dwarf_stack_push(stack, &v, size, type | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The single operand of the DW_OP_constu operation provides an unsigned LEB128 integer constant.
|
||||
bool dw_op_constu(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
uint64_t value = decode_uleb128((unsigned char*)&op1);
|
||||
pst_dwarf_stack_push(stack, &value, sizeof(value), DWARF_TYPE_LONG | DWARF_TYPE_UNSIGNED | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dw_op_consts(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
// The single operand of the DW_OP_consts operation provides a signed LEB128 integer constant.
|
||||
int64_t value = decode_sleb128((unsigned char*)&op1);
|
||||
pst_dwarf_stack_push(stack, &value, sizeof(value), DWARF_TYPE_LONG | DWARF_TYPE_SIGNED | DWARF_TYPE_CONST | DWARF_TYPE_GENERIC);
|
||||
return true;
|
||||
}
|
||||
|
||||
// The DW_OP_dup operation duplicates the value (including its type identifier) at the top of the stack.
|
||||
bool dw_op_dup(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value = pst_dwarf_stack_get(stack, 0);
|
||||
pst_dwarf_stack_push(stack, &value->value, sizeof(value->value), value->type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The DW_OP_drop operation pops the value (including its type identifier) at the top of the stack.
|
||||
bool dw_op_drop(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value = pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_value_fini(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The DW_OP_over operation duplicates the entry currently second in the stack at the top of the stack.
|
||||
// This is equivalent to a DW_OP_pick operation, with index 1.
|
||||
bool dw_op_over(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value = pst_dwarf_stack_get(stack, 1);
|
||||
pst_dwarf_stack_push(stack, &value->value, sizeof(value->value), value->type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The single operand of the DW_OP_pick operation provides a 1-byte index.
|
||||
// A copy of the stack entry (including its type identifier) with the specified index (0 through 255, inclusive) is pushed onto the stack.
|
||||
bool dw_op_pick(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value = pst_dwarf_stack_get(stack, op1);
|
||||
if(value) {
|
||||
pst_dwarf_stack_push(stack, &value->value, sizeof(value->value), value->type);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_swap operation swaps the top two stack entries. The entry at the top of the stack (including its type identifier) becomes the second stack
|
||||
// entry, and the second entry (including its type identifier) becomes the top of the stack.
|
||||
bool dw_op_swap(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value1 = pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_value* value2 = pst_dwarf_stack_pop(stack);
|
||||
if(value1 && value2) {
|
||||
pst_dwarf_stack_push_value(stack, value1);
|
||||
pst_dwarf_stack_push_value(stack, value2);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(value1) {
|
||||
pst_dwarf_value_fini(value1);
|
||||
}
|
||||
if(value2) {
|
||||
pst_dwarf_value_fini(value2);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_rot operation rotates the first three stack entries.
|
||||
// The entry at the top of the stack (including its type identifier) becomes the third stack entry,
|
||||
// the second entry (including its type identifier) becomes the top of the stack,
|
||||
// and the third entry (including its type identifier) becomes the second entry
|
||||
bool dw_op_rot(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value1 = pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_value* value2 = pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_value* value3 = pst_dwarf_stack_pop(stack);
|
||||
if(value1 && value2 && value3) {
|
||||
pst_dwarf_stack_push_value(stack, value1);
|
||||
pst_dwarf_stack_push_value(stack, value3);
|
||||
pst_dwarf_stack_push_value(stack, value2);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(value1) {
|
||||
pst_dwarf_value_fini(value1);
|
||||
}
|
||||
if(value2) {
|
||||
pst_dwarf_value_fini(value2);
|
||||
}
|
||||
if(value3) {
|
||||
pst_dwarf_value_fini(value3);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_abs operation pops the top stack entry, interprets it as a signed value and pushes its absolute value.
|
||||
// If the absolute value cannot be represented, the result is undefined.
|
||||
bool dw_op_abs(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value = pst_dwarf_stack_get(stack, 0);
|
||||
if(value) {
|
||||
uint64_t res = llabs(value->value.int64);
|
||||
pst_dwarf_value_set(value, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC | DWARF_TYPE_LONG);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_and operation pops the top two stack values, performs a bitwise and operation on the two, and pushes the result.
|
||||
bool dw_op_and(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0);
|
||||
pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1);
|
||||
if(value1 && value2) {
|
||||
if(!(value1->type & value2->type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t res = value1->value.uint64 & value2->value.uint64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_GENERIC);
|
||||
|
||||
pst_dwarf_value_fini(value1);
|
||||
pst_dwarf_value_fini(value2);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_div operation pops the top two stack values, divides the former second entry by the former top of the stack using signed division, and pushes the result.
|
||||
bool dw_op_div(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0);
|
||||
pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1);
|
||||
if(value1 && value2) {
|
||||
if(!(value1->type & value2->type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(value2->type & DWARF_TYPE_SIGNED) {
|
||||
if(value1->type & DWARF_TYPE_SIGNED) {
|
||||
if(value1->value.int64 == 0) {
|
||||
return false;
|
||||
}
|
||||
uint64_t res = value2->value.int64 / value1->value.int64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC);
|
||||
return true;
|
||||
} else {
|
||||
if(value1->value.uint64 == 0) {
|
||||
return false;
|
||||
}
|
||||
int64_t res = value2->value.int64 / value1->value.uint64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if(value1->type & DWARF_TYPE_SIGNED) {
|
||||
if(value1->value.int64 == 0) {
|
||||
return false;
|
||||
}
|
||||
int64_t res = value2->value.uint64 / value1->value.int64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC);
|
||||
return true;
|
||||
} else {
|
||||
if(value1->value.uint64 == 0) {
|
||||
return false;
|
||||
}
|
||||
uint64_t res = value2->value.uint64 / value1->value.uint64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_minus operation pops the top two stack values, subtracts the former top of the stack from the former second entry, and pushes the result.
|
||||
bool dw_op_minus(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0);
|
||||
pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1);
|
||||
if(value1 && value2) {
|
||||
if(!(value1->type & value2->type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int res_type = DWARF_TYPE_GENERIC;
|
||||
if(value2->type & DWARF_TYPE_MEMORY_LOC) {
|
||||
res_type |= DWARF_TYPE_MEMORY_LOC;
|
||||
}
|
||||
// use arithmetic by modulo 1 plus
|
||||
uint64_t res = value2->value.uint64 - value1->value.uint64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), res_type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_mod operation pops the top two stack values and pushes the result of the calculation: former second stack entry modulo the former top of the stack.
|
||||
bool dw_op_mod(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0);
|
||||
pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1);
|
||||
if(value1 && value2) {
|
||||
if(!(value1->type & value2->type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(value1->value.uint64 == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t res = value2->value.uint64 % value1->value.uint64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_mul operation pops the top two stack entries, multiplies them together, and pushes the result.
|
||||
bool dw_op_mul(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0);
|
||||
pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1);
|
||||
if(value1 && value2) {
|
||||
if(!(value1->type & value2->type)) {
|
||||
pst_log(SEVERITY_ERROR, "Different types of two stack values for operation: %s(%0x%X, %0x%X)", map->op_name, value1->type, value2->type);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(value2->type & DWARF_TYPE_SIGNED) {
|
||||
if(value1->type & DWARF_TYPE_SIGNED) {
|
||||
uint64_t res = value2->value.int64 * value1->value.int64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
int64_t res = value2->value.int64 * value1->value.uint64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC);
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if(value1->type & DWARF_TYPE_SIGNED) {
|
||||
int64_t res = value2->value.uint64 * value1->value.int64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_SIGNED | DWARF_TYPE_GENERIC);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
uint64_t res = value2->value.uint64 * value1->value.uint64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), DWARF_TYPE_UNSIGNED | DWARF_TYPE_GENERIC);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_neg operation pops the top stack entry, interprets it as a signed value and pushes its negation.
|
||||
// If the negation cannot be represented, the result is undefined.
|
||||
bool dw_op_neg(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value = pst_dwarf_stack_get(stack, 0);
|
||||
if(value) {
|
||||
if(value->type & DWARF_TYPE_CHAR) {
|
||||
value->value.int8 *= -1;
|
||||
} else if(value->type & DWARF_TYPE_SHORT) {
|
||||
value->value.int16 *= -1;
|
||||
} else if(value->type & DWARF_TYPE_INT) {
|
||||
value->value.int32 *= -1;
|
||||
} else {
|
||||
value->value.int64 *= -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_not operation pops the top stack entry, and pushes its bitwise complement.
|
||||
bool dw_op_not(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value = pst_dwarf_stack_get(stack, 0);
|
||||
if(value) {
|
||||
value->value.uint64 = ~value->value.uint64;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_or operation pops the top two stack entries, performs a bitwise or operation on the two, and pushes the result.
|
||||
bool dw_op_or(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0);
|
||||
pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1);
|
||||
if(value1 && value2) {
|
||||
if(!(value1->type & value2->type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t res = value2->value.uint64 | value1->value.uint64;
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), value1->type);
|
||||
|
||||
pst_dwarf_value_fini(value1);
|
||||
pst_dwarf_value_fini(value2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_plus operation pops the top two stack entries, adds them together, and pushes the result
|
||||
bool dw_op_plus(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value1 = pst_dwarf_stack_get(stack, 0);
|
||||
pst_dwarf_value* value2 = pst_dwarf_stack_get(stack, 1);
|
||||
if(value1 && value2) {
|
||||
if(!(value1->type & value2->type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if((value1->type & DWARF_TYPE_SIGNED) && (value2->type & DWARF_TYPE_SIGNED)) {
|
||||
int64_t res = value2->value.int64 + value1->value.int64;
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), value1->type);
|
||||
} else {
|
||||
// if in arithmetic expression even one operand is unsigned then result is unsigned as well
|
||||
int type = (value1->type & (~DWARF_TYPE_SIGNED)) | DWARF_TYPE_UNSIGNED;
|
||||
uint64_t res = value2->value.uint64 + value1->value.uint64;
|
||||
pst_dwarf_stack_push(stack, &res, sizeof(res), type);
|
||||
}
|
||||
|
||||
pst_dwarf_stack_pop(stack); pst_dwarf_stack_pop(stack);
|
||||
pst_dwarf_value_fini(value1);
|
||||
pst_dwarf_value_fini(value2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_plus_uconst operation pops the top stack entry, adds it to the unsigned LEB128 constant operand interpreted as the same type as the
|
||||
// operand popped from the top of the stack and pushes the result.
|
||||
// This operation is supplied specifically to be able to encode more field offsets in two
|
||||
// bytes than can be done with “DW_OP_lit<n> DW_OP_plus.”
|
||||
bool dw_op_plus_uconst(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
pst_dwarf_value* value = pst_dwarf_stack_get(stack, 0);
|
||||
if(value) {
|
||||
uint64_t op = decode_uleb128((unsigned char*)&op1);
|
||||
value->value.uint64 += op;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// The DW_OP_regx operation has a single unsigned LEB128 literal operand that encodes the name of a register
|
||||
bool dw_op_reg_x(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
if(map->op_num != DW_OP_regx && (map->op_num < DW_OP_reg0 || map->op_num > DW_OP_reg31)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t regno = 0;
|
||||
if(map->op_num == DW_OP_regx) {
|
||||
regno = decode_uleb128((unsigned char*)&op1);
|
||||
} else {
|
||||
regno = map->op_num - DW_OP_reg0;
|
||||
}
|
||||
|
||||
pst_dwarf_stack_push(stack, ®no, sizeof(regno), DWARF_TYPE_REGISTER_LOC);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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)
|
||||
// The DW_OP_bregx operation provides the sum of two values specified by its two operands.
|
||||
// The first operand is a register number which is specified by an unsigned LEB128 number. The second operand is a signed LEB128 offset.
|
||||
bool dw_op_breg_x(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
if(map->op_num != DW_OP_bregx && (map->op_num < DW_OP_breg0 || map->op_num > DW_OP_breg31)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int regno = -1; int64_t off = 0;
|
||||
if(map->op_num == DW_OP_bregx) {
|
||||
regno = decode_uleb128((unsigned char*)&op1);
|
||||
off = decode_sleb128((unsigned char*)&op2);
|
||||
} else {
|
||||
regno = find_regnum(map->op_num);
|
||||
off = decode_sleb128((unsigned char*)&op1);
|
||||
}
|
||||
|
||||
unw_word_t val = 0;
|
||||
int ret = unw_get_reg(stack->ctx->curr_frame, regno, &val);
|
||||
if(ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
val += off;
|
||||
pst_dwarf_stack_push(stack, &val, sizeof(val), DWARF_TYPE_GENERIC);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The DW_OP_lit<n> operations encode the unsigned literal values from 0 through 31, inclusive.
|
||||
// Operations other than DW_OP_const_type push a value with the generic type.
|
||||
bool dw_op_lit_x(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
if(map->op_num < DW_OP_lit0 || map->op_num > DW_OP_lit31) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t val = map->op_num - DW_OP_lit0;
|
||||
pst_dwarf_stack_push(stack, &val, sizeof(val), DWARF_TYPE_GENERIC);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The 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.
|
||||
bool dw_op_stack_value(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
|
||||
pst_dwarf_value* v = pst_dwarf_stack_get(stack, 0);
|
||||
if(v) {
|
||||
v->type = DWARF_TYPE_GENERIC;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DW_OP_call_frame_cfa operation pushes the value of the CFA, obtained from the Call Frame Information (see Section 6.4 on page 171).
|
||||
bool dw_op_call_frame_cfa(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
// since we are already know SP value, just push it to DWARF stack
|
||||
// unw_word_t sp;
|
||||
// int ret = unw_get_reg(stack->ctx->curr_frame, UNW_REG_SP, &sp);
|
||||
// if(ret) {
|
||||
// pst_log(SEVERITY_ERROR, "%s: failed to get register 0x%X value. Error: %d", __PRETTY_FUNCTION__, UNW_REG_SP, ret);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
pst_dwarf_stack_push(stack, &stack->ctx->cfa, sizeof(stack->ctx->cfa), /*DWARF_TYPE_MEMORY_LOC | */DWARF_TYPE_GENERIC);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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
|
||||
bool dw_op_fbreg(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2)
|
||||
{
|
||||
// since in signal handler we are know SP value, just use it as DW_AT_frame_base
|
||||
unw_word_t sp;
|
||||
// int ret = unw_get_reg(stack->ctx->curr_frame, UNW_REG_SP, &sp);
|
||||
// if(ret) {
|
||||
// pst_log(SEVERITY_ERROR, "Failed to get register 0x%X value. Error: %d", __PRETTY_FUNCTION__, UNW_REG_SP, ret);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
sp = stack->ctx->cfa;
|
||||
int64_t off = decode_sleb128((unsigned char*)&op1);
|
||||
sp += off;
|
||||
|
||||
pst_dwarf_stack_push(stack, &sp, sizeof(sp), DWARF_TYPE_MEMORY_LOC | DWARF_TYPE_GENERIC);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// DWARF Operations to code & name mapping
|
||||
dwarf_op_map op_map[] = {
|
||||
{DW_OP_addr, "DW_OP_addr", dw_op_addr},
|
||||
{DW_OP_deref, "DW_OP_deref", dw_op_deref},
|
||||
// Constant operations
|
||||
{DW_OP_const1u, "DW_OP_const1u", dw_op_const_x_u},
|
||||
{DW_OP_const1s, "DW_OP_const1s", dw_op_const_x_s},
|
||||
{DW_OP_const2u, "DW_OP_const2u", dw_op_const_x_u},
|
||||
{DW_OP_const2s, "DW_OP_const2s", dw_op_const_x_s},
|
||||
{DW_OP_const4u, "DW_OP_const4u", dw_op_const_x_u},
|
||||
{DW_OP_const4s, "DW_OP_const4s", dw_op_const_x_s},
|
||||
{DW_OP_const8u, "DW_OP_const8u", dw_op_const_x_u},
|
||||
{DW_OP_const8s, "DW_OP_const8s", dw_op_const_x_s},
|
||||
{DW_OP_constu, "DW_OP_constu", dw_op_constu},
|
||||
{DW_OP_consts, "DW_OP_consts", dw_op_consts},
|
||||
// DWARF expression stack operations
|
||||
{DW_OP_dup, "DW_OP_dup", dw_op_dup},
|
||||
{DW_OP_drop, "DW_OP_drop", dw_op_drop},
|
||||
{DW_OP_over, "DW_OP_over", dw_op_over},
|
||||
{DW_OP_pick, "DW_OP_pick", dw_op_pick},
|
||||
{DW_OP_swap, "DW_OP_swap", dw_op_swap},
|
||||
{DW_OP_rot, "DW_OP_rot", dw_op_rot},
|
||||
{DW_OP_xderef, "DW_OP_xderef", dw_op_notimpl},
|
||||
// Arithmetic and Logical Operations
|
||||
{DW_OP_abs, "DW_OP_abs", dw_op_abs},
|
||||
{DW_OP_and, "DW_OP_and", dw_op_and},
|
||||
{DW_OP_div, "DW_OP_div", dw_op_div},
|
||||
{DW_OP_minus, "DW_OP_minus", dw_op_minus},
|
||||
{DW_OP_mod, "DW_OP_mod", dw_op_mod},
|
||||
{DW_OP_mul, "DW_OP_mul", dw_op_mul},
|
||||
{DW_OP_neg, "DW_OP_neg", dw_op_neg},
|
||||
{DW_OP_not, "DW_OP_not", dw_op_not},
|
||||
{DW_OP_or, "DW_OP_or", dw_op_or},
|
||||
{DW_OP_plus, "DW_OP_plus", dw_op_plus},
|
||||
{DW_OP_plus_uconst, "DW_OP_plus_uconst",dw_op_plus_uconst},
|
||||
// not implemented for now
|
||||
{DW_OP_shl, "DW_OP_shl", dw_op_notimpl},
|
||||
{DW_OP_shr, "DW_OP_shr", dw_op_notimpl},
|
||||
{DW_OP_shra, "DW_OP_shra", dw_op_notimpl},
|
||||
{DW_OP_xor, "DW_OP_xor", dw_op_notimpl},
|
||||
{DW_OP_bra, "DW_OP_bra", dw_op_notimpl},
|
||||
{DW_OP_eq, "DW_OP_eq", dw_op_notimpl},
|
||||
{DW_OP_ge, "DW_OP_ge", dw_op_notimpl},
|
||||
{DW_OP_gt, "DW_OP_gt", dw_op_notimpl},
|
||||
{DW_OP_le, "DW_OP_le", dw_op_notimpl},
|
||||
{DW_OP_lt, "DW_OP_lt", dw_op_notimpl},
|
||||
{DW_OP_ne, "DW_OP_ne", dw_op_notimpl},
|
||||
{DW_OP_skip, "DW_OP_skip", dw_op_notimpl},
|
||||
// DWARF5 2.5.1.1 Literal Encodings
|
||||
{DW_OP_lit0, "DW_OP_lit0", dw_op_lit_x},
|
||||
{DW_OP_lit1, "DW_OP_lit1", dw_op_lit_x},
|
||||
{DW_OP_lit2, "DW_OP_lit2", dw_op_lit_x},
|
||||
{DW_OP_lit3, "DW_OP_lit3", dw_op_lit_x},
|
||||
{DW_OP_lit4, "DW_OP_lit4", dw_op_lit_x},
|
||||
{DW_OP_lit5, "DW_OP_lit5", dw_op_lit_x},
|
||||
{DW_OP_lit6, "DW_OP_lit6", dw_op_lit_x},
|
||||
{DW_OP_lit7, "DW_OP_lit7", dw_op_lit_x},
|
||||
{DW_OP_lit8, "DW_OP_lit8", dw_op_lit_x},
|
||||
{DW_OP_lit9, "DW_OP_lit9", dw_op_lit_x},
|
||||
{DW_OP_lit10, "DW_OP_lit10", dw_op_lit_x},
|
||||
{DW_OP_lit11, "DW_OP_lit11", dw_op_lit_x},
|
||||
{DW_OP_lit12, "DW_OP_lit12", dw_op_lit_x},
|
||||
{DW_OP_lit13, "DW_OP_lit13", dw_op_lit_x},
|
||||
{DW_OP_lit14, "DW_OP_lit14", dw_op_lit_x},
|
||||
{DW_OP_lit15, "DW_OP_lit15", dw_op_lit_x},
|
||||
{DW_OP_lit16, "DW_OP_lit16", dw_op_lit_x},
|
||||
{DW_OP_lit17, "DW_OP_lit17", dw_op_lit_x},
|
||||
{DW_OP_lit18, "DW_OP_lit18", dw_op_lit_x},
|
||||
{DW_OP_lit19, "DW_OP_lit19", dw_op_lit_x},
|
||||
{DW_OP_lit20, "DW_OP_lit20", dw_op_lit_x},
|
||||
{DW_OP_lit21, "DW_OP_lit21", dw_op_lit_x},
|
||||
{DW_OP_lit22, "DW_OP_lit22", dw_op_lit_x},
|
||||
{DW_OP_lit23, "DW_OP_lit23", dw_op_lit_x},
|
||||
{DW_OP_lit24, "DW_OP_lit24", dw_op_lit_x},
|
||||
{DW_OP_lit25, "DW_OP_lit25", dw_op_lit_x},
|
||||
{DW_OP_lit26, "DW_OP_lit26", dw_op_lit_x},
|
||||
{DW_OP_lit27, "DW_OP_lit27", dw_op_lit_x},
|
||||
{DW_OP_lit28, "DW_OP_lit28", dw_op_lit_x},
|
||||
{DW_OP_lit29, "DW_OP_lit29", dw_op_lit_x},
|
||||
{DW_OP_lit30, "DW_OP_lit30", dw_op_lit_x},
|
||||
{DW_OP_lit31, "DW_OP_lit31", dw_op_lit_x},
|
||||
// Register location descriptions. I.e. register containing the value
|
||||
// GP Registers
|
||||
{DW_OP_reg0, "DW_OP_reg0", dw_op_reg_x},
|
||||
{DW_OP_reg1, "DW_OP_reg1", dw_op_reg_x},
|
||||
{DW_OP_reg2, "DW_OP_reg2", dw_op_reg_x},
|
||||
{DW_OP_reg3, "DW_OP_reg3", dw_op_reg_x},
|
||||
{DW_OP_reg4, "DW_OP_reg4", dw_op_reg_x},
|
||||
{DW_OP_reg5, "DW_OP_reg5", dw_op_reg_x},
|
||||
{DW_OP_reg6, "DW_OP_reg6", dw_op_reg_x},
|
||||
{DW_OP_reg7, "DW_OP_reg7", dw_op_reg_x},
|
||||
// Extended GP Registers
|
||||
{DW_OP_reg8, "DW_OP_reg8", dw_op_reg_x},
|
||||
{DW_OP_reg9, "DW_OP_reg9", dw_op_reg_x},
|
||||
{DW_OP_reg10, "DW_OP_reg10", dw_op_reg_x},
|
||||
{DW_OP_reg11, "DW_OP_reg11", dw_op_reg_x},
|
||||
{DW_OP_reg12, "DW_OP_reg12", dw_op_reg_x},
|
||||
{DW_OP_reg13, "DW_OP_reg13", dw_op_reg_x},
|
||||
{DW_OP_reg14, "DW_OP_reg14", dw_op_reg_x},
|
||||
{DW_OP_reg15, "DW_OP_reg15", dw_op_reg_x},
|
||||
{DW_OP_reg16, "DW_OP_reg16", dw_op_reg_x}, // Return Address (RA) mapped to RIP
|
||||
// SSE Vector Registers
|
||||
{DW_OP_reg17, "DW_OP_reg17", dw_op_reg_x},
|
||||
{DW_OP_reg18, "DW_OP_reg18", dw_op_reg_x},
|
||||
{DW_OP_reg19, "DW_OP_reg19", dw_op_reg_x},
|
||||
{DW_OP_reg20, "DW_OP_reg20", dw_op_reg_x},
|
||||
{DW_OP_reg21, "DW_OP_reg21", dw_op_reg_x},
|
||||
{DW_OP_reg22, "DW_OP_reg22", dw_op_reg_x},
|
||||
{DW_OP_reg23, "DW_OP_reg23", dw_op_reg_x},
|
||||
{DW_OP_reg24, "DW_OP_reg24", dw_op_reg_x},
|
||||
{DW_OP_reg25, "DW_OP_reg25", dw_op_reg_x},
|
||||
{DW_OP_reg26, "DW_OP_reg26", dw_op_reg_x},
|
||||
{DW_OP_reg27, "DW_OP_reg27", dw_op_reg_x},
|
||||
{DW_OP_reg28, "DW_OP_reg28", dw_op_reg_x},
|
||||
{DW_OP_reg29, "DW_OP_reg29", dw_op_reg_x},
|
||||
{DW_OP_reg30, "DW_OP_reg30", dw_op_reg_x},
|
||||
{DW_OP_reg31, "DW_OP_reg31", dw_op_reg_x},
|
||||
// Register values. I.e. appropriate register value + offset of operation is pushed onto the stack
|
||||
// GP Registers
|
||||
{DW_OP_breg0, "DW_OP_breg0", dw_op_breg_x},
|
||||
{DW_OP_breg1, "DW_OP_breg1", dw_op_breg_x},
|
||||
{DW_OP_breg2, "DW_OP_breg2", dw_op_breg_x},
|
||||
{DW_OP_breg3, "DW_OP_breg3", dw_op_breg_x},
|
||||
{DW_OP_breg4, "DW_OP_breg4", dw_op_breg_x},
|
||||
{DW_OP_breg5, "DW_OP_breg5", dw_op_breg_x},
|
||||
{DW_OP_breg6, "DW_OP_breg6", dw_op_breg_x},
|
||||
{DW_OP_breg7, "DW_OP_breg7", dw_op_breg_x},
|
||||
// Extended GP Registers
|
||||
{DW_OP_breg8, "DW_OP_breg8", dw_op_breg_x},
|
||||
{DW_OP_breg9, "DW_OP_breg9", dw_op_breg_x},
|
||||
{DW_OP_breg10, "DW_OP_breg10", dw_op_breg_x},
|
||||
{DW_OP_breg11, "DW_OP_breg11", dw_op_breg_x},
|
||||
{DW_OP_breg12, "DW_OP_breg12", dw_op_breg_x},
|
||||
{DW_OP_breg13, "DW_OP_breg13", dw_op_breg_x},
|
||||
{DW_OP_breg14, "DW_OP_breg14", dw_op_breg_x},
|
||||
{DW_OP_breg15, "DW_OP_breg15", dw_op_breg_x},
|
||||
{DW_OP_breg16, "DW_OP_breg16", dw_op_breg_x}, // Return Address (RA) mapped to RIP
|
||||
// SSE Vector Registers
|
||||
{DW_OP_breg17, "DW_OP_breg17", dw_op_breg_x},
|
||||
{DW_OP_breg18, "DW_OP_breg18", dw_op_breg_x},
|
||||
{DW_OP_breg19, "DW_OP_breg19", dw_op_breg_x},
|
||||
{DW_OP_breg20, "DW_OP_breg20", dw_op_breg_x},
|
||||
{DW_OP_breg21, "DW_OP_breg21", dw_op_breg_x},
|
||||
{DW_OP_breg22, "DW_OP_breg22", dw_op_breg_x},
|
||||
{DW_OP_breg23, "DW_OP_breg23", dw_op_breg_x},
|
||||
{DW_OP_breg24, "DW_OP_breg24", dw_op_breg_x},
|
||||
{DW_OP_breg25, "DW_OP_breg25", dw_op_breg_x},
|
||||
{DW_OP_breg26, "DW_OP_breg26", dw_op_breg_x},
|
||||
{DW_OP_breg27, "DW_OP_breg27", dw_op_breg_x},
|
||||
{DW_OP_breg28, "DW_OP_breg28", dw_op_breg_x},
|
||||
{DW_OP_breg29, "DW_OP_breg29", dw_op_breg_x},
|
||||
{DW_OP_breg30, "DW_OP_breg30", dw_op_breg_x},
|
||||
{DW_OP_breg31, "DW_OP_breg31", dw_op_breg_x},
|
||||
// special register-related commands
|
||||
{DW_OP_regx, "DW_OP_regx", dw_op_reg_x}, // 1st operand register name
|
||||
{DW_OP_fbreg, "DW_OP_fbreg", dw_op_fbreg}, // base is Frame base register there
|
||||
{DW_OP_bregx, "DW_OP_bregx", dw_op_breg_x}, // base is value of 1st operand's register
|
||||
|
||||
// not implemented
|
||||
{DW_OP_piece, "DW_OP_piece", dw_op_notimpl},
|
||||
{DW_OP_deref_size, "DW_OP_deref_size", dw_op_deref_size},
|
||||
{DW_OP_xderef_size, "DW_OP_xderef_size", dw_op_notimpl},
|
||||
{DW_OP_nop, "DW_OP_nop", dw_op_notimpl},
|
||||
{DW_OP_push_object_address, "DW_OP_push_object_address",dw_op_notimpl},
|
||||
{DW_OP_call2, "DW_OP_call2", dw_op_notimpl},
|
||||
{DW_OP_call4, "DW_OP_call4", dw_op_notimpl},
|
||||
{DW_OP_call_ref, "DW_OP_call_ref", dw_op_notimpl},
|
||||
{DW_OP_form_tls_address, "DW_OP_form_tls_address", dw_op_notimpl},
|
||||
{DW_OP_call_frame_cfa, "DW_OP_call_frame_cfa", dw_op_call_frame_cfa},
|
||||
{DW_OP_bit_piece, "DW_OP_bit_piece", dw_op_notimpl},
|
||||
{DW_OP_implicit_value, "DW_OP_implicit_value", dw_op_notimpl},
|
||||
{DW_OP_stack_value, "DW_OP_stack_value", dw_op_stack_value},
|
||||
|
||||
// GNU extensions
|
||||
// in fact, implementation is at upper layer since this operation contains sub-expression
|
||||
{DW_OP_GNU_entry_value, "DW_OP_GNU_entry_value", dw_op_notimpl}, // seems that it's equal to DW_OP_entry_value from DWARF 5
|
||||
};
|
||||
|
||||
const dwarf_op_map* find_op_map(int op)
|
||||
{
|
||||
for(uint32_t i = 0; i < sizeof(op_map) / sizeof(dwarf_op_map); ++i) {
|
||||
if(op_map[i].op_num == op) {
|
||||
return &op_map[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
#include "../src/dwarf/dwarf_stack.h"
|
||||
#include "../utils/list_head.h"
|
||||
#include "common.h"
|
||||
#include "context.h"
|
||||
#include "dwarf_handler.h"
|
||||
|
||||
|
||||
typedef struct __dwarf_op_map dwarf_op_map;
|
||||
|
||||
typedef bool (*dwarf_operation)(pst_dwarf_stack* stack, const dwarf_op_map* map, Dwarf_Word op1, Dwarf_Word op2);
|
||||
|
||||
typedef struct __dwarf_op_map {
|
||||
int op_num; // DWARF Operation DW_OP_XXX
|
||||
const char* op_name; // string representation of an operation
|
||||
dwarf_operation operation; // function which handles operation
|
||||
} dwarf_op_map;
|
||||
|
||||
const dwarf_op_map* find_op_map(int op);
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* dwarf_parameter.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include "../src/dwarf/dwarf_parameter.h"
|
||||
|
||||
#include <dwarf.h>
|
||||
#include <elfutils/libdw.h>
|
||||
|
||||
#include "../src/dwarf/dwarf_utils.h"
|
||||
#include "context.h"
|
||||
|
||||
//
|
||||
// pst_type
|
||||
//
|
||||
void pst_type_init(pst_type* t, const char* name, uint32_t type)
|
||||
{
|
||||
list_node_init(&t->node);
|
||||
t->name = pst_strdup(name);
|
||||
t->type = type;
|
||||
t->allocated = false;
|
||||
}
|
||||
|
||||
pst_type* pst_type_new(const char* name, uint32_t type)
|
||||
{
|
||||
pst_type* nt = pst_alloc(pst_type);
|
||||
if(nt) {
|
||||
pst_type_init(nt, name, type);
|
||||
nt->allocated = true;
|
||||
}
|
||||
|
||||
return nt;
|
||||
}
|
||||
|
||||
void pst_type_fini(pst_type* t)
|
||||
{
|
||||
pst_free(t->name);
|
||||
if(t->allocated) {
|
||||
pst_free(t);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// pst_parameter
|
||||
//
|
||||
void clear(pst_parameter* param)
|
||||
{
|
||||
pst_type* t = NULL;
|
||||
struct list_node *pos, *tn;
|
||||
list_for_each_entry_safe(t, pos, tn, ¶m->types, node) {
|
||||
list_del(&t->node);
|
||||
pst_type_fini(t);
|
||||
}
|
||||
}
|
||||
|
||||
pst_type* next_type(pst_parameter* param, pst_type* t)
|
||||
{
|
||||
list_node* n = (t == NULL) ? list_first(¶m->types) : list_next(&t->node);
|
||||
pst_type* ret = NULL;
|
||||
if(n) {
|
||||
ret = list_entry(n, pst_type, node);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool pst_parameter_print_dwarf(pst_parameter* param)
|
||||
{
|
||||
if(list_count(¶m->types)) {
|
||||
if(!param->is_return) {
|
||||
if(param->has_value) {
|
||||
param->ctx->print(param->ctx, "%s %s = 0x%lX", next_type(param, NULL)->name, param->name, param->location.value);
|
||||
} else {
|
||||
param->ctx->print(param->ctx, "%s %s = <undefined>", next_type(param, NULL)->name, param->name);
|
||||
}
|
||||
} else {
|
||||
param->ctx->print(param->ctx, "%s", next_type(param, NULL)->name);
|
||||
}
|
||||
} else {
|
||||
if(param->has_value) {
|
||||
param->ctx->print(param->ctx, "%s = 0x%lX", param->name, param->location.value);
|
||||
} else {
|
||||
param->ctx->print(param->ctx, "%s = <undefined>", param->name);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pst_type* pst_parameter_add_type(pst_parameter* param, const char* name, int type)
|
||||
{
|
||||
pst_new(pst_type, t, name, type);
|
||||
list_add_bottom(¶m->types, &t->node);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
bool pst_parameter_handle_type(pst_parameter* param, Dwarf_Attribute* base)
|
||||
{
|
||||
Dwarf_Attribute attr_mem;
|
||||
Dwarf_Attribute* attr;
|
||||
|
||||
// get DIE of return type
|
||||
Dwarf_Die ret_die;
|
||||
|
||||
if(!dwarf_formref_die(base, &ret_die)) {
|
||||
pst_log(SEVERITY_ERROR, "Failed to get parameter DIE");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (dwarf_tag(&ret_die)) {
|
||||
case DW_TAG_base_type: {
|
||||
// get Size attribute and it's value
|
||||
param->size = 0;
|
||||
attr = dwarf_attr(&ret_die, DW_AT_byte_size, &attr_mem);
|
||||
if(attr) {
|
||||
dwarf_formudata(attr, ¶m->size);
|
||||
}
|
||||
pst_log(SEVERITY_DEBUG, "base type '%s'(%lu)", dwarf_diename(&ret_die), param->size);
|
||||
pst_parameter_add_type(param, dwarf_diename(&ret_die), DW_TAG_base_type);
|
||||
param->type = DW_TAG_base_type;
|
||||
|
||||
attr = dwarf_attr(&ret_die, DW_AT_encoding, &attr_mem);
|
||||
if(attr) {
|
||||
param->enc_type = 0;
|
||||
dwarf_formudata(attr, ¶m->enc_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DW_TAG_array_type:
|
||||
pst_log(SEVERITY_DEBUG, "array type");
|
||||
pst_parameter_add_type(param, "[]", DW_TAG_array_type);
|
||||
break;
|
||||
case DW_TAG_structure_type:
|
||||
pst_log(SEVERITY_DEBUG, "structure type");
|
||||
pst_parameter_add_type(param, "struct", DW_TAG_structure_type);
|
||||
break;
|
||||
case DW_TAG_union_type:
|
||||
pst_log(SEVERITY_DEBUG, "union type");
|
||||
pst_parameter_add_type(param, "union", DW_TAG_union_type);
|
||||
break;
|
||||
case DW_TAG_class_type:
|
||||
pst_log(SEVERITY_DEBUG, "class type");
|
||||
pst_parameter_add_type(param, "class", DW_TAG_class_type);
|
||||
break;
|
||||
case DW_TAG_pointer_type:
|
||||
pst_log(SEVERITY_DEBUG, "pointer type");
|
||||
pst_parameter_add_type(param, "*", DW_TAG_pointer_type);
|
||||
break;
|
||||
case DW_TAG_enumeration_type:
|
||||
pst_log(SEVERITY_DEBUG, "enumeration type");
|
||||
pst_parameter_add_type(param, "enum", DW_TAG_enumeration_type);
|
||||
break;
|
||||
case DW_TAG_const_type:
|
||||
pst_log(SEVERITY_DEBUG, "constant type");
|
||||
pst_parameter_add_type(param, "const", DW_TAG_const_type);
|
||||
break;
|
||||
case DW_TAG_subroutine_type:
|
||||
pst_log(SEVERITY_DEBUG, "Skipping subroutine type");
|
||||
break;
|
||||
case DW_TAG_typedef:
|
||||
pst_log(SEVERITY_DEBUG, "typedef '%s' type", dwarf_diename(&ret_die));
|
||||
pst_parameter_add_type(param, dwarf_diename(&ret_die), DW_TAG_typedef);
|
||||
break;
|
||||
default:
|
||||
pst_log(SEVERITY_WARNING, "Unknown 0x%X tag type", dwarf_tag(&ret_die));
|
||||
break;
|
||||
}
|
||||
|
||||
attr = dwarf_attr(&ret_die, DW_AT_type, &attr_mem);
|
||||
if(attr) {
|
||||
return pst_parameter_handle_type(param, attr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pst_parameter_handle_dwarf(pst_parameter* param, Dwarf_Die* result, pst_function* fun)
|
||||
{
|
||||
param->die = result;
|
||||
|
||||
Dwarf_Attribute attr_mem;
|
||||
Dwarf_Attribute* attr;
|
||||
|
||||
param->name = pst_strdup(dwarf_diename(result));
|
||||
param->is_variable = (dwarf_tag(result) == DW_TAG_variable);
|
||||
|
||||
dwarf_decl_line(result, (int*)¶m->line);
|
||||
// Get reference to attribute type of the parameter/variable
|
||||
attr = dwarf_attr(result, DW_AT_type, &attr_mem);
|
||||
pst_log(SEVERITY_DEBUG, "---> Handle '%s' %s", param->name, dwarf_tag(result) == DW_TAG_formal_parameter ? "parameter" : "variable");
|
||||
if(attr) {
|
||||
pst_parameter_handle_type(param, attr);
|
||||
}
|
||||
|
||||
if(dwarf_hasattr(result, DW_AT_location)) {
|
||||
// determine location of parameter in stack/heap or CPU registers
|
||||
attr = dwarf_attr(result, DW_AT_location, &attr_mem);
|
||||
Dwarf_Addr pc;
|
||||
unw_get_reg(param->ctx->curr_frame, UNW_REG_IP, &pc);
|
||||
|
||||
if(handle_location(param->ctx, attr, ¶m->location, pc, fun)) {
|
||||
param->has_value = true;
|
||||
} else {
|
||||
pst_log(SEVERITY_ERROR, "Failed to calculate DW_AT_location expression: %s", param->ctx->buff);
|
||||
}
|
||||
} else if(dwarf_hasattr(result, DW_AT_const_value)) {
|
||||
// no locations definitions, value is constant, known by DWARF directly
|
||||
attr = dwarf_attr(result, DW_AT_const_value, &attr_mem);
|
||||
switch (dwarf_whatform(attr)) {
|
||||
case DW_FORM_string:
|
||||
// do nothing for now
|
||||
pst_log(SEVERITY_WARNING, "Const value form DW_FORM_string value = %s.", dwarf_formstring(attr));
|
||||
break;
|
||||
case DW_FORM_data1:
|
||||
case DW_FORM_data2:
|
||||
case DW_FORM_data4:
|
||||
case DW_FORM_data8:
|
||||
dwarf_formudata(attr, ¶m->location.value);
|
||||
param->has_value = true;
|
||||
break;
|
||||
case DW_FORM_sdata:
|
||||
dwarf_formsdata(attr, (int64_t*)¶m->location.value);
|
||||
param->has_value = true;
|
||||
break;
|
||||
case DW_FORM_udata:
|
||||
dwarf_formudata(attr, ¶m->location.value);
|
||||
param->has_value = true;
|
||||
break;
|
||||
}
|
||||
if(param->has_value) {
|
||||
pst_log(SEVERITY_DEBUG, "Parameter constant value: 0x%lX", param->location.value);
|
||||
}
|
||||
}
|
||||
|
||||
// Additionally handle these attributes:
|
||||
// 1. DW_AT_default_value to get information about default value for DW_TAG_formal_parameter type of function
|
||||
// A DW_AT_default_value attribute for a formal parameter entry. The value of
|
||||
// this attribute may be a constant, or a reference to the debugging information
|
||||
// entry for a variable, or a reference to a debugging information entry containing a DWARF procedure
|
||||
|
||||
// 2. DW_AT_variable_parameter
|
||||
// A DW_AT_variable_parameter attribute, which is a flag, if a formal
|
||||
// parameter entry represents a parameter whose value in the calling function
|
||||
// may be modified by the callee. The absence of this attribute implies that the
|
||||
// parameter’s value in the calling function cannot be modified by the callee.
|
||||
|
||||
// 3. DW_AT_abstract_origin
|
||||
// In place of these omitted attributes, each concrete inlined instance entry has a DW_AT_abstract_origin attribute that may be used to obtain the
|
||||
// missing information (indirectly) from the associated abstract instance entry. The value of the abstract origin attribute is a reference to the associated abstract
|
||||
// instance entry.
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pst_parameter_init(pst_parameter* param, pst_context* ctx)
|
||||
{
|
||||
list_node_init(¶m->node);
|
||||
param->die = NULL;
|
||||
param->name = NULL;
|
||||
param->line = 0;
|
||||
param->size = 0;
|
||||
param->type = 0;
|
||||
param->enc_type = 0;
|
||||
list_head_init(¶m->types);
|
||||
param->is_return = false;
|
||||
param->is_variable = false;
|
||||
param->has_value = false;
|
||||
param->ctx = ctx;
|
||||
pst_dwarf_expr_init(¶m->location);
|
||||
param->allocated = false;
|
||||
}
|
||||
|
||||
pst_parameter* pst_parameter_new(pst_context* ctx)
|
||||
{
|
||||
pst_parameter* param = pst_alloc(pst_parameter);
|
||||
if(param) {
|
||||
pst_parameter_init(param, ctx);
|
||||
param->allocated = true;
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
void pst_parameter_fini(pst_parameter* param)
|
||||
{
|
||||
clear(param);
|
||||
if(param->name) {
|
||||
pst_free(param->name);
|
||||
}
|
||||
if(param->allocated) {
|
||||
pst_free(param);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* dwarf_parameter.h
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#ifndef SRC_DWARF_DWARF_PARAMETER_H_
|
||||
#define SRC_DWARF_DWARF_PARAMETER_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
#include "../src/dwarf/dwarf_expression.h"
|
||||
#include "../utils/list_head.h"
|
||||
#include "context.h"
|
||||
|
||||
typedef struct __pst_type {
|
||||
list_node node; // uplink
|
||||
|
||||
char* name; // type name
|
||||
uint32_t type; // DW_AT_XXX type
|
||||
bool allocated; // whether this struct was allocated or not
|
||||
} pst_type;
|
||||
void pst_type_init(pst_type* t, const char* name, uint32_t type);
|
||||
pst_type* pst_type_new(const char* name, uint32_t type);
|
||||
void pst_type_fini(pst_type* t);
|
||||
|
||||
typedef struct __pst_function pst_function;
|
||||
|
||||
typedef struct pst_parameter{
|
||||
list_node node; // uplink. !!! must be first !!!
|
||||
|
||||
// fields
|
||||
Dwarf_Die* die; // DWARF DIE containing parameter's definition
|
||||
char* name; // parameter's name
|
||||
uint32_t line; // line of parameter definition
|
||||
Dwarf_Word size; // size of parameter in bytes
|
||||
int type; // type of parameter in DW_TAG_XXX types enumeration
|
||||
Dwarf_Word enc_type; // if 'type' is DW_TAG_Base_type, then 'base_type' holds DW_AT_ATE_XXX base type encoding type
|
||||
list_head types; // list of parameter's definitions i.e. 'typedef', 'uint32_t'
|
||||
bool is_return; // whether this parameter is return value of the function
|
||||
bool is_variable; // whether this parameter is function variable or argument of function
|
||||
bool has_value; // whether we got value of parameter or not
|
||||
pst_context* ctx;
|
||||
pst_dwarf_expr location;
|
||||
|
||||
bool allocated;
|
||||
} pst_parameter;
|
||||
void pst_parameter_init(pst_parameter* param, pst_context* ctx);
|
||||
pst_parameter* pst_parameter_new(pst_context* ctx);
|
||||
void pst_parameter_fini(pst_parameter* param);
|
||||
|
||||
bool pst_parameter_handle_dwarf(pst_parameter* param, Dwarf_Die* result, pst_function* fun);
|
||||
bool pst_parameter_print_dwarf(pst_parameter* param);
|
||||
bool pst_parameter_handle_type(pst_parameter* param, Dwarf_Attribute* base);
|
||||
pst_type* pst_parameter_add_type(pst_parameter* param, const char* name, int type);
|
||||
|
||||
#endif /* SRC_DWARF_DWARF_PARAMETER_H_ */
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* dwarf_stack.cpp
|
||||
*
|
||||
* Created on: Jan 29, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include "../src/dwarf/dwarf_stack.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <dwarf.h>
|
||||
|
||||
#include "../src/dwarf/dwarf_operations.h"
|
||||
#include "../utils/allocator.h"
|
||||
#include "../utils/list_head.h"
|
||||
#include "dwarf_handler.h"
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// DWARF Stack value
|
||||
// -----------------------------------------------------------------------------------
|
||||
void pst_dwarf_value_set(pst_dwarf_value* value, void* v, uint32_t s, int t)
|
||||
{
|
||||
pst_assert(value && v && s > 0 && s <= sizeof(value->value));
|
||||
|
||||
// clean-up all bits
|
||||
value->value.uint64 = 0;
|
||||
value->type = t;
|
||||
|
||||
if(t & DWARF_TYPE_SIGNED) {
|
||||
switch(s) {
|
||||
case 1:
|
||||
value->value.int8 = *(int8_t*)v;
|
||||
break;
|
||||
case 2:
|
||||
value->value.int16 = *(int16_t*)v;
|
||||
break;
|
||||
case 4:
|
||||
value->value.int32 = *(int32_t*)v;
|
||||
break;
|
||||
default:
|
||||
value->value.int64 = *(int64_t*)v;
|
||||
break;
|
||||
|
||||
}
|
||||
} else {
|
||||
// GENERIC, SIGNED etc
|
||||
switch(s) {
|
||||
case 1:
|
||||
value->value.uint8 = *(uint8_t*)v;
|
||||
break;
|
||||
case 2:
|
||||
value->value.uint16 = *(uint16_t*)v;
|
||||
break;
|
||||
case 4:
|
||||
value->value.uint32 = *(uint32_t*)v;
|
||||
break;
|
||||
default:
|
||||
value->value.uint64 = *(uint64_t*)v;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pst_dwarf_value_init(pst_dwarf_value* dv, char* v, uint32_t s, int t)
|
||||
{
|
||||
pst_assert(dv && s <= sizeof(dv->value));
|
||||
|
||||
list_node_init(&dv->node);
|
||||
dv->type = t;
|
||||
dv->allocated = false;
|
||||
pst_dwarf_value_set(dv, v, s, t);
|
||||
}
|
||||
|
||||
pst_dwarf_value* pst_dwarf_value_new(char* v, uint32_t s, int t)
|
||||
{
|
||||
pst_assert(v);
|
||||
|
||||
pst_dwarf_value* nv = pst_alloc(pst_dwarf_value);
|
||||
if(nv) {
|
||||
pst_dwarf_value_init(nv, v, s, t);
|
||||
nv->allocated = true;
|
||||
}
|
||||
|
||||
|
||||
return nv;
|
||||
}
|
||||
|
||||
void pst_dwarf_value_fini(pst_dwarf_value* value)
|
||||
{
|
||||
pst_assert(value);
|
||||
|
||||
if(value->allocated) {
|
||||
pst_free(value);
|
||||
} else {
|
||||
value->type = DWARF_TYPE_INVALID;
|
||||
value->value.uint64 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// DWARF stack
|
||||
// -----------------------------------------------------------------------------------
|
||||
void pst_dwarf_stack_push(pst_dwarf_stack* st, void* v, uint32_t s, int t)
|
||||
{
|
||||
pst_new(pst_dwarf_value, value, (char*)v, s, t);
|
||||
list_add_head(&st->values, &value->node);
|
||||
}
|
||||
|
||||
void pst_dwarf_stack_push_value(pst_dwarf_stack* st, pst_dwarf_value* value)
|
||||
{
|
||||
list_add_head(&st->values, &value->node);
|
||||
}
|
||||
|
||||
pst_dwarf_value* pst_dwarf_stack_pop(pst_dwarf_stack* st)
|
||||
{
|
||||
pst_dwarf_value* value = (pst_dwarf_value*)list_first(&st->values);
|
||||
if(value) {
|
||||
list_del(&value->node);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
pst_dwarf_value* pst_dwarf_stack_get(pst_dwarf_stack* st, uint32_t idx)
|
||||
{
|
||||
pst_dwarf_value* value = NULL;
|
||||
struct list_node *pos;
|
||||
list_for_each_entry(value, pos, &st->values, node) {
|
||||
if(!idx) {
|
||||
break;
|
||||
}
|
||||
idx--;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool pst_dwarf_stack_get_value(pst_dwarf_stack* st, uint64_t* value)
|
||||
{
|
||||
assert(st && value);
|
||||
|
||||
if(!list_count(&st->values)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pst_dwarf_value* v = pst_dwarf_stack_get(st, 0);
|
||||
if(v->type & DWARF_TYPE_REGISTER_LOC) {
|
||||
// dereference register location
|
||||
int ret = unw_get_reg(st->ctx->curr_frame, v->value.uint64, value);
|
||||
if(ret) {
|
||||
pst_log(SEVERITY_ERROR, "Failed to get value of register 0x%X. Error: %d", v->value.uint64, ret);
|
||||
return false;
|
||||
}
|
||||
} else if(v->type & DWARF_TYPE_MEMORY_LOC) {
|
||||
// dereference memory location
|
||||
*value = *((uint64_t*)v->value.uint64);
|
||||
} else {
|
||||
*value = v->value.uint64;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pst_dwarf_stack_clear(pst_dwarf_stack* st)
|
||||
{
|
||||
pst_dwarf_value* value = NULL;
|
||||
struct list_node *pos, *tn;
|
||||
list_for_each_entry_safe(value, pos, tn, &st->values, node) {
|
||||
list_del(&value->node);
|
||||
pst_dwarf_value_fini(value);
|
||||
}
|
||||
|
||||
pst_dwarf_op* op = NULL;
|
||||
list_for_each_entry_safe(op, pos, tn, &st->expr, node) {
|
||||
list_del(&op->node);
|
||||
pst_dwarf_op_fini(op);
|
||||
}
|
||||
}
|
||||
|
||||
bool pst_dwarf_stack_calc(pst_dwarf_stack* st, Dwarf_Op *exprs, int expr_len, Dwarf_Attribute* attr, pst_function* fun)
|
||||
{
|
||||
pst_dwarf_stack_clear(st);
|
||||
|
||||
for (int i = 0; i < expr_len; i++) {
|
||||
const dwarf_op_map* map = find_op_map(exprs[i].atom);
|
||||
if(!map) {
|
||||
pst_log(SEVERITY_ERROR, "Unknown operation type 0x%hhX(0x%lX, 0x%lX)", exprs[i].atom, exprs[i].number, exprs[i].number2);
|
||||
return false;
|
||||
}
|
||||
|
||||
pst_new(pst_dwarf_op, op, exprs[i].atom, exprs[i].number, exprs[i].number2);
|
||||
list_add_bottom(&st->expr, &op->node);
|
||||
|
||||
pst_dwarf_value* v = pst_dwarf_stack_get(st, 0);
|
||||
// dereference register location there if it is not last in stack
|
||||
if(v && (v->type & DWARF_TYPE_REGISTER_LOC)) {
|
||||
unw_word_t value = 0;
|
||||
uint64_t regno = *((uint64_t*)v->value.uint64);
|
||||
int ret = unw_get_reg(st->ctx->curr_frame, regno, &value);
|
||||
if(ret) {
|
||||
pst_log(SEVERITY_ERROR, "Failed to ger value of register 0x%X. Error: %d", regno, ret);
|
||||
return false;
|
||||
}
|
||||
pst_dwarf_value_set(v, &value, sizeof(value), DWARF_TYPE_GENERIC);
|
||||
}
|
||||
|
||||
// handle there because it contains sub-expression of a Location in caller's frame
|
||||
if(map->op_num == DW_OP_GNU_entry_value) {
|
||||
if(!fun || !fun->parent) {
|
||||
pst_log(SEVERITY_ERROR, "Cannot calculate DW_OP_GNU_entry_value expression while function and it's caller is undefined");
|
||||
return false;
|
||||
}
|
||||
|
||||
// This opcode has two operands, the first one is uleb128 length and the second is block of that length, containing either a
|
||||
// simple register or DWARF expression
|
||||
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) {
|
||||
pst_call_site* cs = pst_call_site_storage_find(&fun->parent->call_sites, fun);
|
||||
if(!cs) {
|
||||
pst_log(SEVERITY_ERROR, "Failed to find call site while calculate DW_OP_GNU_entry_value expression");
|
||||
return false;
|
||||
}
|
||||
pst_dwarf_expr loc;
|
||||
pst_dwarf_expr_init(&loc);
|
||||
pst_dwarf_expr_setup(&loc, expr, exprlen);
|
||||
pst_call_site_param* param = pst_call_site_find(cs, &loc);
|
||||
pst_dwarf_expr_fini(&loc);
|
||||
if(!param) {
|
||||
pst_log(SEVERITY_ERROR, "Failed to find call site parameter while calculate DW_OP_GNU_entry_value expression");
|
||||
return false;
|
||||
}
|
||||
|
||||
pst_dwarf_stack_push(st, ¶m->value, sizeof(param->value), DWARF_TYPE_GENERIC);
|
||||
|
||||
continue;
|
||||
} else {
|
||||
pst_log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr location");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
pst_log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr expression");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!map->operation(st, map, exprs[i].number, exprs[i].number2)) {
|
||||
pst_log(SEVERITY_ERROR, "Failed to calculate %s(0x%lX, 0x%lX) operation", map->op_name, exprs[i].number, exprs[i].number2);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pst_dwarf_stack_init(pst_dwarf_stack* st, pst_context* ctx)
|
||||
{
|
||||
pst_assert(st && ctx);
|
||||
|
||||
list_head_init(&st->expr);
|
||||
list_head_init(&st->values);
|
||||
st->ctx = ctx;
|
||||
st->allocated = false;
|
||||
}
|
||||
|
||||
pst_dwarf_stack* pst_dwarf_stack_new(pst_context* ctx)
|
||||
{
|
||||
pst_assert(ctx);
|
||||
|
||||
pst_new(pst_dwarf_stack, ns, ctx);
|
||||
if(ns) {
|
||||
pst_dwarf_stack_init(ns, ctx);
|
||||
ns->allocated = true;
|
||||
}
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
void pst_dwarf_stack_fini(pst_dwarf_stack* st)
|
||||
{
|
||||
pst_assert(st);
|
||||
|
||||
pst_dwarf_stack_clear(st);
|
||||
if(st->allocated) {
|
||||
pst_free(st);
|
||||
} else {
|
||||
st->ctx = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* dwarf_stack.h
|
||||
*
|
||||
* Created on: Jan 29, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#ifndef SRC_DWARF_DWARF_STACK_H_
|
||||
#define SRC_DWARF_DWARF_STACK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "context.h"
|
||||
#include "utils/allocator.h"
|
||||
#include "dwarf/dwarf_function.h"
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// DWARF Stack value
|
||||
// -----------------------------------------------------------------------------------
|
||||
|
||||
// DWARF Stack value types, bitmask
|
||||
typedef enum {
|
||||
DWARF_TYPE_INVALID = 0, // no type
|
||||
DWARF_TYPE_SIGNED = 1, // signed type
|
||||
DWARF_TYPE_UNSIGNED = 2, // unsigned type
|
||||
DWARF_TYPE_CONST = 4, // constant signed/unsigned type
|
||||
DWARF_TYPE_GENERIC = 8, // size of machine address type
|
||||
DWARF_TYPE_CHAR = 16, // 1 byte size
|
||||
DWARF_TYPE_FLOAT = 32, // machine-dependent floating point size
|
||||
DWARF_TYPE_REGISTER_LOC = 64, // value located in register specified as 'value'
|
||||
DWARF_TYPE_MEMORY_LOC = 128, // value located in memory address specified as 'value'
|
||||
DWARF_TYPE_PIECE = 256, // piece of whole value located in current value
|
||||
DWARF_TYPE_SHORT = 512, // 2 byte size
|
||||
DWARF_TYPE_INT = 1024, // 4 byte size
|
||||
DWARF_TYPE_LONG = 2048 // 8 byte size
|
||||
} pst_dwarf_value_type;
|
||||
|
||||
// DWARF Stack value storage
|
||||
typedef union {
|
||||
uint64_t uint64_v;
|
||||
#define uint64 uint64_v
|
||||
int64_t int64_v;
|
||||
#define int64 int64_v
|
||||
uint32_t uint32_v[2];
|
||||
#define uint32 uint32_v[0]
|
||||
int32_t int32_v[2];
|
||||
#define int32 int32_v[0]
|
||||
uint16_t uint16_v[4];
|
||||
#define uint16 uint16_v[0]
|
||||
int16_t int16_v[4];
|
||||
#define int16 int16_v[0]
|
||||
uint8_t uint8_v[8];
|
||||
#define uint8 uint8_v[0]
|
||||
int8_t int8_v[8];
|
||||
#define int8 int8_v[0]
|
||||
void* ptr;
|
||||
} pst_sized_value;
|
||||
|
||||
typedef struct __pst_dwarf_value {
|
||||
list_node node; // uplink, !!! must be 1st field in structure !!!
|
||||
|
||||
// fields
|
||||
pst_sized_value value; // value itself
|
||||
int type; // value type. bitmask of DWARF_TYPE_XXX
|
||||
bool allocated;
|
||||
} pst_dwarf_value;
|
||||
|
||||
void pst_dwarf_value_init(pst_dwarf_value* value, char* v, uint32_t s, int t);
|
||||
pst_dwarf_value* pst_dwarf_value_new(char* v, uint32_t s, int t);
|
||||
void pst_dwarf_value_fini(pst_dwarf_value* value);
|
||||
void pst_dwarf_value_set(pst_dwarf_value* value, void* v, uint32_t s, int t);
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// DWARF stack
|
||||
// -----------------------------------------------------------------------------------
|
||||
|
||||
typedef struct __pst_dwarf_stack {
|
||||
list_head expr; // DWARF expression
|
||||
list_head values; // list of values on the stack
|
||||
pst_context* ctx; // context of execution
|
||||
bool allocated; // whether this object was allocated or not
|
||||
} pst_dwarf_stack;
|
||||
|
||||
void pst_dwarf_stack_init(pst_dwarf_stack* st, pst_context* ctx);
|
||||
pst_dwarf_stack* pst_dwarf_stack_new(pst_context* ctx);
|
||||
void pst_dwarf_stack_fini(pst_dwarf_stack* st);
|
||||
|
||||
bool pst_dwarf_stack_calc(pst_dwarf_stack* st, Dwarf_Op *exprs, int expr_len, Dwarf_Attribute* attr, pst_function* fun = NULL);
|
||||
void pst_dwarf_stack_clear(pst_dwarf_stack* st);
|
||||
bool pst_dwarf_stack_get_value(pst_dwarf_stack* st, uint64_t* value);
|
||||
pst_dwarf_value* pst_dwarf_stack_get(pst_dwarf_stack* st, uint32_t idx = 0);
|
||||
pst_dwarf_value* pst_dwarf_stack_pop(pst_dwarf_stack* st);
|
||||
void pst_dwarf_stack_push_value(pst_dwarf_stack* st, pst_dwarf_value* value);
|
||||
void pst_dwarf_stack_push(pst_dwarf_stack* st, void* v, uint32_t s, int t);
|
||||
|
||||
#endif /* SRC_DWARF_DWARF_STACK_H_ */
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* dwarf_utils.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#include <dwarf.h>
|
||||
|
||||
#include "dwarf_utils.h"
|
||||
#include "dwarf_stack.h"
|
||||
#include "dwarf_function.h"
|
||||
|
||||
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
pst_dwarf_expr_setup(loc, expr, exprlen);
|
||||
ctx->print_expr(ctx, expr, exprlen, attr);
|
||||
ret = pst_dwarf_stack_calc(&stack, expr, exprlen, attr, fun);
|
||||
pst_dwarf_stack_get_value(&stack, &loc->value);
|
||||
}
|
||||
} 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) {
|
||||
pst_dwarf_expr_setup(loc, expr, exprlen);
|
||||
// actual location, try to calculate Location expression
|
||||
ret = pst_dwarf_stack_calc(&stack, expr, exprlen, attr, fun);
|
||||
pst_dwarf_stack_get_value(&stack, &loc->value);
|
||||
} else {
|
||||
// Location skipped due to don't match current PC offset
|
||||
pst_log(SEVERITY_DEBUG, "Skip Location list expression: [%d] (low_offset: 0x%" PRIx64 ", high_offset: 0x%" PRIx64 "), \"%s\"", i, start, end, ctx->buff);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
pst_log(SEVERITY_WARNING, "Unknown location attribute form = 0x%X, code = 0x%X, ", attr->form, attr->code);
|
||||
}
|
||||
|
||||
pst_dwarf_stack_fini(&stack);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* dwarf_utils.h
|
||||
*
|
||||
* Created on: Feb 1, 2020
|
||||
* Author: nnosov
|
||||
*/
|
||||
|
||||
#ifndef SRC_DWARF_DWARF_UTILS_H_
|
||||
#define SRC_DWARF_DWARF_UTILS_H_
|
||||
|
||||
#include <elfutils/libdw.h>
|
||||
|
||||
#include "context.h"
|
||||
#include "dwarf_expression.h"
|
||||
#include "dwarf_function.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);
|
||||
bool is_location_form(int form);
|
||||
|
||||
#endif /* SRC_DWARF_DWARF_UTILS_H_ */
|
||||
Reference in New Issue
Block a user