Skip to content

Instantly share code, notes, and snippets.

@ookiineko
Last active May 3, 2024 16:58
Show Gist options
  • Save ookiineko/3b50874d1d783fcaa4a42a8f91d23f6e to your computer and use it in GitHub Desktop.
Save ookiineko/3b50874d1d783fcaa4a42a8f91d23f6e to your computer and use it in GitHub Desktop.
trying to implement vfork(2) for MinGW
/*
* DISCLAIMER
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
*
* The mingw-w64 runtime package and its code is distributed in the hope that it
* will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR
* IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to
* warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifdef __i386__
# define __vfork_longjmp __vfork_longjmp
#endif
.globl __vfork_longjmp
.def __vfork_longjmp; .scl 2; .type 32; .endef
__vfork_longjmp:
#ifdef __i386__
movl 4(%esp), %ecx
movl 8(%esp), %eax /* retval */
movl 0(%ecx), %ebp /* Ebp */
movl 4(%ecx), %ebx /* Ebx */
movl 8(%ecx), %edi /* Edi */
movl 12(%ecx), %esi /* Esi */
movl 16(%ecx), %esp /* Esp */
addl $4, %esp /* get rid of return address */
jmp *20(%ecx) /* Eip */
#elif defined(__x86_64__)
movq %rdx, %rax /* retval */
movq 0x8(%rcx), %rbx /* Rbx */
movq 0x18(%rcx), %rbp /* Rbp */
movq 0x20(%rcx), %rsi /* Rsi */
movq 0x28(%rcx), %rdi /* Rdi */
movq 0x30(%rcx), %r12 /* R12 */
movq 0x38(%rcx), %r13 /* R13 */
movq 0x40(%rcx), %r14 /* R14 */
movq 0x48(%rcx), %r15 /* R15 */
ldmxcsr 0x58(%rcx) /* MxCsr */
fnclex
fldcw 0x5c(%rcx) /* FpCsr */
movdqa 0x60(%rcx), %xmm6 /* Xmm6 */
movdqa 0x70(%rcx), %xmm7 /* Xmm7 */
movdqa 0x80(%rcx), %xmm8 /* Xmm8 */
movdqa 0x90(%rcx), %xmm9 /* Xmm9 */
movdqa 0xa0(%rcx), %xmm10 /* Xmm10 */
movdqa 0xb0(%rcx), %xmm11 /* Xmm11 */
movdqa 0xc0(%rcx), %xmm12 /* Xmm12 */
movdqa 0xd0(%rcx), %xmm13 /* Xmm13 */
movdqa 0xe0(%rcx), %xmm14 /* Xmm14 */
movdqa 0xf0(%rcx), %xmm15 /* Xmm15 */
movq 0x50(%rcx), %rdx /* Rip */
movq 0x10(%rcx), %rsp /* Rsp */
jmp *%rdx
#elif defined(__arm__)
ldr r4, [r0, #0x4] /* R4 */
ldr r5, [r0, #0x8] /* R5 */
ldr r6, [r0, #0xc] /* R6 */
ldr r7, [r0, #0x10] /* R7 */
ldr r8, [r0, #0x14] /* R8 */
ldr r9, [r0, #0x18] /* R9 */
ldr r10, [r0, #0x1c] /* R10 */
ldr r11, [r0, #0x20] /* R11 */
ldr sp, [r0, #0x24] /* Sp */
ldr r2, [r0, #0x28] /* Pc */
ldr r3, [r0, #0x2c] /* Fpscr */
vmsr fpscr, r3
vldr d8, [r0, #0x30] /* D[0] */
vldr d9, [r0, #0x38] /* D[1] */
vldr d10, [r0, #0x40] /* D[2] */
vldr d11, [r0, #0x48] /* D[3] */
vldr d12, [r0, #0x50] /* D[4] */
vldr d13, [r0, #0x58] /* D[5] */
vldr d14, [r0, #0x60] /* D[6] */
vldr d15, [r0, #0x68] /* D[7] */
mov r0, r1 /* retval */
bx r2
#elif defined(__aarch64__)
ldp x19, x20, [x0, #0x10] /* X19, X20 */
ldp x21, x22, [x0, #0x20] /* X21, X22 */
ldp x23, x24, [x0, #0x30] /* X23, X24 */
ldp x25, x26, [x0, #0x40] /* X25, X26 */
ldp x27, x28, [x0, #0x50] /* X27, X28 */
ldp x29, x30, [x0, #0x60] /* Fp, Lr */
ldr x2, [x0, #0x70] /* Sp */
mov sp, x2
ldr w2, [x0, #0x78] /* Fpcr */
msr fpcr, x2
ldr w2, [x0, #0x7c] /* Fpsr */
msr fpsr, x2
ldp d8, d9, [x0, #0x80] /* D[0-1] */
ldp d10, d11, [x0, #0x90] /* D[2-3] */
ldp d12, d13, [x0, #0xa0] /* D[4-5] */
ldp d14, d15, [x0, #0xb0] /* D[6-7] */
mov x0, x1 /* retval */
ret
#endif
/*
* DISCLAIMER
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
*
* The mingw-w64 runtime package and its code is distributed in the hope that it
* will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR
* IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to
* warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef __VFORK___SETJMP_H__
#define __VFORK___SETJMP_H__
typedef
#ifdef __i386__
int
_vfork_jmp_buf[16];
#elif defined(__x86_64__)
__attribute__((__aligned__(16))) unsigned long long
__vfork_jmp_buf[32];
#elif defined(__arm__)
int
__vfork_jmp_buf[28];
#elif defined(__aarch64__)
unsigned long long
__vfork_jmp_buf[24];
#endif
int __cdecl __attribute__((__nothrow__,__returns_twice__))
__vfork_setjmp (__vfork_jmp_buf buf);
void __attribute__((__nothrow__,__noreturn__))
__vfork_longjmp (__vfork_jmp_buf buf, int ret);
#endif /* __VFORK___SETJMP_H__ */
/*
* DISCLAIMER
* This file has no copyright assigned and is placed in the Public Domain.
* This file is part of the mingw-w64 runtime package.
*
* The mingw-w64 runtime package and its code is distributed in the hope that it
* will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR
* IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to
* warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifdef __i386__
# define __vfork_setjmp __vfork_setjmp
#endif
.globl __vfork_setjmp
.def __vfork_setjmp; .scl 2; .type 32; .endef
__vfork_setjmp:
#ifdef __i386__
movl 4(%esp), %ecx
movl %ebp, 0(%ecx) /* Ebp */
movl %ebx, 4(%ecx) /* Ebx */
movl %edi, 8(%ecx) /* Edi */
movl %esi, 12(%ecx) /* Esi */
movl %esp, 16(%ecx) /* Esp */
movl 0(%esp), %eax
movl %eax, 20(%ecx) /* Eip */
xorl %eax, %eax
retl
#elif defined(__x86_64__)
xorq %rdx, %rdx
movq %rdx, (%rcx) /* Frame */
movq %rbx, 0x8(%rcx) /* Rbx */
leaq 0x8(%rsp), %rax
movq %rax, 0x10(%rcx) /* Rsp */
movq %rbp, 0x18(%rcx) /* Rbp */
movq %rsi, 0x20(%rcx) /* Rsi */
movq %rdi, 0x28(%rcx) /* Rdi */
movq %r12, 0x30(%rcx) /* R12 */
movq %r13, 0x38(%rcx) /* R13 */
movq %r14, 0x40(%rcx) /* R14 */
movq %r15, 0x48(%rcx) /* R15 */
movq (%rsp), %rax
movq %rax, 0x50(%rcx) /* Rip */
stmxcsr 0x58(%rcx) /* MxCsr */
fnstcw 0x5c(%rcx) /* FpCsr */
movdqa %xmm6, 0x60(%rcx) /* Xmm6 */
movdqa %xmm7, 0x70(%rcx) /* Xmm7 */
movdqa %xmm8, 0x80(%rcx) /* Xmm8 */
movdqa %xmm9, 0x90(%rcx) /* Xmm9 */
movdqa %xmm10, 0xa0(%rcx) /* Xmm10 */
movdqa %xmm11, 0xb0(%rcx) /* Xmm11 */
movdqa %xmm12, 0xc0(%rcx) /* Xmm12 */
movdqa %xmm13, 0xd0(%rcx) /* Xmm13 */
movdqa %xmm14, 0xe0(%rcx) /* Xmm14 */
movdqa %xmm15, 0xf0(%rcx) /* Xmm15 */
xorq %rax, %rax
retq
#elif defined(__arm__)
mov r1, #0
str r1, [r0] /* Frame */
str r4, [r0, #0x4] /* R4 */
str r5, [r0, #0x8] /* R5 */
str r6, [r0, #0xc] /* R6 */
str r7, [r0, #0x10] /* R7 */
str r8, [r0, #0x14] /* R8 */
str r9, [r0, #0x18] /* R9 */
str r10, [r0, #0x1c] /* R10 */
str r11, [r0, #0x20] /* R11 */
str sp, [r0, #0x24] /* Sp */
str lr, [r0, #0x28] /* Pc */
vmrs r2, fpscr
str r2, [r0, #0x2c] /* Fpscr */
vstr d8, [r0, #0x30] /* D[0] */
vstr d9, [r0, #0x38] /* D[1] */
vstr d10, [r0, #0x40] /* D[2] */
vstr d11, [r0, #0x48] /* D[3] */
vstr d12, [r0, #0x50] /* D[4] */
vstr d13, [r0, #0x58] /* D[5] */
vstr d14, [r0, #0x60] /* D[6] */
vstr d15, [r0, #0x68] /* D[7] */
mov r0, #0
bx lr
#elif defined(__aarch64__)
str xzr, [x0] /* Frame */
stp x19, x20, [x0, #0x10] /* X19, X20 */
stp x21, x22, [x0, #0x20] /* X21, X22 */
stp x23, x24, [x0, #0x30] /* X23, X24 */
stp x25, x26, [x0, #0x40] /* X25, X26 */
stp x27, x28, [x0, #0x50] /* X27, X28 */
stp x29, x30, [x0, #0x60] /* Fp, Lr */
mov x2, sp
str x2, [x0, #0x70] /* Sp */
mrs x2, fpcr
str w2, [x0, #0x78] /* Fpcr */
mrs x2, fpsr
str w2, [x0, #0x7c] /* Fpsr */
stp d8, d9, [x0, #0x80] /* D[0-1] */
stp d10, d11, [x0, #0x90] /* D[2-3] */
stp d12, d13, [x0, #0xa0] /* D[4-5] */
stp d14, d15, [x0, #0xb0] /* D[6-7] */
mov x0, #0
ret
#endif
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "_vfork_setjmp.h"
#ifdef __x86_64__
# define FUNCTION_PREAMBLE_SIZE (0x10)
#endif
static __thread struct
{
__vfork_jmp_buf buf;
#define _vfork_buf _vfork_info.buf
int in_progress;
#define _is_vfork _vfork_info.in_progress
void *frame;
#define _vfork_frame _vfork_info.frame
char *sp;
#define _vfork_sp _vfork_info.sp
char *bp;
#define _vfork_bp _vfork_info.bp
#define _vfork_frame_size \
(_vfork_bp - _vfork_sp + FUNCTION_PREAMBLE_SIZE)
} _vfork_info;
static inline int
_vfork_save_frame (void)
{
if (!(_vfork_frame = malloc (_vfork_frame_size)))
return -1;
memcpy (_vfork_frame, _vfork_sp, _vfork_frame_size);
return 0;
}
__attribute__((__noinline__)) static void
_vfork_restore_frame (void)
{
memcpy (_vfork_sp, _vfork_frame, _vfork_frame_size);
free (_vfork_frame);
}
__attribute__((__noinline__,__returns_twice__)) int
vfork (void)
{
if (!__vfork_setjmp (_vfork_buf))
{
// initial call from parent;
// save current stack frame
#ifdef __x86_64__
__asm__ volatile ("movq %%rsp, %0\n" : "=r"(_vfork_sp));
#endif
_vfork_bp = __builtin_frame_address(0);
if (_vfork_save_frame () < 0)
return -1;
_is_vfork = 1;
// parent act as child
return 0;
}
else
{
// child exited, or called exec(3)
_vfork_restore_frame ();
_is_vfork = 0;
// back to normal parent
return 1; // fake pid for demo
}
}
__attribute__((__noreturn__)) static inline void
_vfork_end (void)
{
__vfork_longjmp (_vfork_buf, 1);
// unreachable
assert (0);
}
__attribute__((__noreturn__)) void
umixw32_exec_demo (void)
{
if (_is_vfork)
{
// our own exec(3) substitute
printf ("pretend to spawn a process\n");
// exec should never return
// now we should be back to parent
_vfork_end ();
// unreachable
assert (0);
}
else
{
// XXX: call the original mingw exec(3) here
// omitted for demo
for (;;);
}
}
int
main ()
{
int pid;
printf ("before vfork\n");
pid = vfork ();
if (pid < 0)
{
perror ("vfork");
return 1;
}
else if (pid == 0)
{
// in child
// Linux manpage say u can only either exec(3) or _exit(3)
umixw32_exec_demo ();
// unreachable
assert (0);
}
else
printf ("in parent\n");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment