plframe_error_t plframe_get_reg (plframe_cursor_t *cursor, plframe_regnum_t regnum, plframe_greg_t *reg) {
    ucontext_t *uap = cursor->uap;
    tinyunw_regnum_t unwreg;
    
    if (cursor->nframe != 0) {
        if (regnum == PLFRAME_X86_64_RIP) {
            tinyunw_get_register(&cursor->unwind_cursor, TINYUNW_X86_64_RIP, reg);
            return PLFRAME_ESUCCESS;
        }
        return PLFRAME_ENOTSUP;
    }
    
    #define MAP_REG(reg)		\
        case PLFRAME_X86_64_ ## reg:	\
            unwreg = TINYUNW_X86_64_ ## reg;	\
            break
    
    switch (regnum) {
        MAP_REG(RAX);
        MAP_REG(RBX);
        MAP_REG(RCX);
        MAP_REG(RDX);
        MAP_REG(RDI);
        MAP_REG(RSI);
        MAP_REG(RBP);
        MAP_REG(RSP);
        MAP_REG(R10);
        MAP_REG(R11);
        MAP_REG(R12);
        MAP_REG(R13);
        MAP_REG(R14);
        MAP_REG(R15);
        MAP_REG(RIP);
        
        /* These registers are not available through the libtinyunwind API, as
           they either can not be easily or safely read at async-signal time
           from the current thread, or have no meaning in x86_64 anyway. */
        case PLFRAME_X86_64_RFLAGS:
            RETGEN(rflags, ss, uap, reg);
            
        case PLFRAME_X86_64_CS:
            RETGEN(cs, ss, uap, reg);
            
        case PLFRAME_X86_64_FS:
            RETGEN(fs, ss, uap, reg);
            
        case PLFRAME_X86_64_GS:
            RETGEN(gs, ss, uap, reg);
        
        default:
            return PLFRAME_ENOTSUP;
    }
    
    #undef MAP_REG
    
    return plframe_error_from_tinyunwerror(tinyunw_get_register(&cursor->unwind_cursor, unwreg, reg));
}
/**
 * @internal
 * 64-bit implementation of plcrash_async_thread_state_get_reg()
 */
