Index: trunk/src/emx/ChangeLog.LIBC
===================================================================
--- trunk/src/emx/ChangeLog.LIBC	(revision 2288)
+++ trunk/src/emx/ChangeLog.LIBC	(revision 2289)
@@ -5,5 +5,14 @@
 2005-08-20: knut st. osmundsen <bird-gccos2-spam@anduin.net>
     - libc:
+        o Define _EMX_SOURCE (__USE_EMX) along with _BSD_SOURCE and _SVID_SOURCE as
+          defaults in features.h.
+        o Protect the fork operations by the means of must complete so
+          signals won't interrupt us till we're finished. This might leave
+          unkillable processes around if fork goes highwire.
+        o Better exception logging during fork.
+        o Use MMX memcpy in fork (if supported).
+        o The C function __libc_Back_processFork() replaces the pure assembly __fork().
         o Added _memcpy_amd.
+        o Fixed some logging statements in sharedpm.c.
         o Log __fmutex_release_internal_must_complete.
         o Corrected a few prototypes in process.h to use pid_t.
Index: trunk/src/emx/include/InnoTekLIBC/backend.h
===================================================================
--- trunk/src/emx/include/InnoTekLIBC/backend.h	(revision 2288)
+++ trunk/src/emx/include/InnoTekLIBC/backend.h	(revision 2289)
@@ -767,4 +767,14 @@
 
 /**
+ * Fork a child process pretty much identical to the calling process.
+ * See SuS for full description of what fork() does and doesn't.
+ *
+ * @returns 0 in the child process.
+ * @returns process identifier of the new child in the parent process. (positive, non-zero)
+ * @returns Negative error code (errno.h) on failure.
+ */
+pid_t __libc_Back_processFork(void);
+
+/**
  * Waits/polls for on one or more processes to change it's running status.
  *
Index: trunk/src/emx/include/InnoTekLIBC/fork.h
===================================================================
--- trunk/src/emx/include/InnoTekLIBC/fork.h	(revision 2288)
+++ trunk/src/emx/include/InnoTekLIBC/fork.h	(revision 2289)
@@ -34,5 +34,8 @@
 
 /** Current fork version. */
-#define __LIBC_FORK_VERSION             0x00010000
+#define __LIBC_FORK_VERSION             0x00020000
+
+/** Current fork module version. */
+#define __LIBC_FORK_MODULE_VERSION      0x00010000
 
 /** Timeout on waiting semaphore in fork operations */
