From be0eb9c92eb7a4fcd9d0d48568c8ed5e8326ef0b Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Wed, 3 Jun 2020 16:32:02 +0200
Subject: [PATCH] ntdll: Move the thread startup code to the Unix library.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
---
 dlls/ntdll/ntdll_misc.h         |  26 ---
 dlls/ntdll/process.c            |   2 +-
 dlls/ntdll/server.c             |   2 +-
 dlls/ntdll/signal_arm.c         | 106 ------------
 dlls/ntdll/signal_arm64.c       | 144 -----------------
 dlls/ntdll/signal_i386.c        | 276 --------------------------------
 dlls/ntdll/signal_powerpc.c     |  90 -----------
 dlls/ntdll/signal_x86_64.c      | 130 ---------------
 dlls/ntdll/thread.c             |  74 ++++++++-
 dlls/ntdll/unix/loader.c        |   3 +-
 dlls/ntdll/unix/signal_arm.c    |  63 ++++++++
 dlls/ntdll/unix/signal_arm64.c  | 103 ++++++++++++
 dlls/ntdll/unix/signal_i386.c   |  88 ++++++++++
 dlls/ntdll/unix/signal_x86_64.c |  84 ++++++++++
 dlls/ntdll/unix/thread.c        |  33 +++-
 dlls/ntdll/unix/unix_private.h  |   8 +-
 dlls/ntdll/unix/virtual.c       |  15 ++
 dlls/ntdll/unixlib.h            |   5 +-
 dlls/ntdll/virtual.c            |  27 ----
 19 files changed, 467 insertions(+), 812 deletions(-)

diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index e9a3230e814..116e072a4a6 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -78,8 +78,6 @@ extern LPCSTR debugstr_ObjectAttributes(const OBJECT_ATTRIBUTES *oa) DECLSPEC_HI
 extern SIZE_T signal_stack_size DECLSPEC_HIDDEN;
 extern SIZE_T signal_stack_mask DECLSPEC_HIDDEN;
 extern void signal_init_process(void) DECLSPEC_HIDDEN;
-extern void signal_start_thread( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend ) DECLSPEC_HIDDEN;
-extern void signal_start_process( LPTHREAD_START_ROUTINE entry, BOOL suspend ) DECLSPEC_HIDDEN;
 extern void version_init(void) DECLSPEC_HIDDEN;
 extern void debug_init(void) DECLSPEC_HIDDEN;
 extern TEB *thread_init(void) DECLSPEC_HIDDEN;
@@ -175,30 +173,6 @@ extern NTSTATUS nt_to_unix_file_name_attr( const OBJECT_ATTRIBUTES *attr, ANSI_S
                                            UINT disposition ) DECLSPEC_HIDDEN;
 
 /* virtual memory */
-extern NTSTATUS virtual_map_section( HANDLE handle, PVOID *addr_ptr, unsigned short zero_bits_64, SIZE_T commit_size,
-                                     const LARGE_INTEGER *offset_ptr, SIZE_T *size_ptr, ULONG alloc_type,
-                                     ULONG protect, pe_image_info_t *image_info ) DECLSPEC_HIDDEN;
-extern void virtual_get_system_info( SYSTEM_BASIC_INFORMATION *info ) DECLSPEC_HIDDEN;
-extern NTSTATUS virtual_create_builtin_view( void *base ) DECLSPEC_HIDDEN;
-extern TEB * virtual_alloc_first_teb(void) DECLSPEC_HIDDEN;
-extern NTSTATUS virtual_alloc_teb( TEB **teb ) DECLSPEC_HIDDEN;
-extern void virtual_free_teb( TEB *teb ) DECLSPEC_HIDDEN;
-extern NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, SIZE_T reserve_size,
-                                            SIZE_T commit_size, SIZE_T *pthread_size ) DECLSPEC_HIDDEN;
-extern void virtual_clear_thread_stack( void *stack_end ) DECLSPEC_HIDDEN;
-extern int virtual_handle_stack_fault( void *addr ) DECLSPEC_HIDDEN;
-extern BOOL virtual_is_valid_code_address( const void *addr, SIZE_T size ) DECLSPEC_HIDDEN;
-extern NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err, BOOL on_signal_stack ) DECLSPEC_HIDDEN;
-extern unsigned int virtual_locked_server_call( void *req_ptr ) DECLSPEC_HIDDEN;
-extern ssize_t virtual_locked_read( int fd, void *addr, size_t size ) DECLSPEC_HIDDEN;
-extern ssize_t virtual_locked_pread( int fd, void *addr, size_t size, off_t offset ) DECLSPEC_HIDDEN;
-extern BOOL virtual_check_buffer_for_read( const void *ptr, SIZE_T size ) DECLSPEC_HIDDEN;
-extern BOOL virtual_check_buffer_for_write( void *ptr, SIZE_T size ) DECLSPEC_HIDDEN;
-extern SIZE_T virtual_uninterrupted_read_memory( const void *addr, void *buffer, SIZE_T size ) DECLSPEC_HIDDEN;
-extern NTSTATUS virtual_uninterrupted_write_memory( void *addr, const void *buffer, SIZE_T size ) DECLSPEC_HIDDEN;
-extern void VIRTUAL_SetForceExec( BOOL enable ) DECLSPEC_HIDDEN;
-extern void virtual_release_address_space(void) DECLSPEC_HIDDEN;
-extern void virtual_set_large_address_space(void) DECLSPEC_HIDDEN;
 extern void virtual_fill_image_information( const pe_image_info_t *pe_info,
                                             SECTION_IMAGE_INFORMATION *info ) DECLSPEC_HIDDEN;
 extern struct _KUSER_SHARED_DATA *user_shared_data DECLSPEC_HIDDEN;
diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c
index c066610ebca..f47d3c4bc6c 100644
--- a/dlls/ntdll/process.c
+++ b/dlls/ntdll/process.c
@@ -734,7 +734,7 @@ NTSTATUS WINAPI NtSetInformationProcess(
                 return STATUS_INVALID_PARAMETER;
             }
             execute_flags = *(ULONG *)ProcessInformation;
-            VIRTUAL_SetForceExec( enable );
+            unix_funcs->virtual_set_force_exec( enable );
         }
         break;
 
diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c
index c7788b99e2d..bde4a2add18 100644
--- a/dlls/ntdll/server.c
+++ b/dlls/ntdll/server.c
@@ -299,5 +299,5 @@ void server_init_process_done(void)
     SERVER_END_REQ;
 
     assert( !status );
-    signal_start_process( entry, suspend );
+    unix_funcs->start_process( entry, suspend, kernel32_start_process );
 }
diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c
index b7e387babf1..8ff9f66d3f0 100644
--- a/dlls/ntdll/signal_arm.c
+++ b/dlls/ntdll/signal_arm.c
@@ -61,7 +61,6 @@
 #include "winnt.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(seh);
-WINE_DECLARE_DEBUG_CHANNEL(relay);
 
 /***********************************************************************
  * signal context platform-specific definitions
@@ -867,111 +866,6 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer,
     return 0;
 }
 
-/***********************************************************************
- *           call_thread_entry_point
- */
-static void call_thread_entry_point( LPTHREAD_START_ROUTINE entry, void *arg )
-{
-    __TRY
-    {
-        TRACE_(relay)( "\1Starting thread proc %p (arg=%p)\n", entry, arg );
-        RtlExitUserThread( entry( arg ));
-    }
-    __EXCEPT(call_unhandled_exception_filter)
-    {
-        NtTerminateThread( GetCurrentThread(), GetExceptionCode() );
-    }
-    __ENDTRY
-    abort();  /* should not be reached */
-}
-
-extern void DECLSPEC_NORETURN start_thread( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend,
-                                            void *relay, TEB *teb );
-__ASM_GLOBAL_FUNC( start_thread,
-                   ".arm\n\t"
-                   "push {r4-r12,lr}\n\t"
-                   /* store exit frame */
-                   "ldr r4, [sp, #40]\n\t"    /* teb */
-                   "str sp, [r4, #0x1d4]\n\t" /* teb->SystemReserved2 */
-                   /* switch to thread stack */
-                   "ldr r4, [r4, #4]\n\t"     /* teb->Tib.StackBase */
-                   "sub sp, r4, #0x1000\n\t"
-                   /* attach dlls */
-                   "bl " __ASM_NAME("attach_thread") "\n\t"
-                   "mov sp, r0\n\t"
-                   /* clear the stack */
-                   "and r0, #~0xff0\n\t"  /* round down to page size */
-                   "bl " __ASM_NAME("virtual_clear_thread_stack") "\n\t"
-                   /* switch to the initial context */
-                   "mov r0, sp\n\t"
-                   "b " __ASM_NAME("set_cpu_context") )
-
-/***********************************************************************
- *           init_thread_context
- */
-static void init_thread_context( CONTEXT *context, LPTHREAD_START_ROUTINE entry, void *arg, void *relay )
-{
-    context->R0 = (DWORD)entry;
-    context->R1 = (DWORD)arg;
-    context->Sp = (DWORD)NtCurrentTeb()->Tib.StackBase;
-    context->Pc = (DWORD)relay;
-}
-
-
-/***********************************************************************
- *           attach_thread
- */
-PCONTEXT DECLSPEC_HIDDEN attach_thread( LPTHREAD_START_ROUTINE entry, void *arg,
-                                        BOOL suspend, void *relay )
-{
-    CONTEXT *ctx;
-
-    if (suspend)
-    {
-        CONTEXT context = { CONTEXT_ALL };
-
-        init_thread_context( &context, entry, arg, relay );
-        wait_suspend( &context );
-        ctx = (CONTEXT *)((ULONG_PTR)context.Sp & ~15) - 1;
-        *ctx = context;
-    }
-    else
-    {
-        ctx = (CONTEXT *)NtCurrentTeb()->Tib.StackBase - 1;
-        init_thread_context( ctx, entry, arg, relay );
-    }
-    ctx->ContextFlags = CONTEXT_FULL;
-    LdrInitializeThunk( ctx, (void **)&ctx->R0, 0, 0 );
-    return ctx;
-}
-
-
-/***********************************************************************
- *           signal_start_thread
- *
- * Thread startup sequence:
- * signal_start_thread()
- *   -> thread_startup()
- *     -> call_thread_entry_point()
- */
-void signal_start_thread( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend )
-{
-    start_thread( entry, arg, suspend, call_thread_entry_point, NtCurrentTeb() );
-}
-
-/**********************************************************************
- *		signal_start_process
- *
- * Process startup sequence:
- * signal_start_process()
- *   -> thread_startup()
- *     -> kernel32_start_process()
- */
-void signal_start_process( LPTHREAD_START_ROUTINE entry, BOOL suspend )
-{
-    start_thread( entry, NtCurrentTeb()->Peb, suspend, kernel32_start_process, NtCurrentTeb() );
-}
-
 /**********************************************************************
  *              DbgBreakPoint   (NTDLL.@)
  */
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c
index c2dd129a569..104ffbf6df9 100644
--- a/dlls/ntdll/signal_arm64.c
+++ b/dlls/ntdll/signal_arm64.c
@@ -64,7 +64,6 @@
 #include "winnt.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(seh);
-WINE_DECLARE_DEBUG_CHANNEL(relay);
 
 /* layering violation: the setjmp buffer is defined in msvcrt, but used by RtlUnwindEx */
 struct MSVCRT_JUMP_BUFFER
@@ -1880,149 +1879,6 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer,
     return 0;
 }
 
-/***********************************************************************
- *           call_thread_entry_point
- */
-static void WINAPI call_thread_entry_point( LPTHREAD_START_ROUTINE entry, void *arg )
-{
-    __TRY
-    {
-        TRACE_(relay)( "\1Starting thread proc %p (arg=%p)\n", entry, arg );
-        RtlExitUserThread( entry( arg ));
-    }
-    __EXCEPT(call_unhandled_exception_filter)
-    {
-        NtTerminateThread( GetCurrentThread(), GetExceptionCode() );
-    }
-    __ENDTRY
-    abort();  /* should not be reached */
-}
-
-extern void DECLSPEC_NORETURN start_thread( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend,
-                                            void *relay, TEB *teb );
-__ASM_GLOBAL_FUNC( start_thread,
-                   "stp x29, x30, [sp,#-16]!\n\t"
-                   "mov x18, x4\n\t"             /* teb */
-                   /* store exit frame */
-                   "mov x29, sp\n\t"
-                   "str x29, [x4, #0x300]\n\t"  /* arm64_thread_data()->exit_frame */
-                   /* switch to thread stack */
-                   "ldr x5, [x4, #8]\n\t"       /* teb->Tib.StackBase */
-                   "sub sp, x5, #0x1000\n\t"
-                   /* attach dlls */
-                   "bl " __ASM_NAME("attach_thread") "\n\t"
-                   "mov sp, x0\n\t"
-                   /* clear the stack */
-                   "and x0, x0, #~0xfff\n\t"  /* round down to page size */
-                   "bl " __ASM_NAME("virtual_clear_thread_stack") "\n\t"
-                   /* switch to the initial context */
-                   "mov x0, sp\n\t"
-                   "ldp q0, q1, [x0, #0x110]\n\t"      /* context->V[0,1] */
-                   "ldp q2, q3, [x0, #0x130]\n\t"      /* context->V[2,3] */
-                   "ldp q4, q5, [x0, #0x150]\n\t"      /* context->V[4,5] */
-                   "ldp q6, q7, [x0, #0x170]\n\t"      /* context->V[6,7] */
-                   "ldp q8, q9, [x0, #0x190]\n\t"      /* context->V[8,9] */
-                   "ldp q10, q11, [x0, #0x1b0]\n\t"    /* context->V[10,11] */
-                   "ldp q12, q13, [x0, #0x1d0]\n\t"    /* context->V[12,13] */
-                   "ldp q14, q15, [x0, #0x1f0]\n\t"    /* context->V[14,15] */
-                   "ldp q16, q17, [x0, #0x210]\n\t"    /* context->V[16,17] */
-                   "ldp q18, q19, [x0, #0x230]\n\t"    /* context->V[18,19] */
-                   "ldp q20, q21, [x0, #0x250]\n\t"    /* context->V[20,21] */
-                   "ldp q22, q23, [x0, #0x270]\n\t"    /* context->V[22,23] */
-                   "ldp q24, q25, [x0, #0x290]\n\t"    /* context->V[24,25] */
-                   "ldp q26, q27, [x0, #0x2b0]\n\t"    /* context->V[26,27] */
-                   "ldp q28, q29, [x0, #0x2d0]\n\t"    /* context->V[28,29] */
-                   "ldp q30, q31, [x0, #0x2f0]\n\t"    /* context->V[30,31] */
-                   "ldr w1, [x0, #0x310]\n\t"          /* context->Fpcr */
-                   "msr fpcr, x1\n\t"
-                   "ldr w1, [x0, #0x314]\n\t"          /* context->Fpsr */
-                   "msr fpsr, x1\n\t"
-                   "ldp x1, x2, [x0, #0x10]\n\t"       /* context->X1,2 */
-                   "ldp x3, x4, [x0, #0x20]\n\t"       /* context->X3,4 */
-                   "ldp x5, x6, [x0, #0x30]\n\t"       /* context->X5,6 */
-                   "ldp x7, x8, [x0, #0x40]\n\t"       /* context->X7,8 */
-                   "ldp x9, x10, [x0, #0x50]\n\t"      /* context->X9,10 */
-                   "ldp x11, x12, [x0, #0x60]\n\t"     /* context->X11,12 */
-                   "ldp x13, x14, [x0, #0x70]\n\t"     /* context->X13,14 */
-                   "ldp x15, x16, [x0, #0x80]\n\t"     /* context->X15,16 */
-                   "ldp x17, x18, [x0, #0x90]\n\t"     /* context->X17,18 */
-                   "ldp x19, x20, [x0, #0xa0]\n\t"     /* context->X19,20 */
-                   "ldp x21, x22, [x0, #0xb0]\n\t"     /* context->X21,22 */
-                   "ldp x23, x24, [x0, #0xc0]\n\t"     /* context->X23,24 */
-                   "ldp x25, x26, [x0, #0xd0]\n\t"     /* context->X25,26 */
-                   "ldp x27, x28, [x0, #0xe0]\n\t"     /* context->X27,28 */
-                   "ldp x29, x30, [x0, #0xf0]\n\t"     /* context->Fp,Lr */
-                   "ldr x17, [x0, #0x100]\n\t"         /* context->Sp */
-                   "mov sp, x17\n\t"
-                   "ldr x17, [x0, #0x108]\n\t"         /* context->Pc */
-                   "ldr x0, [x0, #0x8]\n\t"            /* context->X0 */
-                   "br x17" )
-
-/***********************************************************************
- *           init_thread_context
- */
-static void init_thread_context( CONTEXT *context, LPTHREAD_START_ROUTINE entry, void *arg, void *relay )
-{
-    context->u.s.X0  = (DWORD64)entry;
-    context->u.s.X1  = (DWORD64)arg;
-    context->u.s.X18 = (DWORD64)NtCurrentTeb();
-    context->Sp      = (DWORD64)NtCurrentTeb()->Tib.StackBase;
-    context->Pc      = (DWORD64)relay;
-}
-
-/***********************************************************************
- *           attach_thread
- */
-PCONTEXT DECLSPEC_HIDDEN attach_thread( LPTHREAD_START_ROUTINE entry, void *arg,
-                                        BOOL suspend, void *relay )
-{
-    CONTEXT *ctx;
-
-    if (suspend)
-    {
-        CONTEXT context = { CONTEXT_ALL };
-
-        init_thread_context( &context, entry, arg, relay );
-        wait_suspend( &context );
-        ctx = (CONTEXT *)((ULONG_PTR)context.Sp & ~15) - 1;
-        *ctx = context;
-    }
-    else
-    {
-        ctx = (CONTEXT *)NtCurrentTeb()->Tib.StackBase - 1;
-        init_thread_context( ctx, entry, arg, relay );
-    }
-    ctx->ContextFlags = CONTEXT_FULL;
-    LdrInitializeThunk( ctx, (void **)&ctx->u.s.X0, 0, 0 );
-    return ctx;
-}
-
-/***********************************************************************
- *           signal_start_thread
- *
- * Thread startup sequence:
- * signal_start_thread()
- *   -> start_thread()
- *     -> call_thread_func()
- */
-void signal_start_thread( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend )
-{
-    start_thread( entry, arg, suspend, call_thread_entry_point, NtCurrentTeb() );
-}
-
-/**********************************************************************
- *		signal_start_process
- *
- * Process startup sequence:
- * signal_start_process()
- *   -> start_thread()
- *     -> kernel32_start_process()
- */
-void signal_start_process( LPTHREAD_START_ROUTINE entry, BOOL suspend )
-{
-    start_thread( entry, NtCurrentTeb()->Peb, suspend, kernel32_start_process, NtCurrentTeb() );
-}
-
 /**********************************************************************
  *              DbgBreakPoint   (NTDLL.@)
  */
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index 78d03f669d7..1b0b9352a87 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -63,7 +63,6 @@
 #undef ERR  /* Solaris needs to define this */
 
 WINE_DEFAULT_DEBUG_CHANNEL(seh);
