; x64 POSIX vfork continuation. The parent enters the resume/wait syscall without ; a nested userspace return frame while the child owns the caller's stack. INCLUDE syscall_nr.inc VF_ReturnAddress EQU 0 VF_ParentRsp EQU 8 VF_Rbx EQU 16 VF_Rbp EQU 24 VF_Rsi EQU 32 VF_Rdi EQU 40 VF_R12 EQU 48 VF_R13 EQU 56 VF_R14 EQU 64 VF_R15 EQU 72 VF_Xmm6 EQU 80 VF_Xmm7 EQU 96 VF_Xmm8 EQU 112 VF_Xmm9 EQU 128 VF_Xmm10 EQU 144 VF_Xmm11 EQU 160 VF_Xmm12 EQU 176 VF_Xmm13 EQU 192 VF_Xmm14 EQU 208 VF_Xmm15 EQU 224 VF_Mxcsr EQU 240 VF_X87Control EQU 244 VF_ThreadHandle EQU 256 VF_PreviousSuspendCount EQU 264 VF_ContextSize EQU 280 VF_LocalSize EQU 312 EXTERN PosixVforkPrepare:PROC EXTERN PosixVforkParentFinish:PROC .CODE PUBLIC vfork vfork PROC EXPORT sub rsp, VF_LocalSize lea r10, [rsp + 32] mov rax, [rsp + VF_LocalSize] mov [r10 + VF_ReturnAddress], rax lea rax, [rsp + VF_LocalSize + 8] mov [r10 + VF_ParentRsp], rax mov [r10 + VF_Rbx], rbx mov [r10 + VF_Rbp], rbp mov [r10 + VF_Rsi], rsi mov [r10 + VF_Rdi], rdi mov [r10 + VF_R12], r12 mov [r10 + VF_R13], r13 mov [r10 + VF_R14], r14 mov [r10 + VF_R15], r15 movdqu xmmword ptr [r10 + VF_Xmm6], xmm6 movdqu xmmword ptr [r10 + VF_Xmm7], xmm7 movdqu xmmword ptr [r10 + VF_Xmm8], xmm8 movdqu xmmword ptr [r10 + VF_Xmm9], xmm9 movdqu xmmword ptr [r10 + VF_Xmm10], xmm10 movdqu xmmword ptr [r10 + VF_Xmm11], xmm11 movdqu xmmword ptr [r10 + VF_Xmm12], xmm12 movdqu xmmword ptr [r10 + VF_Xmm13], xmm13 movdqu xmmword ptr [r10 + VF_Xmm14], xmm14 movdqu xmmword ptr [r10 + VF_Xmm15], xmm15 stmxcsr dword ptr [r10 + VF_Mxcsr] fnstcw word ptr [r10 + VF_X87Control] mov rcx, r10 call PosixVforkPrepare test rax, rax jnz vfork_wait add rsp, VF_LocalSize mov eax, -1 ret vfork_wait: ; R12 deliberately holds the heap context across the kernel wait. The child ; restores the caller's original R12 in its independent native context. mov r12, rax mov ecx, dword ptr [r12 + VF_ThreadHandle] lea rdx, [r12 + VF_PreviousSuspendCount] mov eax, SYS_RESUME_THREAD_AND_WAIT int 40h mov r9d, eax ; The child may have overwritten every byte of the old vfork/helper frames. ; Rebuild only a synthetic return frame from the heap continuation. mov r10, r12 mov r11, [r10 + VF_ParentRsp] mov rax, [r10 + VF_ReturnAddress] mov rsp, r11 sub rsp, 8 mov [rsp], rax sub rsp, 40 ldmxcsr dword ptr [r10 + VF_Mxcsr] fldcw word ptr [r10 + VF_X87Control] movdqu xmm6, xmmword ptr [r10 + VF_Xmm6] movdqu xmm7, xmmword ptr [r10 + VF_Xmm7] movdqu xmm8, xmmword ptr [r10 + VF_Xmm8] movdqu xmm9, xmmword ptr [r10 + VF_Xmm9] movdqu xmm10, xmmword ptr [r10 + VF_Xmm10] movdqu xmm11, xmmword ptr [r10 + VF_Xmm11] movdqu xmm12, xmmword ptr [r10 + VF_Xmm12] movdqu xmm13, xmmword ptr [r10 + VF_Xmm13] movdqu xmm14, xmmword ptr [r10 + VF_Xmm14] movdqu xmm15, xmmword ptr [r10 + VF_Xmm15] mov rbx, [r10 + VF_Rbx] mov rbp, [r10 + VF_Rbp] mov rsi, [r10 + VF_Rsi] mov rdi, [r10 + VF_Rdi] mov r12, [r10 + VF_R12] mov r13, [r10 + VF_R13] mov r14, [r10 + VF_R14] mov r15, [r10 + VF_R15] mov rcx, r10 mov edx, r9d call PosixVforkParentFinish add rsp, 40 ret vfork ENDP PUBLIC PosixVforkEnterChild PosixVforkEnterChild PROC mov r10, rcx mov r11, [r10 + VF_ParentRsp] mov rax, [r10 + VF_ReturnAddress] mov rsp, r11 sub rsp, 8 mov [rsp], rax ldmxcsr dword ptr [r10 + VF_Mxcsr] fldcw word ptr [r10 + VF_X87Control] movdqu xmm6, xmmword ptr [r10 + VF_Xmm6] movdqu xmm7, xmmword ptr [r10 + VF_Xmm7] movdqu xmm8, xmmword ptr [r10 + VF_Xmm8] movdqu xmm9, xmmword ptr [r10 + VF_Xmm9] movdqu xmm10, xmmword ptr [r10 + VF_Xmm10] movdqu xmm11, xmmword ptr [r10 + VF_Xmm11] movdqu xmm12, xmmword ptr [r10 + VF_Xmm12] movdqu xmm13, xmmword ptr [r10 + VF_Xmm13] movdqu xmm14, xmmword ptr [r10 + VF_Xmm14] movdqu xmm15, xmmword ptr [r10 + VF_Xmm15] mov rbx, [r10 + VF_Rbx] mov rbp, [r10 + VF_Rbp] mov rsi, [r10 + VF_Rsi] mov rdi, [r10 + VF_Rdi] mov r12, [r10 + VF_R12] mov r13, [r10 + VF_R13] mov r14, [r10 + VF_R14] mov r15, [r10 + VF_R15] xor eax, eax ret PosixVforkEnterChild ENDP END