@@ -272,5 +275,5 @@
 typedef struct __libc_ForkHandle
 {
-    /** Fork version. */
+    /** Fork version (__LIBC_FORK_VERSION). */
     unsigned                uVersion;
     /** Parent process, i.e. the one which have called fork(). */
@@ -422,4 +425,8 @@
      * the front. */
     __LIBC_PFORKCOMPLETIONCALLBACK  papfnCompletionCallbacks;
+    /** Index of the next completion callback for the parent. */
+    volatile unsigned       iCompletionCallbackParent;
+    /** Index of the next completion callback for the child. */
+    volatile unsigned       iCompletionCallbackChild;
 
     /** Pointer to the start of the fork buffer. */
@@ -538,5 +545,5 @@
 typedef struct __libc_ForkModule
 {
-    /** Fork version. */
+    /** Fork module version (__LIBC_FORK_MODULE_VERSION). */
     unsigned                    uVersion;
     /** Fork callback function.
Index: trunk/src/emx/include/emx/asm386.h
===================================================================
--- trunk/src/emx/include/emx/asm386.h	(revision 2288)
+++ trunk/src/emx/include/emx/asm386.h	(revision 2289)
@@ -40,4 +40,5 @@
 
 #define ALIGN   .align  4, 0x90
+#define ALIGNP2(a) .align  a, 0x90
 
 #define SET_ERRNO_CONST(x) \
Index: trunk/src/emx/include/sys/errno.h
===================================================================
--- trunk/src/emx/include/sys/errno.h	(revision 2288)
+++ trunk/src/emx/include/sys/errno.h	(revision 2289)
@@ -194,4 +194,9 @@
 #endif /* _POSIX_SOURCE */
 
+#ifdef __USE_EMX
+#define ENEWVER         90              /* mixing with a new version of a struct */
+#define EBADVER         91              /* bad version number */
+#endif
+
 #ifdef _KERNEL
 /* pseudo-errors returned inside kernel to modify return to process */
Index: trunk/src/emx/include/sys/process.h
===================================================================
--- trunk/src/emx/include/sys/process.h	(revision 2288)
+++ trunk/src/emx/include/sys/process.h	(revision 2289)
@@ -7,6 +7,13 @@
 #define _SYS_PROCESS_H
 
-#if defined (__cplusplus)
-extern "C" {
+#include <sys/cdefs.h>
+#include <sys/_types.h>
+
+__BEGIN_DECLS
+
+#if !defined(_PID_T_DECLARED) && !defined(_PID_T)
+typedef	__pid_t pid_t;
+#define	_PID_T_DECLARED
+#define _PID_T
 #endif
 
@@ -51,7 +58,7 @@
 void exit (int) __attribute__ ((__noreturn__));
 void _exit (int) __attribute__ ((__noreturn__));
-int fork (void);
-int getpid (void);
-int getppid (void);
+pid_t fork (void);
+pid_t getpid (void);
+pid_t getppid (void);
 int spawnl (int, __const__ char *, __const__ char *, ...);
 int spawnle (int, __const__ char *, __const__ char *, ...);
@@ -63,6 +70,6 @@
 int spawnvpe (int, __const__ char *, char * __const__ *, char * __const__ *);
 int system (__const__ char *);
-int wait (int *);
-int waitpid (int, int *, int);
+pid_t wait (int *);
+pid_t waitpid (pid_t, int *, int);
 
 int _execl (__const__ char *, __const__ char *, ...);
@@ -75,6 +82,6 @@
 int _execvpe (__const__ char *, char * __const__ *, char * __const__ *);
 int _fork (void);
-int _getpid (void);
-int _getppid (void);
+pid_t _getpid (void);
+pid_t _getppid (void);
 int _spawnl (int, __const__ char *, __const__ char *, ...);
 int _spawnle (int, __const__ char *, __const__ char *, ...);
@@ -85,11 +92,8 @@
 int _spawnvp (int, __const__ char *, char * __const__ *);
 int _spawnvpe (int, __const__ char *, char * __const__ *, char * __const__ *);
-int _wait (int *);
-int _waitpid (int, int *, int);
+pid_t _wait (int *);
+pid_t _waitpid (pid_t, int *, int);
 
-
-#if defined (__cplusplus)
-}
-#endif
+__END_DECLS
 
 #endif /* not _SYS_PROCESS_H */
Index: trunk/src/emx/src/lib/libc.def
===================================================================
--- trunk/src/emx/src/lib/libc.def	(revision 2288)
+++ trunk/src/emx/src/lib/libc.def	(revision 2289)
@@ -236,5 +236,5 @@
     "___fmutex_request_internal" @249
     "___fmutex_request_internal_must_complete" @250
-    "___fork" @251
+    "___libc_Back_processFork" @251
     ;msun - dead "___fpclassify" @252
     ;msun - dead "___fpclassifyf" @253
@@ -1943,2 +1943,3 @@
     "___libc_LogError" @1941
     "___libc_LogErrorLeave" @1942
+    "__memcpy_amd" @1943
Index: trunk/src/emx/src/lib/libc06b4.def
===================================================================
--- trunk/src/emx/src/lib/libc06b4.def	(revision 2288)
+++ trunk/src/emx/src/lib/libc06b4.def	(revision 2289)
@@ -234,5 +234,5 @@
     "___fmutex_request_internal" @249
     "___fmutex_request_internal_must_complete" @250
-    "___fork" @251
+    "___libc_Back_processFork" @251
     "___fpclassify" = ___nullstub_function @252
     "___fpclassifyf" = ___nullstub_function @253
Index: trunk/src/emx/src/lib/libc06b5.def
===================================================================
--- trunk/src/emx/src/lib/libc06b5.def	(revision 2288)
+++ trunk/src/emx/src/lib/libc06b5.def	(revision 2289)
@@ -237,5 +237,5 @@
     "___fmutex_request_internal" @249
     "___fmutex_request_internal_must_complete" @250
-    "___fork" @251
+    "___libc_Back_processFork" @251
     ;msun - dead "___fpclassify" @252
     ;msun - dead "___fpclassifyf" @253
Index: trunk/src/emx/src/lib/process/fork.c
===================================================================
--- trunk/src/emx/src/lib/process/fork.c	(revision 2288)
+++ trunk/src/emx/src/lib/process/fork.c	(revision 2289)
@@ -1,17 +1,56 @@
-/* fork.c (emx+gcc) -- Copyright (c) 1992-1996 by Eberhard Mattes */
+/* $Id: $ */
+/** @file
+ *
+ * LIBC fork().
+ *
+ * Copyright (c) 2004-2005 knut st. osmundsen <bird@anduin.net>
+ *
+ *
+ * This file is part of InnoTek LIBC.
+ *
+ * InnoTek LIBC is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * InnoTek LIBC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with InnoTek LIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
 
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
 #include "libc-alias.h"
 #include <process.h>
-#include <emx/syscalls.h>
+#include <unistd.h>
+#include <errno.h>
+#include <InnoTekLIBC/backend.h>
 #define __LIBC_LOG_GROUP __LIBC_LOG_GRP_FORK
 #include <InnoTekLIBC/logstrict.h>
 
-int _STD(fork)(void)
+
+/**
+ * Fork of a child process.
+ *
+ * @returns 0 in the child process.
+ * @returns process identifier of the new child in the parent process. (positive, non-zero)
+ * @returns -1 and errno on failure.
+ */
+pid_t _STD(fork)(void)
 {
     LIBCLOG_ENTER("\n");
-    int pid = __fork();
+    pid_t pid = __libc_Back_processFork();
     if (pid >= 0)
         LIBCLOG_RETURN_INT(pid);
-    LIBCLOG_ERROR_RETURN_INT(pid);
+    errno = -pid;
+    LIBCLOG_ERROR_RETURN_INT(-1);
 }
 
Index: trunk/src/emx/src/lib/startup/386/crt0.s
===================================================================
--- trunk/src/emx/src/lib/startup/386/crt0.s	(revision 2288)
+++ trunk/src/emx/src/lib/startup/386/crt0.s	(revision 2289)
@@ -109,5 +109,5 @@
 
 ForkModule:
-    .long  0x00010000                   // uVersion
+    .long  0x00010000                   // uVersion (__LIBC_FORK_MODULE_VERSION)
     .long  __atfork_callback            // pfnAtFork
     .long  ___fork_parent1__            // papParent1
Index: trunk/src/emx/src/lib/startup/386/dll0.s
===================================================================
--- trunk/src/emx/src/lib/startup/386/dll0.s	(revision 2288)
+++ trunk/src/emx/src/lib/startup/386/dll0.s	(revision 2289)
@@ -120,5 +120,5 @@
 
 ForkModule:
-    .long  0x00010000                   // uVersion
+    .long  0x00010000                   // uVersion (__LIBC_FORK_MODULE_VERSION)
     .long  __atfork_callback            // pfnAtFork
     .long  ___fork_parent1__            // papParent1
Index: trunk/src/emx/src/lib/sys/386/__fork.s
===================================================================
--- trunk/src/emx/src/lib/sys/386/__fork.s	(revision 2288)
+++ 	(revision )
@@ -1,125 +1,0 @@
-/* $Id$ */
-/** @file
- *
- * fork() 'syscall'.
- *
- * Copyright (c) 2004-2005 knut st. osmundsen <bird-srcspam@anduin.net>
- * Copyright (c) 2004 nickk
- *
- *
- * This file is part of InnoTek LIBC.
- *
- * InnoTek LIBC is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * InnoTek LIBC is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with InnoTek LIBC; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#include <emx/asm386.h>
-
-    .globl  LABEL(__fork)
-
-    .data
-fHaveFXSR:
-    .byte -1
-
-    .text
-
-/**
- * Save the registers and exception chain before forwarding
- * to __libc_fork().
- *
- * @returns See fork().
- */
-LABEL(__fork):
-    pushl   %ebp
-    movl    %esp, %ebp
-
-    /* Save registers on stack */
-    and     $0xfffffff0, %esp           /* fxsave requires 16 byte alignment. */
-    subl    $512, %esp
-    movl    $0, 0(%esp)                 /* paranoia!!!!!!! touch the stack page preventing faults in fxsave. */
-
-    /* check if the cpu support fxsave. */
-l_retest:
-    cmpb    $1, fHaveFXSR
-    je      l_fxsave
-    cmpb    $0, fHaveFXSR
-    jne     l_check_cpuid
-    jmp     l_fnsave
-
-    /* check cpuid for fxrs - ASSUME all processors have cpuid eax=1! */
-l_check_cpuid:
-    push    %ebx
-    mov     $1, %eax
-    cpuid
-    pop     %ebx
-    testl   $0x1000000, %edx  /* bit 24 - fxsr */
-    setnz   fHaveFXSR
-    jnz     l_fxsave
-
-l_fnsave:
-    fnsave  0(%esp)
-    jmp     l_after_save
-l_fxsave:
-    fxsave  0(%esp)
-l_after_save:
-    pushf
-    pushl   %ebx
-    pushl   %esi
-    pushl   %edi
-    pushl   %ebp
-    pushl   %gs
-    pushl   %fs
-    /* load correct fs before accesing the exception chain head. */
-    movl    $DosTIB, %eax
-    movl    %eax, %fs
-    addr16 pushl %fs:0
-
-    /*
-     * Call the fork worker.
-     */
-    movl    %esp, %eax
-    pushl   %eax
-    pushl   $fork_ret
-    call    LABEL(__libc_fork)
-    addl    $12, %esp                   /* 12 = 8 + fs:[0]. */
-    popl    %fs
-    jmp     fork_skip_regs
-
-    /*
-     * Child returns here.
-     */
-ALIGN
-fork_ret:
-    addr16 popl %fs:0                   /* exception chain */
-    popl    %fs
-    popl    %gs
-    popl    %ebp
-    popl    %edi
-    popl    %esi
-    popl    %ebx
-    popf
-    cmpb    $1, fHaveFXSR               /* check which way it was saved. */
-    je      l_fxrstor
-    frstor  0(%esp)
-    jmp     l_after_restore
-l_fxrstor:
-    fxrstor 0(%esp)
-l_after_restore:
-
-fork_skip_regs:
-    mov     %ebp, %esp
-    popl    %ebp
-    ret
-
Index: trunk/src/emx/src/lib/sys/libcfork.c
===================================================================
--- trunk/src/emx/src/lib/sys/libcfork.c	(revision 2288)
+++ trunk/src/emx/src/lib/sys/libcfork.c	(revision 2289)
@@ -2,7 +2,7 @@
 /** @file
  *
- * LIBC Fork.
- *
- * Copyright (c) 2004 knut st. osmundsen <bird-srcspam@anduin.net>
+ * LIBC SYS Backend fork().
+ *
+ * Copyright (c) 2004-2005 knut st. osmundsen <bird-srcspam@anduin.net>
  *
  *
@@ -92,11 +92,76 @@
 } __LIBC_FORKXCPTREGREC, *__LIBC_PFORKXCPTREGREC;
 
-
-/*******************************************************************************
-*   Global Variables                                                           *
-*******************************************************************************/
-/** Pointer to the forkhandle for the current fork operation.
- * This is *only* used by the exception and signal handlers! */
-static volatile __LIBC_PFORKHANDLE g_pForkHandle = NULL;
+/**
+ * 80-bit MMX/FPU register type.
+ */
+typedef struct X86FPUMMX
+{
+    uint8_t reg[10];
+} X86FPUMMX;
+/** Pointer to 80-bit MMX/FPU register type. */
+typedef X86FPUMMX *PX86FPUMMX;
+
+/**
+ * FPU Extended state (aka FXSAVE/FXRSTORE Memory Region).
+ */
+#pragma pack(1)
+typedef struct X86FXSTATE
+{
+    /** Control word. */
+    uint16_t    FCW;
+    /** Status word. */
+    uint16_t    FSW;
+    /** Tag word (it's a byte actually). */
+    uint8_t     FTW;
+    uint8_t     huh1;
+    /** Opcode. */
+    uint16_t    FOP;
+    /** Instruction pointer. */
+    uint32_t    FPUIP;
+    /** Code selector. */
+    uint16_t    CS;
+    uint16_t    Rsvrd1;
+    /* - offset 16 - */
+    /** Data pointer. */
+    uint32_t    FPUDP;
+    /** Data segment */
+    uint16_t    DS;
+    uint16_t    Rsrvd2;
+    uint32_t    MXCSR;
+    uint32_t    MXCSR_MASK;
+    /* - offset 32 - */
+    union
+    {
+        /** MMX view. */
+        uint64_t    mmx;
+        /** FPU view - todo. */
+        X86FPUMMX   fpu;
+        /** 8-bit view. */
+        uint8_t     au8[16];
+        /** 16-bit view. */
+        uint16_t    au16[8];
+        /** 32-bit view. */
+        uint32_t    au32[4];
+        /** 64-bit view. */
+        uint64_t    au64[2];
+    } aRegs[8];
+    /* - offset 160 - */
+    union
+    {
+        /** 8-bit view. */
+        uint8_t     au8[16];
+        /** 16-bit view. */
+        uint16_t    au16[8];
+        /** 32-bit view. */
+        uint32_t    au32[4];
+        /** 64-bit view. */
+        uint64_t    au64[2];
+    } aXMM[8];
+    /* - offset 288 - */
+    uint32_t    au32RsrvdRest[(512 - 288) / sizeof(uint32_t)];
+} X86FXSTATE;
+#pragma pack()
+/** Pointer to FPU Extended state. */
+typedef X86FXSTATE  *PX86FXSTATE;
 
 
@@ -115,8 +180,8 @@
  * In the 2nd section they are ordered by what they do.
  */
-/* public */
-int __libc_fork(void *pvForkRet, void *pvStackRet);
 /* sequential */
+static int                  forkParDo(void *pvForkRet, void *pvStackRet, __LIBC_PFORKXCPTREGREC pXcptRegRec);
 static int                  forkParCanFork(__LIBC_PFORKMODULE pModules);
+static int                  forkParValidateModules(__LIBC_PFORKMODULE pModules);
 static __LIBC_PFORKHANDLE   forkParAllocHandle(__LIBC_PFORKMODULE pModules, void *pvStackRet, void *pvForkRet);
 static int                  forkParRunPreExecParent(__LIBC_PFORKHANDLE pForkHandle, __LIBC_PFORKMODULE pModules);
@@ -131,5 +196,5 @@
 static void                 forkChlCompletionChild(__LIBC_PFORKHANDLE pForkHandle, int rc);
 static void                 forkParCompletionParent(__LIBC_PFORKHANDLE pForkHandle, int rc);
-static void                 forkBothCompletion(__LIBC_PFORKHANDLE pForkHandle, int rc, __LIBC_FORKCTX enmCtx);
+static void                 forkBothCompletion(__LIBC_PFORKHANDLE pForkHandle, int rc, __LIBC_FORKCTX enmCtx, unsigned volatile *piCompletionCallback);
 static void                 forkBthCloseHandle(__LIBC_PFORKHANDLE pForkHandle, __LIBC_FORKCTX enmContext);
 /* any time */
@@ -150,5 +215,11 @@
 static int                  forkBthBufferSpace(__LIBC_PFORKHANDLE pForkHandle, size_t cb);
 static int                  forkBthBufferGiveWaitNext(__LIBC_PFORKHANDLE pForkHandle, __LIBC_FORKCTX enmCtx);
-static void                 forkBthCopyPages(void *pvTrg, const void *pvSrc, size_t cb);
+static void                 forkBthCopyPagesDetect(void *pvDst, const void *pvSrc, size_t cb);
+static void                 forkBthCopyPagesPlain(void *pvDst, const void *pvSrc, size_t cb);
+static void                 forkBthCopyPagesMMX(void *pvDst, const void *pvSrc, size_t cb);
+#if 0
+static void                 forkBthCopyPagesMMXNonTemporal(void *pvDst, const void *pvSrc, size_t cb);
+static void                 forkBthCopyPagesSSE2(void *pvDst, const void *pvSrc, size_t cb);
+#endif
 static void                 forkBthCallbacksSort(__LIBC_PFORKCALLBACK *papCallbacks);
 static int                  forkBthCallbacksCompare(const void *pv1, const void *pv2);
@@ -165,4 +236,12 @@
                                                     PVOID                        pvWhatEver);
 
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** Pointer to the page copy function. */
+static void                (*pfnForkBthCopyPages)(void *pvDst, const void *pvSrc, size_t cb) = forkBthCopyPagesDetect;
+/** Pointer to the forkhandle for the current fork operation.
+ * This is *only* used by the exception and signal handlers! */
+static volatile __LIBC_PFORKHANDLE g_pForkHandle = NULL;
 
 
@@ -224,5 +303,7 @@
 int __libc_ForkRegisterModule(__LIBC_PFORKMODULE pModule, int fExecutable)
 {
-    LIBCLOG_ENTER("pModule=%p fExecutable=%d\n", (void *)pModule, fExecutable);
+    LIBCLOG_ENTER("pModule=%p:{.uVersion=%#x, .pfnAtFork=%p, .papParent1=%p, .papChild1=%p, .pvDataSegBase=%p, .pvDataSegEnd=%p, .fFlags=%#x, .pNext=%p} fExecutable=%d\n",
+                  (void *)pModule, pModule->uVersion, (void *)pModule->pfnAtFork, (void *)pModule->papParent1, (void *)pModule->papChild1,
+                  (void *)pModule->pvDataSegBase, (void *)pModule->pvDataSegEnd, pModule->fFlags, (void *)pModule->pNext, fExecutable);
     int                 rc;
     __LIBC_PSPMPROCESS  pProcess;
@@ -356,6 +437,172 @@
 
 /**
- * This is the function called by __fork() after registers
- * have been saved on the stack.
+ * Checks if the current CPU supports CPUID.
+ *
+ * @returns 1 if CPUID is supported, 0 if it doesn't.
+ */
+static inline int HasCpuId(void)
+{
+    int         fRet = 0;
+    uint32_t    u1;
+    uint32_t    u2;
+    __asm__ ("pushf\n\t"
+             "pop   %1\n\t"
+             "mov   %1, %2\n\t"
+             "xorl  $0x200000, %1\n\t"
+             "push  %1\n\t"
+             "popf\n\t"
+             "pushf\n\t"
+             "pop   %1\n\t"
+             "cmpl  %1, %2\n\t"
+             "setne %0\n\t"
+             "push  %2\n\t"
+             "popf\n\t"
+             : "=m" (fRet), "=r" (u1), "=r" (u2));
+    return fRet;
+}
+
+
+/**
+ * Performs the cpuid instruction returning edx.
+ *
+ * @param   uOperator   CPUID operation (eax).
+ * @returns EDX after cpuid operation.
+ */
+static inline uint32_t CpuIdEDX(unsigned uOperator)
+{
+    uint32_t    u32EDX;
+    __asm__ ("cpuid"
+             : "=a" (uOperator),
+               "=d" (u32EDX)
+             : "0" (uOperator)
+             : "ebx", "ecx");
+    return u32EDX;
+}
+
+
+/**
+ * Fork a child process pretty much identical to the calling process.
+ * See SuS for full description of what fork() does and doesn't.
+ *
+ * @returns 0 in the child process.
+ * @returns process identifier of the new child in the parent process. (positive, non-zero)
+ * @returns Negative error code (errno.h) on failure.
+ */
+pid_t __libc_Back_processFork(void)
+{
+    LIBCLOG_ENTER("__libc_Back_processFork:\n");
+
+    /*
+     * Determin fxsave/fxrstor support.
+     */
+    static int fHaveFXSR = -1;
+    if (fHaveFXSR == -1)
+        fHaveFXSR = HasCpuId() && (CpuIdEDX(1) & 0x1000000); /* bit 24 - fxsr */
+
+    /*
+     * Save the FPU state
+     */
+    PX86FXSTATE pFPU = (PX86FXSTATE)alloca(512 + 16);
+    pFPU = (PX86FXSTATE)(((uintptr_t)pFPU + 15) & ~15);
+    if (fHaveFXSR)
+        __asm__ __volatile__ ("fxsave %0" : "=m" (*pFPU));
+    else
+        __asm__ __volatile__ ("fnsave %0" : "=m" (*pFPU));
+
+    /*
+     * Take the Exec Semaphore.
+     */
+    int rc = _fmutex_request(&__libc_gmtxExec, 0);
+    if (rc)
+    {
+        rc = -__libc_native2errno(rc);
+        LIBCLOG_ERROR_RETURN_INT(rc);
+    }
+
+    /*
+     * Install exception handler, enter must complete section (pretending we're in the kernel).
+     */
+    g_pForkHandle = NULL;               /* exception handler variable */
+
+    PTIB  pTib = NULL;
+    PPIB  pPib;
+    FS_VAR_SAVE_LOAD();
+    DosGetInfoBlocks(&pTib, &pPib);
+    __LIBC_FORKXCPTREGREC XcptRegRec;
+    XcptRegRec.fDoneCompletion       = 0;
+    XcptRegRec.Core.ExceptionHandler = forkParExceptionHandler;
+    XcptRegRec.Core.prev_structure   = pTib->tib_pexchain;
+    pTib->tib_pexchain = &XcptRegRec.Core;
+
+    ULONG cNesting = 0;
+    DosEnterMustComplete(&cNesting);
+
+    /*
+     * Save registers.
+     */
+    pid_t pid = -1;
+    __asm__ __volatile__ (
+        "pushf\n\t"
+        "pushl   %%ebx\n\t"
+        "pushl   %%esi\n\t"
+        "pushl   %%edi\n\t"
+        "pushl   %%ebp\n\t"
+        "pushl   %%gs\n\t"
+
+    /*
+     * Call the fork worker, forkParDo().
+     */
+        "movl    %%esp, %%eax\n\t"
+        "pushl   %%edx\n\t"
+        "pushl   %%eax\n\t"
+        "pushl   $fork_ret\n\t"
+        "call    _forkParDo\n\t"
+        "addl    $12, %%esp\n\t"         /* __cdecl */
+        "jmp     fork_skip_regs\n\t"
+
+    /*
+     * The Child returns here.
+     */
+        "fork_ret:\n\t"
+        "popl    %%gs\n\t"
+        "popl    %%ebp\n\t"
+        "popl    %%edi\n\t"
+        "popl    %%esi\n\t"
+        "popl    %%ebx\n\t"
+        "popf\n\t"
+        "\n"
+        "fork_skip_regs:\n\t"
+        : "=a" (pid): "d" (&XcptRegRec) : "ecx");
+
+    /*
+     * Restore the exception handler and exit the must complete section.
+     */
+    pTib->tib_pexchain = XcptRegRec.Core.prev_structure;
+    DosExitMustComplete(&cNesting);
+
+    /*
+     * Release the exec semaphore.
+     */
+    _fmutex_release(&__libc_gmtxExec);
+
+    /*
+     * Restore the FPU state.
+     */
+    if (fHaveFXSR)
+        __asm__ __volatile__ ("fxrstor %0" : "=m" (*pFPU));
+    else
+        __asm__ __volatile__ ("frstor %0" : "=m" (*pFPU));
+
+    FS_RESTORE();
+    LIBCLOG_RETURN_INT(pid);
+}
+
+
+/**
+ * This is the function called by __libc_Back_processFork() to drive the
+ * parent side of the fork.
+ *
+ * At this point we're in a must-complete section, owning the exec mutex,
+ * and have forkParExceptionHandler installed.
  *
  *
@@ -375,27 +622,20 @@
  *
  * @returns pid of the forked child process on success.
- * @returns -1 and errno on failure.
+ * @returns Negative error code (errno.h) on failure.
  *
  * @param   pvForkRet       Return address for the child.
  * @param   pvStackRet      ESP for the child when returning from fork.
- */
-int __libc_fork(void *pvForkRet, void *pvStackRet)
+ * @param   pXctpRegRec     Pointer to the exception registration record (fDoneCompletion).
+ */
+static int forkParDo(void *pvForkRet, void *pvStackRet, __LIBC_PFORKXCPTREGREC pXcptRegRec)
 {
     LIBCLOG_ENTER("pvForkRet=%p pvStackRet=%p\n", pvForkRet, pvStackRet);
-    __LIBC_PFORKHANDLE  pForkHandle;
-    __LIBC_PFORKMODULE  pModules;
-    int                 rc;
-    pid_t               pid = -1;
-    PTIB                pTib;
-    PPIB                pPib;
-    __LIBC_FORKXCPTREGREC XcptRegRec;
-    FS_VAR();
 
     /*
      * Get module list.
      */
-    pModules = forkBthGetModules();
+    __LIBC_PFORKMODULE pModules = forkBthGetModules();
     if (!pModules)
-        LIBCLOG_ERROR_RETURN_INT(-1);
+        LIBCLOG_ERROR_RETURN_INT(-ENOSYS);
 
     /*
@@ -406,27 +646,13 @@
     {
         LIBC_ASSERTM_FAILED("Can't fork this process, the executable wasn't built with -Zfork!\n");
-        LIBCLOG_ERROR_RETURN_INT(-1);
-    }
-
-    /*
-     * Take the Exec Semaphore.
-     */
-    rc = _fmutex_request(&__libc_gmtxExec, 0);
-    if (rc)
-    {
-        _sys_set_errno(rc);
-        LIBCLOG_ERROR_RETURN_INT(-1);
-    }
-
-    /*
-     * Register the exception handler.
-     */
-    FS_SAVE_LOAD();
-    DosGetInfoBlocks(&pTib, &pPib);
-    XcptRegRec.fDoneCompletion = 0;
-    XcptRegRec.Core.ExceptionHandler = forkParExceptionHandler;
-    XcptRegRec.Core.prev_structure = pTib->tib_pexchain;
-    pTib->tib_pexchain = &XcptRegRec.Core;
-    g_pForkHandle = NULL;
+        LIBCLOG_ERROR_RETURN_INT(-ENOSYS);
+    }
+
+    /*
+     * Check that we can handle all the modules in the chain.
+     */
+    int rc = forkParValidateModules(pModules);
+    if (rc < 0)
+        LIBCLOG_ERROR_RETURN_INT(rc);
 
     /*
@@ -434,5 +660,7 @@
      * Install signal handlers after that.
      */
-    g_pForkHandle = pForkHandle = forkParAllocHandle(pModules, pvStackRet, pvForkRet);
+    pid_t   pid = -1;
+    __LIBC_PFORKHANDLE pForkHandle = forkParAllocHandle(pModules, pvStackRet, pvForkRet);
+    g_pForkHandle = pForkHandle;
     if (pForkHandle)
     {
@@ -483,8 +711,10 @@
          * Process child context completion callbacks.
          */
-        if (!XcptRegRec.fDoneCompletion)
+        if (!pXcptRegRec->fDoneCompletion)
             forkParCompletionParent(pForkHandle, rc);
         forkBthCloseHandle(pForkHandle, __LIBC_FORK_CTX_PARENT);
     }
+    else
+        rc = -ENOMEM;
 
 
@@ -493,19 +723,9 @@
      */
     if (rc >= 0)
+    {
         __libc_back_processWaitNotifyExec(pid);
-
-    /*
-     * Reregister the exception handler but now replacing the existing chain.
-     */
-    pTib->tib_pexchain = XcptRegRec.Core.prev_structure;
-    g_pForkHandle = NULL;
-    _fmutex_release(&__libc_gmtxExec);
-    FS_RESTORE();
-    if (rc >= 0)
         LIBCLOG_RETURN_INT(pid);
-
-    /* failure - we are non-posix in or errno code perhaps, but couldn't care less for the moment... */
-    errno = -rc;
-    LIBCLOG_ERROR_RETURN_INT(-1);
+    }
+    LIBCLOG_ERROR_RETURN_INT(-rc);
 }
 
@@ -633,4 +853,35 @@
     errno = EINVAL;
     LIBCLOG_RETURN_INT(-1);
+}
+
+
+/**
+ * Validate that we can handle all the modules in the list.
+ *
+ * This routine will check the version number of each module and make
+ * sure we're able to cope with it.
+ *
+ * @returns 0 if all modules returns succesfully.
+ * @returns negative error code (errno.h) on incompatability.
+ * @param   pModules        Head of the module list.
+ */
+static int forkParValidateModules(__LIBC_PFORKMODULE pModules)
+{
+    LIBCLOG_ENTER("pModules=%p\n", (void *)pModules);
+    unsigned            i = 0;
+    __LIBC_PFORKMODULE  pModule;
+    for (pModule = pModules; pModule; pModule = pModule->pNext)
+    {
+        (void)i;
+        LIBCLOG_MSG("%p: #%d uVersion=%#10x pfnAtFork=%p papParent=%p papChild=%p pvDataSegBase=%p pvDataSegEnd=%p fFlags=%#010x pNext=%p\n",
+                    (void *)pModule, i++, pModule->uVersion, (void *)pModule->pfnAtFork, (void *)pModule->papParent1, (void *)pModule->papChild1,
+                    pModule->pvDataSegBase, pModule->pvDataSegEnd, pModule->fFlags, (void *)pModule->pNext);
+        if ((pModule->uVersion >> 16) != (__LIBC_FORK_MODULE_VERSION >> 16))
+        {
+            LIBCLOG_ERROR("Encountered a new and incompatible module version, %#x. pModule=%p\n", pModule->uVersion, (void *)pModule);
+            LIBCLOG_ERROR_RETURN_INT(-ENEWVER);
+        }
+    }
+    LIBCLOG_RETURN_INT(0);
 }
 