-WINE_DECLARE_DEBUG_CHANNEL(relay);
 
 /* not defined for x86, so copy the x86_64 definition */
 typedef struct DECLSPEC_ALIGN(16) _M128A
@@ -959,26 +958,6 @@ static inline void restore_fpu( const CONTEXT *context )
 }
 
 
-/***********************************************************************
- *           restore_fpux
- *
- * Restore the FPU extended context to a sigcontext.
- */
-static inline void restore_fpux( const CONTEXT *context )
-{
-#ifdef __GNUC__
-    /* we have to enforce alignment by hand */
-    char buffer[sizeof(XMM_SAVE_AREA32) + 16];
-    XMM_SAVE_AREA32 *state = (XMM_SAVE_AREA32 *)(((ULONG_PTR)buffer + 15) & ~15);
-
-    memcpy( state, context->ExtendedRegisters, sizeof(*state) );
-    /* reset the current interrupt status */
-    state->StatusWord &= state->ControlWord | 0xff80;
-    __asm__ __volatile__( "fxrstor %0" : : "m" (*state) );
-#endif
-}
-
-
 /***********************************************************************
  *           fpux_to_fpu
  *
@@ -1155,101 +1134,6 @@ __ASM_STDCALL_FUNC( RtlCaptureContext, 4,
                     __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t")
                     "ret $4" )
 
-/***********************************************************************
- *           set_full_cpu_context
- *
- * Set the new CPU context.
- */
-extern void set_full_cpu_context( const CONTEXT *context );
-__ASM_GLOBAL_FUNC( set_full_cpu_context,
-                   "movl 4(%esp),%ecx\n\t"
-                   "movw 0x8c(%ecx),%gs\n\t"  /* SegGs */
-                   "movw 0x90(%ecx),%fs\n\t"  /* SegFs */
-                   "movw 0x94(%ecx),%es\n\t"  /* SegEs */
-                   "movl 0x9c(%ecx),%edi\n\t" /* Edi */
-                   "movl 0xa0(%ecx),%esi\n\t" /* Esi */
-                   "movl 0xa4(%ecx),%ebx\n\t" /* Ebx */
-                   "movl 0xb4(%ecx),%ebp\n\t" /* Ebp */
-                   "movw %ss,%ax\n\t"
-                   "cmpw 0xc8(%ecx),%ax\n\t"  /* SegSs */
-                   "jne 1f\n\t"
-                   /* As soon as we have switched stacks the context structure could
-                    * be invalid (when signal handlers are executed for example). Copy
-                    * values on the target stack before changing ESP. */
-                   "movl 0xc4(%ecx),%eax\n\t" /* Esp */
-                   "leal -4*4(%eax),%eax\n\t"
-                   "movl 0xc0(%ecx),%edx\n\t" /* EFlags */
-                   "movl %edx,3*4(%eax)\n\t"
-                   "movl 0xbc(%ecx),%edx\n\t" /* SegCs */
-                   "movl %edx,2*4(%eax)\n\t"
-                   "movl 0xb8(%ecx),%edx\n\t" /* Eip */
-                   "movl %edx,1*4(%eax)\n\t"
-                   "movl 0xb0(%ecx),%edx\n\t" /* Eax */
-                   "movl %edx,0*4(%eax)\n\t"
-                   "pushl 0x98(%ecx)\n\t"     /* SegDs */
-                   "movl 0xa8(%ecx),%edx\n\t" /* Edx */
-                   "movl 0xac(%ecx),%ecx\n\t" /* Ecx */
-                   "popl %ds\n\t"
-                   "movl %eax,%esp\n\t"
-                   "popl %eax\n\t"
-                   "iret\n"
-                   /* Restore the context when the stack segment changes. We can't use
-                    * the same code as above because we do not know if the stack segment
-                    * is 16 or 32 bit, and 'movl' will throw an exception when we try to
-                    * access memory above the limit. */
-                   "1:\n\t"
-                   "movl 0xa8(%ecx),%edx\n\t" /* Edx */
-                   "movl 0xb0(%ecx),%eax\n\t" /* Eax */
-                   "movw 0xc8(%ecx),%ss\n\t"  /* SegSs */
-                   "movl 0xc4(%ecx),%esp\n\t" /* Esp */
-                   "pushl 0xc0(%ecx)\n\t"     /* EFlags */
-                   "pushl 0xbc(%ecx)\n\t"     /* SegCs */
-                   "pushl 0xb8(%ecx)\n\t"     /* Eip */
-                   "pushl 0x98(%ecx)\n\t"     /* SegDs */
-                   "movl 0xac(%ecx),%ecx\n\t" /* Ecx */
-                   "popl %ds\n\t"
-                   "iret" )
-
-
-/***********************************************************************
- *           set_cpu_context
- *
- * Set the new CPU context. Used by NtSetContextThread.
- */
-void DECLSPEC_HIDDEN set_cpu_context( const CONTEXT *context )
-{
-    DWORD flags = context->ContextFlags & ~CONTEXT_i386;
-
-    if ((flags & CONTEXT_EXTENDED_REGISTERS) && has_fpux()) restore_fpux( context );
-    else if (flags & CONTEXT_FLOATING_POINT) restore_fpu( context );
-
-    if (flags & CONTEXT_DEBUG_REGISTERS)
-    {
-        x86_thread_data()->dr0 = context->Dr0;
-        x86_thread_data()->dr1 = context->Dr1;
-        x86_thread_data()->dr2 = context->Dr2;
-        x86_thread_data()->dr3 = context->Dr3;
-        x86_thread_data()->dr6 = context->Dr6;
-        x86_thread_data()->dr7 = context->Dr7;
-    }
-    if (flags & CONTEXT_FULL)
-    {
-        if (!(flags & CONTEXT_CONTROL))
-            FIXME( "setting partial context (%x) not supported\n", flags );
-        else if (flags & CONTEXT_SEGMENTS)
-            set_full_cpu_context( context );
-        else
-        {
-            CONTEXT newcontext = *context;
-            newcontext.SegDs = get_ds();
-            newcontext.SegEs = get_ds();
-            newcontext.SegFs = get_fs();
-            newcontext.SegGs = get_gs();
-            set_full_cpu_context( &newcontext );
-        }
-    }
-}
-
 
 /***********************************************************************
  *              NtGetContextThread  (NTDLL.@)
@@ -2248,166 +2132,6 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer,
 }
 
 
-extern void DECLSPEC_NORETURN start_thread( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend,
-                                            void *relay );
-__ASM_GLOBAL_FUNC( start_thread,
-                   "pushl %ebp\n\t"
-                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
-                   __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
-                   "movl %esp,%ebp\n\t"
-                   __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
-                   "pushl %ebx\n\t"
-                   __ASM_CFI(".cfi_rel_offset %ebx,-4\n\t")
-                   "pushl %esi\n\t"
-                   __ASM_CFI(".cfi_rel_offset %esi,-8\n\t")
-                   "pushl %edi\n\t"
-                   __ASM_CFI(".cfi_rel_offset %edi,-12\n\t")
-                   /* store exit frame */
-                   "movl %ebp,%fs:0x1f4\n\t"    /* x86_thread_data()->exit_frame */
-                   /* switch to thread stack */
-                   "movl %fs:4,%eax\n\t"        /* NtCurrentTeb()->StackBase */
-                   "leal -0x1000(%eax),%esp\n\t"
-                   /* attach dlls */
-                   "pushl 20(%ebp)\n\t"         /* relay */
-                   "pushl 16(%ebp)\n\t"         /* suspend */
-                   "pushl 12(%ebp)\n\t"         /* arg */
-                   "pushl 8(%ebp)\n\t"          /* entry */
-                   "xorl %ebp,%ebp\n\t"
-                   "call " __ASM_NAME("attach_thread") "\n\t"
-                   "movl %eax,%esi\n\t"
-                   "leal -12(%eax),%esp\n\t"
-                   /* clear the stack */
-                   "andl $~0xfff,%eax\n\t"  /* round down to page size */
-                   "movl %eax,(%esp)\n\t"
-                   "call " __ASM_NAME("virtual_clear_thread_stack") "\n\t"
-                   /* switch to the initial context */
-                   "movl %esi,(%esp)\n\t"
-                   "call " __ASM_NAME("set_cpu_context") )
-
-extern void call_thread_entry(void) DECLSPEC_HIDDEN;
-__ASM_GLOBAL_FUNC( call_thread_entry,
-                   "pushl %ebp\n\t"
-                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
-                   __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
-                   "movl %esp,%ebp\n\t"
-                   __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
-                   "pushl %ebx\n\t"  /* arg */
-                   "pushl %eax\n\t"  /* entry */
-                   "call " __ASM_NAME("call_thread_func") )
-
-/* wrapper for apps that don't declare the thread function correctly */
-extern DWORD call_thread_func_wrapper( LPTHREAD_START_ROUTINE entry, void *arg );
-__ASM_GLOBAL_FUNC(call_thread_func_wrapper,
-                  "pushl %ebp\n\t"
-                  __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
-                  __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
-                  "movl %esp,%ebp\n\t"
-                  __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
-                  "subl $4,%esp\n\t"
-                  "pushl 12(%ebp)\n\t"
-                  "call *8(%ebp)\n\t"
-                  "leave\n\t"
-                  __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
-                  __ASM_CFI(".cfi_same_value %ebp\n\t")
-                  "ret" )
-
-/***********************************************************************
- *           call_thread_func
- */
-void DECLSPEC_HIDDEN call_thread_func( LPTHREAD_START_ROUTINE entry, void *arg )
-{
-    __TRY
-    {
-        TRACE_(relay)( "\1Starting thread proc %p (arg=%p)\n", entry, arg );
-        RtlExitUserThread( call_thread_func_wrapper( entry, arg ));
-    }
-    __EXCEPT(call_unhandled_exception_filter)
-    {
-        NtTerminateThread( GetCurrentThread(), GetExceptionCode() );
-    }
-    __ENDTRY
-    abort();  /* should not be reached */
-}
-
-
-/***********************************************************************
- *           init_thread_context
- */
-static void init_thread_context( CONTEXT *context, LPTHREAD_START_ROUTINE entry, void *arg, void *relay )
-{
-    context->SegCs  = get_cs();
-    context->SegDs  = get_ds();
-    context->SegEs  = get_ds();
-    context->SegFs  = get_fs();
-    context->SegGs  = get_gs();
-    context->SegSs  = get_ds();
-    context->EFlags = 0x202;
-    context->Eax    = (DWORD)entry;
-    context->Ebx    = (DWORD)arg;
-    context->Esp    = (DWORD)NtCurrentTeb()->Tib.StackBase - 16;
-    context->Eip    = (DWORD)relay;
-    context->FloatSave.ControlWord = 0x27f;
-    ((XMM_SAVE_AREA32 *)context->ExtendedRegisters)->ControlWord = 0x27f;
-    ((XMM_SAVE_AREA32 *)context->ExtendedRegisters)->MxCsr = 0x1f80;
-}
-
-
-/***********************************************************************
- *           attach_thread
- */
-PCONTEXT DECLSPEC_HIDDEN attach_thread( LPTHREAD_START_ROUTINE entry, void *arg,
-                                        BOOL suspend, void *relay )
-{
-    CONTEXT *ctx;
-
-    if (suspend)
-    {
-        CONTEXT context = { CONTEXT_ALL };
-
-        init_thread_context( &context, entry, arg, relay );
-        wait_suspend( &context );
-        ctx = (CONTEXT *)((ULONG_PTR)context.Esp & ~15) - 1;
-        *ctx = context;
-    }
-    else
-    {
-        ctx = (CONTEXT *)((char *)NtCurrentTeb()->Tib.StackBase - 16) - 1;
-        init_thread_context( ctx, entry, arg, relay );
-    }
-    ctx->ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS;
-    LdrInitializeThunk( ctx, (void **)&ctx->Eax, 0, 0 );
-    return ctx;
-}
-
-
-/***********************************************************************
- *           signal_start_thread
- *
- * Thread startup sequence:
- * signal_start_thread()
- *   -> start_thread()
- *     -> call_thread_entry()
- *       -> call_thread_func()
- */
-void signal_start_thread( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend )
-{
-    start_thread( entry, arg, suspend, call_thread_entry );
-}
-
-/**********************************************************************
- *		signal_start_process
- *
- * Process startup sequence:
- * signal_start_process()
- *   -> start_thread()
- *     -> kernel32_start_process()
- */
-void signal_start_process( LPTHREAD_START_ROUTINE entry, BOOL suspend )
-{
-    start_thread( entry, NtCurrentTeb()->Peb, suspend, kernel32_start_process );
-}
-
-
 /**********************************************************************
  *		DbgBreakPoint   (NTDLL.@)
  */
