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
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
|
;; 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.
include asmmacros.inc
extern RhpReversePInvokeBadTransition : proc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; RhpWaitForSuspend -- rare path for RhpPInvoke and RhpReversePInvokeReturn
;;
;;
;; INPUT: none
;;
;; TRASHES: none
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NESTED_ENTRY RhpWaitForSuspend, _TEXT
push_vol_reg rax
alloc_stack 60h
; save the arg regs in the caller's scratch space
save_reg_postrsp rcx, 70h
save_reg_postrsp rdx, 78h
save_reg_postrsp r8, 80h
save_reg_postrsp r9, 88h
; save the FP arg regs in our stack frame
save_xmm128_postrsp xmm0, (20h + 0*10h)
save_xmm128_postrsp xmm1, (20h + 1*10h)
save_xmm128_postrsp xmm2, (20h + 2*10h)
save_xmm128_postrsp xmm3, (20h + 3*10h)
END_PROLOGUE
test [RhpTrapThreads], TrapThreadsFlags_TrapThreads
jz NoWait
call RhpWaitForSuspend2
NoWait:
movdqa xmm0, [rsp + 20h + 0*10h]
movdqa xmm1, [rsp + 20h + 1*10h]
movdqa xmm2, [rsp + 20h + 2*10h]
movdqa xmm3, [rsp + 20h + 3*10h]
mov rcx, [rsp + 70h]
mov rdx, [rsp + 78h]
mov r8, [rsp + 80h]
mov r9, [rsp + 88h]
add rsp, 60h
pop rax
ret
NESTED_END RhpWaitForSuspend, _TEXT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; RhpWaitForGCNoAbort -- rare path for RhpPInvokeReturn
;;
;;
;; INPUT: RCX: transition frame
;;
;; TRASHES: RCX, RDX, R8, R9, R10, R11
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NESTED_ENTRY RhpWaitForGCNoAbort, _TEXT
push_vol_reg rax ; don't trash the integer return value
alloc_stack 30h
movdqa [rsp + 20h], xmm0 ; don't trash the FP return value
END_PROLOGUE
mov rdx, [rcx + OFFSETOF__PInvokeTransitionFrame__m_pThread]
test dword ptr [rdx + OFFSETOF__Thread__m_ThreadStateFlags], TSF_DoNotTriggerGc
jnz Done
; passing transition frame pointer in rcx
call RhpWaitForGC2
Done:
movdqa xmm0, [rsp + 20h]
add rsp, 30h
pop rax
ret
NESTED_END RhpWaitForGCNoAbort, _TEXT
EXTERN RhpThrowHwEx : PROC
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; RhpWaitForGC -- rare path for RhpPInvokeReturn
;;
;;
;; INPUT: RCX: transition frame
;;
;; TRASHES: RCX, RDX, R8, R9, R10, R11
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NESTED_ENTRY RhpWaitForGC, _TEXT
push_nonvol_reg rbx
END_PROLOGUE
mov rbx, rcx
test [RhpTrapThreads], TrapThreadsFlags_TrapThreads
jz NoWait
call RhpWaitForGCNoAbort
NoWait:
test [RhpTrapThreads], TrapThreadsFlags_AbortInProgress
jz Done
test dword ptr [rbx + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_THREAD_ABORT
jz Done
mov rcx, STATUS_REDHAWK_THREAD_ABORT
pop rbx
pop rdx ; return address as exception RIP
jmp RhpThrowHwEx ; Throw the ThreadAbortException as a special kind of hardware exception
Done:
pop rbx
ret
NESTED_END RhpWaitForGC, _TEXT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; RhpReversePInvoke
;;
;;
;; INCOMING: RAX -- address of reverse pinvoke frame
;; 0: save slot for previous M->U transition frame
;; 8: save slot for thread pointer to avoid re-calc in epilog sequence
;;
;; PRESERVES: RCX, RDX, R8, R9 -- need to preserve these because the caller assumes they aren't trashed
;;
;; TRASHES: RAX, R10, R11
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LEAF_ENTRY RhpReversePInvoke, _TEXT
;; R10 = GetThread(), TRASHES R11
INLINE_GETTHREAD r10, r11
mov [rax + 8], r10 ; save thread pointer for RhpReversePInvokeReturn
test dword ptr [r10 + OFFSETOF__Thread__m_ThreadStateFlags], TSF_Attached
jz AttachThread
;;
;; Check for the correct mode. This is accessible via various odd things that we cannot completely
;; prevent such as :
;; 1) Registering a reverse pinvoke entrypoint as a vectored exception handler
;; 2) Performing a managed delegate invoke on a reverse pinvoke delegate.
;;
cmp qword ptr [r10 + OFFSETOF__Thread__m_pTransitionFrame], 0
je CheckBadTransition
; rax: reverse pinvoke frame
; r10: thread
; Save previous TransitionFrame prior to making the mode transition so that it is always valid
; whenever we might attempt to hijack this thread.
mov r11, [r10 + OFFSETOF__Thread__m_pTransitionFrame]
mov [rax], r11
mov qword ptr [r10 + OFFSETOF__Thread__m_pTransitionFrame], 0
test [RhpTrapThreads], TrapThreadsFlags_TrapThreads
jnz TrapThread
ret
CheckBadTransition:
;; Allow 'bad transitions' in when the TSF_DoNotTriggerGc mode is set. This allows us to have
;; [NativeCallable] methods that are called via the "restricted GC callouts" as well as from native,
;; which is necessary because the methods are CCW vtable methods on interfaces passed to native.
test dword ptr [r10 + OFFSETOF__Thread__m_ThreadStateFlags], TSF_DoNotTriggerGc
jz BadTransition
;; RhpTrapThreads will always be set in this case, so we must skip that check. We must be sure to
;; zero-out our 'previous transition frame' state first, however.
mov qword ptr [rax], 0
ret
TrapThread:
;; put the previous frame back (sets us back to preemptive mode)
mov qword ptr [r10 + OFFSETOF__Thread__m_pTransitionFrame], r11
AttachThread:
; passing address of reverse pinvoke frame in rax
jmp RhpReversePInvokeAttachOrTrapThread
BadTransition:
mov rcx, qword ptr [rsp] ; arg <- return address
jmp RhpReversePInvokeBadTransition
LEAF_END RhpReversePInvoke, _TEXT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; RhpReversePInvokeAttachOrTrapThread
;;
;;
;; INCOMING: RAX -- address of reverse pinvoke frame
;;
;; PRESERVES: RCX, RDX, R8, R9 -- need to preserve these because the caller assumes they aren't trashed
;;
;; TRASHES: RAX, R10, R11
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NESTED_ENTRY RhpReversePInvokeAttachOrTrapThread, _TEXT
alloc_stack 88h ; alloc scratch area and frame
; save the integer arg regs
save_reg_postrsp rcx, (20h + 0*8)
save_reg_postrsp rdx, (20h + 1*8)
save_reg_postrsp r8, (20h + 2*8)
save_reg_postrsp r9, (20h + 3*8)
; save the FP arg regs
save_xmm128_postrsp xmm0, (20h + 4*8 + 0*10h)
save_xmm128_postrsp xmm1, (20h + 4*8 + 1*10h)
save_xmm128_postrsp xmm2, (20h + 4*8 + 2*10h)
save_xmm128_postrsp xmm3, (20h + 4*8 + 3*10h)
END_PROLOGUE
mov rcx, rax ; rcx <- reverse pinvoke frame
call RhpReversePInvokeAttachOrTrapThread2
movdqa xmm0, [rsp + (20h + 4*8 + 0*10h)]
movdqa xmm1, [rsp + (20h + 4*8 + 1*10h)]
movdqa xmm2, [rsp + (20h + 4*8 + 2*10h)]
movdqa xmm3, [rsp + (20h + 4*8 + 3*10h)]
mov rcx, [rsp + (20h + 0*8)]
mov rdx, [rsp + (20h + 1*8)]
mov r8, [rsp + (20h + 2*8)]
mov r9, [rsp + (20h + 3*8)]
;; epilog
add rsp, 88h
ret
NESTED_END RhpReversePInvokeAttachOrTrapThread, _TEXT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; RhpReversePInvokeReturn
;;
;; IN: RCX: address of reverse pinvoke frame
;;
;; TRASHES: RCX, RDX, R10, R11
;;
;; PRESERVES: RAX (return value)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LEAF_ENTRY RhpReversePInvokeReturn, _TEXT
mov rdx, [rcx + 8] ; get Thread pointer
mov rcx, [rcx + 0] ; get previous M->U transition frame
mov [rdx + OFFSETOF__Thread__m_pTransitionFrame], rcx
cmp [RhpTrapThreads], TrapThreadsFlags_None
jne RhpWaitForSuspend
ret
LEAF_END RhpReversePInvokeReturn, _TEXT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; RhpPInvoke
;;
;; IN: RCX: address of pinvoke frame
;;
;; TRASHES: R10, R11
;;
;; This helper assumes that its callsite is as good to start the stackwalk as the actual PInvoke callsite.
;; The codegenerator must treat the callsite of this helper as GC triggering and generate the GC info for it.
;; Also, the codegenerator must ensure that there are no live GC references in callee saved registers.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LEAF_ENTRY RhpPInvoke, _TEXT
;; R10 = GetThread(), TRASHES R11
INLINE_GETTHREAD r10, r11
mov r11, [rsp] ; r11 <- return address
mov qword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_pThread], r10
mov qword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_FramePointer], rbp
mov qword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_RIP], r11
lea r11, [rsp + 8] ; r11 <- caller SP
mov dword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_SAVE_RSP
mov qword ptr [rcx + OFFSETOF__PInvokeTransitionFrame__m_PreservedRegs], r11
mov qword ptr [r10 + OFFSETOF__Thread__m_pTransitionFrame], rcx
cmp [RhpTrapThreads], TrapThreadsFlags_None
jne @F ; forward branch - predicted not taken
ret
@@:
jmp RhpWaitForSuspend
LEAF_END RhpPInvoke, _TEXT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; RhpPInvokeReturn
;;
;; IN: RCX: address of pinvoke frame
;;
;; TRASHES: RCX, RDX, R8, R9, R10, R11
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LEAF_ENTRY RhpPInvokeReturn, _TEXT
mov rdx, [rcx + OFFSETOF__PInvokeTransitionFrame__m_pThread]
mov qword ptr [rdx + OFFSETOF__Thread__m_pTransitionFrame], 0
cmp [RhpTrapThreads], TrapThreadsFlags_None
jne @F ; forward branch - predicted not taken
ret
@@:
; passing transition frame pointer in rcx
jmp RhpWaitForGC
LEAF_END RhpPInvokeReturn, _TEXT
END
|