static apigee_plcrash_greg_t plcrash_async_thread_state_get_reg_64 (const apigee_plcrash_async_thread_state_t *thread_state, apigee_plcrash_regnum_t regnum) {
    const apigee_plcrash_async_thread_state_t *ts = thread_state;

    switch (regnum) {
        case APIGEE_PLCRASH_X86_64_RAX:
            RETGEN(rax, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_RBX:
            RETGEN(rbx, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_RCX:
            RETGEN(rcx, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_RDX:
            RETGEN(rdx, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_RDI:
            RETGEN(rdi, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_RSI:
            RETGEN(rsi, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_RBP:
            RETGEN(rbp, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_RSP:
            RETGEN(rsp, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_R8:
            RETGEN(r8, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_R9:
            RETGEN(r9, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_R10:
            RETGEN(r10, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_R11:
            RETGEN(r11, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_R12:
            RETGEN(r12, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_R13:
            RETGEN(r13, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_R14:
            RETGEN(r14, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_R15:
            RETGEN(r15, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_RIP:
            RETGEN(rip, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_RFLAGS:
            RETGEN(rflags, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_CS:
            RETGEN(cs, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_FS:
            RETGEN(fs, thread.uts.ts64, ts);
            
        case APIGEE_PLCRASH_X86_64_GS:
            RETGEN(gs, thread.uts.ts64, ts);
            
        default:
            // Unsupported register
            __builtin_trap();
    }

    /* Should not be reachable */
    return 0;
}
/**
 * @internal
 * 32-bit implementation of plcrash_async_thread_state_get_reg()
 */
static apigee_plcrash_greg_t plcrash_async_thread_state_get_reg_32 (const apigee_plcrash_async_thread_state_t *thread_state, apigee_plcrash_regnum_t regnum) {
    const apigee_plcrash_async_thread_state_t *ts = thread_state;

    /* All word-sized registers */
    switch (regnum) {
        case APIGEE_PLCRASH_X86_EAX:
            RETGEN(eax, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_EDX:
            RETGEN(edx, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_ECX:
            RETGEN(ecx, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_EBX:
            RETGEN(ebx, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_EBP:
            RETGEN(ebp, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_ESI:
            RETGEN(esi, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_EDI:
            RETGEN(edi, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_ESP:
            RETGEN(esp, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_EIP:
            RETGEN(eip, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_EFLAGS:
            RETGEN(eflags, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_TRAPNO:
            RETGEN(trapno, exception.ues.es32, ts);
            
        case APIGEE_PLCRASH_X86_CS:
            RETGEN(cs, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_DS:
            RETGEN(ds, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_ES:
            RETGEN(es, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_FS:
            RETGEN(fs, thread.uts.ts32, ts);
            
        case APIGEE_PLCRASH_X86_GS:
            RETGEN(gs, thread.uts.ts32, ts);
            
        default:
            // Unsupported register
            __builtin_trap();
    }
    
    /* Shouldn't be reachable */
    return 0;
}
// PLFrameWalker API
Apigee_plframe_error_t Apigee_plframe_get_reg (Apigee_plframe_cursor_t *cursor, Apigee_plframe_regnum_t regnum, Apigee_plframe_greg_t *reg) {
    ucontext_t *uap = cursor->uap;
    
    /* Supported register for this context state? */
    if (cursor->fp[0] != NULL) {
        if (regnum == Apigee_PLFRAME_PDEF_REG_IP) {
            *reg = (Apigee_plframe_greg_t) cursor->fp[2] - 4; // offset by 1 instruction
            return Apigee_PLFRAME_ESUCCESS;
        }
        
        return Apigee_PLFRAME_ENOTSUP;
    }
    
    /* All GP registers */
    switch (regnum) {
        case Apigee_PLFRAME_PPC_SRR0:
            RETGEN(srr0, ss, uap, reg);
            
        case Apigee_PLFRAME_PPC_SRR1:
            RETGEN(srr1, ss, uap, reg);
            
        case Apigee_PLFRAME_PPC_DAR:
            RETGEN(dar, es, uap, reg);

        case Apigee_PLFRAME_PPC_DSISR:
            RETGEN(dsisr, es, uap, reg);

            
        case Apigee_PLFRAME_PPC_R0:
            RETGEN(r0, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R1:
            RETGEN(r1, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R2:
            RETGEN(r2, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R3:
            RETGEN(r3, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R4:
            RETGEN(r4, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R5:
            RETGEN(r5, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R6:
            RETGEN(r6, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R7:
            RETGEN(r7, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R8:
            RETGEN(r8, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R9:
            RETGEN(r9, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R10:
            RETGEN(r10, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R11:
            RETGEN(r11, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R12:
            RETGEN(r12, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R13:
            RETGEN(r13, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R14:
            RETGEN(r14, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R15:
            RETGEN(r15, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R16:
            RETGEN(r16, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R17:
            RETGEN(r17, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R18:
            RETGEN(r18, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R19:
            RETGEN(r19, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R20:
            RETGEN(r20, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R21:
            RETGEN(r21, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R22:
            RETGEN(r22, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R23:
            RETGEN(r23, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R24:
            RETGEN(r24, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R25:
            RETGEN(r25, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R26:
            RETGEN(r26, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R27:
            RETGEN(r27, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R28:
            RETGEN(r28, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R29:
            RETGEN(r29, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R30:
            RETGEN(r30, ss, uap, reg);
        case Apigee_PLFRAME_PPC_R31:
            RETGEN(r31, ss, uap, reg);
            
        case Apigee_PLFRAME_PPC_CR:
            RETGEN(cr, ss, uap, reg);
            
        case Apigee_PLFRAME_PPC_XER:
            RETGEN(xer, ss, uap, reg);
            
        case Apigee_PLFRAME_PPC_LR:
            RETGEN(lr, ss, uap, reg);
            
        case Apigee_PLFRAME_PPC_CTR:
            RETGEN(ctr, ss, uap, reg);
            
        case Apigee_PLFRAME_PPC_VRSAVE:
            RETGEN(vrsave, ss, uap, reg);

        default:
            // Unsupported register
            return Apigee_PLFRAME_EBADREG;
    }
    
    /* Shouldn't be reachable */
    return Apigee_PLFRAME_EUNKNOWN;
}
// PLFrameWalker API
plframe_error_t plframe_get_reg (plframe_cursor_t *cursor, plframe_regnum_t regnum, plframe_greg_t *reg) {
    ucontext_t *uap = cursor->uap;
    
    /* Supported register for this context state? */
    if (cursor->fp[0] != NULL) {
        if (regnum == PLFRAME_X86_64_RIP) {
            *reg = (plframe_greg_t) cursor->fp[1];
            return PLFRAME_ESUCCESS;
        }
        
        return PLFRAME_ENOTSUP;
    }

    switch (regnum) {
        case PLFRAME_X86_64_RAX:
            RETGEN(rax, ss, uap, reg);

        case PLFRAME_X86_64_RBX:
            RETGEN(rbx, ss, uap, reg);

        case PLFRAME_X86_64_RCX:
            RETGEN(rcx, ss, uap, reg);
            
        case PLFRAME_X86_64_RDX:
            RETGEN(rdx, ss, uap, reg);
            
        case PLFRAME_X86_64_RDI:
            RETGEN(rdi, ss, uap, reg);
            
        case PLFRAME_X86_64_RSI:
            RETGEN(rsi, ss, uap, reg);
            
        case PLFRAME_X86_64_RBP:
            RETGEN(rbp, ss, uap, reg);
            
        case PLFRAME_X86_64_RSP:
            RETGEN(rsp, ss, uap, reg);
            
        case PLFRAME_X86_64_R10:
            RETGEN(r10, ss, uap, reg);
            
        case PLFRAME_X86_64_R11:
            RETGEN(r11, ss, uap, reg);
            
        case PLFRAME_X86_64_R12:
            RETGEN(r12, ss, uap, reg);
            
        case PLFRAME_X86_64_R13:
            RETGEN(r13, ss, uap, reg);
            
        case PLFRAME_X86_64_R14:    
            RETGEN(r14, ss, uap, reg);
            
        case PLFRAME_X86_64_R15:
            RETGEN(r15, ss, uap, reg);
            
        case PLFRAME_X86_64_RIP:
            RETGEN(rip, ss, uap, reg);
            
        case PLFRAME_X86_64_RFLAGS:
            RETGEN(rflags, ss, uap, reg);
            
        case PLFRAME_X86_64_CS:
            RETGEN(cs, ss, uap, reg);
            
        case PLFRAME_X86_64_FS:
            RETGEN(fs, ss, uap, reg);
            
        case PLFRAME_X86_64_GS:
            RETGEN(gs, ss, uap, reg);
            
        default:
            // Unsupported register
            break;
    }
    
    return PLFRAME_ENOTSUP;
}