diff --git a/dlls/ntdll/signal_powerpc.c b/dlls/ntdll/signal_powerpc.c
index 75b18e1e933..7e73322cbeb 100644
--- a/dlls/ntdll/signal_powerpc.c
+++ b/dlls/ntdll/signal_powerpc.c
@@ -58,7 +58,6 @@
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(seh);
-WINE_DECLARE_DEBUG_CHANNEL(relay);
 
 /***********************************************************************
  * signal context platform-specific definitions
@@ -720,95 +719,6 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer,
     return 0;
 }
 
-/***********************************************************************
- *           call_thread_entry_point
- */
-static void WINAPI call_thread_entry_point( LPTHREAD_START_ROUTINE entry, void *arg )
-{
-    __TRY
-    {
-        TRACE_(relay)( "\1Starting thread proc %p (arg=%p)\n", entry, arg );
-        RtlExitUserThread( entry( arg ));
-    }
-    __EXCEPT(call_unhandled_exception_filter)
-    {
-        NtTerminateThread( GetCurrentThread(), GetExceptionCode() );
-    }
-    __ENDTRY
-    abort();  /* should not be reached */
-}
-
-typedef void (WINAPI *thread_start_func)(LPTHREAD_START_ROUTINE,void *);
-
-struct startup_info
-{
-    thread_start_func      start;
-    LPTHREAD_START_ROUTINE entry;
-    void                  *arg;
-    BOOL                   suspend;
-};
-
-/* FIXME: should set the full context instead */
-extern void DECLSPEC_NORETURN switch_to_stack( void (*func)(void *), void *arg, void *stack );
-__ASM_GLOBAL_FUNC( switch_to_stack,
-                   "subi 5, 5, 16\n\t"  /* reserve space on new stack */
-                   "mtctr 3\n\t"        /* func -> ctr */
-                   "mr 3,4\n\t"         /* args -> function param 1 (r3) */
-                   "mr 1,5\n\t"         /* stack */
-                   "li 0, 0\n\t"        /* zero */
-                   "stw 0, 0(1)\n\t"    /* bottom of stack */
-                   "stwu 1, -16(1)\n\t" /* create a frame for this function */
-                   "bctrl" )            /* call ctr */
-
-/***********************************************************************
- *           thread_startup
- */
-static void thread_startup( void *param )
-{
-    CONTEXT context = { 0 };
-    struct startup_info *info = param;
-
-    /* build the initial context */
-    context.ContextFlags = CONTEXT_FULL;
-    context.Gpr1 = (DWORD)NtCurrentTeb()->Tib.StackBase;
-    context.Gpr3 = (DWORD)info->entry;
-    context.Gpr4 = (DWORD)info->arg;
-    context.Iar  = (DWORD)info->start;
-
-    if (info->suspend) wait_suspend( &context );
-    LdrInitializeThunk( &context, (void **)&context.Gpr3, 0, 0 );
-
-    ((thread_start_func)context.Iar)( (LPTHREAD_START_ROUTINE)context.Gpr3, (void *)context.Gpr4 );
-}
-
-/***********************************************************************
- *           signal_start_thread
- *
- * Thread startup sequence:
- * signal_start_thread()
- *   -> thread_startup()
- *     -> call_thread_entry_point()
- */
-void signal_start_thread( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend )
-{
-    struct startup_info info = { call_thread_entry_point, entry, arg, suspend };
-    switch_to_stack( thread_startup, &info, NtCurrentTeb()->Tib.StackBase );
-}
-
-/**********************************************************************
- *		signal_start_process
- *
- * Process startup sequence:
- * signal_start_process()
- *   -> thread_startup()
- *     -> kernel32_start_process()
- */
-void signal_start_process( LPTHREAD_START_ROUTINE entry, BOOL suspend )
-{
-    struct startup_info info = { kernel32_start_process, entry, NtCurrentTeb()->Peb, suspend };
-    switch_to_stack( thread_startup, &info, NtCurrentTeb()->Tib.StackBase );
-}
-
 /**********************************************************************
  *              DbgBreakPoint   (NTDLL.@)
  */
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c
index 7cefd1403f3..0ccc7cbceb6 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -75,7 +75,6 @@
 #endif
 
 WINE_DEFAULT_DEBUG_CHANNEL(seh);
-WINE_DECLARE_DEBUG_CHANNEL(relay);
 
 typedef struct _SCOPE_TABLE
 {
@@ -3661,135 +3660,6 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer,
 }
 
 