@@ -954,31 +1205,36 @@
         if (pForkHandle->enmStage == __LIBC_FORK_STAGE_EXEC)
         {
-            rc = DosOpenMutexSem(NULL, &pForkHandle->hmtx);
-            if (!rc)
+            if (pForkHandle->uVersion == __LIBC_FORK_VERSION)
             {
-                rc = DosOpenEventSem(NULL, &pForkHandle->hevParent);
+                rc = DosOpenMutexSem(NULL, &pForkHandle->hmtx);
                 if (!rc)
                 {
-                    rc = DosOpenEventSem(NULL, &pForkHandle->hevChild);
+                    rc = DosOpenEventSem(NULL, &pForkHandle->hevParent);
                     if (!rc)
                     {
-                        /*
-                         * Succesfully opened the handle for the first time.
-                         * Set the pidChild.
-                         */
-                        PTIB pTib;
-                        PPIB pPib;
-                        DosGetInfoBlocks(&pTib, &pPib);
-                        pForkHandle->pidChild = pPib->pib_ulpid;
-                        LIBC_ASSERTM(pForkHandle->pidParent == pPib->pib_ulppid,
-                                     "Invalid parent pid... pidParent=%04x pib_ulppid=%04lx (pib_ulpid=%04lx).\n",
-                                     pForkHandle->pidParent, pPib->pib_ulppid, pPib->pib_ulpid);
-                        LIBCLOG_RETURN_P(pForkHandle);
+                        rc = DosOpenEventSem(NULL, &pForkHandle->hevChild);
+                        if (!rc)
+                        {
+                            /*
+                             * Succesfully opened the handle for the first time.
+                             * Set the pidChild.
+                             */
+                            PTIB pTib;
+                            PPIB pPib;
+                            DosGetInfoBlocks(&pTib, &pPib);
+                            pForkHandle->pidChild = pPib->pib_ulpid;
+                            LIBC_ASSERTM(pForkHandle->pidParent == pPib->pib_ulppid,
+                                         "Invalid parent pid... pidParent=%04x pib_ulppid=%04lx (pib_ulpid=%04lx).\n",
+                                         pForkHandle->pidParent, pPib->pib_ulppid, pPib->pib_ulpid);
+                            LIBCLOG_RETURN_P(pForkHandle);
+                        }
+                        else
+                            LIBC_ASSERTM_FAILED("DosOpenEventSem(NULL,..:{%lx}) -> %d (child)\n", pForkHandle->hevChild, rc);
                     }
                     else
-                        LIBC_ASSERTM_FAILED("DosOpenEventSem(NULL,..:{%lx}) -> %d (child)\n", pForkHandle->hevChild, rc);
+                        LIBC_ASSERTM_FAILED("DosOpenEventSem(NULL,..:{%lx}) -> %d (parent)\n", pForkHandle->hevParent, rc);
                 }
                 else
-                    LIBC_ASSERTM_FAILED("DosOpenEventSem(NULL,..:{%lx}) -> %d (parent)\n", pForkHandle->hevParent, rc);
+                    LIBC_ASSERTM_FAILED("pForkHandle->uVersion (%#x) != __LIBC_FORK_VERSION (%#x)\n", pForkHandle->uVersion, __LIBC_FORK_VERSION);
             }
             else
@@ -1017,4 +1273,10 @@
 
     /*
+     * Block signals while we're in here. We cannot handle them properly anyway.
+     */
+    ULONG cNesting = 0;
+    DosEnterMustComplete(&cNesting);
+
+    /*
      * Open and process the buffer on first call.
      */
@@ -1023,8 +1285,14 @@
         rc = forkBthBufferWait(pForkHandle, __LIBC_FORK_CTX_CHILD);
         if (rc < 0)
+        {
+            DosExitMustComplete(&cNesting);
             LIBCLOG_RETURN_INT(rc);
+        }
         rc = forkBthBufferProcess(pForkHandle, __LIBC_FORK_CTX_CHILD, NULL);
         if (rc < 0)
+        {
+            DosExitMustComplete(&cNesting);
             LIBCLOG_RETURN_INT(rc);
+        }
 
         /* next stage */
@@ -1037,9 +1305,15 @@
     rc = pModule->pfnAtFork(pModule, pForkHandle, __LIBC_FORK_OP_EXEC_CHILD);
     if (rc < 0)
+    {
+        DosExitMustComplete(&cNesting);
         LIBCLOG_RETURN_INT(rc);
+    }
 
     /* done? */
     if (!fExecutable)
+    {
+        DosExitMustComplete(&cNesting);
         LIBCLOG_RETURN_INT(0);
+    }
 
     /*
@@ -1081,5 +1355,6 @@
                            "D" ((uintptr_t)pForkHandle->pvStackRet),
                            "S" (pForkHandle->pvForkRet));
-    return forkChlDoFork2(pForkHandle); /* just for referencing the function - we'll never get here!!! */
+    /* just for referencing the functions so GCC doesn't optimize them away - we'll never get here!!! */
+    return forkChlDoFork2(pForkHandle) + forkParDo(NULL, NULL, NULL);
 }
 
