Skip to content

Commit c591f97

Browse files
Fix up hijacking on x64 platforms in presence of runtime async (#122631)
Contributes to #122492.
1 parent bbb1028 commit c591f97

File tree

4 files changed

+50
-33
lines changed

4 files changed

+50
-33
lines changed

src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ PTFF_SAVE_R15 equ 00000080h
358358
PTFF_SAVE_ALL_PRESERVED equ 000000F7h ;; NOTE: RBP is not included in this set!
359359
PTFF_SAVE_RSP equ 00008000h
360360
PTFF_SAVE_RAX equ 00000100h ;; RAX is saved in hijack handler - in case it contains a GC ref
361+
PTFF_SAVE_RCX equ 00000200h ;; RCX is saved in hijack handler - in case it contains a GC ref
361362
PTFF_SAVE_ALL_SCRATCH equ 00007F00h
362363
PTFF_THREAD_HIJACK equ 00100000h ;; indicates that this is a frame for a hijacked call
363364

src/coreclr/nativeaot/Runtime/amd64/GcProbe.S

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
#include <unixasmmacros.inc>
77

88
//
9-
// See PUSH_COOP_PINVOKE_FRAME, this macro is very similar, but also saves RAX/RDX and accepts the register
10-
// bitmask in RCX
9+
// See PUSH_COOP_PINVOKE_FRAME, this macro is very similar, but also saves RAX/RCX/RDX and accepts the register
10+
// bitmask in R8
1111
//
1212
// On entry:
1313
// - BITMASK: bitmask describing pushes, may be volatile register or constant value
@@ -20,8 +20,9 @@
2020
//
2121
.macro PUSH_PROBE_FRAME threadReg, trashReg, BITMASK
2222
push_register rdx // save RDX, it might contain an objectref
23+
push_register rcx // save RCX, it might contain an objectref (async continuation)
2324
push_register rax // save RAX, it might contain an objectref
24-
lea \trashReg, [rsp + 0x18]
25+
lea \trashReg, [rsp + 0x20]
2526
push_register \trashReg // save caller`s RSP
2627
push_nonvol_reg r15 // save preserved registers
2728
push_nonvol_reg r14 // ..
@@ -31,12 +32,12 @@
3132
push_register \BITMASK // save the register bitmask passed in by caller
3233
push_register \threadReg // Thread * (unused by stackwalker)
3334
push_nonvol_reg rbp // save caller`s RBP
34-
mov \trashReg, [rsp + 11*8] // Find the return address
35+
mov \trashReg, [rsp + 12*8] // Find the return address
3536
push_register \trashReg // save m_RIP
3637
lea \trashReg, [rsp + 0] // trashReg == address of frame
3738

3839
// allocate space for xmm0, xmm1 and alignment
39-
alloc_stack 0x28
40+
alloc_stack 0x20 + 0
4041

4142
// save xmm0 and xmm1 in case they are used as return values
4243
movdqa [rsp + 0x10], xmm0
@@ -53,7 +54,7 @@
5354
.macro POP_PROBE_FRAME
5455
movdqa xmm1, [rsp + 0]
5556
movdqa xmm0, [rsp + 0x10]
56-
add rsp, 0x28 + 8 // skip xmm0, xmm1 and discard RIP
57+
add rsp, 0x20 + 8 // skip xmm0, xmm1 and discard RIP
5758
pop rbp
5859
pop rax // discard Thread*
5960
pop rax // discard BITMASK
@@ -64,6 +65,7 @@
6465
pop r15
6566
pop rax // discard caller RSP
6667
pop rax
68+
pop rcx
6769
pop rdx
6870
.endm
6971

@@ -76,28 +78,38 @@
7678
//
7779
// Register state on exit:
7880
// R11: thread pointer
79-
// RAX, RDX preserved, other volatile regs trashed
81+
// RAX, RCX, RDX preserved, other volatile regs trashed
8082
//
8183
.macro FixupHijackedCallstack
8284
// preserve RAX, RDX as they may contain return values
8385
push rax
8486
push rdx
8587

88+
// preserve RCX as it may contain async continuation return value
89+
push rcx
90+
91+
// align stack
92+
sub rsp, 0x8
93+
8694
// rax = GetThread(), makes nested calls
8795
INLINE_GETTHREAD
8896
mov r11, rax
8997

98+
add rsp, 0x8
99+
100+
pop rcx
101+
90102
pop rdx
91103
pop rax
92104

93105
// Fix the stack by pushing the original return address
94-
mov rcx, [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress]
95-
push rcx
106+
mov r8, [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress]
107+
push r8
96108

97109
// Clear hijack state
98-
xor ecx, ecx
99-
mov [r11 + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], rcx
100-
mov [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress], rcx
110+
xor r8, r8
111+
mov [r11 + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], r8
112+
mov [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress], r8
101113
.endm
102114

103115
//
@@ -112,12 +124,12 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler
112124
ret
113125

114126
LOCAL_LABEL(WaitForGC):
115-
mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RDX + PTFF_THREAD_HIJACK
127+
mov r8d, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RCX + PTFF_SAVE_RDX + PTFF_THREAD_HIJACK
116128
jmp C_FUNC(RhpWaitForGC)
117129
NESTED_END RhpGcProbeHijack, _TEXT
118130

119131
NESTED_ENTRY RhpWaitForGC, _TEXT, NoHandler
120-
PUSH_PROBE_FRAME r11, rax, rcx
132+
PUSH_PROBE_FRAME r11, rax, r8
121133
END_PROLOGUE
122134

123135
mov rbx, r11

src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
include AsmMacros.inc
55

66
;;
7-
;; See PUSH_COOP_PINVOKE_FRAME, this macro is very similar, but also saves RAX and accepts
7+
;; See PUSH_COOP_PINVOKE_FRAME, this macro is very similar, but also saves RAX/RCX and accepts
88
;; the register bitmask
99
;;
1010
;; On entry:
1111
;; - BITMASK: bitmask describing pushes, a volatile register
1212
;; - RAX: managed function return value, may be an object or byref
13+
;; - RCX: managed function return value (async continuation), may be an object
1314
;; - preserved regs: need to stay preserved, may contain objects or byrefs
1415
;;
1516
;; INVARIANTS
@@ -18,8 +19,9 @@ include AsmMacros.inc
1819
;;
1920
PUSH_PROBE_FRAME macro threadReg, trashReg, BITMASK
2021

22+
push_vol_reg rcx ; save RCX, it might contain an objectref (async continuation)
2123
push_vol_reg rax ; save RAX, it might contain an objectref
22-
lea trashReg, [rsp + 10h]
24+
lea trashReg, [rsp + 18h]
2325
push_vol_reg trashReg ; save caller's RSP
2426
push_nonvol_reg r15 ; save preserved registers
2527
push_nonvol_reg r14 ; ..
@@ -31,12 +33,12 @@ PUSH_PROBE_FRAME macro threadReg, trashReg, BITMASK
3133
push_vol_reg BITMASK ; save the register bitmask passed in by caller
3234
push_vol_reg threadReg ; Thread * (unused by stackwalker)
3335
push_nonvol_reg rbp ; save caller's RBP
34-
mov trashReg, [rsp + 12*8] ; Find the return address
36+
mov trashReg, [rsp + 13*8] ; Find the return address
3537
push_vol_reg trashReg ; save m_RIP
3638
lea trashReg, [rsp + 0] ; trashReg == address of frame
3739

3840
;; allocate scratch space and any required alignment
39-
alloc_stack 20h + 10h
41+
alloc_stack 20h + 10h + 8
4042

4143
;; save xmm0 in case it's being used as a return value
4244
movdqa [rsp + 20h], xmm0
@@ -52,7 +54,7 @@ endm
5254
;;
5355
POP_PROBE_FRAME macro
5456
movdqa xmm0, [rsp + 20h]
55-
add rsp, 20h + 10h + 8 ; deallocate stack and discard saved m_RIP
57+
add rsp, 20h + 10h + 8 + 8 ; deallocate stack and discard saved m_RIP
5658
pop rbp
5759
pop rax ; discard Thread*
5860
pop rax ; discard BITMASK
@@ -65,6 +67,7 @@ POP_PROBE_FRAME macro
6567
pop r15
6668
pop rax ; discard caller RSP
6769
pop rax
70+
pop rcx
6871
endm
6972

7073
;;
@@ -76,20 +79,20 @@ endm
7679
;;
7780
;; Register state on exit:
7881
;; RDX: thread pointer
79-
;; RAX: preserved, other volatile regs trashed
82+
;; RAX/RCX: preserved, other volatile regs trashed
8083
;;
8184
FixupHijackedCallstack macro
82-
;; rdx <- GetThread(), TRASHES rcx
83-
INLINE_GETTHREAD rdx, rcx
85+
;; rdx <- GetThread(), TRASHES r8
86+
INLINE_GETTHREAD rdx, r8
8487

8588
;; Fix the stack by pushing the original return address
86-
mov rcx, [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress]
87-
push rcx
89+
mov r8, [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress]
90+
push r8
8891

8992
;; Clear hijack state
90-
xor ecx, ecx
91-
mov [rdx + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], rcx
92-
mov [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress], rcx
93+
xor r8, r8
94+
mov [rdx + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], r8
95+
mov [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress], r8
9396
endm
9497

9598
;;
@@ -103,12 +106,12 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT
103106
jnz @f
104107
ret
105108
@@:
106-
mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_THREAD_HIJACK
109+
mov r8d, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RCX + PTFF_THREAD_HIJACK
107110
jmp RhpWaitForGC
108111
NESTED_END RhpGcProbeHijack, _TEXT
109112

110113
NESTED_ENTRY RhpWaitForGC, _TEXT
111-
PUSH_PROBE_FRAME rdx, rax, rcx
114+
PUSH_PROBE_FRAME rdx, rax, r8
112115
END_PROLOGUE
113116

114117
mov rbx, rdx
@@ -144,7 +147,7 @@ ifdef FEATURE_GC_STRESS
144147
;;
145148
LEAF_ENTRY RhpGcStressHijack, _TEXT
146149
FixupHijackedCallstack
147-
or ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX
150+
mov r8d, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RCX
148151
jmp RhpGcStressProbe
149152
LEAF_END RhpGcStressHijack, _TEXT
150153

@@ -155,14 +158,14 @@ LEAF_END RhpGcStressHijack, _TEXT
155158
;;
156159
;; Register state on entry:
157160
;; RDX: thread pointer
158-
;; RCX: register bitmask
161+
;; R8: register bitmask
159162
;;
160163
;; Register state on exit:
161-
;; Scratch registers, except for RAX, have been trashed
164+
;; Scratch registers, except for RAX/RCX, have been trashed
162165
;; All other registers restored as they were when the hijack was first reached.
163166
;;
164167
NESTED_ENTRY RhpGcStressProbe, _TEXT
165-
PUSH_PROBE_FRAME rdx, rax, rcx
168+
PUSH_PROBE_FRAME rdx, rax, r8
166169
END_PROLOGUE
167170

168171
call RhpStressGc

src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ C_FUNC(\Name):
243243
#define PTFF_SAVE_ALL_PRESERVED 0x000000F1 // NOTE: RBP is not included in this set!
244244
#define PTFF_SAVE_RSP 0x00008000
245245
#define PTFF_SAVE_RAX 0x00000100 // RAX is saved in hijack handler - in case it contains a GC ref
246+
#define PTFF_SAVE_RCX 0x00000200 // RCX is saved in hijack handler - in case it contains a GC ref
246247
#define PTFF_SAVE_RDX 0x00000400 // RDX is saved in hijack handler - in case it contains a GC ref
247248
#define PTFF_SAVE_ALL_SCRATCH 0x00007F00
248249
#define PTFF_THREAD_HIJACK 0x00100000 // indicates that this is a frame for a hijacked call

0 commit comments

Comments
 (0)