-/***********************************************************************
- *           call_thread_func
- */
-static void WINAPI call_thread_func( LPTHREAD_START_ROUTINE entry, void *arg )
-{
-    __TRY
-    {
-        TRACE_(relay)( "\1Starting thread proc %p (arg=%p)\n", entry, arg );
-        RtlExitUserThread( entry( arg ));
-    }
-    __EXCEPT(call_unhandled_exception_filter)
-    {
-        NtTerminateThread( GetCurrentThread(), GetExceptionCode() );
-    }
-    __ENDTRY
-    abort();  /* should not be reached */
-}
-
-
-extern void DECLSPEC_NORETURN start_thread( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend,
-                                            void *relay );
-__ASM_GLOBAL_FUNC( start_thread,
-                   "subq $56,%rsp\n\t"
-                   __ASM_SEH(".seh_stackalloc 56\n\t")
-                   __ASM_SEH(".seh_endprologue\n\t")
-                   __ASM_CFI(".cfi_adjust_cfa_offset 56\n\t")
-                   "movq %rbp,48(%rsp)\n\t"
-                   __ASM_CFI(".cfi_rel_offset %rbp,48\n\t")
-                   "movq %rbx,40(%rsp)\n\t"
-                   __ASM_CFI(".cfi_rel_offset %rbx,40\n\t")
-                   "movq %r12,32(%rsp)\n\t"
-                   __ASM_CFI(".cfi_rel_offset %r12,32\n\t")
-                   "movq %r13,24(%rsp)\n\t"
-                   __ASM_CFI(".cfi_rel_offset %r13,24\n\t")
-                   "movq %r14,16(%rsp)\n\t"
-                   __ASM_CFI(".cfi_rel_offset %r14,16\n\t")
-                   "movq %r15,8(%rsp)\n\t"
-                   __ASM_CFI(".cfi_rel_offset %r15,8\n\t")
-                   /* store exit frame */
-                   "movq %gs:0x30,%rax\n\t"
-                   "movq %rsp,0x330(%rax)\n\t"      /* amd64_thread_data()->exit_frame */
-                   /* switch to thread stack */
-                   "movq 8(%rax),%rax\n\t"          /* NtCurrentTeb()->Tib.StackBase */
-                   "leaq -0x1000(%rax),%rsp\n\t"
-                   /* attach dlls */
-                   "call " __ASM_NAME("attach_thread") "\n\t"
-                   "movq %rax,%rsp\n\t"
-                   /* clear the stack */
-                   "andq $~0xfff,%rax\n\t"  /* round down to page size */
-                   "movq %rax,%rdi\n\t"
-                   "call " __ASM_NAME("virtual_clear_thread_stack") "\n\t"
-                   /* switch to the initial context */
-                   "movq %rsp,%rdi\n\t"
-                   "call " __ASM_NAME("set_cpu_context") )
-
-/***********************************************************************
- *           init_thread_context
- */
-static void init_thread_context( CONTEXT *context, LPTHREAD_START_ROUTINE entry, void *arg, void *relay )
-{
-    __asm__( "movw %%cs,%0" : "=m" (context->SegCs) );
-    __asm__( "movw %%ss,%0" : "=m" (context->SegSs) );
-    context->Rcx    = (ULONG_PTR)entry;
-    context->Rdx    = (ULONG_PTR)arg;
-    context->Rsp    = (ULONG_PTR)NtCurrentTeb()->Tib.StackBase - 0x28;
-    context->Rip    = (ULONG_PTR)relay;
-    context->EFlags = 0x200;
-    context->u.FltSave.ControlWord = 0x27f;
-    context->u.FltSave.MxCsr = context->MxCsr = 0x1f80;
-}
-
-
-/***********************************************************************
- *           attach_thread
- */
-PCONTEXT DECLSPEC_HIDDEN attach_thread( LPTHREAD_START_ROUTINE entry, void *arg,
-                                        BOOL suspend, void *relay )
-{
-    CONTEXT *ctx;
-
-    if (suspend)
-    {
-        CONTEXT context = { 0 };
-
-        context.ContextFlags = CONTEXT_ALL;
-        init_thread_context( &context, entry, arg, relay );
-        wait_suspend( &context );
-        ctx = (CONTEXT *)((ULONG_PTR)context.Rsp & ~15) - 1;
-        *ctx = context;
-    }
-    else
-    {
-        ctx = (CONTEXT *)((char *)NtCurrentTeb()->Tib.StackBase - 0x30) - 1;
-        init_thread_context( ctx, entry, arg, relay );
-    }
-    ctx->ContextFlags = CONTEXT_FULL;
-    LdrInitializeThunk( ctx, (void **)&ctx->Rcx, 0, 0 );
-    return ctx;
-}
-
-
-/***********************************************************************
- *           signal_start_thread
- *
- * Thread startup sequence:
- * signal_start_thread()
- *   -> start_thread()
- *     -> call_thread_func()
- */
-void signal_start_thread( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend )
-{
-    start_thread( entry, arg, suspend, call_thread_func );
-}
-
-
-/**********************************************************************
- *		signal_start_process
- *
- * Process startup sequence:
- * signal_start_process()
- *   -> start_thread()
- *     -> kernel32_start_process()
- */
-void signal_start_process( LPTHREAD_START_ROUTINE entry, BOOL suspend )
-{
-    start_thread( entry, NtCurrentTeb()->Peb, suspend, kernel32_start_process );
-}
-
-
 /**********************************************************************
  *		DbgBreakPoint   (NTDLL.@)
  */
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index f9ea9203ed8..51777ae6a9a 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -47,6 +47,7 @@
 #include "wine/exception.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(thread);
+WINE_DECLARE_DEBUG_CHANNEL(relay);
 
 #ifndef PTHREAD_STACK_MIN
 #define PTHREAD_STACK_MIN 16384
@@ -335,6 +336,73 @@ void WINAPI RtlExitUserThread( ULONG status )
 }
 
 
+/***********************************************************************
+ *           call_thread_entry_point
+ */
+#ifdef __i386__
+
+extern void call_thread_entry_point(void) DECLSPEC_HIDDEN;
+__ASM_GLOBAL_FUNC( call_thread_entry_point,
+                   "pushl %ebp\n\t"
+                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
+                   __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
+                   "movl %esp,%ebp\n\t"
+                   __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
+                   "pushl %ebx\n\t"  /* arg */
+                   "pushl %eax\n\t"  /* entry */
+                   "call " __ASM_NAME("call_thread_func") )
+
+/* wrapper for apps that don't declare the thread function correctly */
+extern DWORD call_thread_func_wrapper( LPTHREAD_START_ROUTINE entry, void *arg );
+__ASM_GLOBAL_FUNC(call_thread_func_wrapper,
+                  "pushl %ebp\n\t"
+                  __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
+                  __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
+                  "movl %esp,%ebp\n\t"
+                  __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
+                  "subl $4,%esp\n\t"
+                  "pushl 12(%ebp)\n\t"
+                  "call *8(%ebp)\n\t"
+                  "leave\n\t"
+                  __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
+                  __ASM_CFI(".cfi_same_value %ebp\n\t")
+                  "ret" )
+
+void DECLSPEC_HIDDEN call_thread_func( LPTHREAD_START_ROUTINE entry, void *arg )
+{
+    __TRY
+    {
+        TRACE_(relay)( "\1Starting thread proc %p (arg=%p)\n", entry, arg );
+        RtlExitUserThread( call_thread_func_wrapper( entry, arg ));
+    }
+    __EXCEPT(call_unhandled_exception_filter)
+    {
+        NtTerminateThread( GetCurrentThread(), GetExceptionCode() );
+    }
+    __ENDTRY
+    abort();  /* should not be reached */
+}
+
+#else  /* __i386__ */
+
+static void WINAPI call_thread_entry_point( LPTHREAD_START_ROUTINE entry, void *arg )
+{
+    __TRY
+    {
+        TRACE_(relay)( "\1Starting thread proc %p (arg=%p)\n", entry, arg );
+        RtlExitUserThread( entry( arg ));
+    }
+    __EXCEPT(call_unhandled_exception_filter)
+    {
+        NtTerminateThread( GetCurrentThread(), GetExceptionCode() );
+    }
+    __ENDTRY
+    abort();  /* should not be reached */
+}
+
+#endif  /* __i386__ */
+
+
 /***********************************************************************
  *           start_thread
  *
@@ -342,7 +410,6 @@ void WINAPI RtlExitUserThread( ULONG status )
  */
 static void start_thread( struct startup_info *info )
 {
-    BOOL suspend;
     TEB *teb = info->teb;
     struct ntdll_thread_data *thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch;
     struct debug_info debug_info;
@@ -350,10 +417,7 @@ static void start_thread( struct startup_info *info )
     debug_info.str_pos = debug_info.out_pos = 0;
     thread_data->debug_info = &debug_info;
     thread_data->pthread_id = pthread_self();
-
-    unix_funcs->init_thread( teb );
-    unix_funcs->server_init_thread( info->entry_point, &suspend, NULL, NULL, NULL );
-    signal_start_thread( (LPTHREAD_START_ROUTINE)info->entry_point, info->entry_arg, suspend );
+    unix_funcs->start_thread( info->entry_point, info->entry_arg, call_thread_entry_point, teb );
 }
 
 
diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c
index 0f342e8277e..8692dd931c3 100644
--- a/dlls/ntdll/unix/loader.c
+++ b/dlls/ntdll/unix/loader.c
@@ -1035,7 +1035,8 @@ static struct unix_funcs unix_funcs =
     virtual_release_address_space,
     virtual_set_large_address_space,
     init_threading,