@@ -1113,5 +1388,5 @@
     rc = forkBthBufferSpace(pForkHandle, offsetof(__LIBC_FORKPKGHDR, u.Next.achStart));
     if (rc < 0)
-        LIBCLOG_RETURN_INT(rc);
+        forkChlFatalError(pForkHandle, rc, NULL);
     forkBthBufferNext(pForkHandle);
     forkBthBufferEnd(pForkHandle);
@@ -1139,5 +1414,4 @@
         forkChlFatalError(pForkHandle, rc, NULL);
 
-
     /*
      * Do completion child calls (success).
@@ -1161,8 +1435,10 @@
 
     /*
-     * The return here will go back to the inline assmebly which called us
-     * clean up the stack and jump to the resume location in __fork.s.
-     */
-    _fmutex_release(&__libc_gmtxExec);
+     * The return here will go back to the inline assmebly which called us in forkChlDoFork()
+     * and restore the stack pointer to the __libc_Back_processFork() and jump to the fork_ret
+     * label in __libc_Back_processFork().
+     * __libc_Back_processFork() will restore the exception handler chain, exit
+     * the must-complete section and release the execute semaphore.
+     */
     __libc_spmExeInited();
     pTib->tib_pexchain = END_OF_CHAIN;
@@ -1297,5 +1573,5 @@
 {
     LIBCLOG_ENTER("pForkHandle=%p rc=%d\n", (void *)pForkHandle, rc);
-    forkBothCompletion(pForkHandle, rc, __LIBC_FORK_CTX_CHILD);
+    forkBothCompletion(pForkHandle, rc, __LIBC_FORK_CTX_CHILD, &pForkHandle->iCompletionCallbackChild);
     LIBCLOG_RETURN_VOID();
 }
