summaryrefslogtreecommitdiff
path: root/src/vm/amd64/RedirectedHandledJITCase.asm
blob: a6d349635747d504e83a2e5882436d40d4a395b1 (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
; 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
include asmconstants.inc

Thread__GetAbortContext equ ?GetAbortContext@Thread@@QEAAPEAU_CONTEXT@@XZ

extern FixContextHandler:proc
extern LinkFrameAndThrow:proc
extern GetCurrentSavedRedirectContext:proc
extern Thread__GetAbortContext:proc
extern HijackHandler:proc
extern ThrowControlForThread:proc
extern FixRedirectContextHandler:proc

;
; WARNING!!  These functions immediately ruin thread unwindability.  This is
; WARNING!!  OK as long as there is a mechanism for saving the thread context
; WARNING!!  prior to running these functions as well as a mechanism for 
; WARNING!!  restoring the context prior to any stackwalk.  This means that
; WARNING!!  we need to ensure that no GC can occur while the stack is 
; WARNING!!  unwalkable.  This further means that we cannot allow any exception
; WARNING!!  to occure when the stack is unwalkable
;


; If you edit this macro, make sure you update GetCONTEXTFromRedirectedStubStackFrame.
; This function is used by both the personality routine and the debugger to retrieve the original CONTEXT.
GenerateRedirectedHandledJITCaseStub macro reason

extern ?RedirectedHandledJITCaseFor&reason&@Thread@@CAXXZ:proc

NESTED_ENTRY RedirectedHandledJITCaseFor&reason&_Stub, _TEXT, FixRedirectContextHandler

        ;
        ; To aid debugging, we'll fake a call to this function.  Allocate an
        ; extra stack slot that is hidden from the unwind info, where we can
        ; stuff the "return address".  If we wanted to preserve full
        ; unwindability, we would need to copy all preserved registers.
        ; Ordinarily, rbp is used for the frame pointer, so since we're only
        ; interested is debugability, we'll just handle that common case.
        ;

        push            rax                     ; where to stuff the fake return address
        push_nonvol_reg rbp                     ; save interrupted rbp for stack walk
        alloc_stack     28h                     ; CONTEXT*, callee scratch area
        set_frame       rbp, 0

.errnz REDIRECTSTUB_ESTABLISHER_OFFSET_RBP, REDIRECTSTUB_ESTABLISHER_OFFSET_RBP has changed - update asm stubs

        END_PROLOGUE

        ;
        ; Align rsp.  rsp must misaligned at entry to any C function.
        ;
        and             rsp, -16

        ;
        ; Save a copy of the redirect CONTEXT* in case an exception occurs.
        ; The personality routine will use this to restore unwindability for
        ; the exception dispatcher.
        ;
        call            GetCurrentSavedRedirectContext

        mov             [rbp+20h], rax
.errnz REDIRECTSTUB_RBP_OFFSET_CONTEXT - 20h, REDIRECTSTUB_RBP_OFFSET_CONTEXT has changed - update asm stubs

        ;
        ; Fetch the interrupted rip and save it as our return address.
        ;
        mov             rax, [rax + OFFSETOF__CONTEXT__Rip]
        mov             [rbp+30h], rax

        ;
        ; Call target, which will do whatever we needed to do in the context
        ; of the target thread, and will RtlRestoreContext when it is done.
        ;
        call            ?RedirectedHandledJITCaseFor&reason&@Thread@@CAXXZ
        
        int             3                       ; target shouldn't return.

; Put a label here to tell the debugger where the end of this function is.
PATCH_LABEL RedirectedHandledJITCaseFor&reason&_StubEnd

NESTED_END RedirectedHandledJITCaseFor&reason&_Stub, _TEXT

        endm


GenerateRedirectedHandledJITCaseStub GCThreadControl
GenerateRedirectedHandledJITCaseStub DbgThreadControl
GenerateRedirectedHandledJITCaseStub UserSuspend
GenerateRedirectedHandledJITCaseStub YieldTask

ifdef _DEBUG
ifdef HAVE_GCCOVER
GenerateRedirectedHandledJITCaseStub GCStress
endif
endif


; scratch area; padding; GSCookie
OFFSET_OF_FRAME = SIZEOF_MAX_OUTGOING_ARGUMENT_HOMES + 8 + SIZEOF_GSCookie

; force evaluation to avoid "expression is too complex errors"
SIZEOF__FaultingExceptionFrame = SIZEOF__FaultingExceptionFrame

GenerateRedirectedStubWithFrame macro STUB, FILTER, TARGET

altentry STUB&_RspAligned

NESTED_ENTRY STUB, _TEXT, FILTER

        ;
        ; IN: rcx: original IP before redirect
        ;

        mov             rdx, rsp

        ; This push of the return address must not be recorded in the unwind
        ; info.  After this push, unwinding will work.
        push            rcx

        test            rsp, 0fh
        jnz             STUB&_FixRsp

STUB&_RspAligned:

        ; Any stack operations hereafter must be recorded in the unwind info, but
        ; only nonvolatile register locations are needed.  Anything else is only
        ; a "sub rsp, 8" to the unwinder.

        ; m_ctx must be 16-byte aligned
.errnz (OFFSET_OF_FRAME + SIZEOF__FaultingExceptionFrame) MOD 16

        alloc_stack     OFFSET_OF_FRAME + SIZEOF__FaultingExceptionFrame

.errnz THROWSTUB_ESTABLISHER_OFFSET_FaultingExceptionFrame - OFFSET_OF_FRAME, THROWSTUB_ESTABLISHER_OFFSET_FaultingExceptionFrame has changed - update asm stubs

        END_PROLOGUE

        lea             rcx, [rsp + OFFSET_OF_FRAME]

        mov             dword ptr [rcx], 0                                                          ; Initialize vtbl (it is not strictly necessary)
        mov             dword ptr [rcx + OFFSETOF__FaultingExceptionFrame__m_fFilterExecuted], 0    ; Initialize BOOL for personality routine

        call            TARGET

        ; Target should not return.
        int             3

NESTED_END STUB, _TEXT

; This function is used by the stub above to adjust the stack alignment.  The
; stub can't conditionally push something on the stack because the unwind
; encodings have no way to express that.
;
; CONSIDER: we could move the frame pointer above the FaultingExceptionFrame,
; and detect the misalignment adjustment in
; GetFrameFromRedirectedStubStackFrame.  This is probably less code and more
; straightforward.
LEAF_ENTRY STUB&_FixRsp, _TEXT

        call            STUB&_RspAligned

        ; Target should not return.
        int             3
        
LEAF_END STUB&_FixRsp, _TEXT

        endm


REDIRECT_FOR_THROW_CONTROL_FRAME_SIZE = SIZEOF_MAX_OUTGOING_ARGUMENT_HOMES + 8

NESTED_ENTRY RedirectForThrowControl2, _TEXT

        ; On entry
        ; rcx -> FaultingExceptionFrame
        ; rdx -> Original RSP

        alloc_stack     REDIRECT_FOR_THROW_CONTROL_FRAME_SIZE

        save_reg_postrsp    rcx, REDIRECT_FOR_THROW_CONTROL_FRAME_SIZE + 8h     ; FaultingExceptionFrame
        save_reg_postrsp    rdx, REDIRECT_FOR_THROW_CONTROL_FRAME_SIZE + 10h    ; Original RSP

        END_PROLOGUE

        ; Fetch rip from a CONTEXT, and store it as our return address.
        CALL_GETTHREAD

        mov             rcx, rax
        call            Thread__GetAbortContext

        mov             rax, [rax + OFFSETOF__CONTEXT__Rip]
        mov             rdx, [rsp + REDIRECT_FOR_THROW_CONTROL_FRAME_SIZE + 10h] ; Original RSP
        mov             [rdx - 8], rax

        mov             rcx, [rsp + REDIRECT_FOR_THROW_CONTROL_FRAME_SIZE + 8h] ; FaultingExceptionFrame
        call            ThrowControlForThread

        ; ThrowControlForThread doesn't return.
        int             3

NESTED_END RedirectForThrowControl2, _TEXT

GenerateRedirectedStubWithFrame RedirectForThrowControl, HijackHandler, RedirectForThrowControl2


NAKED_THROW_HELPER_FRAME_SIZE = SIZEOF_MAX_OUTGOING_ARGUMENT_HOMES + 8

NESTED_ENTRY NakedThrowHelper2, _TEXT

        ; On entry
        ; rcx -> FaultingExceptionFrame

        alloc_stack     NAKED_THROW_HELPER_FRAME_SIZE
        END_PROLOGUE

        call            LinkFrameAndThrow

        ; LinkFrameAndThrow doesn't return.
        int             3

NESTED_END NakedThrowHelper2, _TEXT

GenerateRedirectedStubWithFrame NakedThrowHelper, FixContextHandler, NakedThrowHelper2


        end