171 lines
4.4 KiB
ArmAsm
171 lines
4.4 KiB
ArmAsm
#include "RCTDefines.h"
|
|
|
|
#if RCT_DEV && defined(__x86_64__)
|
|
|
|
.globl _RCTProfileTrampoline
|
|
_RCTProfileTrampoline:
|
|
|
|
/**
|
|
* Saves all the state so we can restore it before calling the functions being
|
|
* profiled. Registers have the same value at the point of the function call,
|
|
* the only thing we can change is the return value, so we return to
|
|
* `RCTProfileTrampoline` rather than to its caller.
|
|
*
|
|
* Save all the parameters registers (%rdi, %rsi, %rdx, %rcx, %r8, %r9), they
|
|
* have the 6 first arguments of the function call, and %rax which in special
|
|
* cases might be a pointer used for struct returns.
|
|
*
|
|
* We have to save %r12 since its value should be preserved across function
|
|
* calls and we'll use it to keep the stack pointer
|
|
*/
|
|
pushq %rdi
|
|
pushq %rsi
|
|
pushq %rdx
|
|
pushq %rcx
|
|
pushq %r8
|
|
pushq %r9
|
|
pushq %rax
|
|
pushq %r12
|
|
|
|
/**
|
|
* Store the stack pointer in the callee saved register %r12 and align the
|
|
* stack - it has to 16-byte aligned at the point of the function call
|
|
*/
|
|
movq %rsp, %r12
|
|
andq $-0x10, %rsp
|
|
|
|
/**
|
|
* void RCTProfileGetImplementation(id object, SEL selector)
|
|
*
|
|
* This is a C function defined in `RCTProfile.m`, the object and the selector
|
|
* already have to be on %rdi and %rsi respectively, as in any ObjC call.
|
|
*/
|
|
callq _RCTProfileGetImplementation
|
|
|
|
// Restore/unalign the stack pointer, so we can access the registers we stored
|
|
movq %r12, %rsp
|
|
|
|
/**
|
|
* pop %r12 before pushing %rax, which contains the address of the actual
|
|
* function we have to call, than we keep %r12 at the bottom of the stack to
|
|
* reference the stack pointer
|
|
*/
|
|
popq %r12
|
|
pushq %rax
|
|
pushq %r12
|
|
|
|
// align stack
|
|
movq %rsp, %r12
|
|
andq $-0x10, %rsp
|
|
|
|
/**
|
|
* Allocate memory to save parent before start profiling: the address is put
|
|
* at the bottom of the stack at the function call, so ret can actually return
|
|
* to the caller. In this case it has the address of RCTProfileTrampoline's
|
|
* caller where we'll have to return to after we're finished.
|
|
*
|
|
* We can't store it on the stack or in any register, since we have to be in
|
|
* the exact same state we were at the moment we were called, so the solution
|
|
* is to allocate a tiny bit of memory to save this address
|
|
*/
|
|
|
|
// allocate 16 bytes
|
|
movq $0x10, %rdi
|
|
callq _malloc
|
|
|
|
// store the initial value of calle saved registers %r13 and %r14
|
|
movq %r13, 0x0(%rax)
|
|
movq %r14, 0x8(%rax)
|
|
|
|
// mov the pointers we need to the callee saved registers
|
|
movq 0x48(%rsp), %r13 // caller of RCTProfileTrampoline
|
|
movq %rax, %r14 // allocated memory's address
|
|
|
|
/**
|
|
* Move self and cmd back to the registers and call start profile: it uses
|
|
* the object and the selector to label the call in the profile.
|
|
*/
|
|
movq 0x40(%r12), %rdi // object
|
|
movq 0x38(%r12), %rsi // selector
|
|
|
|
// void RCTProfileTrampolineStart(id, SEL) in RCTProfile.m
|
|
callq _RCTProfileTrampolineStart
|
|
|
|
// unalign the stack and restore %r12
|
|
movq %r12, %rsp
|
|
popq %r12
|
|
|
|
// Restore registers for actual function call
|
|
popq %r11
|
|
popq %rax
|
|
popq %r9
|
|
popq %r8
|
|
popq %rcx
|
|
popq %rdx
|
|
popq %rsi
|
|
popq %rdi
|
|
|
|
/**
|
|
* delete parent caller (saved in %r13) `call` will add the new address so
|
|
* we return to RCTProfileTrampoline rather than to its caller
|
|
*/
|
|
addq $0x8, %rsp
|
|
|
|
// call the actual function and save the return value
|
|
callq *%r11
|
|
pushq %rax
|
|
|
|
// align stack
|
|
pushq %r12
|
|
movq %rsp, %r12
|
|
andq $-0x10, %rsp
|
|
|
|
// void RCTProfileTrampolineEnd(void) in RCTProfile.m - just ends this profile
|
|
callq _RCTProfileTrampolineEnd
|
|
|
|
// unalign stack and restore %r12
|
|
movq %r12, %rsp
|
|
popq %r12
|
|
|
|
// save the return of the actual function call
|
|
popq %rax
|
|
|
|
/**
|
|
* Restore the initial value of the callee saved registers, saved in the
|
|
* memory allocated.
|
|
*/
|
|
movq %r13, %rcx
|
|
movq %r14, %rdx
|
|
movq 0x0(%r14), %r13
|
|
movq 0x8(%r14), %r14
|
|
|
|
/**
|
|
* Save caller address and actual function return (previously in the allocated
|
|
* memory) and align the stack
|
|
*/
|
|
pushq %rcx
|
|
pushq %rax
|
|
pushq %r12
|
|
movq %rsp, %r12
|
|
andq $-0x10, %rsp
|
|
|
|
// Free the memory allocated to stash callee saved registers
|
|
movq %rdx, %rdi
|
|
callq _free
|
|
|
|
// unalign stack and restore %r12
|
|
movq %r12, %rsp
|
|
popq %r12
|
|
|
|
/**
|
|
* pop the caller address to %rcx and the actual function return value to
|
|
* %rax, so it's the return value of RCTProfileTrampoline
|
|
*/
|
|
popq %rax
|
|
popq %rcx
|
|
|
|
// jump to caller
|
|
jmpq *%rcx
|
|
|
|
#endif
|