@@ -1312,5 +1588,5 @@
 {
     LIBCLOG_ENTER("pForkHandle=%p rc=%d\n", (void *)pForkHandle, rc);
-    forkBothCompletion(pForkHandle, rc, __LIBC_FORK_CTX_PARENT);
+    forkBothCompletion(pForkHandle, rc, __LIBC_FORK_CTX_PARENT, &pForkHandle->iCompletionCallbackParent);
     LIBCLOG_RETURN_VOID();
 }
@@ -1320,12 +1596,13 @@
  * Execute completion callbacks for a context.
  *
- * @param   pForkHandle Fork Handle.
- * @param   rc          The error code (errno.h) for the fork operation.
- *                      Negative means error, zero or positive means success.
- * @param   enmCtx      The Context.
- */
-void                 forkBothCompletion(__LIBC_PFORKHANDLE pForkHandle, int rc, __LIBC_FORKCTX enmCtx)
-{
-
+ * @param   pForkHandle             Fork Handle.
+ * @param   rc                      The error code (errno.h) for the fork operation.
+ *                                  Negative means error, zero or positive means success.
+ * @param   enmCtx                  The Context.
+ * @param   piCompletionCallaback   Pointer to the variable containing the current completion callback index.
+ *                                  This is both input and output.
+ */
+void                 forkBothCompletion(__LIBC_PFORKHANDLE pForkHandle, int rc, __LIBC_FORKCTX enmCtx, unsigned volatile *piCompletionCallback)
+{
     /*
      * Call completion callbacks in reverse registration order.
@@ -1337,6 +1614,7 @@
     __LIBC_PFORKCOMPLETIONCALLBACK  papfn = pForkHandle->papfnCompletionCallbacks;
     unsigned                        iCC;
-    for (iCC = 0; iCC < cCCs; iCC++)
-    {
+    while ((iCC = *piCompletionCallback) < cCCs)
+    {
+        *piCompletionCallback += 1;
         if (    papfn[iCC].enmContext == __LIBC_FORK_CTX_BOTH
             ||  papfn[iCC].enmContext == enmCtx)
@@ -1465,6 +1743,6 @@
             pChunk->pv = pvStart;
             pChunk->cb = pChunk->cbVirt = cbRange;
-            pChunk->offData = 16 - ((uintptr_t)&pChunk->achData[0] & 15);
-            if (pChunk->offData == 16)
+            pChunk->offData = 64 - ((uintptr_t)&pChunk->achData[0] & 63);
+            if (pChunk->offData == 64)
                 pChunk->offData = 0;    /* fixme: probably could do this without if's. */
 
@@ -1506,5 +1784,5 @@
                         break;          /* need to flush the buffer. */
                 }
-                forkBthCopyPages(&pChunk->achData[pChunk->offData], pvStart, pChunk->cb);
+                pfnForkBthCopyPages(&pChunk->achData[pChunk->offData], pvStart, pChunk->cb);
             }
             else
