Welcome to mirror list, hosted at ThFree Co, Russian Federation.

PInvoke.asm « amd64 « Runtime « Native « src - github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: b4435852d5ee9682d01a544b3d451a034a7578ec (plain)
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