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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
|
; Licensed to the .NET Foundation under one or more agreements.
; The .NET Foundation licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.
; ==++==
;
;
; ==--==
ifdef FEATURE_COMINTEROP
include AsmMacros.inc
include asmconstants.inc
extern CallDescrWorkerUnwindFrameChainHandler:proc
extern ReverseComUnwindFrameChainHandler:proc
extern COMToCLRWorker:proc
extern JIT_FailFast:proc
extern s_gsCookie:qword
NESTED_ENTRY GenericComCallStub, _TEXT, ReverseComUnwindFrameChainHandler
;
; Set up a ComMethodFrame and call COMToCLRWorker.
;
; Stack frame layout:
;
; (stack parameters)
; ...
; r9
; r8
; rdx
; rcx
; UnmanagedToManagedFrame::m_ReturnAddress
; UnmanagedToManagedFrame::m_Datum
; Frame::m_Next
; __VFN_table <-- rsp + GenericComCallStub_ComMethodFrame_OFFSET
; GSCookie
; (optional padding to qword align xmm save area)
; xmm3
; xmm2
; xmm1
; xmm0 <-- rsp + GenericComCallStub_XMM_SAVE_OFFSET
; r12
; r13
; r14
; (optional padding to qword align rsp)
; callee's r9
; callee's r8
; callee's rdx
; callee's rcx
GenericComCallStub_STACK_FRAME_SIZE = 0
; ComMethodFrame MUST be the highest part of the stack frame, immediately
; below the return address and MethodDesc*, so that
; UnmanagedToManagedFrame::m_ReturnAddress and
; UnmanagedToManagedFrame::m_Datum are the right place.
GenericComCallStub_STACK_FRAME_SIZE = GenericComCallStub_STACK_FRAME_SIZE + (SIZEOF__ComMethodFrame - 8)
GenericComCallStub_ComMethodFrame_NEGOFFSET = GenericComCallStub_STACK_FRAME_SIZE
GenericComCallStub_STACK_FRAME_SIZE = GenericComCallStub_STACK_FRAME_SIZE + SIZEOF_GSCookie
; Ensure that the offset of the XMM save area will be 16-byte aligned.
if ((GenericComCallStub_STACK_FRAME_SIZE + 8) MOD 16) ne 0
GenericComCallStub_STACK_FRAME_SIZE = GenericComCallStub_STACK_FRAME_SIZE + 8
endif
; XMM save area MUST be immediately below GenericComCallStub
; (w/ alignment padding)
GenericComCallStub_STACK_FRAME_SIZE = GenericComCallStub_STACK_FRAME_SIZE + 4*16
GenericComCallStub_XMM_SAVE_NEGOFFSET = GenericComCallStub_STACK_FRAME_SIZE
; Add in the callee scratch area size.
GenericComCallStub_CALLEE_SCRATCH_SIZE = 4*8
GenericComCallStub_STACK_FRAME_SIZE = GenericComCallStub_STACK_FRAME_SIZE + GenericComCallStub_CALLEE_SCRATCH_SIZE
; Now we have the full size of the stack frame. The offsets have been computed relative to the
; top, so negate them to make them relative to the post-prologue rsp.
GenericComCallStub_ComMethodFrame_OFFSET = GenericComCallStub_STACK_FRAME_SIZE - GenericComCallStub_ComMethodFrame_NEGOFFSET
GenericComCallStub_XMM_SAVE_OFFSET = GenericComCallStub_STACK_FRAME_SIZE - GenericComCallStub_XMM_SAVE_NEGOFFSET
OFFSETOF_GSCookie = GenericComCallStub_ComMethodFrame_OFFSET - SIZEOF_GSCookie
.allocstack 8 ; UnmanagedToManagedFrame::m_Datum, pushed by prepad
;
; Allocate the remainder of the ComMethodFrame. The fields
; will be filled in by COMToCLRWorker
;
alloc_stack SIZEOF__ComMethodFrame - 10h
;
; Save ComMethodFrame* to pass to COMToCLRWorker
;
mov r10, rsp
alloc_stack GenericComCallStub_ComMethodFrame_OFFSET
;
; Save argument registers
;
SAVE_ARGUMENT_REGISTERS GenericComCallStub_STACK_FRAME_SIZE + 8h
;
; spill the fp args
;
SAVE_FLOAT_ARGUMENT_REGISTERS GenericComCallStub_XMM_SAVE_OFFSET
END_PROLOGUE
mov rcx, s_gsCookie
mov [rsp + OFFSETOF_GSCookie], rcx
;
; Call COMToCLRWorker. Note that the first parameter (pThread) is
; filled in by callee.
;
ifdef _DEBUG
mov rcx, 0cccccccccccccccch
endif
mov rdx, r10
call COMToCLRWorker
ifdef _DEBUG
mov rcx, s_gsCookie
cmp [rsp + OFFSETOF_GSCookie], rcx
je GoodGSCookie
call JIT_FailFast
GoodGSCookie:
endif ; _DEBUG
;
; epilogue
;
add rsp, GenericComCallStub_STACK_FRAME_SIZE
ret
NESTED_END GenericComCallStub, _TEXT
; ARG_SLOT COMToCLRDispatchHelperWithStack(DWORD dwStackSlots, // rcx
; ComMethodFrame *pFrame, // rdx
; PCODE pTarget, // r8
; PCODE pSecretArg, // r9
; INT_PTR pDangerousThis // rbp+40h
; );
NESTED_ENTRY COMToCLRDispatchHelperWithStack, _TEXT, CallDescrWorkerUnwindFrameChainHandler
ComMethodFrame_Arguments_OFFSET = SIZEOF__ComMethodFrame
ComMethodFrame_XMM_SAVE_OFFSET = GenericComCallStub_XMM_SAVE_OFFSET - GenericComCallStub_ComMethodFrame_OFFSET
push_nonvol_reg rdi ; save nonvolatile registers
push_nonvol_reg rsi ;
push_nonvol_reg rbp ;
set_frame rbp, 0 ; set frame pointer
END_PROLOGUE
;
; copy stack
;
lea rsi, [rdx + ComMethodFrame_Arguments_OFFSET]
add ecx, 4 ; outgoing argument homes
mov eax, ecx ; number of stack slots
shl eax, 3 ; compute number of argument bytes
add eax, 8h ; alignment padding
and rax, 0FFFFFFFFFFFFFFf0h ; for proper stack alignment, v-liti remove partial register stall
sub rsp, rax ; allocate argument list
mov rdi, rsp ; set destination argument list address
rep movsq ; copy arguments to the stack
; Stack layout:
;
; callee's rcx (to be loaded into rcx) <- rbp+40h
; r9 (to be loaded into r10)
; r8 (IL stub entry point)
; rdx (ComMethodFrame ptr)
; rcx (number of stack slots to repush)
; return address
; saved rdi
; saved rsi
; saved rbp <- rbp
; alignment
; (stack parameters)
; callee's r9
; callee's r8
; callee's rdx
; callee's rcx (not loaded into rcx) <- rsp
;
; load fp registers
;
movdqa xmm0, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 00h]
movdqa xmm1, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 10h]
movdqa xmm2, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 20h]
movdqa xmm3, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 30h]
;
; load secret arg and target
;
mov r10, r9
mov rax, r8
;
; load argument registers
;
mov rcx, [rbp + 40h] ; ignoring the COM IP at [rsp]
mov rdx, [rsp + 08h]
mov r8, [rsp + 10h]
mov r9, [rsp + 18h]
;
; call the target
;
call rax
; It is important to have an instruction between the previous call and the epilog.
; If the return address is in epilog, OS won't call personality routine because
; it thinks personality routine does not help in this case.
nop
;
; epilog
;
lea rsp, 0[rbp] ; deallocate argument list
pop rbp ; restore nonvolatile register
pop rsi ;
pop rdi ;
ret
NESTED_END COMToCLRDispatchHelperWithStack, _TEXT
; ARG_SLOT COMToCLRDispatchHelper(DWORD dwStackSlots, // rcx
; ComMethodFrame *pFrame, // rdx
; PCODE pTarget, // r8
; PCODE pSecretArg, // r9
; INT_PTR pDangerousThis // rsp + 28h on entry
; );
NESTED_ENTRY COMToCLRDispatchHelper, _TEXT, CallDescrWorkerUnwindFrameChainHandler
;
; Check to see if we have stack to copy and, if so, tail call to
; the routine that can handle that.
;
test ecx, ecx
jnz COMToCLRDispatchHelperWithStack
alloc_stack 28h ; alloc scratch space + alignment, pDangerousThis moves to [rsp+50]
END_PROLOGUE
; get pointer to arguments
lea r11, [rdx + ComMethodFrame_Arguments_OFFSET]
;
; load fp registers
;
movdqa xmm0, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 00h]
movdqa xmm1, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 10h]
movdqa xmm2, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 20h]
movdqa xmm3, [rdx + ComMethodFrame_XMM_SAVE_OFFSET + 30h]
;
; load secret arg and target
;
mov r10, r9
mov rax, r8
;
; load argument registers
;
mov rcx, [rsp + 50h] ; ignoring the COM IP at [r11 + 00h]
mov rdx, [r11 + 08h]
mov r8, [r11 + 10h]
mov r9, [r11 + 18h]
;
; call the target
;
call rax
; It is important to have an instruction between the previous call and the epilog.
; If the return address is in epilog, OS won't call personality routine because
; it thinks personality routine does not help in this case.
nop
;
; epilog
;
add rsp, 28h
ret
NESTED_END COMToCLRDispatchHelper, _TEXT
endif ; FEATURE_COMINTEROP
end
|