-    init_thread,
+    start_thread,
+    start_process,
     abort_thread,
     exit_thread,
     exit_process,
diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c
index 8228f3f92a2..cd35c23aa91 100644
--- a/dlls/ntdll/unix/signal_arm.c
+++ b/dlls/ntdll/unix/signal_arm.c
@@ -363,6 +363,69 @@ void signal_init_thread( TEB *teb )
 }
 
 
+/***********************************************************************
+ *           init_thread_context
+ */
+static void init_thread_context( CONTEXT *context, LPTHREAD_START_ROUTINE entry, void *arg, void *relay )
+{
+    context->R0 = (DWORD)entry;
+    context->R1 = (DWORD)arg;
+    context->Sp = (DWORD)NtCurrentTeb()->Tib.StackBase;
+    context->Pc = (DWORD)relay;
+}
+
+
+/***********************************************************************
+ *           attach_thread
+ */
+PCONTEXT DECLSPEC_HIDDEN attach_thread( LPTHREAD_START_ROUTINE entry, void *arg,
+                                        BOOL suspend, void *relay )
+{
+    CONTEXT *ctx;
+
+    if (suspend)
+    {
+        CONTEXT context = { CONTEXT_ALL };
+
+        init_thread_context( &context, entry, arg, relay );
+        wait_suspend( &context );
+        ctx = (CONTEXT *)((ULONG_PTR)context.Sp & ~15) - 1;
+        *ctx = context;
+    }
+    else
+    {
+        ctx = (CONTEXT *)NtCurrentTeb()->Tib.StackBase - 1;
+        init_thread_context( ctx, entry, arg, relay );
+    }
+    ctx->ContextFlags = CONTEXT_FULL;
+    LdrInitializeThunk( ctx, (void **)&ctx->R0, 0, 0 );
+    return ctx;
+}
+
+
+/***********************************************************************
+ *           signal_start_thread
+ */
+__ASM_GLOBAL_FUNC( signal_start_thread,
+                   ".arm\n\t"
+                   "push {r4-r12,lr}\n\t"
+                   /* store exit frame */
+                   "ldr r4, [sp, #40]\n\t"    /* teb */
+                   "str sp, [r4, #0x1d4]\n\t" /* teb->SystemReserved2 */
+                   /* switch to thread stack */
+                   "ldr r4, [r4, #4]\n\t"     /* teb->Tib.StackBase */
+                   "sub sp, r4, #0x1000\n\t"
+                   /* attach dlls */
+                   "bl " __ASM_NAME("attach_thread") "\n\t"
+                   "mov sp, r0\n\t"
+                   /* clear the stack */
+                   "and r0, #~0xff0\n\t"  /* round down to page size */
+                   "bl " __ASM_NAME("virtual_clear_thread_stack") "\n\t"
+                   /* switch to the initial context */
+                   "mov r0, sp\n\t"
+                   "b " __ASM_NAME("set_cpu_context") )
+
+
 extern void DECLSPEC_NORETURN call_thread_exit_func( int status, void (*func)(int), TEB *teb );
 __ASM_GLOBAL_FUNC( call_thread_exit_func,
                    ".arm\n\t"
diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c
index ca47481b194..add8b4385c4 100644
--- a/dlls/ntdll/unix/signal_arm64.c
+++ b/dlls/ntdll/unix/signal_arm64.c
@@ -370,6 +370,109 @@ void signal_init_thread( TEB *teb )
 }
 
 
+/***********************************************************************
+ *           init_thread_context
+ */
+static void init_thread_context( CONTEXT *context, LPTHREAD_START_ROUTINE entry, void *arg, void *relay )
+{
+    context->u.s.X0  = (DWORD64)entry;
+    context->u.s.X1  = (DWORD64)arg;
+    context->u.s.X18 = (DWORD64)NtCurrentTeb();
+    context->Sp      = (DWORD64)NtCurrentTeb()->Tib.StackBase;
+    context->Pc      = (DWORD64)relay;
+}
+
+
+/***********************************************************************
+ *           attach_thread
+ */
+PCONTEXT DECLSPEC_HIDDEN attach_thread( LPTHREAD_START_ROUTINE entry, void *arg,
+                                        BOOL suspend, void *relay )
+{
+    CONTEXT *ctx;
+
+    if (suspend)
+    {
+        CONTEXT context = { CONTEXT_ALL };
+
+        init_thread_context( &context, entry, arg, relay );
+        wait_suspend( &context );
+        ctx = (CONTEXT *)((ULONG_PTR)context.Sp & ~15) - 1;
+        *ctx = context;
+    }
+    else
+    {
+        ctx = (CONTEXT *)NtCurrentTeb()->Tib.StackBase - 1;
+        init_thread_context( ctx, entry, arg, relay );
+    }
+    ctx->ContextFlags = CONTEXT_FULL;
+    LdrInitializeThunk( ctx, (void **)&ctx->u.s.X0, 0, 0 );
+    return ctx;
+}
+
+
+/***********************************************************************
+ *           signal_start_thread
+ */
+__ASM_GLOBAL_FUNC( signal_start_thread,
+                   "stp x29, x30, [sp,#-16]!\n\t"
+                   "mov x18, x4\n\t"             /* teb */
+                   /* store exit frame */
+                   "mov x29, sp\n\t"
+                   "str x29, [x4, #0x300]\n\t"  /* arm64_thread_data()->exit_frame */
+                   /* switch to thread stack */
+                   "ldr x5, [x4, #8]\n\t"       /* teb->Tib.StackBase */
+                   "sub sp, x5, #0x1000\n\t"
+                   /* attach dlls */
+                   "bl " __ASM_NAME("attach_thread") "\n\t"
+                   "mov sp, x0\n\t"
+                   /* clear the stack */
+                   "and x0, x0, #~0xfff\n\t"  /* round down to page size */
+                   "bl " __ASM_NAME("virtual_clear_thread_stack") "\n\t"
+                   /* switch to the initial context */
+                   "mov x0, sp\n\t"
+                   "ldp q0, q1, [x0, #0x110]\n\t"      /* context->V[0,1] */
+                   "ldp q2, q3, [x0, #0x130]\n\t"      /* context->V[2,3] */
+                   "ldp q4, q5, [x0, #0x150]\n\t"      /* context->V[4,5] */
+                   "ldp q6, q7, [x0, #0x170]\n\t"      /* context->V[6,7] */
+                   "ldp q8, q9, [x0, #0x190]\n\t"      /* context->V[8,9] */
+                   "ldp q10, q11, [x0, #0x1b0]\n\t"    /* context->V[10,11] */
+                   "ldp q12, q13, [x0, #0x1d0]\n\t"    /* context->V[12,13] */
+                   "ldp q14, q15, [x0, #0x1f0]\n\t"    /* context->V[14,15] */
+                   "ldp q16, q17, [x0, #0x210]\n\t"    /* context->V[16,17] */
+                   "ldp q18, q19, [x0, #0x230]\n\t"    /* context->V[18,19] */
+                   "ldp q20, q21, [x0, #0x250]\n\t"    /* context->V[20,21] */
+                   "ldp q22, q23, [x0, #0x270]\n\t"    /* context->V[22,23] */
+                   "ldp q24, q25, [x0, #0x290]\n\t"    /* context->V[24,25] */
+                   "ldp q26, q27, [x0, #0x2b0]\n\t"    /* context->V[26,27] */
+                   "ldp q28, q29, [x0, #0x2d0]\n\t"    /* context->V[28,29] */
+                   "ldp q30, q31, [x0, #0x2f0]\n\t"    /* context->V[30,31] */
+                   "ldr w1, [x0, #0x310]\n\t"          /* context->Fpcr */
+                   "msr fpcr, x1\n\t"
+                   "ldr w1, [x0, #0x314]\n\t"          /* context->Fpsr */
+                   "msr fpsr, x1\n\t"
+                   "ldp x1, x2, [x0, #0x10]\n\t"       /* context->X1,2 */
+                   "ldp x3, x4, [x0, #0x20]\n\t"       /* context->X3,4 */
+                   "ldp x5, x6, [x0, #0x30]\n\t"       /* context->X5,6 */
+                   "ldp x7, x8, [x0, #0x40]\n\t"       /* context->X7,8 */
+                   "ldp x9, x10, [x0, #0x50]\n\t"      /* context->X9,10 */
+                   "ldp x11, x12, [x0, #0x60]\n\t"     /* context->X11,12 */
+                   "ldp x13, x14, [x0, #0x70]\n\t"     /* context->X13,14 */
+                   "ldp x15, x16, [x0, #0x80]\n\t"     /* context->X15,16 */
+                   "ldp x17, x18, [x0, #0x90]\n\t"     /* context->X17,18 */
+                   "ldp x19, x20, [x0, #0xa0]\n\t"     /* context->X19,20 */
+                   "ldp x21, x22, [x0, #0xb0]\n\t"     /* context->X21,22 */
+                   "ldp x23, x24, [x0, #0xc0]\n\t"     /* context->X23,24 */
+                   "ldp x25, x26, [x0, #0xd0]\n\t"     /* context->X25,26 */
+                   "ldp x27, x28, [x0, #0xe0]\n\t"     /* context->X27,28 */
+                   "ldp x29, x30, [x0, #0xf0]\n\t"     /* context->Fp,Lr */
+                   "ldr x17, [x0, #0x100]\n\t"         /* context->Sp */
+                   "mov sp, x17\n\t"
+                   "ldr x17, [x0, #0x108]\n\t"         /* context->Pc */
+                   "ldr x0, [x0, #0x8]\n\t"            /* context->X0 */
+                   "br x17" )
+
+
 extern void DECLSPEC_NORETURN call_thread_exit_func( int status, void (*func)(int), TEB *teb );
 __ASM_GLOBAL_FUNC( call_thread_exit_func,
                    "stp x29, x30, [sp,#-16]!\n\t"
diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c
index 8902be00cd1..515090d9af8 100644
--- a/dlls/ntdll/unix/signal_i386.c
+++ b/dlls/ntdll/unix/signal_i386.c
@@ -960,6 +960,94 @@ void signal_init_thread( TEB *teb )
 }
 
 
