code hierarchy refactoring

This commit is contained in:
2020-02-05 09:41:22 +04:00
parent c21c623be4
commit 3c7105eb97
35 changed files with 145 additions and 113 deletions
+88
View File
@@ -0,0 +1,88 @@
#use Bash instead of SH
export SHELL := /bin/bash
# echo command color definitions
ifndef NO_COLOR
RED=\e[0;31m
GREEN=\e[0;32m
YELLOW=\e[1;33m
NC=\e[0m # No Color
COLOR=-fdiagnostics-color
else
RED=
GREEN=
YELLOW=
NC=
COLOR=
endif
CXX = gcc
CC = gcc
RM = rm -f
AR = ar rvs
BUILD_DIR = ./build
RESULT_DIR = ../build
LIB_STATIC = $(RESULT_DIR)/libpst.a
#Software LABEL, BUILD number, and DATE of build
$(shell mkdir -p ${BUILD_DIR})
SOFT_BUILD = $(shell STR=`cat $(BUILD_DIR)/version.h 2> /dev/null | grep 'SOFT_BUILD' | sed 's/[a-z A-Z\#\-\._]//g'`; let "STR += 1"; echo $$STR; echo "SOFT_BUILD $$STR" > $(BUILD_DIR)/version.h;)
DATE_BUILD = $(shell date +"%D %T")
#source and header files search path. add here new directories which contains files specified for $(SRC)
VPATH = . dwarf arch utils
SRC = $(wildcard $(addsuffix /*.cpp,${VPATH}))
OBJ = $(patsubst %.cpp,%.o,$(addprefix $(BUILD_DIR)/,$(notdir $(SRC))))
INCS = -I"./dwarf" -I"./" -I"./utils" -I"./arch"
FLAGS = -Wall -ggdb -fPIC -O3 -rdynamic -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DSOFT_BUILD=$(SOFT_BUILD) -DUSER_BUILD=$(USER) -DDATE_BUILD="$(DATE_BUILD)"
.PHONY: all clean
all: $(LIB_STATIC)
clean:
${RM} $(BUILD_DIR)/*.o $(BUILD_DIR)/*.dep $(BIN) $(LIB_STATIC) $(BUILD_DIR)/prepare.bld $(RESULT_DIR)/prepare.res $(BUILD_DIR)/version.h
@if [ -z "$$(ls -A $(BUILD_DIR) 2>&1)" ]; then ${RM} -r $(BUILD_DIR); fi
@if [ -z "$$(ls -A $(RESULT_DIR) 2>&1)" ]; then ${RM} -r $(RESULT_DIR); fi
$(BUILD_DIR)/prepare.bld:
@if [ ! -e $(BUILD_DIR) ]; then mkdir -vp $(BUILD_DIR); fi
@touch $@
$(RESULT_DIR)/prepare.res:
@if [ ! -e $(RESULT_DIR) ]; then mkdir -vp $(RESULT_DIR); fi
@touch $@
$(BUILD_DIR)/%.o: %.cpp
#compile source code directly to $BUILD_DIR directory
@printf "Building %-60s" $@
@OUT=$$($(CXX) $(COLOR) -o $@ -c $< $(FLAGS) $(INCS) 2>&1); \
if [ $$? -ne "0" ]; \
then echo -e "${RED}[FAILED]${NC}"; echo -e "$$OUT"; \
else \
if [ -n "$$OUT" ]; \
then echo -e "${YELLOW}[DONE]${NC}"; echo -e "'$$OUT'"; \
else \
echo -e "${GREEN}[DONE]${NC}"; \
fi; \
fi
#create dependencies
@$(CXX) -MM -MT '$@' -c $< > $@.dep $(FLAGS) $(INCS)
#create library
$(LIB_STATIC): $(RESULT_DIR)/prepare.res $(BUILD_DIR)/prepare.bld $(OBJ)
@rm -fv $@
@printf "Create %-60s" $@
@OUT=$$($(AR) $@ $(OBJ) 2>&1); \
if [ $$? -ne "0" ]; \
then echo -e "${RED}[FAILED]${NC}"; echo -e "$$OUT"; \
else \
echo -e "${GREEN}[DONE]${NC}"; \
fi
#include dependencies for track changes in sourcecode and related header files
DEPEND := $(OBJ:.o=.o.dep)
-include $(DEPEND)
+152
View File
@@ -0,0 +1,152 @@
/*
* registers.cpp
*
* Created on: Jan 27, 2020
* Author: nnosov
*/
#include <dwarf.h>
#include <elfutils/libdwfl.h>
#include "common.h"
#include "registers.h"
dwarf_reg_map reg_map[] = {
// GP Registers
{0x0, "RAX", DW_OP_reg0},
{0x1, "RDX", DW_OP_reg1},
{0x2, "RCX", DW_OP_reg2},
{0x3, "RBX", DW_OP_reg3},
{0x4, "RSI", DW_OP_reg4},
{0x5, "RDI", DW_OP_reg5},
{0x6, "RBP", DW_OP_reg6},
{0x7, "RSP", DW_OP_reg7}, // Stack pointer address (SP) mapped to RSP
// Extended GP Registers
{0x8, "R8", DW_OP_reg8},
{0x9, "R9", DW_OP_reg9},
{0xA, "R10", DW_OP_reg10},
{0xB, "R11", DW_OP_reg11},
{0xC, "R12", DW_OP_reg12},
{0xD, "R13", DW_OP_reg13},
{0xE, "R14", DW_OP_reg14},
{0xF, "R15", DW_OP_reg15},
{0x10, "RIP", DW_OP_reg16}, // Return Address (RA) mapped to RIP
// SSE Vector Registers
{0x11, "XMM0", DW_OP_reg17},
{0x12, "XMM1", DW_OP_reg18},
{0x13, "XMM2", DW_OP_reg19},
{0x14, "XMM3", DW_OP_reg20},
{0x15, "XMM4", DW_OP_reg21},
{0x16, "XMM5", DW_OP_reg22},
{0x17, "XMM6", DW_OP_reg23},
{0x18, "XMM7", DW_OP_reg24},
{0x19, "XMM8", DW_OP_reg25},
{0x1a, "XMM9", DW_OP_reg26},
{0x1b, "XMM10", DW_OP_reg27},
{0x1c, "XMM11", DW_OP_reg28},
{0x1d, "XMM12", DW_OP_reg29},
{0x1e, "XMM13", DW_OP_reg30},
{0x1f, "XMM14", DW_OP_reg31},
// GP Registers
{0x0, "RAX", DW_OP_breg0},
{0x1, "RDX", DW_OP_breg1},
{0x2, "RCX", DW_OP_breg2},
{0x3, "RBX", DW_OP_breg3},
{0x4, "RSI", DW_OP_breg4},
{0x5, "RDI", DW_OP_breg5},
{0x6, "RBP", DW_OP_breg6},
{0x7, "RSP", DW_OP_breg7},
// Extended GP Registers
{0x8, "R8", DW_OP_breg8},
{0x9, "R9", DW_OP_breg9},
{0xA, "R10", DW_OP_breg10},
{0xB, "R11", DW_OP_breg11},
{0xC, "R12", DW_OP_breg12},
{0xD, "R13", DW_OP_breg13},
{0xE, "R14", DW_OP_breg14},
{0xF, "R15", DW_OP_breg15},
{0x10, "RIP", DW_OP_breg16}, // Return Address (RA) mapped to RIP
// SSE Vector Registers
{0x11, "XMM0", DW_OP_breg17},
{0x12, "XMM1", DW_OP_breg18},
{0x13, "XMM2", DW_OP_breg19},
{0x14, "XMM3", DW_OP_breg20},
{0x15, "XMM4", DW_OP_breg21},
{0x16, "XMM5", DW_OP_breg22},
{0x17, "XMM6", DW_OP_breg23},
{0x18, "XMM7", DW_OP_breg24},
{0x19, "XMM8", DW_OP_breg25},
{0x1a, "XMM9", DW_OP_breg26},
{0x1b, "XMM10", DW_OP_breg27},
{0x1c, "XMM11", DW_OP_breg28},
{0x1d, "XMM12", DW_OP_breg29},
{0x1e, "XMM13", DW_OP_breg30},
{0x1f, "XMM14", DW_OP_breg31},
{0x20, "XMM15", 0xff}, // no mapping to dwarf registers
};
int regnum = sizeof(reg_map) / sizeof(dwarf_reg_map);
int find_regnum(uint32_t op)
{
for(int i = 0; i < regnum; ++i) {
if(reg_map[i].op_num == op) {
return reg_map[i].regno;
}
}
return -1;
}
pst_reg_error pst_get_reg(pst_context* ctx, int regno, uint64_t& regval)
{
#ifdef USE_LIBUNWIND
int ret = unw_get_reg(ctx->curr_frame, regno, &regval);
switch (ret) {
case UNW_EUNSPEC:
return REG_CFI_ERROR;
break;
case UNW_EBADREG:
return REG_UNDEFINED;
break;
default:
return REG_UNDEFINED;
break;
}
return REG_OK;
#else
Dwarf_Op ops_mem[3];
Dwarf_Op* ops;
size_t nops;
char str[512];
if(dwarf_frame_register(ctx->frame, regno, ops_mem, &ops, &nops) != -1) {
if(nops != 0 || ops != ops_mem) {
if(nops != 0 || ops != NULL) {
str[0] = 0;
ctx->print_expr_block(ops, nops, str, sizeof(str));
dwarf_stack st(ctx);
if(st.calc_expression(ops, nops, NULL) && st.get_value(regval)) {
pst_log(SEVERITY_DEBUG, "CFI register 0x%X(%s) expression: %s ==> %#lX", regno, reg_map[regno].regname, str, regval);
return REG_OK;
} else {
pst_log(SEVERITY_ERROR, "Failed to calculate register 0x%X(%s) CFI expression %s", regno, reg_map[regno].regname, str);
return REG_EXPR_ERROR;
}
} else {
pst_log(SEVERITY_DEBUG, "CFI expression for register 0x%X(%s) is SAME VALUE", regno, reg_map[regno].regname);
return REG_SAME;
}
} else {
pst_log(SEVERITY_DEBUG, "CFI expression for register 0x%X(%s) is UNDEFINED", regno, reg_map[regno].regname);
return REG_UNDEFINED;
}
} else {
pst_log(SEVERITY_ERROR, "Failed to get CFI expression for register 0x%X", regno);
}
return REG_CFI_ERROR;
#endif
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
#include <string.h>
#include "context.h"
typedef struct __dwarf_reg_map {
int regno; // platform-dependent register number
const char* regname; // register name
uint32_t op_num; // number of DWARF DW_OP_reg(x)/DW_OP_breg(x) operation corresponds to this register
} dwarf_reg_map;
int find_regnum(uint32_t op);
typedef enum {
REG_OK = 0,
REG_UNDEFINED,
REG_SAME,
REG_CFI_ERROR,
REG_EXPR_ERROR
} pst_reg_error;
pst_reg_error pst_get_reg(pst_context* ctx, int regno, uint64_t& regval);
+128
View File
@@ -0,0 +1,128 @@
/*
* common.cpp
*
* Created on: Jan 8, 2020
* Author: nnosov
*/
#include <inttypes.h>
#include <stddef.h>
#include <dwarf.h>
#include <stdarg.h>
#include <stdio.h>
#include <limits.h>
#include <elfutils/libdwfl.h>
#include <libunwind.h>
#include "common.h"
#include "dwarf/dwarf_operations.h"
#include "utils/log.h"
#include "arch/registers.h"
int32_t decode_sleb128(uint8_t *sleb128)
{
int32_t num = 0, shift = 0, size = 0;
do {
num |= ((*sleb128 & 0x7f) << shift);
shift += 7;
size += 8;
} while(*sleb128++ & 0x80);
if((shift < size) && (*(--sleb128) & 0x40)) {
num |= - (1 << shift);
}
return num;
}
uint32_t decode_uleb128(uint8_t *uleb128)
{
uint32_t num = 0, shift = 0;
do {
num |= ((*uleb128 & 0x7f) << shift);
shift += 7;
} while(*uleb128++ & 0x80);
return num;
}
// Utility function to encode a ULEB128 value to a buffer. Returns
// the length in bytes of the encoded value.
inline unsigned encode_uleb128(uint64_t value, uint8_t *p, unsigned PadTo = 0)
{
uint8_t *orig_p = p;
unsigned count = 0;
do {
uint8_t Byte = value & 0x7f;
value >>= 7;
count++;
if (value != 0 || count < PadTo)
Byte |= 0x80; // Mark this byte to show that more bytes will follow.
*p++ = Byte;
} while (value != 0);
// Pad with 0x80 and emit a null byte at the end.
if (count < PadTo) {
for (; count < PadTo - 1; ++count)
*p++ = '\x80';
*p++ = '\x00';
}
return (unsigned)(p - orig_p);
}
// Utility function to encode a SLEB128 value to a buffer. Returns
// the length in bytes of the encoded value.
inline unsigned encode_sleb128(int64_t value, uint8_t *p, unsigned PadTo = 0)
{
uint8_t *orig_p = p;
unsigned count = 0;
bool More;
do {
uint8_t Byte = value & 0x7f;
// NOTE: this assumes that this signed shift is an arithmetic right shift.
value >>= 7;
More = !((((value == 0 ) && ((Byte & 0x40) == 0)) ||
((value == -1) && ((Byte & 0x40) != 0))));
count++;
if (More || count < PadTo)
Byte |= 0x80; // Mark this byte to show that more bytes will follow.
*p++ = Byte;
} while (More);
// Pad with 0x80 and emit a terminating byte at the end.
if (count < PadTo) {
uint8_t PadValue = value < 0 ? 0x7f : 0x00;
for (; count < PadTo - 1; ++count)
*p++ = (PadValue | 0x80);
*p++ = PadValue;
}
return (unsigned)(p - orig_p);
}
// Utility function to get the size of the ULEB128-encoded value.
unsigned getULEB128Size(uint64_t Value)
{
unsigned Size = 0;
do {
Value >>= 7;
Size += sizeof(int8_t);
} while (Value);
return Size;
}
// Utility function to get the size of the SLEB128-encoded value.
unsigned getSLEB128Size(int64_t Value)
{
unsigned Size = 0;
int Sign = Value >> (8 * sizeof(Value) - 1);
bool IsMore;
do {
unsigned Byte = Value & 0x7f;
Value >>= 7;
IsMore = Value != Sign || ((Byte ^ Sign) & 0x40) != 0;
Size += sizeof(int8_t);
} while (IsMore);
return Size;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef __PST_COMMON_H__
#define __PST_COMMON_H__
#include <stdint.h>
#define USE_LIBUNWIND
// TBD custom assertion if needed
#define pst_assert(expr) assert(expr)
// platform-dependent address size
#define PST_GENERIC_SIZE (8) // for x86_64 architecture
int32_t decode_sleb128(uint8_t *sleb128);
uint32_t decode_uleb128(uint8_t *uleb128);
#endif // __PST_COMMON_H__
+204
View File
@@ -0,0 +1,204 @@
/*
* context.cpp
*
* Created on: Jan 28, 2020
* Author: nnosov
*/
#include <limits.h>
#include <dwarf.h>
#include "context.h"
#include "../src/dwarf/dwarf_operations.h"
#include "registers.h"
extern dwarf_reg_map reg_map[];
extern int regnum;
pst_log logger; // logger for library
pst_allocator allocator; // custom allocator for PST library
void clean_print(pst_context* ctx) {
ctx->buff[0] = 0;
ctx->offset = 0;
}
void print_stack(pst_context* ctx, int max, uint64_t next_cfa)
{
pst_log(SEVERITY_DEBUG, "CFA = %#lX, NEXT_CFA = %#lX, SP = %#lX", ctx->cfa, next_cfa, ctx->sp);
ctx->clean_print(ctx);
int i = 0;
if(ctx->cfa > ctx->sp) {
ctx->print(ctx, "Args: ");
for(; i < max && (ctx->cfa - i) > ctx->sp; ++i) {
ctx->print(ctx, "#%d 0x%lX ", i, *(uint64_t*)(ctx->cfa - i));
}
}
if((ctx->cfa - i) > next_cfa) {
ctx->print(ctx, "Vars: ");
for(; i < max && (ctx->cfa - i) > next_cfa; ++i) {
ctx->print(ctx, "#%d 0x%lX ", i, *(uint64_t*)(ctx->cfa - i));
}
}
}
void print_registers(pst_context* ctx, int from, int to)
{
ctx->clean_print(ctx);
for(int i = from; i < regnum && i <= to; ++i) {
unw_word_t regval;
if(!unw_get_reg(ctx->curr_frame, reg_map[i].regno, &regval)) {
ctx->print(ctx, "%s: %#lX ", reg_map[i].regname, regval);
} else {
ctx->print(ctx, "%s: <undef>", reg_map[i].regname);
}
}
}
bool print(pst_context* ctx, const char* fmt, ...)
{
bool nret = true;
va_list args;
va_start(args, fmt);
int size = sizeof(ctx->buff) - ctx->offset;
int ret = vsnprintf(ctx->buff + ctx->offset, size, fmt, args);
if(ret >= size || ret < 0) {
nret = false;
}
ctx->offset += ret;
va_end(args);
return nret;
}
bool print_expr_block (pst_context* ctx, Dwarf_Op *exprs, int exprlen, Dwarf_Attribute* attr)
{
ctx->clean_print(ctx);
for (int i = 0; i < exprlen; i++) {
const dwarf_op_map* map = find_op_map(exprs[i].atom);
if(map) {
if(map->op_num >= DW_OP_breg0 && map->op_num <= DW_OP_breg16) {
int32_t off = decode_sleb128((unsigned char*)&exprs[i].number);
int regno = map->op_num - DW_OP_breg0;
unw_word_t ptr = 0;
unw_get_reg(ctx->curr_frame, regno, &ptr);
ctx->print(ctx, "%s(*%s%s%d) reg_value: 0x%lX", map->op_name, unw_regname(regno), off >=0 ? "+" : "", off, ptr);
} else if(map->op_num >= DW_OP_reg0 && map->op_num <= DW_OP_reg16) {
unw_word_t value = 0;
int regno = map->op_num - DW_OP_reg0;
unw_get_reg(ctx->curr_frame, regno, &value);
ctx->print(ctx, "%s(*%s) value: 0x%lX", map->op_name, unw_regname(regno), value);
} else if(map->op_num == DW_OP_GNU_entry_value) {
if(!attr) {
pst_log(SEVERITY_ERROR, "No attribute of DW_OP_GNU_entry_value provided");
return false;
}
uint32_t value = decode_uleb128((unsigned char*)&exprs[i].number);
ctx->print(ctx, "%s(%u, ", map->op_name, value);
Dwarf_Attribute attr_mem;
if(!dwarf_getlocation_attr(attr, exprs, &attr_mem)) {
Dwarf_Op *expr;
size_t exprlen;
if (dwarf_getlocation(&attr_mem, &expr, &exprlen) == 0) {
ctx->print_expr(ctx, expr, exprlen, &attr_mem);
ctx->print(ctx, ") ");
} 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;
}
} else if(map->op_num == DW_OP_stack_value) {
ctx->print(ctx, "%s", map->op_name);
} else if(map->op_num == DW_OP_plus_uconst) {
uint32_t value = decode_uleb128((unsigned char*)&exprs[i].number);
ctx->print(ctx, "%s(+%u) ", map->op_name, value);
} else if(map->op_num == DW_OP_bregx) {
uint32_t regno = decode_uleb128((unsigned char*)&exprs[i].number);
int32_t off = decode_sleb128((unsigned char*)&exprs[i].number2);
unw_word_t ptr = 0;
unw_get_reg(ctx->curr_frame, regno, &ptr);
//ptr += off;
ctx->print(ctx, "%s(%s%s%d) reg_value = 0x%lX", map->op_name, unw_regname(regno), off >= 0 ? "+" : "", off, ptr);
} else if(map->op_num == DW_OP_regx) {
int32_t reg = decode_sleb128((unsigned char*)&exprs[i].number);
unw_word_t value = 0;
unw_get_reg(ctx->curr_frame, reg, &value);
ctx->print(ctx, "%s(%s) value = 0x%lX", map->op_name, unw_regname(reg), value);
} else if(map->op_num == DW_OP_addr) {
ctx->print(ctx, "%s value = %p", map->op_name, (void*)exprs[i].number);
} else if(map->op_num == DW_OP_fbreg) {
int32_t off = decode_sleb128((unsigned char*)&exprs[i].number);
ctx->print(ctx, "%s(SP%s%d) ", map->op_name, off >=0 ? "+" : "", off);
} else {
ctx->print(ctx, "%s(0x%lX, 0x%lx) ", map->op_name, exprs[i].number, exprs[i].number2);
}
} else {
ctx->print(ctx, "0x%hhX(0x%lX, 0x%lx)", exprs[i].atom, exprs[i].number, exprs[i].number2);
}
}
return true;
}
void pst_context_init(pst_context* ctx, ucontext_t* hctx)
{
// global
pst_alloc_init(&allocator);
pst_log_init_console(&logger);
// methods
ctx->clean_print = clean_print;
ctx->print = print;
ctx->print_expr = print_expr_block;
ctx->print_registers = print_registers;
ctx->print_stack = print_stack;
// fields
ctx->hcontext = hctx;
ctx->base_addr = 0;
ctx->sp = 0;
ctx->cfa = 0;
ctx->curr_frame = NULL;
ctx->frame = NULL;
ctx->dwfl = NULL;
ctx->module = NULL;
}
void pst_context_fini(pst_context* ctx)
{
ctx->hcontext = NULL;
ctx->clean_print(ctx);
ctx->base_addr = 0;
ctx->sp = 0;
ctx->cfa = 0;
ctx->curr_frame = NULL;
ctx->frame = NULL;
ctx->dwfl = NULL;
ctx->module = NULL;
// global
pst_log_fini(&logger);
pst_alloc_fini(&allocator);
}
char* pst_strdup(const char* str)
{
pst_assert(str);
uint32_t len = strlen(str);
char* dst = (char*)allocator.alloc(&allocator, len + 1);
memcpy(dst, str, len);
dst[len] = 0;
return dst;
}
+60
View File
@@ -0,0 +1,60 @@
/*
* context.h
*
* Created on: Jan 28, 2020
* Author: nnosov
*/
#ifndef FRAMEWORK_CONTEXT_H_
#define FRAMEWORK_CONTEXT_H_
#include <string.h>
#include <stdlib.h>
#include <execinfo.h>
#include <libunwind.h>
#include <elfutils/libdwfl.h>
#include "../src/utils/allocator.h"
#include "../src/utils/log.h"
extern pst_log logger; // logger for whole PST library
extern pst_allocator allocator; // custom allocator for PST library
#define pst_alloc(TYPE) (TYPE*)allocator.alloc(&allocator, sizeof(TYPE))
#define pst_free(NAME) allocator.free(&allocator, NAME)
#define pst_log(SEVERITY, FORMAT, ...) logger.log(&logger, SEVERITY, FORMAT, ##__VA_ARGS__)
char* pst_strdup(const char* str);
typedef struct pst_context {
// methods
void (*clean_print) (pst_context* ctx);
bool (*print) (pst_context* ctx, const char* fmt, ...);
bool (*print_expr) (pst_context* ctx, Dwarf_Op *exprs, int exprlen, Dwarf_Attribute* attr);
void (*print_registers) (pst_context* ctx, int from, int to);
void (*print_stack) (pst_context* ctx, int max, uint64_t next_cfa);
// fields
ucontext_t* hcontext; // context of signal handler
unw_context_t context; // context of stack trace
unw_cursor_t cursor; // libunwind stack frame storage
unw_cursor_t* curr_frame; // callee libunwind frame
Dwarf_Addr base_addr; // base address where process loaded
Dwarf_Addr sp; // stack pointer of currently processed stack frame
Dwarf_Addr cfa; // CFA (Canonical Frame Address) of currently processed stack frame
Dwarf_Frame* frame; // currently examined libdwfl frame
Dwfl* dwfl; // DWARF context
Dwfl_Module* module; // currently processed CU
// print buffer
char buff[8192]; // stack trace buffer
uint32_t offset; // offset in the 'buff'
} pst_context;
void pst_context_init(pst_context* ctx, ucontext_t* hctx);
void pst_context_fini(pst_context* ctx);
#endif /* FRAMEWORK_CONTEXT_H_ */
+351
View File
@@ -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(&param->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(&param->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, &param->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);
}
}
+90
View File
@@ -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_ */
+165
View File
@@ -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);
}
}
+49
View File
@@ -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_ */
+482
View File
@@ -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(&param->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);
}
}
+49
View File
@@ -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_ */
+278
View File
@@ -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);
}
+32
View File
@@ -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_ */
+839
View File
@@ -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, &regno, 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;
}
+22
View File
@@ -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);
+298
View File
@@ -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, &param->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(&param->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(&param->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(&param->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, &param->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, &param->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*)&param->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, &param->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, &param->location.value);
param->has_value = true;
break;
case DW_FORM_sdata:
dwarf_formsdata(attr, (int64_t*)&param->location.value);
param->has_value = true;
break;
case DW_FORM_udata:
dwarf_formudata(attr, &param->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(&param->node);
param->die = NULL;
param->name = NULL;
param->line = 0;
param->size = 0;
param->type = 0;
param->enc_type = 0;
list_head_init(&param->types);
param->is_return = false;
param->is_variable = false;
param->has_value = false;
param->ctx = ctx;
pst_dwarf_expr_init(&param->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);
}
}
+59
View File
@@ -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_ */
+295
View File
@@ -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, &param->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;
}
}
+97
View File
@@ -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_ */
+78
View File
@@ -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;
}
+30
View File
@@ -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_ */
+76
View File
@@ -0,0 +1,76 @@
/*
* allocator.cpp
*
* Created on: Jan 28, 2020
* Author: nnosov
*/
#include "../src/utils/allocator.h"
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include "common.h"
void heap_free(pst_allocator* alloc, void* buff)
{
pthread_mutex_lock(&alloc->lock);
alloc->size -= malloc_usable_size(buff);
pthread_mutex_unlock(&alloc->lock);
free(buff);
}
void* heap_alloc(pst_allocator* alloc, uint32_t size)
{
void* buff = malloc(size);
pthread_mutex_lock(&alloc->lock);
alloc->size += malloc_usable_size(buff);
pthread_mutex_unlock(&alloc->lock);
return buff;
}
void* heap_realloc(pst_allocator* alloc, void* buff, uint32_t new_size)
{
pthread_mutex_lock(&alloc->lock);
alloc->size -= malloc_usable_size(buff);
void* new_buff = realloc(buff, new_size);
alloc->size += malloc_usable_size(new_buff);
pthread_mutex_unlock(&alloc->lock);
return new_buff;
}
void pst_alloc_init(pst_allocator* alloc)
{
alloc->type = ALLOC_HEAP;
alloc->base = NULL;
alloc->size = 0;
alloc->alloc = heap_alloc;
alloc->free = heap_free;
alloc->realloc = heap_realloc;
pthread_mutex_init(&alloc->lock, NULL);
return;
}
void pst_alloc_init_custom(pst_allocator* alloc, void* buff, uint32_t size)
{
// TBD to implement custom allocator
pst_alloc_init(alloc);
return;
}
void pst_alloc_fini(pst_allocator* alloc)
{
if(alloc->type == ALLOC_HEAP || alloc->type == ALLOC_CUSTOM) {
alloc->type = ALLOC_NONE;
alloc->base = NULL;
alloc->size = 0;
}
pthread_mutex_destroy(&alloc->lock);
}
+59
View File
@@ -0,0 +1,59 @@
/*
* allocator.h
*
* Created on: Jan 28, 2020
* Author: nnosov
*/
#ifndef PST_ALLOCATOR_H_
#define PST_ALLOCATOR_H_
#include <stdint.h>
#include <assert.h>
#include <pthread.h>
// concatenation
#define CAT(a, ...) CAT2(a, __VA_ARGS__)
#define CAT2(a, ...) a ## __VA_ARGS__
// declaration with initialization and parameters of initialization
#define pst_decl(TYPE, NAME, ...) \
TYPE NAME; CAT2(TYPE, _init) (&NAME, __VA_ARGS__);
// declaration with initialization
#define pst_decl0(TYPE, NAME) \
TYPE NAME; CAT2(TYPE, _init) (&NAME);
// allocation with initialization
#define pst_new(TYPE, NAME, ...) \
TYPE* NAME; NAME = CAT2(TYPE, _new) (__VA_ARGS__);
// de-initialization and deletion if was previously allocated
#define pst_fini(TYPE, NAME) \
CAT2(TYPE, _fini) (NAME);
typedef enum {
ALLOC_NONE = 0, // not initialized
ALLOC_HEAP = 1, // use libc memory allocator
ALLOC_CUSTOM = 2 // use custom allocator in predefined range of memory
} pst_alloc_type;
typedef struct pst_allocator {
// methods
void* (*alloc)(pst_allocator* alloc, uint32_t size);
void (*free)(pst_allocator* alloc, void* buff);
void* (*realloc)(pst_allocator* alloc, void* buff, uint32_t new_size);
// fields
int type;
void* base;
uint32_t size;
pthread_mutex_t lock;
} pst_allocator;
void pst_alloc_init(pst_allocator* alloc);
void pst_alloc_init_custom(pst_allocator* alloc, void* buff, uint32_t size);
void pst_alloc_fini(pst_allocator* alloc);
#endif /* PST_ALLOCATOR_H_ */
+328
View File
@@ -0,0 +1,328 @@
/* =============================================================================
* CDL (Configuration Definition Language) validator $Revision: 1.4 $
* (C)2004-2007 Nikolay Nosov. All rights reserved.
*
* File: $RCSfile: hash_multimap.c,v $
* Purpose: Hash Multimap implementation
* Written by: Nikolay Nosov
* Last modified: $Date: 2008/06/21 12:15:53 $ by $Author: nnosov $.
*
* For more information please visit
* http://nsoft.volgocity.ru/cdl or
* http://cdl.sourceforge.net
* ===========================================================================*/
#include "../src/utils/hash_multimap.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
/* Default hash function
@param key hash key
@param size hash key size in bytes
@return hash value, depends of the hash key
*/
static unsigned int default_hash_fn(const void *key, int size)
{
register int i, j;
unsigned int cs = 0;
unsigned int ps = 0;
if(key)
{
for(i = 0, j = 0; i < size; i++, j++)
{
ps |= ((unsigned int)((const char*)key)[i]) << (j * 8);
if(j == 3 || j == (size - 1))
{
cs ^= ps;
ps = 0;
j = 0;
}
}
}
return cs;
}
static int default_compare_fn(const void *key1, const void *key2, const int size)
{
return !memcmp(key1, key2, size);
}
/** Initialize hash node. Must be called before first usage of the node.
@param node pointer to the hash node descriptor
*/
void hash_node_init(struct hash_node *node)
{
node->key = NULL;
node->key_size = 0;
list_node_init(&node->node);
}
/** Cleanup hash node
@param node pointer to the hash node descriptor
*/
void hash_node_cleanup(struct hash_node *node)
{
if(node->key) free(node->key);
list_node_init(&node->node);
node->key = NULL;
node->key_size = 0;
}
/** Initialize hash table
@param map pointer to the hash table descriptor
@param hash_shift size of the hash table in bits. Should be greater than
HASH_MIN_SHIFT and less than HASH_MAX_SHIFT. In case of less than HASH_MIN_SHIFT,
will be followed by HASH_MIN_SHIFT, in case of greater HASH_MAX_SHIFT will be
followed by HASH_MAX_SHIFT
@param hf pointer to the user defined hash function. If NULL, then default hash
function will be used
@param cf pointer to the user defined compare function. if NULL, then the default
compare function will be used
*/
int hash_head_init(struct hash_head *map, unsigned int hash_shift, _hash_fn hf, _compare_fn cf)
{
int i;
if(map)
{
if(hash_shift <= HASH_MIN_SHIFT) {
map->hash_shift = HASH_MIN_SHIFT;
} else if(hash_shift >= HASH_MAX_SHIFT) {
map->hash_shift = HASH_MAX_SHIFT;
} else {
map->hash_shift = hash_shift;
}
map->hash_size = 1UL << map->hash_shift;
map->hash_mask = (map->hash_size - 1);
map->bucket = (struct list_head *)malloc(sizeof(struct list_head) * map->hash_size);
if(map->bucket)
{
for(i = 0; i < map->hash_size; i++)
{
list_head_init(map->bucket + i);
}
if(hf) {
map->hash_fn = hf;
} else {
map->hash_fn = default_hash_fn;
}
if(cf) {
map->compare_fn = cf;
} else {
map->compare_fn = default_compare_fn;
}
}
else
return ENOMEM;
}
else
return ENODEV;
return 0;
}
/** Cleanup hash table descriptor
@param map pointer to the hash table descriptor
*/
void hash_head_cleanup(struct hash_head *map)
{
struct list_node *p, *n;
struct hash_node *node;
int i;
if(map)
{
if(map->bucket)
{
for(i = 0; i < map->hash_size; i++)
{
list_for_each_safe(p, n, map->bucket + i)
{
node = list_entry(p, struct hash_node, node);
hash_node_cleanup(node);
}
}
free(map->bucket);
}
}
}
/** Find first node which key is equal to the key represented
@param map pointer to the hash table descriptor
@param key pointer to the hash key
@param key_size size of the hash key
@return pointer to the key found, or NULL in case of hash table have not nodes
with the key equals to the key represented
*/
struct hash_node* hash_find(struct hash_head *map, const void *key, int key_size)
{
struct list_head *list;
struct list_node *n;
struct hash_node *node;
list = map->bucket + (map->hash_fn(key, key_size) & map->hash_mask);
list_for_each(n, list)
{
node = list_entry(n, struct hash_node, node);
if(key_size == node->key_size)
{
if(map->compare_fn(key, node->key, key_size)) return node;
}
}
return NULL;
}
/** Find next node, which key is equal to the key represented
@param hn1 pointer to the previous node found.
@return pointer to the next node with the same key, or NULL in case of the current
node is the last node with appropriate key
*/
struct hash_node* hash_find_next(struct hash_head *map, struct hash_node *hn1)
{
struct list_node *n = &hn1->node;
struct hash_node *hn2;
while((n = list_next(n)) != NULL)
{
hn2 = list_entry(n, struct hash_node, node);
if(hn1->key_size == hn2->key_size)
{
if(map->compare_fn(hn1->key, hn2->key, hn2->key_size)) return hn2;
}
}
return NULL;
}
/** Add node to the hash table
@param map pointer to the hash table
@param node pointer to the node, which will be added to the table
@param key pointer to the hash key
@param key_size size of the hash key
@return zero in case of success, ENOMEM in case of error
*/
int hash_add(struct hash_head *map, struct hash_node *node, const void *key, int key_size)
{
node->key = (char*)malloc(key_size);
if(node->key)
{
memcpy(node->key, key, key_size);
node->key_size = key_size;
list_add_bottom(map->bucket + (map->hash_fn(key, key_size) & map->hash_mask), &node->node);
return 0;
}
return ENOMEM;
}
/** Remove node from the hash table
@param node pointer to the node descriptor
*/
void hash_del(struct hash_node *node)
{
list_del_init(&node->node);
hash_node_cleanup(node);
}
/** Initialize iterator for usage. Must be called before first iterator usage.
@param map pointer to the hash table descriptor
@param iter pointer to the iterator descriptor
*/
void hash_iterator_init(struct hash_head *map, struct hash_iterator *iter)
{
iter->map = map;
iter->map_idx = 0;
iter->current = NULL;
}
/** Move iterator to the first node in the table, and return pointer to the node
@param iter pointer to the iterator descriptor
@return pointer to the first node in the table, or NULL in case of error
*/
struct hash_node * hash_node_first(struct hash_iterator *iter)
{
iter->current = list_first(iter->map->bucket);
iter->map_idx = 0;
if (iter->current)
{
return list_entry(iter->current, struct hash_node, node);
}
return NULL;
}
/** Move iterator to the next node in the table and return pointer to the node
@param iter pointer to the iterator descriptor
@return pointer to the next node in the table, or NULL in case of error
*/
struct hash_node * hash_node_next(struct hash_iterator *iter)
{
if(iter->map_idx >= iter->map->hash_size) return NULL;
if(iter->current) {
iter->current = list_next(iter->current);
}
if(iter->current) {
return list_entry(iter->current, struct hash_node, node);
}
while(iter->map_idx < iter->map->hash_size) {
iter->current = list_first(iter->map->bucket + iter->map_idx);
iter->map_idx++;
if(iter->current) {
return list_entry(iter->current, struct hash_node, node);
}
}
return NULL;
}
/** Remove current node, pointed by iterator, and move iterator to the next node
@param iter pointer to the iterator descriptor
*/
void hash_node_del(struct hash_iterator *iter)
{
struct list_node *tmp;
tmp = iter->current;
hash_node_next(iter);
list_del_init(tmp);
}
/** Return total count of elements in the hash table
@param head pointer to the hash table descriptor
*/
int hash_count(struct hash_head *head)
{
int i;
int count = 0;
for(i = 0; i < head->hash_size; i++) count += list_count(head->bucket + i);
return count;
}
+94
View File
@@ -0,0 +1,94 @@
#ifndef HASH_MULTIMAP_H
#define HASH_MULTIMAP_H
#include "../src/utils/list_head.h"
#define HASH_MIN_SHIFT (8)
#define HASH_MAX_SHIFT (15)
#define hash_entry(ptr, type, member) container_of(ptr, type, member)
/** Hash multimap node descriptor
@param key hash key value
@param key_size hash key size in bytes
@param node uplink to hash multimap table
*/
struct hash_node
{
char *key;
int key_size;
struct list_node node;
};
/** Hash function prototype
@param key hash key value
@param key_size hash key size in bytes
@return hash value
*/
typedef unsigned int (* _hash_fn)(const void *key, int key_size);
/** Compare function prototype
*
* @param key1 pointer to the first key for comparison
* @param key2 pointer to the second key for comparison
* @param size size of compared part of the keys in bytes
*
* @return zero if keys equals each other, othervize 1
*/
typedef int (*_compare_fn)(const void *key1, const void *key2, int size);
/** Hash multimap table descriptor
@param hash_shift size of hash table as power of 2
@param hash_size size of the hash table
@param hash_mask mask for the hash value
@param bucket pointer to the array of linked lists. in other words, pointer to
hash multimap table
@param count count of the nodes in the hash table
*/
struct hash_head
{
unsigned char hash_shift;
unsigned short hash_size;
unsigned int hash_mask;
struct list_head *bucket;
//int count;
_hash_fn hash_fn;
_compare_fn compare_fn;
};
/** Hash table iterator
@param map pointer to the hash table descriptor, used for iteration
@param current pointer to the current list node (bucket[map_idx])
@param map_idx current bucket index
*/
struct hash_iterator
{
struct hash_head *map; // pointer to the hash map header
struct list_node *current; // pointer to current list node in the bucket[map_idx] list
unsigned int map_idx; // index of current list in the hash map bucket
};
void hash_node_init(struct hash_node *node);
void hash_node_cleanup(struct hash_node *node);
int hash_head_init(struct hash_head *map, unsigned int hash_shift = HASH_MIN_SHIFT, _hash_fn hf = 0, _compare_fn cf = 0);
void hash_head_cleanup(struct hash_head *map);
struct hash_node* hash_find(struct hash_head *map, const void *key, int key_size);
struct hash_node* hash_find_next(struct hash_head *map, struct hash_node *node);
int hash_add(struct hash_head *map, struct hash_node *node, const void *key, int key_size);
void hash_del(struct hash_node *node);
void hash_iterator_init(struct hash_head *map, struct hash_iterator *iter);
struct hash_node * hash_node_first(struct hash_iterator *iter);
struct hash_node * hash_node_next(struct hash_iterator *iter);
void hash_node_del(struct hash_iterator *iter);
int hash_count(struct hash_head *head);
#endif // HASH_MULTIMAP_H
+338
View File
@@ -0,0 +1,338 @@
/*******************************************************************************
Copyright (c) 2006, Nikolay Nosov
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
#ifndef __LIST_HEAD_H__
#define __LIST_HEAD_H__
/**
Double linked list for user space implementation
*/
#include <stddef.h> /* for NULL declaration */
/** Get offset of a member
@param TYPE the type of the struct
@param MEMBER the name of the member, which offset to be computed
*/
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
/** Casts a member of a structure out to the containing structure
@param ptr the pointer to the member.
@param type the type of the container struct this is embedded in
@param member the name of the member within the struct.
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type, member) );})
/** Get the struct for this entry
@param ptr the &struct list_head pointer.
@param type the type of the struct this is embedded in.
@param member the name of the list_struct within the struct
*/
#define list_entry(ptr, type, member) container_of(ptr, type, member)
/** Iterate over a list
@param pos the &struct list_head to use as a loop counter.
@param head the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->first; pos; \
pos = pos->next)
/**
* list_for_each_entry - iterate over list of given type
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct hlist_node to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define list_for_each_entry(tpos, pos, head, member) \
for (pos = (head)->first; \
pos && ({ tpos = list_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
/** Iterate over list of given type safe against removal of list entry
* @tpos: the type * to use as a loop cursor.
* @pos: the &struct list_node to use as a loop cursor.
* @n: another &struct list_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_node within the struct.
*/
#define list_for_each_entry_safe(tpos, pos, n, head, member) \
for (pos = (head)->first; \
pos && ({ n = pos->next; 1; }) && \
({ tpos = list_entry(pos, typeof(*tpos), member); 1;}); \
pos = n)
/** Iterate over a list backwards
@param pos the &struct list_head to use as a loop counter.
@param head the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->last; pos; \
pos = pos->prev)
/** Iterate over a list safe against removal of list entry
@param pos the &struct list_head to use as a loop counter.
@param n another &struct list_head to use as temporary storage
@param head the head for your list.
*/
#if !defined(list_for_each_safe)
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
#endif
/* to compiler be happy */
struct list_head;
/** Node of the list
@param next next node in the list
@param prev previous node in the list
@param head head of the list
*/
typedef struct list_node
{
struct list_node *next, *prev;
struct list_head *head;
} list_node;
/** Head of the list
@param first first node in the list
@param last last node in the list
@param count cont of the nodes
*/
typedef struct list_head
{
struct list_node *first, *last;
int count;
} list_head;
/**
Compile time initialization of the head struct
*/
#define LIST_HEAD_INIT { .first = NULL, .last = NULL, .count = 0 }
/**
Compile time initialization of the node struct
*/
#define LIST_NODE_INIT { .prev = NULL, .next = NULL, .head = NULL }
/** Runtime initialization of the head struct
(should be applied before first using of the list)
@param node head, which to be initialized.
*/
static inline void list_head_init(struct list_head *head)
{
head->first = head->last = NULL;
head->count = 0;
}
/** Runtime initialization of the node struct
(should be applied before first using of the node)
@param node node, which to be initialized.
*/
static inline void list_node_init(struct list_node *node)
{
node->next = node->prev = NULL;
node->head = NULL;
}
/** Add node to begin of the list
@param head the head of the list
@param node node of the list, which to be added
*/
static inline void list_add_head(struct list_head *head, struct list_node *node)
{
struct list_node *pfirst = head->first;
head->first = node;
node->next = pfirst;
node->prev = NULL;
node->head = head;
if(pfirst) pfirst->prev = node;
else head->last = node;
head->count++;
}
/** Add node to the end of the list
@param head the head of the list;
@param node node of the list, which to be added
*/
static inline void list_add_bottom(struct list_head *head, struct list_node *node)
{
struct list_node *plast = head->last;
head->last = node;
node->next = NULL;
node->prev = plast;
node->head = head;
if(plast) plast->next = node;
else head->first = node;
head->count++;
}
/** Add node in the list after given 'base' node
@param base the base node, after that node will be added
@param node node of the list, which to be added
*/
static inline void list_add_after(struct list_node *base, struct list_node *node)
{
struct list_node *pnext = base->next;
base->next = node;
node->next = pnext;
node->prev = base;
node->head = base->head;
if(pnext) pnext->prev = node;
else base->head->last = node;
base->head->count++;
}
/** Adds node in the list before given 'base' node
@param base the base node, before that node will be added
@param node node of the list, which to be added
*/
static inline void list_add_before(struct list_node *base, struct list_node *node)
{
struct list_node *pprev = base->prev;
base->prev = node;
node->next = base;
node->prev = pprev;
node->head = base->head;
if(pprev) pprev->next = node;
else base->head->first = node;
base->head->count++;
}
/** Remove node from the list
@param node the node, which to be removed
*/
static inline void list_del(struct list_node *node)
{
if(node->head)
{
if(node->prev) node->prev->next = node->next;
else node->head->first = node->next;
if(node->next) node->next->prev = node->prev;
else node->head->last = node->prev;
node->head->count--;
}
node->prev = node->next = NULL;
node->head = NULL;
}
static inline void list_del_init(struct list_node *node)
{
list_del(node);
list_node_init(node);
}
/** Count of the nodes in the list
@param head the head of the list
@return count of the nodes in the list
*/
static inline int list_count(struct list_head *head)
{
return head->count;
}
/** First element of the list
@param head the head of the list
@return pointer to first element of the list
*/
static inline struct list_node* list_first(struct list_head *head)
{
return head->first;
}
/** Last element of the list
@param head the head of the list
@return pointer to last element of the list
*/
static inline struct list_node* list_last(struct list_head *head)
{
return head->last;
}
/** Next element of the list
@param head current element of the list
@return pointer to next element relatively of current
*/
static inline struct list_node* list_next(struct list_node *node)
{
return node->next;
}
/** Previous element of the list
@param head current element of the list
@return pointer to previous element relatively of current
*/
static inline struct list_node* list_prev(struct list_node *node)
{
return node->prev;
}
#endif // __LIST_HEAD_H__
+246
View File
@@ -0,0 +1,246 @@
#include "../../src/utils/log.h"
#include <time.h>
#include <stdlib.h>
const char * const severity_map[] = {
" [DEBUG] : ",
" [INFO] : ",
" [WARNING]: ",
" [ERROR] : "
};
void format_string(pst_log* log, const char* fmt, va_list args)
{
log->mStringLen += vsnprintf(log->mString + log->mStringLen, log->mStringSize - log->mStringLen, fmt, args);
}
void format_prefix(pst_log* log, SC_LogSeverity severity)
{
time_t rawTime;
struct tm * timeinfo;
time(&rawTime);
timeinfo = gmtime(&rawTime);
log->mStringLen += strftime(log->mString, sizeof(log->mString), "%d-%m-%Y %H:%M:%S", timeinfo);
strncpy(log->mString + log->mStringLen, severity_map[(int)severity], strlen(severity_map[(int)severity]));
log->mStringLen += strlen(severity_map[(int)severity]);
}
void format_postfix(pst_log* log)
{
if((log->mStringLen + 2) < log->mStringSize) {
log->mString[log->mStringLen++] = '\n';
log->mString[log->mStringLen++] = 0;
}
}
//variable argument number logging
void log(pst_log* log, SC_LogSeverity severity, const char* fmt, ...)
{
if (severity < log->mCurrentSeverity)
return;
pthread_mutex_lock(&log->mLock);
if(!log->is_opened(log)) {
log->open(log);
}
log->mStringLen = 0;
format_prefix(log, severity);
va_list args;
va_start(args, fmt);
format_string(log, fmt, args);
va_end(args);
format_postfix(log);
log->send_message(log, severity);
pthread_mutex_unlock(&log->mLock);
}
void log_init_base(pst_log* plog, const char* source)
{
// methods
plog->log = log;
// fields
plog->mpSource = 0;
if(source) {
plog->mpSource = strdup(source);
}
plog->mString[0] = 0;
plog->mStringSize = sizeof(plog->mString);
plog->mStringLen = 0;
plog->mCurrentSeverity = SEVERITY_DEBUG;
pthread_mutex_init(&plog->mLock, NULL);
plog->child = 0;
}
void pst_log_fini(pst_log* log)
{
pthread_mutex_destroy(&log->mLock);
log->close(log);
if(log->mpSource) {
free(log->mpSource);
log->mpSource = 0;
}
}
//
// Console logger implementation
//
#define RED "\e[0;31m"
#define GREEN "\e[0;32m"
#define YELLOW "\e[1;33m"
#define NC "\e[0m" // No Color
//actually sends message to the source
void send_msg_console(pst_log* log, SC_LogSeverity severity)
{
const char* color = NC;
switch(severity) {
case SEVERITY_DEBUG:
color = NC;
break;
case SEVERITY_ERROR:
color = RED;
break;
case SEVERITY_INFO:
color = GREEN;
break;
case SEVERITY_WARNING:
color = YELLOW;
break;
default:
color = NC;
break;
}
fprintf(stderr, "%s%s%s", color, log->mString, NC);
}
void close_console(pst_log* log) {
// do nothing
}
bool open_console(pst_log* log) {
return true;
}
bool is_opened_console(pst_log* log) {
return true;
}
void pst_log_init_console(pst_log* log)
{
log_init_base(log, NULL);
log->close = close_console;
log->open = open_console;
log->is_opened = is_opened_console;
log->send_message = send_msg_console;
}
//
// Log to file
//
typedef struct _file_spec {
FILE* fd;
uint64_t max_bytes;
uint64_t num_bytes;
char* dir;
char* fname;
} file_spec;
//open file for writing
bool open_file(pst_log* log)
{
file_spec* fsp = (file_spec*)log->child;
if(!fsp) {
fprintf(stderr, "Wrong initialization of File-based logger\n");
return false;
}
if(!fsp->fd) {
// extract log and path names
uint32_t namelen = strlen(log->mpSource);
if(namelen) {
const char* ptr = strrchr(log->mpSource, '/');
uint32_t pathlen = 0;
if(ptr) {
pathlen = ptr - log->mpSource + 1;
namelen -= pathlen + 1;
}
fsp->dir = strndup(log->mpSource, pathlen);
fsp->fname = strndup(log->mpSource + pathlen, namelen);
//open the file finally
fsp->fd = fopen(log->mpSource, "a+");
}
}
return (fsp->fd != 0);
}
void close_file(pst_log* log)
{
file_spec* fsp = (file_spec*)log->child;
if(!fsp) {
fprintf(stderr, "Wrong initialization of File-based logger while close\n");
return;
}
if(fsp->fd) {
fclose(fsp->fd);
}
fsp->fd = 0;
fsp->max_bytes = 0;
}
bool is_file_opened(pst_log* log)
{
file_spec* fsp = (file_spec*)log->child;
if(!fsp) {
fprintf(stderr, "Wrong initialization of File-based logger while check open\n");
return false;
}
return (fsp->fd != 0);
}
void send_msg_file(pst_log* log, SC_LogSeverity severity)
{
file_spec* fsp = (file_spec*)log->child;
if(!fsp) {
fprintf(stderr, "Wrong initialization of File-based logger while check send message\n");
return;
}
if(fsp->fd && fsp->num_bytes < fsp->max_bytes) {
if(fputs(log->mString, fsp->fd) >= 0) {
fflush(fsp->fd);
fsp->num_bytes += log->mStringLen;
} else {
fclose(fsp->fd);
fsp->fd = 0;
}
}
}
void pst_log_init_file(pst_log* log, const char* path, uint64_t max_bytes)
{
log_init_base(log, path);
file_spec* fsp = (file_spec*)malloc(sizeof(file_spec));
fsp->fd = 0;
fsp->max_bytes = max_bytes;
fsp->num_bytes = 0;
log->child = fsp;
log->close = close_file;
log->open = open_file;
log->is_opened = is_file_opened;
log->send_message = send_msg_file;
}
+50
View File
@@ -0,0 +1,50 @@
#ifndef PST_LOG_H_
#define PST_LOG_H_
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <limits.h>
// Log message severity
enum SC_LogSeverity {
SEVERITY_DEBUG = 0,
SEVERITY_INFO,
SEVERITY_WARNING,
SEVERITY_ERROR,
//----------------
SEVERITY_MAX
};
#define LOG_BUFF_SIZE (1024*1024)
typedef struct __pst_log pst_log;
typedef struct __pst_log {
// methods
void (*close) (pst_log* log);
bool (*open) (pst_log* log);
bool (*is_opened) (pst_log* log);
void (*log) (pst_log* log, SC_LogSeverity severity, const char* fmt, ...);
void (*send_message) (pst_log* log, SC_LogSeverity severity);
// fields
char* mpSource; // source for store log messages (file, IP:port or something else)
char mString[LOG_BUFF_SIZE]; // formatted string to be printed
uint32_t mStringSize; // size of the 'mString' buffer
uint32_t mStringLen; // length of the formatted string
SC_LogSeverity mCurrentSeverity; // maximum severity value to be logged
pthread_mutex_t mLock;
void* child;
} pst_log;
void pst_log_init_console(pst_log* log);
void pst_log_init_file(pst_log* log, const char* path, uint64_t max_bytes = 64 * 1024 * 1024/* 64Mb */);
void pst_log_fini(pst_log* log);
#endif /* PST_LOG_H_ */