Source/PLCrashAsyncDwarfCFAState.hpp (142 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.
*/
#ifndef PLCRASH_ASYNC_DWARF_CFA_STATE_H
#define PLCRASH_ASYNC_DWARF_CFA_STATE_H 1
#include <cstddef>
#include <stdint.h>
#include "PLCrashAsync.h"
#include "PLCrashAsyncDwarfFDE.hpp"
#include "PLCrashAsyncDwarfCIE.hpp"
#include "PLCrashAsyncDwarfPrimitives.hpp"
#include "PLCrashFeatureConfig.h"
#include "PLCrashMacros.h"
#if PLCRASH_FEATURE_UNWIND_DWARF
/**
* @internal
* @ingroup plcrash_async_dwarf_cfa_state
* @{
*/
PLCR_CPP_BEGIN_NS
namespace async {
/* Maximum DWARF register number supported by dwarf_cfa_state and dwarf_cfa_state_regnum_t. */
#define DWARF_CFA_STATE_REGNUM_MAX UINT32_MAX
/* Consumes around 1.65k (on 32-bit and 64-bit systems). */
#define DWARF_CFA_STATE_MAX_REGISTERS 100
template <typename machine_ptr, typename machine_ptr_s> class dwarf_cfa_state_iterator;
/** DWARF CFA-defined register number .*/
typedef uint32_t dwarf_cfa_state_regnum_t;
/**
* Canonical Frame Address type, as defined in the DWARF 4 specification,
* section 6.4.2.2, CFA Definition Instructions.
*/
typedef enum {
/** CFA is undefined. */
DWARF_CFA_STATE_CFA_TYPE_UNDEFINED = 0,
/** CFA is defined by a DWARF expression. */
DWARF_CFA_STATE_CFA_TYPE_EXPRESSION = 1,
/** CFA is defined by a register value + unsigned offset. */
DWARF_CFA_STATE_CFA_TYPE_REGISTER = 2,
/** CFA is defined by a register value + signed offset. */
DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED = 3
} dwarf_cfa_state_cfa_type_t;
/**
* @internal
* A CFA value rule. The rule is interpreted to derive the Canonical Frame Address, as defined in
* DWARF section 6.4.2.2.
*
* The canonical frame address is generally defined to be the value of the stack pointer at the
* call site in the previous frame, which may in fact differ from its value on entry to the current
* frame.
*
* The CFA rule may be used to derive the canonical frame address from the current frame's thread state,
* by either:
* - Evaluating a DWARF expression to generate the frame address, or an indirect pointer to the
* frame address value.
* - Directly appying an offset to the value of the given register in the current thread state. This
* requires either treating the available offset as a signed or unsigned value, depending
* on the specific CFA type.
*
* The invariants for applying a DWARF CFA rule are defined in the @a dwarf_cfa_state_cfa_type_t documentation,
* as well as the relevant DWARF specification sections listed above.
*/
template <typename machine_ptr, typename machine_ptr_s> class dwarf_cfa_rule {
private:
/** The CFA type */
dwarf_cfa_state_cfa_type_t _cfa_type;
union {
/**
* CFA register value. (CFA = register + offset). Valid if type is DWARF_CFA_STATE_CFA_TYPE_REGISTER(_SIGNED).
*/
struct {
/** CFA register */
dwarf_cfa_state_regnum_t regnum;
/** CFA register offset. */
union {
/** The unsigned offset to be used with DWARF_CFA_STATE_CFA_TYPE_REGISTER rules. */
machine_ptr u_off;
/** The signed offset to be used with DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED rules. */
machine_ptr_s s_off;
} offset;
} reg;
/** CFA expression (CFA = expression). Valid if type is DWARF_CFA_STATE_CFA_TYPE_EXPRESSION. */
struct {
/**
* Target-relative absolute address of the expression opcode stream.
*/
pl_vm_address_t address;
/**
* Total length of the opcode stream, in bytes.
*/
pl_vm_size_t length;
} expression;
} _cfa_data;
public:
/**
* Configure the target as a DWARF_CFA_STATE_CFA_TYPE_REGISTER register rule.
*
* @param regnum The DWARF register number.
* @param offset The unsigned register offset.
*/
void set_register_rule (dwarf_cfa_state_regnum_t regnum, machine_ptr offset) {
_cfa_type = DWARF_CFA_STATE_CFA_TYPE_REGISTER;
_cfa_data.reg.regnum = regnum;
_cfa_data.reg.offset.u_off = offset;
}
/**
* Configure the target as a DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED register rule.
*
* @param regnum The DWARF register number.
* @param offset The signed register offset.
*/
void set_register_rule_signed (dwarf_cfa_state_regnum_t regnum, machine_ptr offset) {
_cfa_type = DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED;
_cfa_data.reg.regnum = regnum;
_cfa_data.reg.offset.s_off = offset;
}
/**
* Configure the target as a DWARF_CFA_STATE_CFA_TYPE_EXPRESSION rule.
*
* @param address Target-relative absolute address of the expression opcode stream.
* @param length Total length of the opcode stream, in bytes.
*/
void set_expression_rule (pl_vm_address_t address, pl_vm_address_t length) {
_cfa_type = DWARF_CFA_STATE_CFA_TYPE_EXPRESSION;
_cfa_data.expression.address = address;
_cfa_data.expression.length = length;
}
/**
* Configure the target as a DWARF_CFA_STATE_CFA_TYPE_UNDEFINED rule.
*/
void set_undefined_rule (void) {
_cfa_type = DWARF_CFA_STATE_CFA_TYPE_UNDEFINED;
}
/**
* Return the CFA rule type.
*/
dwarf_cfa_state_cfa_type_t type (void) {
return _cfa_type;
}
/**
* Return the register number for this rule. This method is only applicable to and may only be called on
* DWARF_CFA_STATE_CFA_TYPE_REGISTER and DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED rules.
*/
dwarf_cfa_state_regnum_t register_number (void) {
PLCF_ASSERT(_cfa_type == DWARF_CFA_STATE_CFA_TYPE_REGISTER || _cfa_type == DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED);
return _cfa_data.reg.regnum;
}
/**
* Return the unsigned register offset for this rule. This method is only applicable to and may only be called on
* DWARF_CFA_STATE_CFA_TYPE_REGISTER rules.
*/
machine_ptr register_offset (void) {
PLCF_ASSERT(_cfa_type == DWARF_CFA_STATE_CFA_TYPE_REGISTER);
return _cfa_data.reg.offset.u_off;
}
/**
* Return the signed register offset for this rule. This method is only applicable to and may only be called on
* DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED rules.
*/
machine_ptr_s register_offset_signed (void) {
PLCF_ASSERT(_cfa_type == DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED);
return _cfa_data.reg.offset.s_off;
}
/**
* Return the target-relative absolute address of the expression opcode stream for this rule. This method is only applicable to and may only be called on
* DWARF_CFA_STATE_CFA_TYPE_EXPRESSION rules.
*/
pl_vm_address_t expression_address (void) {
PLCF_ASSERT(_cfa_type == DWARF_CFA_STATE_CFA_TYPE_EXPRESSION);
return _cfa_data.expression.address;
}
/**
* Rethrn the otal length of the opcode stream, in bytes, for this rule. This method is only applicable to and may only be called on
* DWARF_CFA_STATE_CFA_TYPE_EXPRESSION rules.
*/
pl_vm_size_t expression_length (void) {
PLCF_ASSERT(_cfa_type == DWARF_CFA_STATE_CFA_TYPE_EXPRESSION);
return _cfa_data.expression.length;
}
};
/**
* @internal
*
* Manages CFA register table row, using sparsely allocated register column entries. The class represents
* a single address-based row within the CFA register table, and supports applying deltas to the row
* register state as required for evaluation of a CFA opcode stream.
*
* Register numbers are sparsely allocated in the architecture-specific extensions to the DWARF spec,
* requiring a solution other than allocating arrays large enough to hold the largest possible register number.
* For example, ARM allocates or has set aside register values up to 8192, with 8192–16383 reserved for additional
* vendor co-processor allocations.
*
* The actual total number of supported, active registers is much smaller. This class is built to decrease the
* total amount of fixed stack space to be allocated.
*
* @todo If we introduce our own async-safe heap allocator, it may be preferrable to use the heap for entries.
*/
template <typename machine_ptr, typename machine_ptr_s>
class dwarf_cfa_state {
private:
/* Private configuration defines */
#define DWARF_CFA_STATE_MAX_STATES 6
#define DWARF_CFA_STATE_BUCKET_COUNT 14
#define DWARF_CFA_STATE_INVALID_ENTRY_IDX UINT8_MAX
/** A single register entry */
typedef struct dwarf_cfa_reg_entry {
/**
* Associated rule value. Must be cast to a uint64_t value when evalating PLCRASH_DWARF_CFA_REG_RULE_EXPRESSION and
* PLCRASH_DWARF_CFA_REG_RULE_VAL_EXPRESSION rules.
*/
int64_t value;
/** The DWARF register number */
dwarf_cfa_state_regnum_t regnum;
/** DWARF register rule */
plcrash_dwarf_cfa_reg_rule_t rule;
/** Next entry in the list, or NULL */
uint8_t next;
} dwarf_cfa_reg_entry_t;
/** Current call frame value configuration. */
dwarf_cfa_rule<machine_ptr,machine_ptr_s> _cfa_value[DWARF_CFA_STATE_MAX_STATES];
/** Current number of defined register entries */
uint8_t _register_count[DWARF_CFA_STATE_MAX_STATES];
/**
* Active entry lookup table. Maps from regnum to a table index. Most architectures
* define <20 valid register numbers.
*
* This provides a maximum of MAX_STATES saved states (DW_CFA_remember_state), with BUCKET_COUNT register
* buckets available in each stack entry. Each bucket may hold multiple register entries; the
* maximum number of register entries depends on the configured size of _entries.
*
* The pre-allocated entry set is shared between each saved state, as to decrease total
* memory cost of unused states.
*/
uint8_t _table_stack[DWARF_CFA_STATE_MAX_STATES][DWARF_CFA_STATE_BUCKET_COUNT];
/** Current position in the table stack */
uint8_t _table_depth;
/** Free list of entries. These are unused records from the entries table. */
uint8_t _free_list;
/**
* Statically allocated set of entries; these will be inserted into the free
* list upon construction, and then moved into the entry table as registers
* are set.
*/
dwarf_cfa_reg_entry_t _entries[DWARF_CFA_STATE_MAX_REGISTERS];
public:
dwarf_cfa_state (void);
plcrash_error_t eval_program (plcrash_async_mobject_t *mobj,
machine_ptr pc,
machine_ptr initial_pc_value,
plcrash_async_dwarf_cie_info_t *cie_info,
gnu_ehptr_reader<machine_ptr> *ptr_reader,
const plcrash_async_byteorder_t *byteorder,
pl_vm_address_t address,
pl_vm_off_t offset,
pl_vm_size_t length);
plcrash_error_t apply_state (task_t task,
plcrash_async_dwarf_cie_info_t *cie_info,
const plcrash_async_thread_state_t *thread_state,
const plcrash_async_byteorder_t *byteorder,
plcrash_async_thread_state_t *new_thread_state);
bool set_register (dwarf_cfa_state_regnum_t regnum, plcrash_dwarf_cfa_reg_rule_t rule, machine_ptr value);
bool get_register_rule (dwarf_cfa_state_regnum_t regnum, plcrash_dwarf_cfa_reg_rule_t *rule, machine_ptr *value);
void remove_register (dwarf_cfa_state_regnum_t regnum);
uint8_t get_register_count (void);
void set_cfa_register (dwarf_cfa_state_regnum_t regnum, machine_ptr offset);
void set_cfa_register_signed (dwarf_cfa_state_regnum_t regnum, machine_ptr_s offset);
void set_cfa_expression (pl_vm_address_t address, pl_vm_size_t length);
dwarf_cfa_rule<machine_ptr,machine_ptr_s> get_cfa_rule (void);
bool push_state (void);
bool pop_state (void);
friend class dwarf_cfa_state_iterator<machine_ptr, machine_ptr_s>;
};
/**
* @internal
*
* A dwarf_cfa_state iterator; iterates DWARF CFA register records. The target stack must
* not be modified while iteration is performed.
*/
template <typename machine_ptr, typename machine_ptr_s>
class dwarf_cfa_state_iterator {
private:
/** Current bucket index */
uint8_t _bucket_idx;
/** Current entry index, or DWARF_CFA_STATE_INVALID_ENTRY_IDX if iteration has not started */
uint8_t _cur_entry_idx;
/** Borrowed reference to the backing DWARF CFA state */
dwarf_cfa_state<machine_ptr, machine_ptr_s> *_stack;
public:
dwarf_cfa_state_iterator(dwarf_cfa_state<machine_ptr, machine_ptr_s> *stack);
bool next (dwarf_cfa_state_regnum_t *regnum, plcrash_dwarf_cfa_reg_rule_t *rule, machine_ptr *value);
};
PLCR_CPP_END_NS
}
/*
* @}
*/
#endif /* PLCRASH_FEATURE_UNWIND_DWARF */
#endif /* PLCRASH_ASYNC_DWARF_STACK_H */