in Source/PLCrashAsyncCompactUnwindEncoding.c [674:951]
plcrash_error_t plcrash_async_cfe_entry_init (plcrash_async_cfe_entry_t *entry, cpu_type_t cpu_type, uint32_t encoding) {
plcrash_error_t ret;
/* Target-neutral initialization */
entry->cpu_type = cpu_type;
entry->stack_adjust = 0;
entry->return_address_register = PLCRASH_REG_INVALID;
/* Perform target-specific decoding */
if (cpu_type == CPU_TYPE_X86) {
uint32_t mode = encoding & UNWIND_X86_MODE_MASK;
switch (mode) {
case UNWIND_X86_MODE_EBP_FRAME: {
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAME_PTR;
/* Extract the register frame offset */
entry->stack_offset = -(EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET) * sizeof(uint32_t));
/* Extract the register values. They're stored as a bitfield of of 3 bit values. We support
* sparse entries, but terminate the loop if no further entries remain. */
uint32_t regs = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS);
entry->register_count = 0;
for (uint32_t i = 0; i < PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX; i++) {
/* Check for completion */
uint32_t remaining = regs >> (3 * i);
if (remaining == 0)
break;
/* Map to the correct PLCrashReporter register name */
uint32_t reg = remaining & 0x7;
ret = plcrash_async_map_register_name(reg, &entry->register_list[i], cpu_type);
if (ret != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to map register value of %" PRIx32, reg);
return ret;
}
/* Update the register count */
entry->register_count++;
}
return PLCRASH_ESUCCESS;
}
case UNWIND_X86_MODE_STACK_IMMD:
case UNWIND_X86_MODE_STACK_IND: {
/* These two types are identical except for the interpretation of the stack offset and adjustment values */
if (mode == UNWIND_X86_MODE_STACK_IMMD) {
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_IMMD;
entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE) * sizeof(uint32_t);
} else {
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_INDIRECT;
entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
entry->stack_adjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST) * sizeof(uint32_t);
}
/* Extract the register values */
entry->register_count = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
uint32_t encoded_regs = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
uint32_t decoded_regs[PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX];
ret = plcrash_async_cfe_register_decode(encoded_regs, entry->register_count, decoded_regs);
if (ret != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to decode register list: %d", ret);
return ret;
}
/* Map to the correct PLCrashReporter register names */
for (uint32_t i = 0; i < entry->register_count; i++) {
ret = plcrash_async_map_register_name(decoded_regs[i], &entry->register_list[i], cpu_type);
if (ret != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to map register value of %" PRIx32, entry->register_list[i]);
return ret;
}
}
return PLCRASH_ESUCCESS;
}
case UNWIND_X86_MODE_DWARF:
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_DWARF;
/* Extract the register frame offset */
entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_DWARF_SECTION_OFFSET);
entry->register_count = 0;
return PLCRASH_ESUCCESS;
case 0:
/* Handle a NULL encoding. This interpretation is derived from Apple's actual implementation; the correct interpretation of
* a 0x0 value is not defined in what documentation exists. */
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_NONE;
entry->stack_offset = 0;
entry->register_count = 0;
return PLCRASH_ESUCCESS;
default:
PLCF_DEBUG("Unexpected entry mode of %" PRIx32, mode);
return PLCRASH_ENOTSUP;
}
// Unreachable
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
__builtin_trap();
return PLCRASH_EINTERNAL;
#pragma clang diagnostic pop
} else if (cpu_type == CPU_TYPE_X86_64) {
uint32_t mode = encoding & UNWIND_X86_64_MODE_MASK;
switch (mode) {
case UNWIND_X86_64_MODE_RBP_FRAME: {
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAME_PTR;
/* Extract the register frame offset */
entry->stack_offset = -(EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_OFFSET) * sizeof(uint64_t));
/* Extract the register values. They're stored as a bitfield of of 3 bit values. We support
* sparse entries, but terminate the loop if no further entries remain. */
uint32_t regs = EXTRACT_BITS(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
entry->register_count = 0;
for (uint32_t i = 0; i < PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX; i++) {
/* Check for completion */
uint32_t remaining = regs >> (3 * i);
if (remaining == 0)
break;
/* Map to the correct PLCrashReporter register name */
uint32_t reg = remaining & 0x7;
ret = plcrash_async_map_register_name(reg, &entry->register_list[i], cpu_type);
if (ret != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to map register value of %" PRIx32, reg);
return ret;
}
/* Update the register count */
entry->register_count++;
}
return PLCRASH_ESUCCESS;
}
case UNWIND_X86_64_MODE_STACK_IMMD:
case UNWIND_X86_64_MODE_STACK_IND: {
/* These two types are identical except for the interpretation of the stack offset and adjustment values */
if (mode == UNWIND_X86_64_MODE_STACK_IMMD) {
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_IMMD;
entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE) * sizeof(uint64_t);
} else {
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_INDIRECT;
entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
entry->stack_adjust = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST) * sizeof(uint64_t);
}
/* Extract the register values */
entry->register_count = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
uint32_t encoded_regs = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
uint32_t decoded_regs[PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX];
ret = plcrash_async_cfe_register_decode(encoded_regs, entry->register_count, decoded_regs);
if (ret != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to decode register list: %d", ret);
return ret;
}
/* Map to the correct PLCrashReporter register names */
for (uint32_t i = 0; i < entry->register_count; i++) {
ret = plcrash_async_map_register_name(decoded_regs[i], &entry->register_list[i], cpu_type);
if (ret != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to map register value of %" PRIx32, entry->register_list[i]);
return ret;
}
}
return PLCRASH_ESUCCESS;
}
case UNWIND_X86_64_MODE_DWARF:
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_DWARF;
/* Extract the register frame offset */
entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_64_DWARF_SECTION_OFFSET);
entry->register_count = 0;
return PLCRASH_ESUCCESS;
case 0:
/* Handle a NULL encoding. This interpretation is derived from Apple's actual implementation; the correct interpretation of
* a 0x0 value is not defined in what documentation exists. */
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_NONE;
entry->stack_offset = 0;
entry->register_count = 0;
return PLCRASH_ESUCCESS;
default:
PLCF_DEBUG("Unexpected entry mode of %" PRIx32, mode);
return PLCRASH_ENOTSUP;
}
// Unreachable
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
__builtin_trap();
return PLCRASH_EINTERNAL;
#pragma clang diagnostic pop
} else if (cpu_type == CPU_TYPE_ARM64) {
uint32_t mode = encoding & UNWIND_ARM64_MODE_MASK;
switch (mode) {
case UNWIND_ARM64_MODE_FRAME:
// Fall through
case UNWIND_ARM64_MODE_FRAMELESS:
if (mode == UNWIND_ARM64_MODE_FRAME) {
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAME_PTR;
/* The stack offset will be calculated below */
} else {
/*
* The compact_unwind header documents this as UNWIND_ARM64_MODE_LEAF, but actually defines UNWIND_ARM64_MODE_FRAMELESS.
* Reviewing the libunwind stepWithCompactEncodingFrameless() assembly demonstrates that this actually uses the
* i386/x86-64 frameless immediate style of encoding an offset from the stack pointer. Unlike x86, however, the
* offset is multipled by 16 bytes (since each register is stored in pairs), rather than the platform word size.
*
* The header discrepancy was reported as rdar://15057141
*/
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_IMMD;
entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK) * (sizeof(uint64_t) * 2);
entry->return_address_register = PLCRASH_ARM64_LR;
}
/* Extract the register values */
size_t reg_pos = 0;
entry->register_count = 0;
#define CHECK_REG(name, val1, val2) do { \
if ((encoding & name) == name) { \
PLCF_ASSERT(entry->register_count+2 <= PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX); \
entry->register_list[reg_pos++] = val2; \
entry->register_list[reg_pos++] = val1; \
entry->register_count += 2; \
} \
} while(0)
CHECK_REG(UNWIND_ARM64_FRAME_X27_X28_PAIR, PLCRASH_ARM64_X27, PLCRASH_ARM64_X28);
CHECK_REG(UNWIND_ARM64_FRAME_X25_X26_PAIR, PLCRASH_ARM64_X25, PLCRASH_ARM64_X26);
CHECK_REG(UNWIND_ARM64_FRAME_X23_X24_PAIR, PLCRASH_ARM64_X23, PLCRASH_ARM64_X24);
CHECK_REG(UNWIND_ARM64_FRAME_X21_X22_PAIR, PLCRASH_ARM64_X21, PLCRASH_ARM64_X22);
CHECK_REG(UNWIND_ARM64_FRAME_X19_X20_PAIR, PLCRASH_ARM64_X19, PLCRASH_ARM64_X20);
#undef CHECK_REG
/* Offset depends on the number of saved registers */
if (mode == UNWIND_ARM64_MODE_FRAME)
entry->stack_offset = -(entry->register_count * sizeof(uint64_t));
return PLCRASH_ESUCCESS;
case UNWIND_ARM64_MODE_DWARF:
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_DWARF;
/* Extract the register frame offset */
entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_ARM64_DWARF_SECTION_OFFSET);
entry->register_count = 0;
return PLCRASH_ESUCCESS;
case 0:
/* Handle a NULL encoding. This interpretation is derived from Apple's actual implementation; the correct interpretation of
* a 0x0 value is not defined in what documentation exists. */
entry->type = PLCRASH_ASYNC_CFE_ENTRY_TYPE_NONE;
entry->stack_offset = 0;
entry->register_count = 0;
return PLCRASH_ESUCCESS;
default:
PLCF_DEBUG("Unexpected entry mode of %" PRIx32, mode);
return PLCRASH_ENOTSUP;
}
}
PLCF_DEBUG("Unsupported CPU type: %" PRIu32, cpu_type);
return PLCRASH_ENOTSUP;
}