360 lines
10 KiB
C++
360 lines
10 KiB
C++
/*
|
|
* 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 "logger/log.h"
|
|
#include "common.h"
|
|
#include "dwarf_operations.h"
|
|
|
|
extern SC_LogBase* logger;
|
|
|
|
bool dwarf_value::get_int(int64_t& v)
|
|
{
|
|
switch (size) {
|
|
case 1:
|
|
v = *((int8_t*)value);
|
|
break;
|
|
case 2:
|
|
v = *((int16_t*)value);
|
|
break;
|
|
case 4:
|
|
v = *((int32_t*)value);
|
|
break;
|
|
case 8:
|
|
v = *((int64_t*)value);
|
|
break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool dwarf_value::get_uint(uint64_t& v)
|
|
{
|
|
if(type & DWARF_TYPE_SIGNED) {
|
|
int64_t sig;
|
|
if(!get_int(sig)) {
|
|
return false;
|
|
}
|
|
v = llabs(sig);
|
|
} else {
|
|
return get_generic(v);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
__dwarf_value& dwarf_value::operator+=(const __dwarf_value& rhs)
|
|
{
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool dwarf_value::get_generic(uint64_t& v)
|
|
{
|
|
switch (size) {
|
|
case 1:
|
|
v = *((uint8_t*)value);
|
|
break;
|
|
case 2:
|
|
v = *((uint16_t*)value);
|
|
break;
|
|
case 4:
|
|
v = *((uint32_t*)value);
|
|
break;
|
|
case 8:
|
|
v = *((uint64_t*)value);
|
|
break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool __pst_context::print(const char* fmt, ...)
|
|
{
|
|
bool nret = true;
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
int size = sizeof(buff) - offset;
|
|
int ret = vsnprintf(buff + offset, size, fmt, args);
|
|
if(ret >= size || ret < 0) {
|
|
nret = false;
|
|
}
|
|
offset += ret;
|
|
va_end(args);
|
|
|
|
return nret;
|
|
}
|
|
|
|
void __pst_context::log(SC_LogSeverity severity, const char* fmt, ...)
|
|
{
|
|
uint32_t str_len = 0;
|
|
char str[PATH_MAX]; str[0] = 0;
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
str_len += vsnprintf(str + str_len, sizeof(str) - str_len, fmt, args);
|
|
va_end(args);
|
|
|
|
logger->Log(severity, "%s", str);
|
|
}
|
|
|
|
uint32_t __pst_context::print_expr_block (Dwarf_Op *exprs, int len, char* buff, uint32_t buff_size, Dwarf_Attribute* attr)
|
|
{
|
|
uint32_t offset = 0;
|
|
for (int i = 0; i < len; i++) {
|
|
//printf ("%s", (i + 1 < len ? ", " : ""));
|
|
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(&cursor, regno, &ptr);
|
|
//ptr += off;
|
|
|
|
offset += snprintf(buff + offset, buff_size - offset, "%s(*%s%s%d) reg_value: 0x%lX ", map->op_name, unw_regname(regno), off >=0 ? "+" : "", off, ptr);
|
|
} else 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(&cursor, regno, &value);
|
|
offset += snprintf(buff + offset, buff_size - offset, "%s(*%s) value: 0x%lX", map->op_name, unw_regname(regno), value);
|
|
} else if(map->op_num == DW_OP_GNU_entry_value) {
|
|
uint32_t value = decode_uleb128((unsigned char*)&exprs[i].number);
|
|
offset += snprintf(buff + offset, buff_size - offset, "%s(%u, ", map->op_name, value);
|
|
Dwarf_Attribute attr_mem;
|
|
if(!dwarf_getlocation_attr(attr, exprs, &attr_mem)) {
|
|
Dwarf_Op *expr;
|
|
size_t exprlen;
|
|
if (dwarf_getlocation(&attr_mem, &expr, &exprlen) == 0) {
|
|
offset += print_expr_block (expr, exprlen, buff + offset, buff_size - offset, &attr_mem);
|
|
offset += snprintf(buff + offset, buff_size - offset, ") ");
|
|
} else {
|
|
log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr location");
|
|
}
|
|
} else {
|
|
log(SEVERITY_ERROR, "Failed to get DW_OP_GNU_entry_value attr expression");
|
|
}
|
|
} else if(map->op_num == DW_OP_stack_value) {
|
|
offset += snprintf(buff + offset, buff_size - offset, "%s", map->op_name);
|
|
} else if(map->op_num == DW_OP_plus_uconst) {
|
|
uint32_t value = decode_uleb128((unsigned char*)&exprs[i].number);
|
|
offset += snprintf(buff + offset, buff_size - offset, "%s(+%u) ", map->op_name, value);
|
|
} else if(map->op_num == DW_OP_bregx) {
|
|
uint32_t regno = decode_uleb128((unsigned char*)&exprs[i].number);
|
|
int32_t off = decode_sleb128((unsigned char*)&exprs[i].number2);
|
|
|
|
unw_word_t ptr = 0;
|
|
unw_get_reg(&cursor, regno, &ptr);
|
|
//ptr += off;
|
|
|
|
offset += snprintf(buff + offset, buff_size - offset, "%s(%s%s%d) reg_value = 0x%lX", map->op_name, unw_regname(regno), off >= 0 ? "+" : "", off, ptr);
|
|
} else 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(&cursor, reg, &value);
|
|
|
|
offset += snprintf(buff + offset, buff_size - offset, "%s(%s) value = 0x%lX", map->op_name, unw_regname(reg), value);
|
|
} else if(map->op_num == DW_OP_addr) {
|
|
offset += snprintf(buff + offset, buff_size - offset, "%s value = 0x%X", map->op_name, *((uint32_t*)exprs[i].number));
|
|
} else {
|
|
offset += snprintf(buff + offset, buff_size - offset, "%s(0x%lX, 0x%lx) ", map->op_name, exprs[i].number, exprs[i].number2);
|
|
}
|
|
} else {
|
|
offset += snprintf(buff + offset, buff_size - offset, "0x%hhX(0x%lX, 0x%lx) ", exprs[i].atom, exprs[i].number, exprs[i].number2);
|
|
}
|
|
// if(exprs[i].atom >= DW_OP_reg0 && exprs[i].atom <= DW_OP_bregx) {
|
|
// print_framereg(exprs[i].atom);
|
|
// }DW_OP_addr
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
bool __pst_context::calc_expression(Dwarf_Op *exprs, int expr_len, Dwarf_Attribute* attr)
|
|
{
|
|
stack.clear();
|
|
for (int i = 0; i < expr_len; i++) {
|
|
const dwarf_op_map* map = find_op_map(exprs[i].atom);
|
|
if(!map) {
|
|
log(SEVERITY_ERROR, "Unknown operation type 0x%hhX(0x%lX, 0x%lX)", exprs[i].atom, exprs[i].number, exprs[i].number2);
|
|
return false;
|
|
}
|
|
|
|
dwarf_value* v = stack.get();
|
|
if(v && v->type == DWARF_TYPE_REGISTER_LOC) {
|
|
// dereference register location
|
|
unw_word_t value = 0;
|
|
uint64_t regno = *((uint64_t*)v->value);
|
|
if(unw_get_reg(&cursor, regno, &value)) {
|
|
log(SEVERITY_ERROR, "Failed to ger value of register 0x%lX", regno);
|
|
return false;
|
|
}
|
|
v->replace(&value, sizeof(value), DWARF_TYPE_GENERIC);
|
|
}
|
|
|
|
if(!map->operation(this, map, exprs[i].number, exprs[i].number2)) {
|
|
log(SEVERITY_ERROR, "Failed to calculate %s(0x%lX, 0x%lX) operation", map->op_name, exprs[i].number, exprs[i].number2);
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
if(stack.Size()) {
|
|
unw_word_t value = 0;
|
|
dwarf_value* v = stack.get();
|
|
v->get_uint(value);
|
|
if(v->type & DWARF_TYPE_REGISTER_LOC) {
|
|
// dereference register location
|
|
uint64_t regno;
|
|
v->get_uint(regno);
|
|
if(unw_get_reg(&cursor, regno, &value)) {
|
|
log(SEVERITY_ERROR, "Failed to get value of register 0x%lX", regno);
|
|
return false;
|
|
}
|
|
}
|
|
log(SEVERITY_INFO, "Found 0x%X value of location expression", value);
|
|
return true;
|
|
} else {
|
|
log(SEVERITY_ERROR, "No value found for location expression");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|