Source/PLCrashAsyncCompactUnwindEncoding.h (55 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_COMPACT_UNWIND_ENCODING_H
#define PLCRASH_ASYNC_COMPACT_UNWIND_ENCODING_H 1
#include "PLCrashAsync.h"
#include "PLCrashAsyncImageList.h"
#include "PLCrashAsyncThread.h"
#include "PLCrashFeatureConfig.h"
#include <mach-o/compact_unwind_encoding.h>
#if PLCRASH_FEATURE_UNWIND_COMPACT
/**
* @internal
* @ingroup plcrash_async_cfe
* @{
*/
/**
* @internal
* A CFE reader instance. Performs CFE data parsing from a backing memory object.
*/
typedef struct plcrash_async_cfe_reader {
/** A memory object containing the CFE data at the starting address. */
plcrash_async_mobject_t *mobj;
/** The target CPU type. */
cpu_type_t cpu_type;
/** The unwind info header. Note that the header values may require byte-swapping for the local process' use. */
struct unwind_info_section_header header;
/** The byte order of the encoded data (including the header). */
const plcrash_async_byteorder_t *byteorder;
} plcrash_async_cfe_reader_t;
/**
* Supported CFE entry formats.
*/
typedef enum {
/**
* The frame pointer (fp) is valid. To walk the stack, the previous frame pointer may be popped from
* the current frame pointer, followed by the return address.
*
* All non-volatile registers that need to be restored will be saved on the stack, ranging from fp±regsize through
* fp±1020. The actual direction depends on the stack growth direction of the target platform.
*/
PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAME_PTR = 1,
/**
* The frame pointer (eg, ebp/rbp) is invalid, but the stack size is constant and is small enough (<= 1024) that it
* may be encoded in the CFE entry itself.
*
* The return address may be found at the provided ± offset from the stack pointer, followed all non-volatile
* registers that need to be restored. The actual direction of the offset depends on the stack growth direction of
* the target platform.
*/
PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_IMMD = 2,
/**
* The frame pointer (eg, ebp/rbp) is invalid, but the stack size is constant and is too large (>= 1024) to be
* encoded in the CFE entry itself. Instead, the fixed stack size value must be extracted from an actual instruction
* (eg, subl) within the target function, and used as the constant stack size. The decoded stack offset may be
* added to the start address of the function to determine the location of the actual stack size.
*
* The return address may be found at the derived ± offset from the stack pointer, followed all non-volatile
* registers that need to be restored. The actual direction of the offset epends on the stack growth direction of
* the target platform.
*/
PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_INDIRECT = 3,
/**
* The unwinding information for the target address could not be encoded using the CFE format. Instead, DWARF
* frame information must be used.
*
* An offset to the DWARF FDE in the __eh_frame section is be provided.
*/
PLCRASH_ASYNC_CFE_ENTRY_TYPE_DWARF = 4,
/**
* No unwind information is available for the target address. This value is only returned in the case where an
* unwind table entry exists for the given address, but the entry is empty.
*/
PLCRASH_ASYNC_CFE_ENTRY_TYPE_NONE = 5
} plcrash_async_cfe_entry_type_t;
/** Maximum number of registers supported by the permutation register encoding. @sa plcrash_async_cfe_register_decode and plcrash_async_cfe_register_encode. */
#define PLCRASH_ASYNC_CFE_PERMUTATION_REGISTER_MAX 6
/** Maximum number of saved non-volatile registers that may be represented in an i386 or x86-64 CFE entry */
#define PLCRASH_ASYNC_CFE_SAVED_REGISTER_X86_MAX 6
/** Maximum number of saved non-volatile registers that may be represented in an ARM64 CFE entry */
#define PLCRASH_ASYNC_CFE_SAVED_REGISTER_ARM64_MAX 10
#define _PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX(a, b) (((a) > (b)) ? (a) : (b))
/** Maximum number of saved non-volatile registers that may be represented in a CFE entry */
#define PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX _PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX(PLCRASH_ASYNC_CFE_SAVED_REGISTER_X86_MAX, PLCRASH_ASYNC_CFE_SAVED_REGISTER_ARM64_MAX)
/**
* @internal
*
* A decoded CFE entry. The entry represents the data necessary to unwind the stack frame at a given PC, including
* restoration of saved registers.
*/
typedef struct plcrash_async_cfe_entry {
/** The CFE entry type. */
plcrash_async_cfe_entry_type_t type;
/** The target CPU type. */
cpu_type_t cpu_type;
/**
* Encoded stack offset. Interpretation of this value depends on the CFE type:
* - PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAME_PTR: Saved non-volatile registers may be found at ± offset from the frame
* pointer (eg, ebp/rbp).
* - PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_IMMD: The return address may be found at ± offset from the stack
* pointer (eg, esp/rsp), and is followed all non-volatile registers that need to be restored.
* - PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_INDIRECT: The actual offset may be loaded from the target function's
* instruction prologue. The offset given here must be added to the start address of the function to determine
* the location of the actual stack size as encoded in the prologue.
*
* The return address may be found at ± offset from the stack pointer (eg, esp/rsp), and is followed all
* non-volatile registers that need to be restored.
*
* TODO: Need a mechanism to define the actual size of the offset. For x86-32/x86-64, it is defined as being
* encoded in a subl instruction.
* - PLCRASH_ASYNC_CFE_ENTRY_TYPE_DWARF: The offset to the DWARF FDE in the __eh_frame section.
* - PLCRASH_ASYNC_CFE_ENTRY_TYPE_NONE: Unused.
*/
intptr_t stack_offset;
/**
* Stack adjustment offset. This is an offset to be applied to the final stack value read via
* PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_INDIRECT.
*
* This value is unused for all other CFE types.
*/
uint32_t stack_adjust;
/**
* The link register to be used for the return address (eg, such as in a ARM leaf frame), or PLCRASH_REG_INVALID if the return address
* is found on the stack. This value is only supported for the following CFE types:
* - PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_IMMD and
* - PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_INDIRECT
*/
plcrash_regnum_t return_address_register;
/**
* The number of non-volatile registers that need to be restored from the stack.
*/
uint32_t register_count;
/**
* The ordered list of register_count non-volatile registers that must be restored from the stack. These values are
* specific to the target platform, and are defined in the @a plcrash_async_thread API. @sa plcrash_x86_regnum_t
* and @sa plcrash_x86_64_regnum_t. Note that the list may be sparse; some entries may be set to a value of
* PLCRASH_REG_INVALID.
*/
plcrash_regnum_t register_list[PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX];
} plcrash_async_cfe_entry_t;
plcrash_error_t plcrash_async_cfe_reader_init (plcrash_async_cfe_reader_t *reader, plcrash_async_mobject_t *mobj, cpu_type_t cputype);
plcrash_error_t plcrash_async_cfe_reader_find_pc (plcrash_async_cfe_reader_t *reader, pl_vm_address_t pc, pl_vm_address_t *function_base, uint32_t *encoding);
void plcrash_async_cfe_reader_free (plcrash_async_cfe_reader_t *reader);
plcrash_error_t plcrash_async_cfe_entry_init (plcrash_async_cfe_entry_t *entry, cpu_type_t cpu_type, uint32_t encoding);
plcrash_async_cfe_entry_type_t plcrash_async_cfe_entry_type (plcrash_async_cfe_entry_t *entry);
intptr_t plcrash_async_cfe_entry_stack_offset (plcrash_async_cfe_entry_t *entry);
uint32_t plcrash_async_cfe_entry_stack_adjustment (plcrash_async_cfe_entry_t *entry);
plcrash_regnum_t plcrash_async_cfe_entry_return_address_register (plcrash_async_cfe_entry_t *entry);
uint32_t plcrash_async_cfe_entry_register_count (plcrash_async_cfe_entry_t *entry);
void plcrash_async_cfe_entry_register_list (plcrash_async_cfe_entry_t *entry, plcrash_regnum_t register_list[]);
plcrash_error_t plcrash_async_cfe_entry_apply (task_t task,
pl_vm_address_t function_address,
const plcrash_async_thread_state_t *thread_state,
plcrash_async_cfe_entry_t *entry,
plcrash_async_thread_state_t *new_thread_state);
void plcrash_async_cfe_entry_free (plcrash_async_cfe_entry_t *entry);
uint32_t plcrash_async_cfe_register_encode (const uint32_t registers[], uint32_t count);
plcrash_error_t plcrash_async_cfe_register_decode (uint32_t permutation, uint32_t count, uint32_t registers[]);
/*
* @} plcrash_async_cfe
*/
#endif /* PLCRASH_FEATURE_UNWIND_COMPACT */
#endif /* PLCRASH_ASYNC_COMPACT_UNWIND_ENCODING_H */