plcrashreporter/Source/PLCrashAsyncDwarfPrimitives...

480 строки
17 KiB
C++

/*
* 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_PRIVATE_H
#define PLCRASH_ASYNC_DWARF_PRIVATE_H 1
#include "PLCrashAsync.h"
#include "PLCrashAsyncImageList.h"
#include "PLCrashAsyncThread.h"
#include "PLCrashFeatureConfig.h"
#include "PLCrashMacros.h"
#include <inttypes.h>
#if PLCRASH_FEATURE_UNWIND_DWARF
PLCR_CPP_BEGIN_NS
namespace async {
/**
* @internal
* @ingroup plcrash_async_dwarf
* @{
*/
/**
* DWARF CFA opcodes, as defined by the DWARF 4 Specification (Section 7.23).
*
* The DWARF CFA opcodes are expressed in two forms; the first form includes an non-zero
* opcode in the top two bits, with the bottom 6 bits used to provide a constant value
* operand.
*
* The second form includes zeros in the top two bits, with the actual opcode
* stored in the bottom 6 bits.
*/
typedef enum {
/** DW_CFA_advance_loc: 'delta' stored in low six bits */
DW_CFA_advance_loc = 0x40,
/** DW_CFA_offset: 'register' stored in low six bits, operand is ULEB128 offset */
DW_CFA_offset = 0x80,
/** DW_CFA_restore: 'register' stored in low six bits */
DW_CFA_restore = 0xc0,
/** DW_CFA_nop */
DW_CFA_nop = 0,
/** DW_CFA_set_loc */
DW_CFA_set_loc = 0x01,
/** DW_CFA_advance_loc1, operand is 1-byte delta */
DW_CFA_advance_loc1 = 0x02,
/** DW_CFA_advance_loc2, operand is 2-byte delta */
DW_CFA_advance_loc2 = 0x03,
/** DW_CFA_advance_loc4, operand is 4-byte delta */
DW_CFA_advance_loc4 = 0x04,
/** DW_CFA_offset_extended, operands are ULEB128 register and ULEB128 offset */
DW_CFA_offset_extended = 0x05,
/** DW_CFA_restore_extended, operand is ULEB128 register */
DW_CFA_restore_extended = 0x06,
/** DW_CFA_undefined, operand is ULEB128 register */
DW_CFA_undefined = 0x07,
/** DW_CFA_same_value, operand is ULEB128 register */
DW_CFA_same_value = 0x08,
/** DW_CFA_register, operands are ULEB128 register, and ULEB128 register */
DW_CFA_register = 0x09,
/** DW_CFA_remember_state */
DW_CFA_remember_state = 0x0a,
/** DW_CFA_restore_state */
DW_CFA_restore_state = 0x0b,
/** DW_CFA_def_cfa, operands are ULEB128 register and ULEB128 offset */
DW_CFA_def_cfa = 0x0c,
/** DW_CFA_def_cfa_register, operand is ULEB128 register */
DW_CFA_def_cfa_register = 0x0d,
/** DW_CFA_def_cfa_offset, operand is ULEB128 offset */
DW_CFA_def_cfa_offset = 0x0e,
/** DW_CFA_def_cfa_expression */
DW_CFA_def_cfa_expression = 0x0f,
/** DW_CFA_expression, operands are ULEB128 register, BLOCK */
DW_CFA_expression = 0x10,
/** DW_CFA_offset_extended_sf, operands are ULEB128 register, SLEB128 offset */
DW_CFA_offset_extended_sf = 0x11,
/** DW_CFA_def_cfa_sf, operands are ULEB128, register SLEB128 offset */
DW_CFA_def_cfa_sf = 0x12,
/** DW_CFA_def_cfa_offset_sf, operand is SLEB128 offset */
DW_CFA_def_cfa_offset_sf = 0x13,
/** DW_CFA_val_offset, operands are ULEB128, ULEB128 */
DW_CFA_val_offset = 0x14,
/** DW_CFA_val_offset_sf, operands are ULEB128, SLEB128 */
DW_CFA_val_offset_sf = 0x15,
/** DW_CFA_val_expression, operands are ULEB128, BLOCK */
DW_CFA_val_expression = 0x16,
/** DW_CFA_lo_user */
DW_CFA_lo_user = 0x1c,
/** DW_CFA_hi_user */
DW_CFA_hi_user = 0x3f,
} DW_CFA_t;
/**
* Exception handling pointer encoding constants, as defined by the LSB Specification:
* http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
*
* The upper 4 bits indicate how the value is to be applied. The lower 4 bits indicate the encoding format of the data.
*/
typedef enum DW_EH_PE {
/**
* Value is an indirect reference. This value is not specified by the LSB, and appears to be a
* GCC extension; unfortunately, the intended use is not clear:
*
* - Apple's implementation of libunwind treats this as an indirected reference to a target-width pointer value,
* as does the upstream libunwind.
* - LLDB does not appear to support indirect encoding at all.
* - LLVM's asm printer decodes it as an independent flag on the encoding type value; eg, DW_EH_PE_indirect | DW_EH_PE_uleb128 | DW_EH_PE_pcrel
* LLVM/clang does not seem to otherwise emit this value.
* - GDB explicitly does not support indirect encodings.
*
* For our purposes, we treat the value as per LLVM's asm printer, and may re-evaluate if the indirect encoding
* is ever seen in the wild.
*/
DW_EH_PE_indirect = 0x80,
/** No value is present. */
DW_EH_PE_omit = 0xff,
/**
* If no flags are set (0x0), the value is a literal pointer whose size is determined by the architecture. Note that this has two different meanings,
* and is not a flag, but rather, the absense of a flag (0x0). If a relative flag is not set in the high 4 bits of the DW_EH_PE encoding, this
* signifies that no offset is to be applied. If the bottom 4 bits are 0, this signifies a native-width pointer value reference.
*
* As such, it's possible to mix DW_EH_PE_absptr with other relative flags, eg, DW_EH_PE_textrel. Examples:
*
* - DW_EH_PE_absptr|DW_EH_PE_textrel (0x20): Address is relative to TEXT segment, and is an architecture-native pointer width.
* - DW_EH_PE_absptr|DW_EH_PE_uleb128 (0x1): The address is absolute, and is a ULEB128 value.
* - DW_EH_PE_absptr|DW_EH_PE_uleb128|DW_EH_PE_indirect (0x81): The address is absolute, indirect, and is a ULEB128 value.
*/
DW_EH_PE_absptr = 0x00,
/** Unsigned value encoded using LEB128 as defined by DWARF Debugging Information Format, Revision 2.0.0. */
DW_EH_PE_uleb128 = 0x01,
/** Unsigned 16-bit value */
DW_EH_PE_udata2 = 0x02,
/* Unsigned 32-bit value */
DW_EH_PE_udata4 = 0x03,
/** Unsigned 64-bit value */
DW_EH_PE_udata8 = 0x04,
/** Signed value encoded using LEB128 as defined by DWARF Debugging Information Format, Revision 2.0.0. */
DW_EH_PE_sleb128 = 0x09,
/** Signed 16-bit value */
DW_EH_PE_sdata2 = 0x0a,
/** Signed 32-bit value */
DW_EH_PE_sdata4 = 0x0b,
/** Signed 64-bit value */
DW_EH_PE_sdata8 = 0x0c,
/** Value is relative to the current program counter. */
DW_EH_PE_pcrel = 0x10,
/** Value is relative to the beginning of the __TEXT section. */
DW_EH_PE_textrel = 0x20,
/** Value is relative to the beginning of the __DATA section. */
DW_EH_PE_datarel = 0x30,
/** Value is relative to the beginning of the function. */
DW_EH_PE_funcrel = 0x40,
/**
* Value is aligned to an address unit sized boundary. The meaning of this flag is not defined in the
* LSB 4.1.0 specification; review of various implementations demonstrate that:
*
* - The value must be aligned relative to the VM load address of the eh_frame/debug_frame section that contains
* it.
* - Some implementations assume that an aligned pointer value is always the architecture's natural pointer size.
* Other implementations, such as gdb, permit the use of alternative value types (uleb, sleb, data2/4/8, etc).
*
* In our implementation, we support the combination of DW_EH_PE_aligned with any other supported value type.
*/
DW_EH_PE_aligned = 0x50,
} DW_EH_PE_t;
/** Mask for the lower four bits of a DW_EH_PE_t value, defining the encoding type. */
#define DW_EH_PE_MASK_ENCODING 0x0F
/**
* DWARF CFA register rules, as defined in DWARF 4 Section 6.4.1.
*/
typedef enum {
/**
* The previous value of this register is saved at the address CFA+N where CFA is the current
* CFA value and N is a signed offset.
*/
PLCRASH_DWARF_CFA_REG_RULE_OFFSET = 0,
/**
* The previous value of this register is the value CFA+N where CFA is the current CFA value and N is a signed offset.
*/
PLCRASH_DWARF_CFA_REG_RULE_VAL_OFFSET = 1,
/**
* The previous value of this register is stored in another register numbered R.
*/
PLCRASH_DWARF_CFA_REG_RULE_REGISTER = 2,
/**
* The previous value of this register is located at the address produced by executing the DWARF expression E.
*/
PLCRASH_DWARF_CFA_REG_RULE_EXPRESSION = 3,
/**
* The previous value of this register is the value produced by executing the DWARF expression E.
*/
PLCRASH_DWARF_CFA_REG_RULE_VAL_EXPRESSION = 4,
/**
* This register has not been modified from the previous frame. (By convention, it is preserved by the callee, but
* the callee has not modified it.)
*
* The register's value may be found in the frame's thread state. For frames other than the first, the
* register may not have been restored, and thus may be unavailable.
*/
PLCRASH_DWARF_CFA_REG_RULE_SAME_VALUE = 5,
} plcrash_dwarf_cfa_reg_rule_t;
/**
* @internal
*
* GNU eh_frame pointer reader. Implements reading of pointer values encoded using the GNU eh_frame scheme.
*
* @tparam machine_ptr The target's native unsigned word type.
*/
template <typename machine_ptr> class gnu_ehptr_reader {
public:
gnu_ehptr_reader (const plcrash_async_byteorder_t *byteorder);
void set_frame_section_base (machine_ptr frame_section_base, machine_ptr frame_section_vm_addr);
void set_text_base (machine_ptr text_base);
void set_data_base (machine_ptr data_base);
void set_func_base (machine_ptr func_base);
plcrash_error_t read (plcrash_async_mobject_t *mobj,
pl_vm_address_t location,
pl_vm_off_t offset,
DW_EH_PE_t encoding,
machine_ptr *result,
size_t *size);
private:
const plcrash_async_byteorder_t *_byteorder;
typedef struct base_addr {
/** If false, no address is available. If true, address is valid. */
bool valid;
/** The defined base address. */
machine_ptr address;
} base_addr_t;
/** If false, no frame section base has been set */
bool _has_frame_section_base;
/**
* The base address (in-memory) of the loaded debug_frame or eh_frame section, or PL_VM_ADDRESS_INVALID. This is
* used to calculate the offset of DW_EH_PE_aligned from the start of the frame section.
*
* This address should be the actual base address at which the section has been mapped.
*/
machine_ptr _frame_section_base;
/**
* The base VM address of the eh_frame or debug_frame section, or PL_VM_ADDRESS_INVALID. This is used to calculate
* alignment for DW_EH_PE_aligned-encoded values.
*
* This address should be the aligned base VM address at which the section will (or has been loaded) during
* execution, and will be used to calculate DW_EH_PE_aligned alignment.
*/
machine_ptr _frame_section_vm_addr;
/** The base address of the text segment to be applied to DW_EH_PE_textrel offsets, or PL_VM_ADDRESS_INVALID. */
base_addr_t _text_base;
/** The base address of the data segment to be applied to DW_EH_PE_datarel offsets, or PL_VM_ADDRESS_INVALID. */
base_addr_t _data_base;
/** The base address of the function to be applied to DW_EH_PE_funcrel offsets, or PL_VM_ADDRESS_INVALID. */
base_addr_t _func_base;
};
plcrash_error_t plcrash_async_dwarf_read_uleb128 (plcrash_async_mobject_t *mobj, pl_vm_address_t location, pl_vm_off_t offset, uint64_t *result, pl_vm_size_t *size);
plcrash_error_t plcrash_async_dwarf_read_sleb128 (plcrash_async_mobject_t *mobj, pl_vm_address_t location, pl_vm_off_t offset, int64_t *result, pl_vm_size_t *size);
plcrash_error_t plcrash_async_dwarf_read_task_sleb128 (task_t task, pl_vm_address_t location, pl_vm_off_t offset, int64_t *result, pl_vm_size_t *size);
plcrash_error_t plcrash_async_dwarf_read_task_uleb128 (task_t task, pl_vm_address_t location, pl_vm_off_t offset, uint64_t *result, pl_vm_size_t *size);
/**
* @internal
*
* Read a value that is either 1, 2, 4, or 8 bytes in size, allowing natural unsigned integer overflow
* to occur.
*
* Returns true on success, false on failure.
*
* @param mobj Memory object from which to read the value.
* @param byteorder Byte order of the target value.
* @param base_addr The base address (within @a mobj's address space) from which to perform the read.
* @param offset An offset to be applied to base_addr.
* @param data_size The size of the value to be read. If an unsupported size is supplied, false will be returned.
* @param dest The destination value.
*/
template <typename T>
plcrash_error_t plcrash_async_dwarf_read_uintmax64 (plcrash_async_mobject_t *mobj,
const plcrash_async_byteorder_t *byteorder,
pl_vm_address_t base_addr,
pl_vm_off_t offset,
uint8_t data_size,
T *dest)
{
union udata {
uint8_t u8;
uint16_t u16;
uint32_t u32;
uint64_t u64;
} *data;
data = (union udata *) plcrash_async_mobject_remap_address(mobj, base_addr, offset, data_size);
if (data == NULL)
return PLCRASH_EINVAL;
switch (data_size) {
case 1:
*dest = data->u8;
break;
case 2:
*dest = byteorder->swap16(data->u16);
break;
case 4:
*dest = byteorder->swap32(data->u32);
break;
case 8:
*dest = (T)byteorder->swap64(data->u64);
break;
default:
PLCF_DEBUG("Unhandled data width %" PRIu64, (uint64_t) data_size);
return PLCRASH_EINVAL;
}
return PLCRASH_ESUCCESS;
}
/**
* @internal
*
* Read a value that is either 1, 2, 4, or 8 bytes in size from @a task, allowing natural unsigned integer overflow
* to occur.
*
* Returns true on success, false on failure.
*
* @param task Task from which to read the value.
* @param byteorder Byte order of the target value.
* @param base_addr The base address (within @a mobj's address space) from which to perform the read.
* @param offset An offset to be applied to base_addr.
* @param data_size The size of the value to be read. If an unsupported size is supplied, false will be returned.
* @param dest The destination value.
*/
template <typename T>
plcrash_error_t plcrash_async_dwarf_read_task_uintmax64 (task_t task,
const plcrash_async_byteorder_t *byteorder,
pl_vm_address_t base_addr,
pl_vm_off_t offset,
uint8_t data_size,
T *dest)
{
plcrash_error_t err;
union {
uint8_t u8;
uint16_t u16;
uint32_t u32;
uint64_t u64;
} data;
switch (data_size) {
case 1:
if ((err = plcrash_async_task_read_uint8(task, base_addr, offset, &data.u8)) != PLCRASH_ESUCCESS)
return err;
*dest = data.u8;
break;
case 2:
if ((err = plcrash_async_task_read_uint16(task, byteorder, base_addr, offset, &data.u16)) != PLCRASH_ESUCCESS)
return err;
*dest = data.u16;
break;
case 4:
if ((err = plcrash_async_task_read_uint32(task, byteorder, base_addr, offset, &data.u32)) != PLCRASH_ESUCCESS)
return err;
*dest = data.u32;
break;
case 8:
if ((err = plcrash_async_task_read_uint64(task, byteorder, base_addr, offset, &data.u64)) != PLCRASH_ESUCCESS)
return err;
*dest = (T)data.u64;
break;
default:
PLCF_DEBUG("Unhandled data width %" PRIu64, (uint64_t) data_size);
return PLCRASH_EINVAL;
}
return PLCRASH_ESUCCESS;
}
PLCR_CPP_END_NS
}
/*
* @}
*/
#endif /* PLCRASH_FEATURE_UNWIND_DWARF */
#endif /* PLCRASH_ASYNC_DWARF_PRIVATE_H */