Source/PLCrashAsyncThread_current.S (240 lines of code) (raw):
/*
* Author: Landon Fuller <landonf@plausible.coop>
*
* Copyright (c) 2012-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_current_defs.h"
#include "PLCrashNamespace.h"
/*
* plcrash_error_t plcrash_async_thread_state_current (plcrash_async_thread_state_current_callback callback,
* void *context);
*/
/* Assembler underscore-prefixed symbol handling for use with the PLCrashNamespace.h symbol namespacing mechanism. */
#ifdef PLCRASHREPORTER_PREFIX
#define _SYM_2(_name) _ ## _name
#define _SYM_1(_name) _SYM_2(_name)
#define SYM(_name) _SYM_1(_name)
#else
#define SYM(_name) _ ## _name
#endif
.text
#if __arm__ || __arm64__
.align 2
#endif
.globl SYM(plcrash_async_thread_state_current)
SYM(plcrash_async_thread_state_current):
#if __x86_64__
pushq %rbp
movq %rsp, %rbp
subq $720, %rsp // Size of 712 + 8 bytes for required alignment
#define MOVQ(reg, offset) movq %##reg, offset(%rsp)
// These assumed offsets are compile-time validated in PLCrashLogWriter_trampoline.m, and are ABI-stable.
MOVQ (rax, 16)
MOVQ (rbx, 24);
MOVQ (rcx, 32);
MOVQ (rdx, 40);
MOVQ (rdi, 48);
MOVQ (rsi, 56);
/* ->rbp: Use our saved copy of the caller's frame pointer */
movq (%rbp), %rcx
movq %rcx, 64(%rsp)
/* ->rsp: Use our saved copy of the caller's stack pointer. */
MOVQ (rbp, 72);
MOVQ (r8, 80);
MOVQ (r9, 88);
MOVQ (r10, 96);
MOVQ (r11, 104);
MOVQ (r12, 112);
MOVQ (r13, 120);
MOVQ (r14, 128);
MOVQ (r15, 136);
/* Use the return address for our RIP value */
movq 0x8(%rbp), %rcx
movq %rcx, 144(%rsp)
/* Fetch the FLAGS state via the stack */
pushfq
popq %rcx
movq %rcx, 152(%rsp)
/* The segment registers are 16 bit. Zero out the rest */
/* Do this by moving it to a 64 bit register and then to memory. */
/* The move to via the 64 bit register will automatically clear out the bits */
movq %cs, %rcx
movq %rcx, 160(%rsp)
movq %fs, %rcx
movq %rcx, 168(%rsp)
movq %gs, %rcx
movq %rcx, 176(%rsp)
/* Move mctx to 3rd argument of plcrash_log_writer_write_curthread_stub */
movq %rsp, %rdx
xorb %al, %al
callq SYM(plcrash_async_thread_state_current_stub)
addq $720, %rsp
popq %rbp
ret
#elif __i386__
pushl %ebp
movl %esp, %ebp
subl $616, %esp // Size of 600 for context + 4 bytes for required alignment + 12 bytes for call arguments
// OFF is the offset from the top of the stack to the mctx structure
#define OFF 16
#define MOVL(reg, offset) movl %##reg, OFF+offset(%esp)
// These assumed offsets are compile-time validated in PLCrashLogWriter_trampoline.m, and are ABI-stable.
/* es */
movl $0, 0+OFF(%esp) // trapno
/* ss */
MOVL (eax, 12)
MOVL (ebx, 16);
MOVL (ecx, 20);
MOVL (edx, 24);
MOVL (edi, 28);
MOVL (esi, 32);
/* ->ebp: Use our saved copy of the caller's frame pointer */
movl (%ebp), %ecx
MOVL (ecx, 36)
/* ->esp: Use our saved copy of the caller's stack pointer. */
MOVL (ebp, 40);
/* ss.eflags */
pushf
pop %eax
movl %eax, 48+OFF(%esp)
/* Use the return address for our RIP value */
movl 0x4(%ebp), %eax
movl %eax, 52+OFF(%esp)
/* The segment registers are 16 bit. Zero out the rest */
xorl %eax, %eax
movl %cs, %eax
movl %eax, OFF+56(%esp);
movl %ds, %eax
movl %eax, OFF+60(%esp);
movl %es, %eax
movl %eax, OFF+64(%esp);
movl %fs, %eax
movl %eax, OFF+68(%esp);
movl %gs, %eax
movl %eax, OFF+72(%esp);
#undef MOVL
/* Set up our argument stack: writer (arg0), image_list, file, siginfo, mctx */
/* Set up our argument stack: callback (arg0), context, mctx */
movl 8(%ebp), %eax // arg0: callback
movl %eax, (%esp)
movl 12(%ebp), %eax // arg1: context
movl %eax, 4(%esp)
movl %esp, %eax // arg3: mctx
addl $OFF, %eax
movl %eax, 8(%esp)
call SYM(plcrash_async_thread_state_current_stub)
addl $616, %esp
popl %ebp
ret
#elif defined(__arm64__)
stp fp, lr, [sp, #-16]!
add fp, sp, 0
sub sp, sp, #816 // Size of 816 for context
// These assumed offsets are compile-time validated in PLCrashLogWriter_trampoline.m, and are ABI-stable.
/* Save x0 before we stomp it. The destination address is our SP + 16 (offset to x[] array) */
str x0, [sp, #16]
/* Save the mcontext_t pointer */
mov x0, sp
/* Write out GP registers. The base offset of x[1] is SP + 24. */
add x0, x0, #24
stp x1, x2, [x0], #16
stp x3, x4, [x0], #16
stp x5, x6, [x0], #16
stp x7, x8, [x0], #16
stp x9, x10, [x0], #16
stp x11, x12, [x0], #16
stp x13, x14, [x0], #16
stp x15, x16, [x0], #16
stp x17, x18, [x0], #16
stp x19, x20, [x0], #16
stp x21, x22, [x0], #16
stp x23, x24, [x0], #16
stp x25, x26, [x0], #16
stp x27, x28, [x0], #16
/* Fetch caller's frame pointer, save to fp */
ldr x2, [fp]
str x2, [x0], #8
/* Fetch the link register from our caller's frame */
ldr x1, [x2, #8]
str x1, [x0], #8
/* Use the caller's SP value */
add x1, fp, #16 // account for the 16 byte push in prologue
str x1, [x0], #8
/* Use the return address as the PC value */
ldr x1, [fp, #8]
str x1, [x0], #8
/* Fetch CPSR */
// mrs x1, spsr_el1 // TODO_ARM64: The architecture manual says we can read CPSR bits individually,
// str x1, [x0], #8 // but we still need to figure out how.
str wzr, [x0], #8 // In the meantime, just zero out the 32-bit value.
/* Restore the r0-r1 argument values. The source address is our SP + 16 (offset to r[] array). */
ldr x0, [sp, #16];
ldr x1, [sp, #24];
/* Provide arg 3 (mctx) */
mov x2, sp
bl SYM(plcrash_async_thread_state_current_stub)
add sp, fp, 0
ldp fp, lr, [sp], #16
ret lr
#elif defined(__arm__)
.arm
push {r7, lr}
mov r7, sp
sub sp, sp, #340 // Size of 340 for context
// These assumed offsets are compile-time validated in PLCrashLogWriter_trampoline.m, and are ABI-stable.
/* Save r0 before we stomp it. The destination address is our SP + 12 (offset to r[] array) */
str r0, [sp, #12]
/* Save the mcontext_t pointer */
mov r0, sp
/* Write out GP registers. The offset is r[1]. */
add r0, r0, #16
stmia r0, {r1-r12}
/* Fetch our caller's frame pointer */
ldr r2, [r7]
/* Overwrite r[7] with the caller's fp value */
str r2, [r0, #24]
/* Use the caller's SP value */
add r1, r7, #8 // account for the 2 byte push in prologue
str r1, [r0, #48] // 64 - 16 byte offset to r[1]
/* Fetch the link register from our caller's frame */
ldr r1, [r2, #4]
str r1, [r0, #52] // 68 - 16 byte offset to r[1]
/* Use the return address for our PC value */
ldr r1, [r7, #4]
str r1, [r0, #56] // 72 - 16 byte offset to r[1]
/* Fetch CPSR */
mrs r1, cpsr
str r1, [r0, #60] // 76 - 16 byte offset to r[1]
/* Restore the r0-r1 argument values. The source address is our SP + 12 (offset to r[] array). */
ldr r0, [sp, #12];
ldr r1, [sp, #16];
/* Provide arg 3 (mctx) */
mov r2, sp
bl SYM(plcrash_async_thread_state_current_stub)
mov sp, r7
pop {r7, pc}
#else
#error Unsupported Platform
#endif