RCTProfileTrampoline-x86_64.S
5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#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