+
+// x86_64
+
+#elif __i386__
+
+#define OTOOL "/usr/bin/xcrun otool -arch i386 "
+
+typedef uint8_t insn_t;
+#define BREAK_INSN ((insn_t)0xcc) // int3
+
+uintptr_t eip = 0;
+uintptr_t esp = 0;
+uintptr_t ebx = 0;
+uintptr_t ebp = 0;
+uintptr_t edi = 0;
+uintptr_t esi = 0;
+uintptr_t espfix = 0;
+
+void handle_exception(i386_thread_state_t *state)
+{
+ unw_cursor_t curs;
+ unw_word_t reg;
+ int err;
+ int step;
+
+ err = unw_init_local(&curs, (unw_context_t *)state);
+ testassert(!err);
+
+ step = unw_step(&curs);
+ testassert(step == UNW_STEP_SUCCESS);
+
+ err = unw_get_reg(&curs, UNW_REG_IP, ®);
+ testassert(!err);
+ testassert(reg == eip);
+
+ err = unw_get_reg(&curs, UNW_X86_ESP, ®);
+ testassert(!err);
+ testassert(reg == esp);
+
+ err = unw_get_reg(&curs, UNW_X86_EBX, ®);
+ testassert(!err);
+ testassert(reg == ebx);
+
+ err = unw_get_reg(&curs, UNW_X86_EBP, ®);
+ testassert(!err);
+ testassert(reg == ebp);
+
+ err = unw_get_reg(&curs, UNW_X86_EDI, ®);
+ testassert(!err);
+ testassert(reg == edi);
+
+ err = unw_get_reg(&curs, UNW_X86_ESI, ®);
+ testassert(!err);
+ testassert(reg == esi);
+
+
+ // set thread state to unwound state
+ state->__eip = eip;
+ state->__esp = esp + espfix;
+ state->__ebx = ebx;
+ state->__ebp = ebp;
+ state->__edi = edi;
+ state->__esi = esi;
+
+ caught = true;
+}
+
+
+void sigtrap(int sig, siginfo_t *info, void *cc)
+{
+ ucontext_t *uc = (ucontext_t *)cc;
+ mcontext_t mc = (mcontext_t)uc->uc_mcontext;
+
+ testprintf(" handled\n");
+
+ testassert(sig == SIGTRAP);
+ testassert((uintptr_t)info->si_addr-1 == clobbered);
+
+ handle_exception(&mc->__ss);
+ // handle_exception changed register state for continuation
+}
+
+__asm__(
+"\n .text"
+"\n .globl _callit"
+"\n _callit:"
+// save sp and return address to variables
+"\n call 1f"
+"\n 1: popl %edx"
+"\n movl (%esp), %eax"
+"\n movl %eax, _eip-1b(%edx)"
+"\n movl %esp, _esp-1b(%edx)"
+"\n addl $4, _esp-1b(%edx)" // rewind to pre-call value
+"\n movl $0, _espfix-1b(%edx)"
+// save other non-volatile registers to variables
+"\n movl %ebx, _ebx-1b(%edx)"
+"\n movl %ebp, _ebp-1b(%edx)"
+"\n movl %edi, _edi-1b(%edx)"
+"\n movl %esi, _esi-1b(%edx)"
+"\n jmpl *12(%esp)"
+ );
+
+__asm__(
+"\n .text"
+"\n .globl _callit_stret"
+"\n _callit_stret:"
+// save sp and return address to variables
+"\n call 1f"
+"\n 1: popl %edx"
+"\n movl (%esp), %eax"
+"\n movl %eax, _eip-1b(%edx)"
+"\n movl %esp, _esp-1b(%edx)"
+"\n addl $4, _esp-1b(%edx)" // rewind to pre-call value
+"\n movl $4, _espfix-1b(%edx)"
+// save other non-volatile registers to variables
+"\n movl %ebx, _ebx-1b(%edx)"
+"\n movl %ebp, _ebp-1b(%edx)"
+"\n movl %edi, _edi-1b(%edx)"
+"\n movl %esi, _esi-1b(%edx)"
+"\n jmpl *16(%esp)"
+ );
+
+
+// i386
+#elif __arm64__
+
+#include <sys/ucontext.h>
+
+// runs on iOS device, no xcrun command present
+#define OTOOL "/usr/bin/otool -arch arm64 "
+
+typedef uint32_t insn_t;
+#define BREAK_INSN ((insn_t)0xd4200020) // brk #1
+
+uintptr_t x19 = 0;
+uintptr_t x20 = 0;
+uintptr_t x21 = 0;
+uintptr_t x22 = 0;
+uintptr_t x23 = 0;
+uintptr_t x24 = 0;
+uintptr_t x25 = 0;
+uintptr_t x26 = 0;
+uintptr_t x27 = 0;
+uintptr_t x28 = 0;
+uintptr_t fp = 0;
+uintptr_t sp = 0;
+uintptr_t pc = 0;
+
+void handle_exception(arm_thread_state64_t *state)
+{
+ unw_cursor_t curs;
+ unw_word_t reg;
+ int err;
+ int step;
+
+ err = unw_init_local(&curs, (unw_context_t *)state);
+ testassert(!err);
+
+ step = unw_step(&curs);
+ testassert(step == UNW_STEP_SUCCESS);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X19, ®);
+ testassert(!err);
+ testassert(reg == x19);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X20, ®);
+ testassert(!err);
+ testassert(reg == x20);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X21, ®);
+ testassert(!err);
+ testassert(reg == x21);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X22, ®);
+ testassert(!err);
+ testassert(reg == x22);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X23, ®);
+ testassert(!err);
+ testassert(reg == x23);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X24, ®);
+ testassert(!err);
+ testassert(reg == x24);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X25, ®);
+ testassert(!err);
+ testassert(reg == x25);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X26, ®);
+ testassert(!err);
+ testassert(reg == x26);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X27, ®);
+ testassert(!err);
+ testassert(reg == x27);
+
+ err = unw_get_reg(&curs, UNW_ARM64_X28, ®);
+ testassert(!err);
+ testassert(reg == x28);
+
+ err = unw_get_reg(&curs, UNW_ARM64_FP, ®);
+ testassert(!err);
+ testassert(reg == fp);
+
+ err = unw_get_reg(&curs, UNW_ARM64_SP, ®);
+ testassert(!err);
+ testassert(reg == sp);
+
+ err = unw_get_reg(&curs, UNW_REG_IP, ®);
+ testassert(!err);
+ testassert(reg == pc);
+
+ // libunwind restores PC into LR and doesn't track LR
+ // err = unw_get_reg(&curs, UNW_ARM64_LR, ®);
+ // testassert(!err);
+ // testassert(reg == lr);
+
+ // set thread state to unwound state
+ state->__x[19] = x19;
+ state->__x[20] = x20;
+ state->__x[20] = x21;
+ state->__x[22] = x22;
+ state->__x[23] = x23;
+ state->__x[24] = x24;
+ state->__x[25] = x25;
+ state->__x[26] = x26;
+ state->__x[27] = x27;
+ state->__x[28] = x28;
+ state->__fp = fp;
+ state->__lr = pc; // libunwind restores PC into LR
+ state->__sp = sp;
+ state->__pc = pc;
+
+ caught = true;
+}
+
+
+void sigtrap(int sig, siginfo_t *info, void *cc)
+{
+ ucontext_t *uc = (ucontext_t *)cc;
+ struct __darwin_mcontext64 *mc = (struct __darwin_mcontext64 *)uc->uc_mcontext;
+
+ testprintf(" handled\n");
+
+ testassert(sig == SIGTRAP);
+ testassert((uintptr_t)info->si_addr == clobbered);
+
+ handle_exception(&mc->__ss);
+ // handle_exception changed register state for continuation
+}
+
+
+__asm__(
+"\n .text"
+"\n .globl _callit"
+"\n _callit:"
+// save sp and return address to variables
+"\n mov x16, sp"
+"\n adrp x17, _sp@PAGE"
+"\n str x16, [x17, _sp@PAGEOFF]"
+"\n adrp x17, _pc@PAGE"
+"\n str lr, [x17, _pc@PAGEOFF]"
+// save other non-volatile registers to variables
+"\n adrp x17, _x19@PAGE"
+"\n str x19, [x17, _x19@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x20, [x17, _x20@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x21, [x17, _x21@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x22, [x17, _x22@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x23, [x17, _x23@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x24, [x17, _x24@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x25, [x17, _x25@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x26, [x17, _x26@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x27, [x17, _x27@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str x28, [x17, _x28@PAGEOFF]"
+"\n adrp x17, _x19@PAGE"
+"\n str fp, [x17, _fp@PAGEOFF]"
+"\n br x2"
+ );
+
+
+// arm64
+#else
+
+#error unknown architecture
+
+#endif
+
+
+insn_t set(uintptr_t dst, insn_t newvalue)