Source/PLCrashAsyncDwarfCFAState.cpp (160 lines of code) (raw):
/*
* Copyright (c) 2013 Plausible Labs Cooperative, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "PLCrashAsyncDwarfCFAState.hpp"
#include "PLCrashFeatureConfig.h"
#include "PLCrashMacros.h"
#if PLCRASH_FEATURE_UNWIND_DWARF
using namespace plcrash::async;
/**
* @internal
* @ingroup plcrash_async_dwarf
* @defgroup plcrash_async_dwarf_cfa_state DWARF CFA Register State
* @{
*/
/**
* Push a state onto the state stack; all existing values will be saved on the stack, and registers
* will be set to their default state.
*
* @return Returns true on success, or false if insufficient space is available on the state
* stack.
*/
template <typename machine_ptr, typename machine_ptr_s>
bool dwarf_cfa_state<machine_ptr, machine_ptr_s>::push_state (void) {
PLCF_ASSERT(_table_depth+1 <= DWARF_CFA_STATE_MAX_STATES);
if (_table_depth+1 == DWARF_CFA_STATE_MAX_STATES)
return false;
_table_depth++;
_register_count[_table_depth] = 0;
_cfa_value[_table_depth].set_undefined_rule();
plcrash_async_memset(_table_stack[_table_depth], DWARF_CFA_STATE_INVALID_ENTRY_IDX, sizeof(_table_stack[0]));
return true;
}
/**
* Pop a previously saved state from the state stack. All existing values will be discarded on the stack, and registers
* will be reinitialized from the saved state.
*
* @return Returns true on success, or false if no states are available on the state stack.
*/
template <typename machine_ptr, typename machine_ptr_s>
bool dwarf_cfa_state<machine_ptr, machine_ptr_s>::pop_state (void) {
if (_table_depth == 0)
return false;
_table_depth--;
return true;
}
/*
* Default constructor.
*/
template <typename machine_ptr, typename machine_ptr_s>
dwarf_cfa_state<machine_ptr, machine_ptr_s>::dwarf_cfa_state (void) {
/* The size must be smaller than the invalid entry index, which is used as a NULL flag */
PLCR_ASSERT_STATIC(max_size, DWARF_CFA_STATE_MAX_REGISTERS < DWARF_CFA_STATE_INVALID_ENTRY_IDX);
/* Initialize the free list */
for (uint8_t i = 0; i < DWARF_CFA_STATE_MAX_REGISTERS; i++)
_entries[i].next = i+1;
/* Set the terminator flag on the last entry */
_entries[DWARF_CFA_STATE_MAX_REGISTERS-1].next = DWARF_CFA_STATE_INVALID_ENTRY_IDX;
/* First free entry is _entries[0] */
_free_list = 0;
/* Initial register count */
_register_count[0] = 0;
/* Set up the table */
_table_depth = 0;
plcrash_async_memset(_table_stack[0], DWARF_CFA_STATE_INVALID_ENTRY_IDX, sizeof(_table_stack[0]));
/* Default CFA */
_cfa_value[0].set_undefined_rule();
}
/**
* Add a new register.
*
* @param regnum The DWARF register number.
* @param rule The DWARF CFA rule for @a regnum.
* @param value The data value to be used when interpreting @a rule. May either be signed or unsigned.
*/
template <typename machine_ptr, typename machine_ptr_s>
bool dwarf_cfa_state<machine_ptr, machine_ptr_s>::set_register (dwarf_cfa_state_regnum_t regnum, plcrash_dwarf_cfa_reg_rule_t rule, machine_ptr value) {
/* Check for an existing entry, or find the target entry off which we'll chain our entry */
unsigned int bucket = regnum % (sizeof(_table_stack[0]) / sizeof(_table_stack[0][0]));
dwarf_cfa_reg_entry_t *parent = NULL;
for (uint8_t parent_idx = _table_stack[_table_depth][bucket]; parent_idx != DWARF_CFA_STATE_INVALID_ENTRY_IDX; parent_idx = parent->next) {
parent = &_entries[parent_idx];
/* If an existing entry is found, we can re-use it directly */
if (parent->regnum == regnum) {
parent->value = value;
parent->rule = rule;
return true;
}
/* Otherwise, make sure we terminate with parent == last element */
if (parent->next == DWARF_CFA_STATE_INVALID_ENTRY_IDX)
break;
}
/* 'parent' now either points to the end of the list, or is NULL (in which case the table
* slot was empty */
dwarf_cfa_reg_entry *entry = NULL;
uint8_t entry_idx;
/* Fetch a free entry */
if (_free_list == DWARF_CFA_STATE_INVALID_ENTRY_IDX) {
/* No free entries */
return false;
}
entry_idx = _free_list;
entry = &_entries[entry_idx];
_free_list = entry->next;
/* Intialize the entry */
entry->regnum = regnum;
entry->rule = rule;
entry->value = value;
entry->next = DWARF_CFA_STATE_INVALID_ENTRY_IDX;
/* Either insert in the parent, or insert as the first table element */
if (parent == NULL) {
_table_stack[_table_depth][bucket] = entry_idx;
} else {
parent->next = entry - _entries;
}
_register_count[_table_depth]++;
return true;
}
/**
* Fetch the register entry data for a given DWARF register number, returning
* true on success, or false if no entry has been added for the register.
*
* @param regnum The DWARF register number.
* @param[out] rule On success, the DWARF CFA rule for @a regnum.
* @param[out] value On success, the data value to be used when interpreting @a rule.
*/
template <typename machine_ptr, typename machine_ptr_s>
bool dwarf_cfa_state<machine_ptr, machine_ptr_s>::get_register_rule (dwarf_cfa_state_regnum_t regnum, plcrash_dwarf_cfa_reg_rule_t *rule, machine_ptr *value) {
/* Search for the entry */
unsigned int bucket = regnum % (sizeof(_table_stack[0]) / sizeof(_table_stack[0][0]));
dwarf_cfa_reg_entry_t *entry = NULL;
for (uint8_t entry_idx = _table_stack[_table_depth][bucket]; entry_idx != DWARF_CFA_STATE_INVALID_ENTRY_IDX; entry_idx = entry->next) {
entry = &_entries[entry_idx];
if (entry->regnum != regnum) {
if (entry->next == DWARF_CFA_STATE_INVALID_ENTRY_IDX)
break;
continue;
}
/* Existing entry found, we can re-use it directly */
*value = (machine_ptr)entry->value;
*rule = (plcrash_dwarf_cfa_reg_rule_t) entry->rule;
return true;
}
/* Not found? */
return false;
}
/**
* Remove a register from the current state.
*
* @param regnum The DWARF register number to be removed.
*/
template <typename machine_ptr, typename machine_ptr_s>
void dwarf_cfa_state<machine_ptr, machine_ptr_s>::remove_register (dwarf_cfa_state_regnum_t regnum) {
/* Search for the entry */
unsigned int bucket = regnum % (sizeof(_table_stack[0]) / sizeof(_table_stack[0][0]));
dwarf_cfa_reg_entry *prev = NULL;
dwarf_cfa_reg_entry_t *entry = NULL;
for (uint8_t entry_idx = _table_stack[_table_depth][bucket]; entry_idx != DWARF_CFA_STATE_INVALID_ENTRY_IDX; entry_idx = entry->next) {
prev = entry;
entry = &_entries[entry_idx];
if (entry->regnum != regnum)
continue;
/* Remove from the bucket chain */
if (prev != NULL) {
prev->next = entry->next;
} else {
_table_stack[_table_depth][bucket] = entry->next;
}
/* Re-insert in the free list */
entry->next = _free_list;
_free_list = entry_idx;
/* Decrement the register count */
_register_count[_table_depth]--;
}
}
/**
* Return the number of register rules set for the current register state.
*/
template <typename machine_ptr, typename machine_ptr_s>
uint8_t dwarf_cfa_state<machine_ptr, machine_ptr_s>::get_register_count (void) {
return _register_count[_table_depth];
}
/**
* Set a register-based DWARF_CFA_STATE_CFA_TYPE_REGISTER rule.
*
* @param regnum The base register for the canonical frame address.
* @param offset The unsigned offset.
*/
template <typename machine_ptr, typename machine_ptr_s>
void dwarf_cfa_state<machine_ptr, machine_ptr_s>::set_cfa_register (dwarf_cfa_state_regnum_t regnum, machine_ptr offset) {
_cfa_value[_table_depth].set_register_rule(regnum, offset);
}
/**
* Set a register-based DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED rule.
*
* @param regnum The base register for the canonical frame address.
* @param offset The unsigned offset.
*/
template <typename machine_ptr, typename machine_ptr_s>
void dwarf_cfa_state<machine_ptr, machine_ptr_s>::set_cfa_register_signed (dwarf_cfa_state_regnum_t regnum, machine_ptr_s offset) {
_cfa_value[_table_depth].set_register_rule_signed(regnum, offset);
}
/**
* Set an expression-based canonical frame address rule.
*
* @param address Target-relative address of the expression opcode stream.
* @param length Length in bytes of the opcode stream.
*/
template <typename machine_ptr, typename machine_ptr_s>
void dwarf_cfa_state<machine_ptr, machine_ptr_s>::set_cfa_expression (pl_vm_address_t address, pl_vm_size_t length) {
_cfa_value[_table_depth].set_expression_rule(address, length);
}
/**
* Return the canonical frame address rule defined for the current state.
*/
template <typename machine_ptr, typename machine_ptr_s>
dwarf_cfa_rule<machine_ptr, machine_ptr_s> dwarf_cfa_state<machine_ptr, machine_ptr_s>::get_cfa_rule (void) {
return _cfa_value[_table_depth];
}
/**
* Construct an iterator for @a stack. The @a stack <em>must not</em> be mutated
* during iteration.
*
* @param stack The stack to be iterated.
*/
template <typename machine_ptr, typename machine_ptr_s>
dwarf_cfa_state_iterator<machine_ptr, machine_ptr_s>::dwarf_cfa_state_iterator(dwarf_cfa_state<machine_ptr, machine_ptr_s> *stack) {
_stack = stack;
_bucket_idx = 0;
_cur_entry_idx = DWARF_CFA_STATE_INVALID_ENTRY_IDX;
}
/**
* Enumerate the next register entry. Returns true on success, or false if no additional entries are available.
*
* @param[out] regnum On success, the DWARF register number.
* @param[out] rule On success, the DWARF CFA rule for @a regnum.
* @param[out] value On success, the data value to be used when interpreting @a rule.
*/
template <typename machine_ptr, typename machine_ptr_s>
bool dwarf_cfa_state_iterator<machine_ptr, machine_ptr_s>::next (dwarf_cfa_state_regnum_t *regnum, plcrash_dwarf_cfa_reg_rule_t *rule, machine_ptr *value) {
/* Fetch the next entry in the bucket chain */
if (_cur_entry_idx != DWARF_CFA_STATE_INVALID_ENTRY_IDX) {
_cur_entry_idx = _stack->_entries[_cur_entry_idx].next;
/* Advance to the next bucket if we've reached the end of the current chain */
if (_cur_entry_idx == DWARF_CFA_STATE_INVALID_ENTRY_IDX)
_bucket_idx++;
}
/*
* On the first iteration, or after the end of a bucket chain has been reached, find the next valid bucket chain.
* Otherwise, we have a valid bucket chain and simply need the next entry.
*/
if (_cur_entry_idx == DWARF_CFA_STATE_INVALID_ENTRY_IDX) {
for (; _bucket_idx < DWARF_CFA_STATE_BUCKET_COUNT; _bucket_idx++) {
if (_stack->_table_stack[_stack->_table_depth][_bucket_idx] != DWARF_CFA_STATE_INVALID_ENTRY_IDX) {
_cur_entry_idx = _stack->_table_stack[_stack->_table_depth][_bucket_idx];
break;
}
}
/* If we get here without a valid entry, we've hit the end of all bucket chains. */
if (_cur_entry_idx == DWARF_CFA_STATE_INVALID_ENTRY_IDX)
return false;
}
typename dwarf_cfa_state<machine_ptr, machine_ptr_s>::dwarf_cfa_reg_entry_t *entry = &_stack->_entries[_cur_entry_idx];
*regnum = entry->regnum;
*value = (machine_ptr)entry->value;
*rule = (plcrash_dwarf_cfa_reg_rule_t) entry->rule;
return true;
}
/* Provide explicit 32/64-bit instantiations */
template class plcrash::async::dwarf_cfa_state<uint32_t, int32_t>;
template class plcrash::async::dwarf_cfa_state_iterator<uint32_t, int32_t>;
template class plcrash::async::dwarf_cfa_state<uint64_t, int64_t>;
template class plcrash::async::dwarf_cfa_state_iterator<uint64_t, int64_t>;
/*
* @}
*/
#endif /* PLCRASH_FEATURE_UNWIND_DWARF */