Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
191 lines (163 sloc) 5.24 KB
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "RCTDefines.h"
#include "RCTMacros.h"
#if RCT_PROFILE && defined(__x86_64__)
.globl SYMBOL_NAME(RCTProfileTrampoline)
SYMBOL_NAME(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
*/
subq $0x80+8, %rsp // 8 x 16-bytes xmm registers + 8-bytes alignment
movdqa %xmm0, 0x70(%rsp)
movdqa %xmm1, 0x60(%rsp)
movdqa %xmm2, 0x50(%rsp)
movdqa %xmm3, 0x40(%rsp)
movdqa %xmm4, 0x30(%rsp)
movdqa %xmm5, 0x20(%rsp)
movdqa %xmm6, 0x10(%rsp)
movdqa %xmm7, 0x00(%rsp)
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 SYMBOL_NAME_PIC(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 SYMBOL_NAME_PIC(RCTProfileMalloc)
// 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 0xd8(%rsp), %r13 // caller of RCTProfileTrampoline (0xd8 is stack top)
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 SYMBOL_NAME_PIC(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
movdqa 0x00(%rsp), %xmm7
movdqa 0x10(%rsp), %xmm6
movdqa 0x20(%rsp), %xmm5
movdqa 0x30(%rsp), %xmm4
movdqa 0x40(%rsp), %xmm3
movdqa 0x50(%rsp), %xmm2
movdqa 0x60(%rsp), %xmm1
movdqa 0x70(%rsp), %xmm0
addq $0x80+8, %rsp
/**
* 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
pushq %rdx
subq $0x20, %rsp // 2 16-bytes xmm register
movdqa %xmm0, 0x00(%rsp)
movdqa %xmm1, 0x10(%rsp)
// void RCTProfileTrampolineEnd(void) in RCTProfile.m - just ends this profile
callq SYMBOL_NAME_PIC(RCTProfileTrampolineEnd)
/**
* Restore the initial value of the callee saved registers, saved in the
* memory allocated.
*/
movq %r13, %rcx
movq %r14, %rdi
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 %r12
movq %rsp, %r12
andq $-0x10, %rsp
// Free the memory allocated to stash callee saved registers
callq SYMBOL_NAME_PIC(RCTProfileFree)
// unalign stack and restore %r12
movq %r12, %rsp
popq %r12
/**
* pop the caller address to %rcx and the actual function return value(s)
* so it's the return value of RCTProfileTrampoline
*/
popq %rcx
movdqa 0x00(%rsp), %xmm0
movdqa 0x10(%rsp), %xmm1
addq $0x20, %rsp
popq %rdx
popq %rax
// jump to caller
jmpq *%rcx
#endif