@@ -1575,5 +1853,5 @@
                                 break;          /* need to flush the buffer. */
                         }
-                        forkBthCopyPages(&pChunk->achData[pChunk->offData], pvStart, pChunk->cb);
+                        pfnForkBthCopyPages(&pChunk->achData[pChunk->offData], pvStart, pChunk->cb);
                     }
                 }
@@ -2107,5 +2385,5 @@
          */
         if (pChunk->cb)
-            forkBthCopyPages(pChunk->pv, &pChunk->achData[pChunk->offData], pChunk->cb);
+            pfnForkBthCopyPages(pChunk->pv, &pChunk->achData[pChunk->offData], pChunk->cb);
 
         /* next */
@@ -2295,37 +2573,236 @@
 
 /**
- * Copy page memory.
- *
- * @param   pvTrg   Target address.
+ * Copy page memory, detect the right worker function and calls it.
+ *
+ * @param   pvDst   Target address.
  * @param   pvSrc   Source address.
  * @param   cb      Number of bytes to copy.
- * @remark  We will later make alignment requirements depending on which
- *          fast copy algorithms we end up using...
- */
-void forkBthCopyPages(void *pvTrg, const void *pvSrc, size_t cb)
-{
-    LIBCLOG_ENTER("pvTrg=%p pvSrc=%p cb=0x%08x\n", pvTrg, pvSrc, cb);
-    /** @todo install exception handler to catch access violations! */
-
-    /** @todo optimize memcpy for duplicate pages. This can assume 4 byte alignment, we can
-     * if required force 8 or 16 alignment if required. The SSE[1-3] and MMX states are saved
-     * on the stack and can be restored in both contexts if we like to use those instruction
-     * extensions for faster memcpy.
-     */
-#if 0
-    memcpy(pvTrg, pvSrc, cb);
-#else
+ */
+void forkBthCopyPagesDetect(void *pvDst, const void *pvSrc, size_t cb)
+{
+    /*
+     * Decide which function to use.
+     */
+    pfnForkBthCopyPages = forkBthCopyPagesPlain; /* default, always works. */
+    if (HasCpuId())
+    {
+        unsigned uEdx = CpuIdEDX(1);
+        if (uEdx & (23/*MMX*/ << 1))
+        {
+            pfnForkBthCopyPages = forkBthCopyPagesMMX;
+#if 0  /* missing hardware to test this. */
+            /* hope this is right */
+            if (    (uEdx & (1 << 24)) /*FXSR*/
+                &&  (uEdx & (1 << 25)) /*SSE*/
+                &&  (uEdx & (1 << 26)) /*SSE2*/)
+                pfnForkBthCopyPages = forkBthCopyPagesSSE2;
+#endif
+#if 0 /* too slow for some peculiar reason... */
+            else
+            {
+                if (    uEdx & (23/*MMX*/ << 1)
+                    &&  uEdx & (25/*SSE*/ << 1))
+                    pfnForkBthCopyPages = forkBthCopyPagesMMXNonTemporal;
+            }
+#endif
+        }
+    }
+    pfnForkBthCopyPages(pvDst, pvSrc, cb);
+}
+
+
+__asm__(".align 4\n\t");
+/**
+ * Copy page memory, plain stupid rep movsl.
+ *
+ * @param   pvDst   Target address.
+ * @param   pvSrc   Source address.
+ * @param   cb      Number of bytes to copy.
+ */
+static void forkBthCopyPagesPlain(void *pvDst, const void *pvSrc, size_t cb)
+{
+    LIBCLOG_ENTER("pvDst=%p pvSrc=%p cb=0x%08x\n", pvDst, pvSrc, cb);
     register unsigned long int r0,r1,r2;
-    __asm__ __volatile__							
-      ("cld\n\t"								
-       "rep; movsl"							
+    __asm__ __volatile__
+      ("cld\n\t"
+       "rep; movsl"
        : "=&c" (r0), "=&D" (r1), "=&S" (r2)
-       : "0" (cb / 4), "1" (pvTrg), "2" (pvSrc)
+       : "0" (cb / 4), "1" (pvDst), "2" (pvSrc)
        : "memory");
-
-    memcpy(pvTrg, pvSrc, cb);
+    LIBCLOG_RETURN_VOID();
+}
+
+
+__asm__(".align 4\n\t");
+/**
+ * Copy page memory, MMX.
+ *
+ * @param   pvDst   Target address.
+ * @param   pvSrc   Source address.
+ * @param   cb      Number of bytes to copy.
+ */
+static void forkBthCopyPagesMMX(void *pvDst, const void *pvSrc, size_t cb)
+{
+    LIBCLOG_ENTER("pvDst=%p pvSrc=%p cb=0x%08x\n", pvDst, pvSrc, cb);
+    register unsigned long int r0,r1,r2;
+    __asm__ __volatile__
+      ("\
+1:                                                              \n\
+        movq   (%0), %%mm1                                      \n\
+        movq  8(%0), %%mm2                                      \n\
+        movq 16(%0), %%mm3                                      \n\
+        movq 24(%0), %%mm4                                      \n\
+        movq %%mm1,   (%1)                                      \n\
+        movq %%mm2,  8(%1)                                      \n\
+        movq %%mm3, 16(%1)                                      \n\
+        movq %%mm4, 24(%1)                                      \n\
+        movq 32(%0), %%mm5                                      \n\
+        movq 40(%0), %%mm6                                      \n\
+        movq 48(%0), %%mm7                                      \n\
+        movq 56(%0), %%mm0                                      \n\
+        addl $64, %0                                            \n\
+        movq %%mm5, 32(%1)                                      \n\
+        movq %%mm6, 40(%1)                                      \n\
+        movq %%mm7, 48(%1)                                      \n\
+        movq %%mm0, 56(%1)                                      \n\
+        addl $64, %1                                            \n\
+                                                                \n\
+        decl %2                                                 \n\
+        jnz 1b                                                  \n\
+                                                                \n\
+        emms                                                    \n\
+        "
+       : "=&r" (r0), "=&r" (r1), "=&r" (r2)
+       : "0" (pvSrc), "1" (pvDst), "2" (cb/64)
+       : "memory");
+    LIBCLOG_RETURN_VOID();
+}
+
+
+#if 0 /* too slow */
+__asm__(".align 4\n\t");
+/**
+ * Copy page memory, MMX with non-temporal stores.
+ *
+ * @param   pvDst   Target address.
+ * @param   pvSrc   Source address.
+ * @param   cb      Number of bytes to copy.
+ * @remark requires the Pentium III MMX extensions (appears to mean SSE).
+ */
+static void forkBthCopyPagesMMXNonTemporal(void *pvDst, const void *pvSrc, size_t cb)
+{
+    LIBCLOG_ENTER("pvDst=%p pvSrc=%p cb=0x%08x\n", pvDst, pvSrc, cb);
+    register unsigned long int r0,r1,r2;
+    __asm__ __volatile__
+      ("\n\
+        movq   (%0), %%mm1                                          \n\
+        movq  8(%0), %%mm2                                          \n\
+        prefetchnta 320(%0)                                         \n\
+        movq 16(%0), %%mm3                                          \n\
+        movq 24(%0), %%mm4                                          \n\
+        movntq %%mm1,   (%1)                                        \n\
+        movntq %%mm2,  8(%1)                                        \n\
+        movntq %%mm3, 16(%1)                                        \n\
+        movntq %%mm4, 24(%1)                                        \n\
+        movq 32(%0), %%mm5                                          \n\
+        movq 40(%0), %%mm6                                          \n\
+        movq 48(%0), %%mm7                                          \n\
+        movq 56(%0), %%mm0                                          \n\
+        addl $64, %0                                                \n\
+        movntq %%mm5, 32(%1)                                        \n\
+        movntq %%mm6, 40(%1)                                        \n\
+        movntq %%mm7, 48(%1)                                        \n\
+        movntq %%mm0, 56(%1)                                        \n\
+        addl $64, %1                                                \n\
+        decl %2                                                     \n\
+        1:                                                          \n\
+        movq   (%0), %%mm1                                          \n\
+        movq  8(%0), %%mm2                                          \n\
+        movq 16(%0), %%mm3                                          \n\
+        movq 24(%0), %%mm4                                          \n\
+        movntq %%mm1,   (%1)                                        \n\
+        movntq %%mm2,  8(%1)                                        \n\
+        movntq %%mm3, 16(%1)                                        \n\
+        movntq %%mm4, 24(%1)                                        \n\
+        movq 32(%0), %%mm5                                          \n\
+        movq 40(%0), %%mm6                                          \n\
+        movq 48(%0), %%mm7                                          \n\
+        movq 56(%0), %%mm0                                          \n\
+        addl $64, %0                                                \n\
+        prefetchnta 320(%0)                                         \n\
+        movntq %%mm5, 32(%1)                                        \n\
+        movntq %%mm6, 40(%1)                                        \n\
+        movntq %%mm7, 48(%1)                                        \n\
+        movntq %%mm0, 56(%1)                                        \n\
+        addl $64, %1                                                \n\
+                                                                    \n\
+        decl %2                                                     \n\
+        jnz 1b                                                      \n\
+        emms                                                        \n\
+        "
+        : "=&r" (r0), "=&r" (r1), "=&r" (r2)
+        : "0" (pvSrc), "1" (pvDst), "2" (cb/64)
+        : "memory");
+    LIBCLOG_RETURN_VOID();
+}
 #endif
+
+#if 0 /* untested */
+/**
+ * Copy page memory, SSE.
+ *
+ * @param   pvDst   Target address.
+ * @param   pvSrc   Source address.
+ * @param   cb      Number of bytes to copy.
+ */
+static void forkBthCopyPagesSSE2(void *pvDst, const void *pvSrc, size_t cb)
+{
+    LIBCLOG_ENTER("pvDst=%p pvSrc=%p cb=0x%08x\n", pvDst, pvSrc, cb);
+    if ((uintptr_t)pvDst & 15)  asm("int3");
+    if ((uintptr_t)pvSrc & 15)  asm("int3");
+    if (cb & 127)  asm("int3");
+
+    register unsigned long int r0,r1,r2;
+    __asm__ __volatile__
+      ("prefetchnta    (%0)                             \n\
+        prefetchnta  64(%0)                             \n\
+        prefetchnta 128(%0)                             \n\
+        prefetchnta 192(%0)                             \n\
+        prefetchnta 256(%0)                             \n\
+        jmp 1f                                          \n\
+                                                        \n\
+        .align 4                                        \n\
+        1:                                              \n\
+        prefetchnta 320(%0)                             \n\
+        movdqa    (%0), %%xmm0                          \n\
+        movdqa  16(%0), %%xmm1                          \n\
+        movntdq %%xmm0,   (%1)                          \n\
+        movntdq %%xmm1, 16(%1)                          \n\
+        movdqa  32(%0), %%xmm2                          \n\
+        movdqa  48(%0), %%xmm3                          \n\
+        movntdq %%xmm2, 32(%1)                          \n\
+        movntdq %%xmm3, 48(%1)                          \n\
+        movdqa  64(%0), %%xmm4                          \n\
+        movdqa  80(%0), %%xmm5                          \n\
+        movntdq %%xmm4, 64(%1)                          \n\
+        movntdq %%xmm5, 80(%1)                          \n\
+        movdqa  96(%0), %%xmm6                          \n\
+        movdqa  112(%0), %%xmm7                         \n\
+        movntdq %%xmm6, 96(%1)                          \n\
+        movntdq %%xmm7, 112(%1)                         \n\
+                                                        \n\
+        addl $128, %0                                   \n\
+        addl $128, %1                                   \n\
+        decl %2                                         \n\
+        jnz 1b                                          \n\
+                                                        \n\
+        sfence                                          \n\
+       "
+        : "=&r" (r0), "=&r" (r1), "=&r" (r2)
+        : "0" (pvDst), "1" (pvSrc), "2" (cb / 128)
+        : "memory");
     LIBCLOG_RETURN_VOID();
 }
+#endif
 
 
@@ -2590,5 +3067,9 @@
     if (pXcptRepRec->fHandlerFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
         return XCPT_CONTINUE_SEARCH;
-    LIBCLOG_MSG2("forkParExceptionHandler: ExceptionNum=%#lx eip=%#lx\n", pXcptRepRec->ExceptionNum, pCtx->ctx_RegEip);
+    LIBCLOG_MSG2("forkParExceptionHandler: ExceptionNum=%#lx ExceptionAddress=%p eip=%#lx ExceptionInfo={%#08lx,%#08lx,%#08lx,%#08lx} fHandlerFlags=%#lx\n",
+                 pXcptRepRec->ExceptionNum, pXcptRepRec->ExceptionAddress, pCtx->ctx_RegEip,
+                 pXcptRepRec->ExceptionInfo[0], pXcptRepRec->ExceptionInfo[1],
+                 pXcptRepRec->ExceptionInfo[2], pXcptRepRec->ExceptionInfo[3],
+                 pXcptRepRec->fHandlerFlags);
 
     switch (pXcptRepRec->ExceptionNum)
@@ -2716,5 +3197,9 @@
     DosGetInfoBlocks(&pTib, &pPib);
     pTib->tib_pexchain = END_OF_CHAIN;
-    LIBCLOG_MSG2("forkParExceptionHandler: ExceptionNum=%#lx eip=%#lx\n", pXcptRepRec->ExceptionNum, pCtx->ctx_RegEip);
+    LIBCLOG_MSG2("forkChlExceptionHandler: ExceptionNum=%#lx ExceptionAddress=%p eip=%#lx ExceptionInfo={%#08lx,%#08lx,%#08lx,%#08lx} fHandlerFlags=%#lx\n",
+                 pXcptRepRec->ExceptionNum, pXcptRepRec->ExceptionAddress, pCtx->ctx_RegEip,
+                 pXcptRepRec->ExceptionInfo[0], pXcptRepRec->ExceptionInfo[1],
+                 pXcptRepRec->ExceptionInfo[2], pXcptRepRec->ExceptionInfo[3],
+                 pXcptRepRec->fHandlerFlags);
 
     switch (pXcptRepRec->ExceptionNum)
