Source/PLCrashAsyncThread_arm.c (398 lines of code) (raw):
/*
* Author: Landon Fuller <landonf@plausiblelabs.com>
*
* Copyright (c) 2008-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 "PLCrashAsyncThread.h"
#include "PLCrashAsync.h"
#include <signal.h>
#include <stdlib.h>
#include <assert.h>
#include <mach/thread_status.h>
#if defined(__arm__) || defined(__arm64__)
#if __DARWIN_UNIX03
#define THREAD_STATE_REG_PREFIX(name) __ ## name
#else
#define THREAD_STATE_REG_PREFIX(name) name
#endif
#define THREAD_STATE_GET(name, type, ts) (ts->arm_state. type . THREAD_STATE_REG_PREFIX(name))
#define THREAD_STATE_SET(name, type, ts, regnum, value) { \
ts->valid_regs |= 1ULL << regnum; \
(ts->arm_state. type . THREAD_STATE_REG_PREFIX(name)) = value; \
}
#if defined(__LP64__)
#if __DARWIN_OPAQUE_ARM_THREAD_STATE64
#define THREAD_STATE_OPAQUE_PREFIX(name) __opaque_ ## name
#define THREAD_STATE_OPAQUE_TYPE void *
#else
#define THREAD_STATE_OPAQUE_PREFIX THREAD_STATE_REG_PREFIX
#define THREAD_STATE_OPAQUE_TYPE uint64_t
#endif
/*
* Pointer authentication codes (on arm64e for example) must be stripped out by applying ARM64_PTR_MASK bitmask.
* See https://developer.apple.com/documentation/security/preparing_your_app_to_work_with_pointer_authentication
*
* Note: Even if pointer authentication (ptrauth) is not available at the compile time, the binary still can be used
* in an environment with PAC.
*
* Do not use arm_thread_state64_get_* to access to specific fields because arm64e injects additional checks that can
* prevent to get the values despite of the fact that the actual data was already read before.
*/
#define THREAD_STATE_GET_PTR(name, type, ts) ({ \
plcrash_greg_t ptr = (plcrash_greg_t) ts->arm_state. type . THREAD_STATE_OPAQUE_PREFIX(name); \
(ptr & ARM64_PTR_MASK); \
})
#define THREAD_STATE_GET_FPTR THREAD_STATE_GET_PTR
#define THREAD_STATE_SET_PTR(name, type, ts, regnum, value) { \
ts->valid_regs |= 1ULL << regnum; \
(ts->arm_state. type . THREAD_STATE_OPAQUE_PREFIX(name)) = (THREAD_STATE_OPAQUE_TYPE) value; \
}
#define THREAD_STATE_SET_FPTR THREAD_STATE_SET_PTR
#else // __LP64__
#define THREAD_STATE_GET_PTR THREAD_STATE_GET
#define THREAD_STATE_GET_FPTR THREAD_STATE_GET
#define THREAD_STATE_SET_PTR THREAD_STATE_SET
#define THREAD_STATE_SET_FPTR THREAD_STATE_SET
#endif // __LP64__
/* Mapping of DWARF register numbers to PLCrashReporter register numbers. */
struct dwarf_register_table {
/** Standard register number. */
plcrash_regnum_t regnum;
/** DWARF register number. */
uint64_t dwarf_value;
};
/*
* ARM GP registers defined as callee-preserved, as per Apple's iOS ARM Function Call Guide
*/
static const plcrash_regnum_t arm_nonvolatile_registers[] = {
PLCRASH_ARM_R4,
PLCRASH_ARM_R5,
PLCRASH_ARM_R6,
PLCRASH_ARM_R7,
PLCRASH_ARM_R8,
PLCRASH_ARM_R10,
PLCRASH_ARM_R11,
};
/*
* ARM GP registers defined as callee-preserved, as per ARM's Procedure Call Standard for the
* ARM 64-bit Architecture (AArch64), 22nd May 2013.
*/
static const plcrash_regnum_t arm64_nonvolatile_registers[] = {
PLCRASH_ARM64_X19,
PLCRASH_ARM64_X20,
PLCRASH_ARM64_X21,
PLCRASH_ARM64_X22,
PLCRASH_ARM64_X23,
PLCRASH_ARM64_X24,
PLCRASH_ARM64_X25,
PLCRASH_ARM64_X26,
PLCRASH_ARM64_X27,
PLCRASH_ARM64_X28,
#ifdef __APPLE__
// AAPCS 64 Section 5.2.3 allows an implementation to define the minimum
// level of conformance with respect to maintaining frame records.
//
// Apple's ARM64 Function Calling Conventions states:
// The frame pointer register (x29) must always address a valid frame record, although some functions—such
// as leaf functions or tail calls—may elect not to create an entry in this list. As a result, stack traces will
// always be meaningful, even without debug information.
PLCRASH_ARM64_FP,
#else
#error Define OS frame pointer behavior as per AAPCS64 Section 5.2.3
#endif
};
/**
* DWARF register mappings as defined in ARM's "DWARF for the ARM Architecture", ARM IHI 0040B,
* issued November 30th, 2012.
*
* Note that not all registers have DWARF register numbers allocated, eg, the ARM standard states
* in Section 3.1:
*
* The CPSR, VFP and FPA control registers are not allocated a numbering above. It is
* considered unlikely that these will be needed for producing a stack back-trace in a
* debugger.
*/
static const struct dwarf_register_table arm_dwarf_table [] = {
{ PLCRASH_ARM_R0, 0 },
{ PLCRASH_ARM_R1, 1 },
{ PLCRASH_ARM_R2, 2 },
{ PLCRASH_ARM_R3, 3 },
{ PLCRASH_ARM_R4, 4 },
{ PLCRASH_ARM_R5, 5 },
{ PLCRASH_ARM_R6, 6 },
{ PLCRASH_ARM_R7, 7 },
{ PLCRASH_ARM_R8, 8 },
{ PLCRASH_ARM_R9, 9 },
{ PLCRASH_ARM_R10, 10 },
{ PLCRASH_ARM_R11, 11 },
{ PLCRASH_ARM_R12, 12 },
{ PLCRASH_ARM_SP, 13 },
{ PLCRASH_ARM_LR, 14 },
{ PLCRASH_ARM_PC, 15 }
};
/**
* DWARF register mappings as defined in ARM's "DWARF for the ARM 64-bit Architecture (AArch64)", ARM IHI 0057B,
* issued May 22nd, 2013.
*
* Note that not all registers have DWARF register numbers allocated, eg, the ARM standard states
* in Section 3.1:
*
* The CPSR, VFP and FPA control registers are not allocated a numbering above. It is
* considered unlikely that these will be needed for producing a stack back-trace in a
* debugger.
*/
static const struct dwarf_register_table arm64_dwarf_table [] = {
// TODO_ARM64: These should be validated against actual arm64 DWARF data.
{ PLCRASH_ARM64_X0, 0 },
{ PLCRASH_ARM64_X1, 1 },
{ PLCRASH_ARM64_X2, 2 },
{ PLCRASH_ARM64_X3, 3 },
{ PLCRASH_ARM64_X4, 4 },
{ PLCRASH_ARM64_X5, 5 },
{ PLCRASH_ARM64_X6, 6 },
{ PLCRASH_ARM64_X7, 7 },
{ PLCRASH_ARM64_X8, 8 },
{ PLCRASH_ARM64_X9, 9 },
{ PLCRASH_ARM64_X10, 10 },
{ PLCRASH_ARM64_X11, 11 },
{ PLCRASH_ARM64_X12, 12 },
{ PLCRASH_ARM64_X13, 13 },
{ PLCRASH_ARM64_X14, 14 },
{ PLCRASH_ARM64_X15, 15 },
{ PLCRASH_ARM64_X16, 16 },
{ PLCRASH_ARM64_X17, 17 },
{ PLCRASH_ARM64_X18, 18 },
{ PLCRASH_ARM64_X19, 19 },
{ PLCRASH_ARM64_X20, 20 },
{ PLCRASH_ARM64_X21, 21 },
{ PLCRASH_ARM64_X22, 22 },
{ PLCRASH_ARM64_X23, 23 },
{ PLCRASH_ARM64_X24, 24 },
{ PLCRASH_ARM64_X25, 25 },
{ PLCRASH_ARM64_X26, 26 },
{ PLCRASH_ARM64_X27, 27 },
{ PLCRASH_ARM64_X28, 28 },
{ PLCRASH_ARM64_FP, 29 },
{ PLCRASH_ARM64_LR, 30 },
{ PLCRASH_ARM64_SP, 31 },
};
static inline plcrash_greg_t plcrash_async_thread_state_get_reg_32 (const plcrash_async_thread_state_t *ts, plcrash_regnum_t regnum) {
switch (regnum) {
case PLCRASH_ARM_R0: return THREAD_STATE_GET(r[0], thread.ts_32, ts);
case PLCRASH_ARM_R1: return THREAD_STATE_GET(r[1], thread.ts_32, ts);
case PLCRASH_ARM_R2: return THREAD_STATE_GET(r[2], thread.ts_32, ts);
case PLCRASH_ARM_R3: return THREAD_STATE_GET(r[3], thread.ts_32, ts);
case PLCRASH_ARM_R4: return THREAD_STATE_GET(r[4], thread.ts_32, ts);
case PLCRASH_ARM_R5: return THREAD_STATE_GET(r[5], thread.ts_32, ts);
case PLCRASH_ARM_R6: return THREAD_STATE_GET(r[6], thread.ts_32, ts);
case PLCRASH_ARM_R7: return THREAD_STATE_GET(r[7], thread.ts_32, ts);
case PLCRASH_ARM_R8: return THREAD_STATE_GET(r[8], thread.ts_32, ts);
case PLCRASH_ARM_R9: return THREAD_STATE_GET(r[9], thread.ts_32, ts);
case PLCRASH_ARM_R10: return THREAD_STATE_GET(r[10], thread.ts_32, ts);
case PLCRASH_ARM_R11: return THREAD_STATE_GET(r[11], thread.ts_32, ts);
case PLCRASH_ARM_R12: return THREAD_STATE_GET(r[12], thread.ts_32, ts);
case PLCRASH_ARM_SP: return THREAD_STATE_GET(sp, thread.ts_32, ts);
case PLCRASH_ARM_LR: return THREAD_STATE_GET(lr, thread.ts_32, ts);
case PLCRASH_ARM_PC: return THREAD_STATE_GET(pc, thread.ts_32, ts);
case PLCRASH_ARM_CPSR: return THREAD_STATE_GET(cpsr, thread.ts_32, ts);
default: __builtin_trap();
}
}
static inline plcrash_greg_t plcrash_async_thread_state_get_reg_64 (const plcrash_async_thread_state_t *ts, plcrash_regnum_t regnum) {
switch (regnum) {
case PLCRASH_ARM64_X0: return THREAD_STATE_GET(x[0], thread.ts_64, ts);
case PLCRASH_ARM64_X1: return THREAD_STATE_GET(x[1], thread.ts_64, ts);
case PLCRASH_ARM64_X2: return THREAD_STATE_GET(x[2], thread.ts_64, ts);
case PLCRASH_ARM64_X3: return THREAD_STATE_GET(x[3], thread.ts_64, ts);
case PLCRASH_ARM64_X4: return THREAD_STATE_GET(x[4], thread.ts_64, ts);
case PLCRASH_ARM64_X5: return THREAD_STATE_GET(x[5], thread.ts_64, ts);
case PLCRASH_ARM64_X6: return THREAD_STATE_GET(x[6], thread.ts_64, ts);
case PLCRASH_ARM64_X7: return THREAD_STATE_GET(x[7], thread.ts_64, ts);
case PLCRASH_ARM64_X8: return THREAD_STATE_GET(x[8], thread.ts_64, ts);
case PLCRASH_ARM64_X9: return THREAD_STATE_GET(x[9], thread.ts_64, ts);
case PLCRASH_ARM64_X10: return THREAD_STATE_GET(x[10], thread.ts_64, ts);
case PLCRASH_ARM64_X11: return THREAD_STATE_GET(x[11], thread.ts_64, ts);
case PLCRASH_ARM64_X12: return THREAD_STATE_GET(x[12], thread.ts_64, ts);
case PLCRASH_ARM64_X13: return THREAD_STATE_GET(x[13], thread.ts_64, ts);
case PLCRASH_ARM64_X14: return THREAD_STATE_GET(x[14], thread.ts_64, ts);
case PLCRASH_ARM64_X15: return THREAD_STATE_GET(x[15], thread.ts_64, ts);
case PLCRASH_ARM64_X16: return THREAD_STATE_GET(x[16], thread.ts_64, ts);
case PLCRASH_ARM64_X17: return THREAD_STATE_GET(x[17], thread.ts_64, ts);
case PLCRASH_ARM64_X18: return THREAD_STATE_GET(x[18], thread.ts_64, ts);
case PLCRASH_ARM64_X19: return THREAD_STATE_GET(x[19], thread.ts_64, ts);
case PLCRASH_ARM64_X20: return THREAD_STATE_GET(x[20], thread.ts_64, ts);
case PLCRASH_ARM64_X21: return THREAD_STATE_GET(x[21], thread.ts_64, ts);
case PLCRASH_ARM64_X22: return THREAD_STATE_GET(x[22], thread.ts_64, ts);
case PLCRASH_ARM64_X23: return THREAD_STATE_GET(x[23], thread.ts_64, ts);
case PLCRASH_ARM64_X24: return THREAD_STATE_GET(x[24], thread.ts_64, ts);
case PLCRASH_ARM64_X25: return THREAD_STATE_GET(x[25], thread.ts_64, ts);
case PLCRASH_ARM64_X26: return THREAD_STATE_GET(x[26], thread.ts_64, ts);
case PLCRASH_ARM64_X27: return THREAD_STATE_GET(x[27], thread.ts_64, ts);
case PLCRASH_ARM64_X28: return THREAD_STATE_GET(x[28], thread.ts_64, ts);
case PLCRASH_ARM64_FP: return THREAD_STATE_GET_PTR(fp, thread.ts_64, ts);
case PLCRASH_ARM64_SP: return THREAD_STATE_GET_PTR(sp, thread.ts_64, ts);
case PLCRASH_ARM64_LR: return THREAD_STATE_GET_FPTR(lr, thread.ts_64, ts);
case PLCRASH_ARM64_PC: return THREAD_STATE_GET_FPTR(pc, thread.ts_64, ts);
case PLCRASH_ARM64_CPSR: return THREAD_STATE_GET(cpsr, thread.ts_64, ts);
default: __builtin_trap();
}
}
// PLCrashAsyncThread API
plcrash_greg_t plcrash_async_thread_state_get_reg (const plcrash_async_thread_state_t *ts, plcrash_regnum_t regnum) {
if (ts->arm_state.thread.ash.flavor == ARM_THREAD_STATE32) {
return plcrash_async_thread_state_get_reg_32(ts, regnum);
} else {
return plcrash_async_thread_state_get_reg_64(ts, regnum);
}
}
static inline void plcrash_async_thread_state_set_reg_32 (plcrash_async_thread_state_t *ts, plcrash_regnum_t regnum, plcrash_greg_t reg) {
switch (regnum) {
case PLCRASH_ARM_R0: THREAD_STATE_SET(r[0], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R1: THREAD_STATE_SET(r[1], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R2: THREAD_STATE_SET(r[2], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R3: THREAD_STATE_SET(r[3], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R4: THREAD_STATE_SET(r[4], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R5: THREAD_STATE_SET(r[5], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R6: THREAD_STATE_SET(r[6], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R7: THREAD_STATE_SET(r[7], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R8: THREAD_STATE_SET(r[8], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R9: THREAD_STATE_SET(r[9], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R10: THREAD_STATE_SET(r[10], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R11: THREAD_STATE_SET(r[11], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_R12: THREAD_STATE_SET(r[12], thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_SP: THREAD_STATE_SET(sp, thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_LR: THREAD_STATE_SET(lr, thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_PC: THREAD_STATE_SET(pc, thread.ts_32, ts, regnum, (uint32_t)reg); break;
case PLCRASH_ARM_CPSR: THREAD_STATE_SET(cpsr, thread.ts_32, ts, regnum, (uint32_t)reg); break;
default: __builtin_trap(); // Unsupported register
}
}
static inline void plcrash_async_thread_state_set_reg_64 (plcrash_async_thread_state_t *ts, plcrash_regnum_t regnum, plcrash_greg_t reg) {
switch (regnum) {
case PLCRASH_ARM64_X0: THREAD_STATE_SET(x[0], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X1: THREAD_STATE_SET(x[1], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X2: THREAD_STATE_SET(x[2], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X3: THREAD_STATE_SET(x[3], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X4: THREAD_STATE_SET(x[4], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X5: THREAD_STATE_SET(x[5], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X6: THREAD_STATE_SET(x[6], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X7: THREAD_STATE_SET(x[7], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X8: THREAD_STATE_SET(x[8], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X9: THREAD_STATE_SET(x[9], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X10: THREAD_STATE_SET(x[10], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X11: THREAD_STATE_SET(x[11], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X12: THREAD_STATE_SET(x[12], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X13: THREAD_STATE_SET(x[13], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X14: THREAD_STATE_SET(x[14], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X15: THREAD_STATE_SET(x[15], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X16: THREAD_STATE_SET(x[16], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X17: THREAD_STATE_SET(x[17], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X18: THREAD_STATE_SET(x[18], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X19: THREAD_STATE_SET(x[19], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X20: THREAD_STATE_SET(x[20], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X21: THREAD_STATE_SET(x[21], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X22: THREAD_STATE_SET(x[22], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X23: THREAD_STATE_SET(x[23], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X24: THREAD_STATE_SET(x[24], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X25: THREAD_STATE_SET(x[25], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X26: THREAD_STATE_SET(x[26], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X27: THREAD_STATE_SET(x[27], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_X28: THREAD_STATE_SET(x[28], thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_FP: THREAD_STATE_SET_PTR(fp, thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_SP: THREAD_STATE_SET_PTR(sp, thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_LR: THREAD_STATE_SET_FPTR(lr, thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_PC: THREAD_STATE_SET_FPTR(pc, thread.ts_64, ts, regnum, reg); break;
case PLCRASH_ARM64_CPSR: THREAD_STATE_SET(cpsr, thread.ts_64, ts, regnum, (uint32_t)reg); break;
default: __builtin_trap();
}
}
// PLCrashAsyncThread API
void plcrash_async_thread_state_set_reg (plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum, plcrash_greg_t reg) {
if (thread_state->arm_state.thread.ash.flavor == ARM_THREAD_STATE32) {
plcrash_async_thread_state_set_reg_32(thread_state, regnum, reg);
} else {
plcrash_async_thread_state_set_reg_64(thread_state, regnum, reg);
}
}
// PLCrashAsyncThread API
size_t plcrash_async_thread_state_get_reg_count (const plcrash_async_thread_state_t *thread_state) {
/* Last is an index value, so increment to get the count */
if (thread_state->arm_state.thread.ash.flavor == ARM_THREAD_STATE32) {
return PLCRASH_ARM_LAST_REG+1;
} else {
return PLCRASH_ARM64_LAST_REG+1;
}
}
static inline char const *plcrash_async_thread_state_get_reg_name_32 (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum) {
switch ((plcrash_arm_regnum_t) regnum) {
case PLCRASH_ARM_R0: return "r0";
case PLCRASH_ARM_R1: return "r1";
case PLCRASH_ARM_R2: return "r2";
case PLCRASH_ARM_R3: return "r3";
case PLCRASH_ARM_R4: return "r4";
case PLCRASH_ARM_R5: return "r5";
case PLCRASH_ARM_R6: return "r6";
case PLCRASH_ARM_R7: return "r7";
case PLCRASH_ARM_R8: return "r8";
case PLCRASH_ARM_R9: return "r9";
case PLCRASH_ARM_R10: return "r10";
case PLCRASH_ARM_R11: return "r11";
case PLCRASH_ARM_R12: return "r12";
case PLCRASH_ARM_SP: return "sp";
case PLCRASH_ARM_LR: return "lr";
case PLCRASH_ARM_PC: return "pc";
case PLCRASH_ARM_CPSR: return "cpsr";
}
/* Unsupported register is an implementation error (checked in unit tests) */
PLCF_DEBUG("Missing register name for register id: %d", regnum);
abort();
}
static inline char const *plcrash_async_thread_state_get_reg_name_64 (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum) {
switch ((plcrash_arm64_regnum_t) regnum) {
case PLCRASH_ARM64_X0: return "x0";
case PLCRASH_ARM64_X1: return "x1";
case PLCRASH_ARM64_X2: return "x2";
case PLCRASH_ARM64_X3: return "x3";
case PLCRASH_ARM64_X4: return "x4";
case PLCRASH_ARM64_X5: return "x5";
case PLCRASH_ARM64_X6: return "x6";
case PLCRASH_ARM64_X7: return "x7";
case PLCRASH_ARM64_X8: return "x8";
case PLCRASH_ARM64_X9: return "x9";
case PLCRASH_ARM64_X10: return "x10";
case PLCRASH_ARM64_X11: return "x11";
case PLCRASH_ARM64_X12: return "x12";
case PLCRASH_ARM64_X13: return "x13";
case PLCRASH_ARM64_X14: return "x14";
case PLCRASH_ARM64_X15: return "x15";
case PLCRASH_ARM64_X16: return "x16";
case PLCRASH_ARM64_X17: return "x17";
case PLCRASH_ARM64_X18: return "x18";
case PLCRASH_ARM64_X19: return "x19";
case PLCRASH_ARM64_X20: return "x20";
case PLCRASH_ARM64_X21: return "x21";
case PLCRASH_ARM64_X22: return "x22";
case PLCRASH_ARM64_X23: return "x23";
case PLCRASH_ARM64_X24: return "x24";
case PLCRASH_ARM64_X25: return "x25";
case PLCRASH_ARM64_X26: return "x26";
case PLCRASH_ARM64_X27: return "x27";
case PLCRASH_ARM64_X28: return "x28";
case PLCRASH_ARM64_FP: return "fp";
case PLCRASH_ARM64_SP: return "sp";
case PLCRASH_ARM64_LR: return "lr";
case PLCRASH_ARM64_PC: return "pc";
case PLCRASH_ARM64_CPSR: return "cpsr";
}
/* Unsupported register is an implementation error (checked in unit tests) */
PLCF_DEBUG("Missing register name for register id: %d", regnum);
abort();
}
// PLCrashAsyncThread API
char const *plcrash_async_thread_state_get_reg_name (const plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum) {
if (thread_state->arm_state.thread.ash.flavor == ARM_THREAD_STATE32) {
return plcrash_async_thread_state_get_reg_name_32(thread_state, regnum);
} else {
return plcrash_async_thread_state_get_reg_name_64(thread_state, regnum);
}
}
// PLCrashAsyncThread API
void plcrash_async_thread_state_clear_volatile_regs (plcrash_async_thread_state_t *thread_state) {
const plcrash_regnum_t *table;
size_t table_count = 0;
if (thread_state->arm_state.thread.ash.flavor == ARM_THREAD_STATE32) {
table = arm_nonvolatile_registers;
table_count = sizeof(arm_nonvolatile_registers) / sizeof(arm_nonvolatile_registers[0]);
} else {
table = arm64_nonvolatile_registers;
table_count = sizeof(arm64_nonvolatile_registers) / sizeof(arm64_nonvolatile_registers[0]);
}
size_t reg_count = plcrash_async_thread_state_get_reg_count(thread_state);
for (size_t reg = 0; reg < reg_count; reg++) {
/* Skip unset registers */
if (!plcrash_async_thread_state_has_reg(thread_state, (uint32_t)reg))
continue;
/* Check for the register in the preservation table */
bool preserved = false;
for (size_t i = 0; i < table_count; i++) {
if (table[i] == reg) {
preserved = true;
break;
}
}
/* If not preserved, clear */
if (!preserved)
plcrash_async_thread_state_clear_reg(thread_state, (uint32_t)reg);
}
}
// PLCrashAsyncThread API
bool plcrash_async_thread_state_map_reg_to_dwarf (plcrash_async_thread_state_t *thread_state, plcrash_regnum_t regnum, uint64_t *dwarf_reg) {
const struct dwarf_register_table *table;
size_t table_count = 0;
if (thread_state->arm_state.thread.ash.flavor == ARM_THREAD_STATE32) {
table = arm_dwarf_table;
table_count = sizeof(arm_dwarf_table) / sizeof(arm_dwarf_table[0]);
} else {
table = arm64_dwarf_table;
table_count = sizeof(arm64_dwarf_table) / sizeof(arm64_dwarf_table[0]);
}
for (size_t i = 0; i < table_count; i++) {
if (table[i].regnum == regnum) {
*dwarf_reg = table[i].dwarf_value;
return true;
}
}
/* Unknown register. */
return false;
}
// PLCrashAsyncThread API
bool plcrash_async_thread_state_map_dwarf_to_reg (const plcrash_async_thread_state_t *thread_state, uint64_t dwarf_reg, plcrash_regnum_t *regnum) {
const struct dwarf_register_table *table;
size_t table_count = 0;
if (thread_state->arm_state.thread.ash.flavor == ARM_THREAD_STATE32) {
table = arm_dwarf_table;
table_count = sizeof(arm_dwarf_table) / sizeof(arm_dwarf_table[0]);
} else {
table = arm64_dwarf_table;
table_count = sizeof(arm64_dwarf_table) / sizeof(arm64_dwarf_table[0]);
}
for (size_t i = 0; i < table_count; i++) {
if (table[i].dwarf_value == dwarf_reg) {
*regnum = table[i].regnum;
return true;
}
}
/* Unknown DWARF register. */
return false;
}
#endif /* __arm__ || __arm64__ */