-
Notifications
You must be signed in to change notification settings - Fork 4.6k
/
GcProbe.S
173 lines (147 loc) · 6.04 KB
/
GcProbe.S
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
// 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 RAX/RDX and accepts the register
// bitmask in RCX
//
// On entry:
// - BITMASK: bitmask describing pushes, may be volatile register or constant value
// - RAX: managed function return value, may be an object or byref
// - 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 rdx // save RDX, it might contain an objectref
push_register rax // save RAX, it might contain an objectref
lea \trashReg, [rsp + 0x18]
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_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 + 11*8] // Find the return address
push_register \trashReg // save m_RIP
lea \trashReg, [rsp + 0] // trashReg == address of frame
// allocate space for xmm0, xmm1 and alignment
alloc_stack 0x28
// save xmm0 and xmm1 in case they are used as return values
movdqa [rsp + 0x10], xmm0
movdqa [rsp + 0] , xmm1
// 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 xmm1, [rsp + 0]
movdqa xmm0, [rsp + 0x10]
add rsp, 0x28 + 8 // skip xmm0, xmm1 and discard RIP
pop rbp
pop rax // discard Thread*
pop rax // discard BITMASK
pop rbx
pop r12
pop r13
pop r14
pop r15
pop rax // discard caller RSP
pop rax
pop rdx
.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
// RCX: return value flags
// RAX, RDX preserved, other volatile regs trashed
//
.macro FixupHijackedCallstack
// preserve RAX, RDX as they may contain return values
push rax
push rdx
// rax = GetThread(), makes nested calls
INLINE_GETTHREAD
mov r11, rax
pop rdx
pop rax
// Fix the stack by pushing the original return address
mov rcx, [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress]
push rcx
// Fetch the return address flags
mov rcx, [r11 + OFFSETOF__Thread__m_uHijackedReturnValueFlags]
// Clear hijack state
xor r9, r9
mov [r11 + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], r9
mov [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress], r9
mov [r11 + OFFSETOF__Thread__m_uHijackedReturnValueFlags], r9
.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):
or ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RDX
jmp C_FUNC(RhpWaitForGC)
NESTED_END RhpGcProbeHijack, _TEXT
NESTED_ENTRY RhpWaitForGC, _TEXT, NoHandler
PUSH_PROBE_FRAME r11, rax, rcx
END_PROLOGUE
mov rbx, r11
mov rdi, [rbx + OFFSETOF__Thread__m_pDeferredTransitionFrame]
call C_FUNC(RhpWaitForGC2)
mov rax, [rbx + OFFSETOF__Thread__m_pDeferredTransitionFrame]
test dword ptr [rax + OFFSETOF__PInvokeTransitionFrame__m_Flags], PTFF_THREAD_ABORT
jnz LOCAL_LABEL(Abort)
POP_PROBE_FRAME
ret
LOCAL_LABEL(Abort):
POP_PROBE_FRAME
mov rcx, STATUS_REDHAWK_THREAD_ABORT
pop rdx // return address as exception RIP
jmp C_FUNC(RhpThrowHwEx) // Throw the ThreadAbortException as a special kind of hardware exception
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
END_PROLOGUE
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