-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Expand file tree
/
Copy pathGcProbe.S
More file actions
223 lines (190 loc) · 7.98 KB
/
GcProbe.S
File metadata and controls
223 lines (190 loc) · 7.98 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
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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
.intel_syntax noprefix
#include <AsmOffsets.inc> // generated by the build from AsmOffsets.cpp
#include <unixasmmacros.inc>
//
// See PUSH_COOP_PINVOKE_FRAME, this macro is very similar, but also saves volatile argument registers
// and accepts the register bitmask
//
// On entry:
// - BITMASK: bitmask describing pushes, may be volatile register or constant value
// - RAX: managed function return value, may be an object or byref
// - RSI, RDI, RCX, RDX, R8, R9: may contain objects or byrefs at the hijack point
// - preserved regs: need to stay preserved, may contain objects or byrefs
//
// INVARIANTS
// - The macro assumes it is called from a prolog, prior to a frame pointer being setup.
// - All preserved registers remain unchanged from their values in managed code.
//
.macro PUSH_PROBE_FRAME threadReg, trashReg, BITMASK
push_register r9 // save R9, it might contain an objectref
push_register r8 // save R8, it might contain an objectref
push_register rdx // save RDX, it might contain an objectref
push_register rcx // save RCX, it might contain an objectref (async continuation)
push_register rax // save RAX, it might contain an objectref
lea \trashReg, [rsp + 0x30]
push_register \trashReg // save caller`s RSP
push_nonvol_reg r15 // save preserved registers
push_nonvol_reg r14 // ..
push_nonvol_reg r13 // ..
push_nonvol_reg r12 // ..
push_register rdi // save RDI, volatile on Unix, might contain an objectref
push_register rsi // save RSI, volatile on Unix, might contain an objectref
push_nonvol_reg rbx // ..
push_register \BITMASK // save the register bitmask passed in by caller
push_register \threadReg // Thread * (unused by stackwalker)
push_nonvol_reg rbp // save caller`s RBP
mov \trashReg, [rsp + 16*8] // Find the return address
push_register \trashReg // save m_RIP
lea \trashReg, [rsp + 0] // trashReg == address of frame
// allocate space for xmm0..xmm7 (FP argument registers)
alloc_stack 0x80 + 0
// save FP argument registers in case they contain live values at the hijack point
movdqa [rsp + 0x70], xmm0
movdqa [rsp + 0x60], xmm1
movdqa [rsp + 0x50], xmm2
movdqa [rsp + 0x40], xmm3
movdqa [rsp + 0x30], xmm4
movdqa [rsp + 0x20], xmm5
movdqa [rsp + 0x10], xmm6
movdqa [rsp + 0x00], xmm7
// link the frame into the Thread
mov [\threadReg + OFFSETOF__Thread__m_pDeferredTransitionFrame], \trashReg
.endm
//
// Remove the frame from a previous call to PUSH_PROBE_FRAME from the top of the stack and restore preserved
// registers and return value to their values from before the probe was called (while also updating any
// object refs or byrefs).
.macro POP_PROBE_FRAME
movdqa xmm7, [rsp + 0x00]
movdqa xmm6, [rsp + 0x10]
movdqa xmm5, [rsp + 0x20]
movdqa xmm4, [rsp + 0x30]
movdqa xmm3, [rsp + 0x40]
movdqa xmm2, [rsp + 0x50]
movdqa xmm1, [rsp + 0x60]
movdqa xmm0, [rsp + 0x70]
free_stack 0x80 + 8 // skip xmm0..xmm7 and discard RIP
pop_nonvol_reg rbp
pop_register rax // discard Thread*
pop_register rax // discard BITMASK
pop_nonvol_reg rbx
pop_register rsi
pop_register rdi
pop_nonvol_reg r12
pop_nonvol_reg r13
pop_nonvol_reg r14
pop_nonvol_reg r15
pop_register rax // discard caller RSP
pop_register rax
pop_register rcx
pop_register rdx
pop_register r8
pop_register r9
.endm
//
// The prolog for all GC suspension hijacks (normal and stress). Fixes up the hijacked return address, and
// clears the hijack state.
//
// Register state on entry:
// All registers correct for return to the original return address.
//
// Register state on exit:
// R11: thread pointer
// RAX, RCX, RDX, RSI, RDI, R8, R9, xmm0-xmm7 preserved, R10 trashed
//
.macro FixupHijackedCallstack
// preserve volatile argument registers across INLINE_GETTHREAD
push_register rax
push_register rdx
// preserve RCX as it may contain async continuation return value
push_register rcx
// preserve RSI, RDI, R8 and R9 as they may contain GC refs
push_register rsi
push_register rdi
push_register r8
push_register r9
// allocate space for xmm0..xmm7 + alignment (0x80 for xmm regs + 0x8 for 16-byte alignment)
alloc_stack 0x88
// save FP argument registers that would be clobbered by INLINE_GETTHREAD call
movdqa [rsp + 0x70], xmm0
movdqa [rsp + 0x60], xmm1
movdqa [rsp + 0x50], xmm2
movdqa [rsp + 0x40], xmm3
movdqa [rsp + 0x30], xmm4
movdqa [rsp + 0x20], xmm5
movdqa [rsp + 0x10], xmm6
movdqa [rsp + 0x00], xmm7
// rax = GetThread(), makes nested calls
INLINE_GETTHREAD
mov r11, rax
// restore FP argument registers
movdqa xmm7, [rsp + 0x00]
movdqa xmm6, [rsp + 0x10]
movdqa xmm5, [rsp + 0x20]
movdqa xmm4, [rsp + 0x30]
movdqa xmm3, [rsp + 0x40]
movdqa xmm2, [rsp + 0x50]
movdqa xmm1, [rsp + 0x60]
movdqa xmm0, [rsp + 0x70]
free_stack 0x88
pop_register r9
pop_register r8
pop_register rdi
pop_register rsi
pop_register rcx
pop_register rdx
pop_register rax
// Fix the stack by pushing the original return address
mov r10, [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress]
push r10
// Clear hijack state
xor r10, r10
mov [r11 + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], r10
mov [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress], r10
.endm
//
// GC Probe Hijack target
//
NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler
END_PROLOGUE
FixupHijackedCallstack
test dword ptr [C_VAR(RhpTrapThreads)], TrapThreadsFlags_TrapThreads
jnz LOCAL_LABEL(WaitForGC)
ret
LOCAL_LABEL(WaitForGC):
mov r10d, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RSI + PTFF_SAVE_RDI + PTFF_SAVE_RAX + PTFF_SAVE_RCX + PTFF_SAVE_RDX + PTFF_SAVE_R8 + PTFF_SAVE_R9 + PTFF_THREAD_HIJACK
jmp C_FUNC(RhpWaitForGC)
NESTED_END RhpGcProbeHijack, _TEXT
NESTED_ENTRY RhpWaitForGC, _TEXT, NoHandler
PUSH_PROBE_FRAME r11, rax, r10
END_PROLOGUE
mov rbx, r11
mov rdi, [rbx + OFFSETOF__Thread__m_pDeferredTransitionFrame]
call C_FUNC(RhpWaitForGC2)
POP_PROBE_FRAME
ret
NESTED_END RhpWaitForGC, _TEXT
LEAF_ENTRY RhpGcPoll, _TEXT
cmp dword ptr [C_VAR(RhpTrapThreads)], TrapThreadsFlags_None
jne LOCAL_LABEL(RhpGcPoll_RarePath)
ret
LOCAL_LABEL(RhpGcPoll_RarePath):
jmp C_FUNC(RhpGcPollRare)
LEAF_END RhpGcPoll, _TEXT
NESTED_ENTRY RhpGcPollRare, _TEXT, NoHandler
PUSH_COOP_PINVOKE_FRAME rdi
call C_FUNC(RhpGcPoll2)
POP_COOP_PINVOKE_FRAME
ret
NESTED_END RhpGcPollRare, _TEXT
#ifdef FEATURE_GC_STRESS
//
// GC Stress Hijack targets
//
LEAF_ENTRY RhpGcStressHijack, _TEXT
// NYI
int 3
LEAF_END RhpGcStressHijack, _TEXT
#endif // FEATURE_GC_STRESS