+/***********************************************************************
+ *           init_thread_context
+ */
+static void init_thread_context( CONTEXT *context, LPTHREAD_START_ROUTINE entry, void *arg, void *relay )
+{
+    context->SegCs  = get_cs();
+    context->SegDs  = get_ds();
+    context->SegEs  = get_ds();
+    context->SegFs  = get_fs();
+    context->SegGs  = get_gs();
+    context->SegSs  = get_ds();
+    context->EFlags = 0x202;
+    context->Eax    = (DWORD)entry;
+    context->Ebx    = (DWORD)arg;
+    context->Esp    = (DWORD)NtCurrentTeb()->Tib.StackBase - 16;
+    context->Eip    = (DWORD)relay;
+    context->FloatSave.ControlWord = 0x27f;
+    ((XMM_SAVE_AREA32 *)context->ExtendedRegisters)->ControlWord = 0x27f;
+    ((XMM_SAVE_AREA32 *)context->ExtendedRegisters)->MxCsr = 0x1f80;
+}
+
+
+/***********************************************************************
+ *           attach_thread
+ */
+PCONTEXT DECLSPEC_HIDDEN attach_thread( LPTHREAD_START_ROUTINE entry, void *arg,
+                                        BOOL suspend, void *relay )
+{
+    CONTEXT *ctx;
+
+    if (suspend)
+    {
+        CONTEXT context = { CONTEXT_ALL };
+
+        init_thread_context( &context, entry, arg, relay );
+        wait_suspend( &context );
+        ctx = (CONTEXT *)((ULONG_PTR)context.Esp & ~15) - 1;
+        *ctx = context;
+    }
+    else
+    {
+        ctx = (CONTEXT *)((char *)NtCurrentTeb()->Tib.StackBase - 16) - 1;
+        init_thread_context( ctx, entry, arg, relay );
+    }
+    ctx->ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS;
+    LdrInitializeThunk( ctx, (void **)&ctx->Eax, 0, 0 );
+    return ctx;
+}
+
+
+/***********************************************************************
+ *           signal_start_thread
+ */
+__ASM_GLOBAL_FUNC( signal_start_thread,
+                   "pushl %ebp\n\t"
+                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
+                   __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
+                   "movl %esp,%ebp\n\t"
+                   __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
+                   "pushl %ebx\n\t"
+                   __ASM_CFI(".cfi_rel_offset %ebx,-4\n\t")
+                   "pushl %esi\n\t"
+                   __ASM_CFI(".cfi_rel_offset %esi,-8\n\t")
+                   "pushl %edi\n\t"
+                   __ASM_CFI(".cfi_rel_offset %edi,-12\n\t")
+                   /* store exit frame */
+                   "movl %ebp,%fs:0x1f4\n\t"    /* x86_thread_data()->exit_frame */
+                   /* switch to thread stack */
+                   "movl %fs:4,%eax\n\t"        /* NtCurrentTeb()->StackBase */
+                   "leal -0x1000(%eax),%esp\n\t"
+                   /* attach dlls */
+                   "pushl 20(%ebp)\n\t"         /* relay */
+                   "pushl 16(%ebp)\n\t"         /* suspend */
+                   "pushl 12(%ebp)\n\t"         /* arg */
+                   "pushl 8(%ebp)\n\t"          /* entry */
+                   "xorl %ebp,%ebp\n\t"
+                   "call " __ASM_NAME("attach_thread") "\n\t"
+                   "movl %eax,%esi\n\t"
+                   "leal -12(%eax),%esp\n\t"
+                   /* clear the stack */
+                   "andl $~0xfff,%eax\n\t"  /* round down to page size */
+                   "movl %eax,(%esp)\n\t"
+                   "call " __ASM_NAME("virtual_clear_thread_stack") "\n\t"
+                   /* switch to the initial context */
+                   "movl %esi,(%esp)\n\t"
+                   "call " __ASM_NAME("set_cpu_context") )
+
+
 /***********************************************************************
  *           signal_exit_thread
  */
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c
index abe4d6905e6..aec0228de02 100644
--- a/dlls/ntdll/unix/signal_x86_64.c
+++ b/dlls/ntdll/unix/signal_x86_64.c
@@ -650,6 +650,90 @@ void signal_init_thread( TEB *teb )
 }
 
 
+/***********************************************************************
+ *           init_thread_context
+ */
+static void init_thread_context( CONTEXT *context, LPTHREAD_START_ROUTINE entry, void *arg, void *relay )
+{
+    __asm__( "movw %%cs,%0" : "=m" (context->SegCs) );
+    __asm__( "movw %%ss,%0" : "=m" (context->SegSs) );
+    context->Rcx    = (ULONG_PTR)entry;
+    context->Rdx    = (ULONG_PTR)arg;
+    context->Rsp    = (ULONG_PTR)NtCurrentTeb()->Tib.StackBase - 0x28;
+    context->Rip    = (ULONG_PTR)relay;
+    context->EFlags = 0x200;
+    context->u.FltSave.ControlWord = 0x27f;
+    context->u.FltSave.MxCsr = context->MxCsr = 0x1f80;
+}
+
+
+/***********************************************************************
+ *           attach_thread
+ */
+PCONTEXT DECLSPEC_HIDDEN attach_thread( LPTHREAD_START_ROUTINE entry, void *arg,
+                                        BOOL suspend, void *relay )
+{
+    CONTEXT *ctx;
+
+    if (suspend)
+    {
+        CONTEXT context = { 0 };
+
+        context.ContextFlags = CONTEXT_ALL;
+        init_thread_context( &context, entry, arg, relay );
+        wait_suspend( &context );
+        ctx = (CONTEXT *)((ULONG_PTR)context.Rsp & ~15) - 1;
+        *ctx = context;
+    }
+    else
+    {
+        ctx = (CONTEXT *)((char *)NtCurrentTeb()->Tib.StackBase - 0x30) - 1;
+        init_thread_context( ctx, entry, arg, relay );
+    }
+    ctx->ContextFlags = CONTEXT_FULL;
+    LdrInitializeThunk( ctx, (void **)&ctx->Rcx, 0, 0 );
+    return ctx;
+}
+
+
+/***********************************************************************
+ *           signal_start_thread
+ */
+__ASM_GLOBAL_FUNC( signal_start_thread,
+                   "subq $56,%rsp\n\t"
+                   __ASM_SEH(".seh_stackalloc 56\n\t")
+                   __ASM_SEH(".seh_endprologue\n\t")
+                   __ASM_CFI(".cfi_adjust_cfa_offset 56\n\t")
+                   "movq %rbp,48(%rsp)\n\t"
+                   __ASM_CFI(".cfi_rel_offset %rbp,48\n\t")
+                   "movq %rbx,40(%rsp)\n\t"
+                   __ASM_CFI(".cfi_rel_offset %rbx,40\n\t")
+                   "movq %r12,32(%rsp)\n\t"
+                   __ASM_CFI(".cfi_rel_offset %r12,32\n\t")
+                   "movq %r13,24(%rsp)\n\t"
+                   __ASM_CFI(".cfi_rel_offset %r13,24\n\t")
+                   "movq %r14,16(%rsp)\n\t"
+                   __ASM_CFI(".cfi_rel_offset %r14,16\n\t")
+                   "movq %r15,8(%rsp)\n\t"
+                   __ASM_CFI(".cfi_rel_offset %r15,8\n\t")
+                   /* store exit frame */
+                   "movq %gs:0x30,%rax\n\t"
+                   "movq %rsp,0x330(%rax)\n\t"      /* amd64_thread_data()->exit_frame */
+                   /* switch to thread stack */
+                   "movq 8(%rax),%rax\n\t"          /* NtCurrentTeb()->Tib.StackBase */
+                   "leaq -0x1000(%rax),%rsp\n\t"
+                   /* attach dlls */
+                   "call " __ASM_NAME("attach_thread") "\n\t"
+                   "movq %rax,%rsp\n\t"
+                   /* clear the stack */
+                   "andq $~0xfff,%rax\n\t"  /* round down to page size */
+                   "movq %rax,%rdi\n\t"
+                   "call " __ASM_NAME("virtual_clear_thread_stack") "\n\t"
+                   /* switch to the initial context */
+                   "movq %rsp,%rdi\n\t"
+                   "call " __ASM_NAME("set_cpu_context") )
+
+
 /***********************************************************************
  *           signal_exit_thread
  */
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c
index 603ceaf6d27..7e18caeaebf 100644
--- a/dlls/ntdll/unix/thread.c
+++ b/dlls/ntdll/unix/thread.c
@@ -26,6 +26,7 @@
 #include "wine/port.h"
 
 #include <assert.h>
+#include <errno.h>
 #include <limits.h>
 #include <stdarg.h>
 #include <pthread.h>
@@ -92,11 +93,24 @@ void CDECL init_threading( int *nb_threads_ptr, struct ldt_copy **ldt_copy )
 
 
 /***********************************************************************
- *           init_thread
+ *           start_thread
  */
-void CDECL init_thread( TEB *teb )
+void CDECL start_thread( PRTL_THREAD_START_ROUTINE entry, void *arg, void *relay, TEB *teb )
 {
+    BOOL suspend;
+
     signal_init_thread( teb );
+    server_init_thread( entry, &suspend, NULL, NULL, NULL );
+    signal_start_thread( entry, arg, suspend, relay, teb );
+}
+
+
+/***********************************************************************
+ *           start_process
+ */
+void CDECL start_process( PRTL_THREAD_START_ROUTINE entry, BOOL suspend, void *relay )
+{
+    signal_start_thread( entry, NtCurrentTeb()->Peb, suspend, relay, NtCurrentTeb() );
 }
 
 
@@ -131,6 +145,21 @@ void CDECL exit_process( int status )
 }
 
 
+/**********************************************************************
+ *           wait_suspend
+ *
+ * Wait until the thread is no longer suspended.
+ */
+void wait_suspend( CONTEXT *context )
+{
+    int saved_errno = errno;
+
+    /* wait with 0 timeout, will only return once the thread is no longer suspended */
+    server_select( NULL, 0, SELECT_INTERRUPTIBLE, 0, context, NULL, NULL );
+    errno = saved_errno;
+}
+
+
 /***********************************************************************
  *              set_thread_context
  */
diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h
index 8562efb7dd4..dd94cde1cf1 100644
--- a/dlls/ntdll/unix/unix_private.h
+++ b/dlls/ntdll/unix/unix_private.h
@@ -50,6 +50,8 @@ static inline struct ntdll_thread_data *ntdll_get_thread_data(void)
     return (struct ntdll_thread_data *)&NtCurrentTeb()->GdiTebBatch;
 }
 
+void WINAPI LdrInitializeThunk(CONTEXT*,void**,ULONG_PTR,ULONG_PTR);
+
 void CDECL mmap_add_reserved_area( void *addr, SIZE_T size ) DECLSPEC_HIDDEN;
 void CDECL mmap_remove_reserved_area( void *addr, SIZE_T size ) DECLSPEC_HIDDEN;
 int  CDECL mmap_is_in_reserved_area( void *addr, SIZE_T size ) DECLSPEC_HIDDEN;
@@ -104,7 +106,8 @@ extern void CDECL server_init_process_done(void) DECLSPEC_HIDDEN;
 extern size_t CDECL server_init_thread( void *entry_point, BOOL *suspend, unsigned int *cpus,
                                         BOOL *wow64, timeout_t *start_time ) DECLSPEC_HIDDEN;
 extern void CDECL init_threading( int *nb_threads, struct ldt_copy **ldt_copy ) DECLSPEC_HIDDEN;
-extern void CDECL init_thread( TEB *teb ) DECLSPEC_HIDDEN;
+extern void CDECL DECLSPEC_NORETURN start_thread( PRTL_THREAD_START_ROUTINE entry, void *arg, void *relay, TEB *teb ) DECLSPEC_HIDDEN;
+extern void CDECL DECLSPEC_NORETURN start_process( PRTL_THREAD_START_ROUTINE entry, BOOL suspend, void *relay ) DECLSPEC_HIDDEN;
 extern void CDECL DECLSPEC_NORETURN abort_thread( int status ) DECLSPEC_HIDDEN;
 extern void CDECL DECLSPEC_NORETURN exit_thread( int status ) DECLSPEC_HIDDEN;
 extern void CDECL DECLSPEC_NORETURN exit_process( int status ) DECLSPEC_HIDDEN;
@@ -125,6 +128,7 @@ extern void start_server( BOOL debug ) DECLSPEC_HIDDEN;
 
 extern NTSTATUS context_to_server( context_t *to, const CONTEXT *from ) DECLSPEC_HIDDEN;
 extern NTSTATUS context_from_server( CONTEXT *to, const context_t *from ) DECLSPEC_HIDDEN;
+extern void wait_suspend( CONTEXT *context ) DECLSPEC_HIDDEN;
 extern NTSTATUS set_thread_context( HANDLE handle, const context_t *context, BOOL *self ) DECLSPEC_HIDDEN;
 extern NTSTATUS get_thread_context( HANDLE handle, context_t *context, unsigned int flags, BOOL *self ) DECLSPEC_HIDDEN;
 
@@ -132,6 +136,8 @@ extern void signal_init_threading(void) DECLSPEC_HIDDEN;
 extern NTSTATUS signal_alloc_thread( TEB *teb ) DECLSPEC_HIDDEN;
 extern void signal_free_thread( TEB *teb ) DECLSPEC_HIDDEN;
 extern void signal_init_thread( TEB *teb ) DECLSPEC_HIDDEN;
+extern void DECLSPEC_NORETURN signal_start_thread( PRTL_THREAD_START_ROUTINE entry, void *arg,
+                                                   BOOL suspend, void *relay, TEB *teb ) DECLSPEC_HIDDEN;
 extern void DECLSPEC_NORETURN signal_exit_thread( int status, void (*func)(int) ) DECLSPEC_HIDDEN;
 
 #endif /* __NTDLL_UNIX_PRIVATE_H */
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c
index 0346d0d9753..9e229c5f4d2 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -2721,6 +2721,21 @@ done:
 }
 
 
+/***********************************************************************
+ *           virtual_clear_thread_stack
+ *
+ * Clear the stack contents before calling the main entry point, some broken apps need that.
+ */
+void virtual_clear_thread_stack( void *stack_end )
+{
+    void *stack = NtCurrentTeb()->Tib.StackLimit;
+    size_t size = (char *)stack_end - (char *)stack;
+
+    wine_anon_mmap( stack, size, PROT_READ | PROT_WRITE, MAP_FIXED );
+    if (force_exec_prot) mprotect( stack, size, PROT_READ | PROT_WRITE | PROT_EXEC );
+}
+
+
 /***********************************************************************
  *           virtual_handle_fault
  */
diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h
index fb755373f07..69581c23914 100644
--- a/dlls/ntdll/unixlib.h
+++ b/dlls/ntdll/unixlib.h
@@ -28,7 +28,7 @@ struct ldt_copy;
 struct msghdr;
 
 /* increment this when you change the function table */
-#define NTDLL_UNIXLIB_VERSION 16
+#define NTDLL_UNIXLIB_VERSION 17
 
 struct unix_funcs
 {
@@ -112,7 +112,8 @@ struct unix_funcs
 
     /* thread/process functions */
     void          (CDECL *init_threading)( int *nb_threads, struct ldt_copy **ldt_copy );
-    void          (CDECL *init_thread)( TEB *teb );
+    void          (CDECL *start_thread)( PRTL_THREAD_START_ROUTINE entry, void *arg, void *relay, TEB *teb );
+    void          (CDECL *start_process)( PRTL_THREAD_START_ROUTINE entry, BOOL suspend, void *relay );
     void          (CDECL *abort_thread)( int status );
     void          (CDECL *exit_thread)( int status );
     void          (CDECL *exit_process)( int status );
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c
index 709d2b26cdb..e277de16fe8 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -84,8 +84,6 @@ static SIZE_T signal_stack_align;
 #define ROUND_SIZE(addr,size) \
    (((SIZE_T)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask)
 
-static BOOL force_exec_prot;  /* whether to force PROT_EXEC on all PROT_READ mmaps */
-
 /***********************************************************************
  *           get_vprot_flags
  *
@@ -139,31 +137,6 @@ static NTSTATUS get_vprot_flags( DWORD protect, unsigned int *vprot, BOOL image
 }
 
 
-/***********************************************************************
- *           virtual_clear_thread_stack
- *
- * Clear the stack contents before calling the main entry point, some broken apps need that.
- */
-void virtual_clear_thread_stack( void *stack_end )
-{
-    void *stack = NtCurrentTeb()->Tib.StackLimit;
-    size_t size = (char *)stack_end - (char *)stack;
-
-    wine_anon_mmap( stack, size, PROT_READ | PROT_WRITE, MAP_FIXED );
-    if (force_exec_prot) mprotect( stack, size, PROT_READ | PROT_WRITE | PROT_EXEC );
-}
-
-/***********************************************************************
- *           VIRTUAL_SetForceExec
- *
- * Whether to force exec prot on all views.
- */
-void VIRTUAL_SetForceExec( BOOL enable )
-{
-    force_exec_prot = enable;
-    unix_funcs->virtual_set_force_exec( enable );
-}
-
 /**********************************************************************
  *           RtlCreateUserStack (NTDLL.@)
  */
-- 
GitLab