Index: branches/libc-0.6/src/emx/ChangeLog.LIBC
===================================================================
--- branches/libc-0.6/src/emx/ChangeLog.LIBC	(revision 2784)
+++ branches/libc-0.6/src/emx/ChangeLog.LIBC	(revision 2786)
@@ -2,4 +2,10 @@
 
 TODO: open replace on RAMFS fails with error 32!
+
+2006-08-27: knut st. osmundsen <bird-gccos2-spam@anduin.net>
+    - libc:
+        o Fixed problems with fork() and module loading/unloading. Fixes #76.
+        o Fixed atexit() and on_exit() problem with callbacks in unloaded DLLs. Fixes #103.
+        o Added usage counting to _CRT_init and _CRT_term. Fixing #114.
 
 2006-03-26: knut st. osmundsen <bird-gccos2-spam@anduin.net>
Index: branches/libc-0.6/src/emx/include/InnoTekLIBC/atexit.h
===================================================================
--- branches/libc-0.6/src/emx/include/InnoTekLIBC/atexit.h	(revision 2784)
+++ branches/libc-0.6/src/emx/include/InnoTekLIBC/atexit.h	(revision 2786)
@@ -37,5 +37,7 @@
     __LIBC_ATEXITTYPE_ONEXIT,
     /** State transition state. */
-    __LIBC_ATEXITTYPE_TRANS
+    __LIBC_ATEXITTYPE_TRANS,
+    /** The module containing the callback was unloaded. */
+    __LIBC_ATEXITTYPE_UNLOADED
 } __LIBC_ATEXITTYPE;
 
@@ -47,4 +49,7 @@
     /** Entry type. */
     __LIBC_ATEXITTYPE volatile enmType;
+    /** The (native) module handle this callback is connected to.
+     * This will be 0 if no such association. */
+    uintptr_t                  hmod;
     union
     {
@@ -92,6 +97,17 @@
  * @returns Pointer to new entry.
  * @returns NULL on failure.
+ *
+ * @param   pvCallback      The callback address.
+ *                          This used to initialize the __LIBC_AT_EXIT::hmod field.
  */
-__LIBC_PATEXIT __libc_atexit_new(void);
+__LIBC_PATEXIT __libc_atexit_new(void *pvCallback);
+
+/**
+ * Invalidate all atexit and on_exit callback for a
+ * module which is being unloaded.
+ *
+ * @param   hmod        The module handle.
+ */
+void __libc_atexit_unload(uintptr_t hmod);
 
 __END_DECLS
Index: branches/libc-0.6/src/emx/include/InnoTekLIBC/fork.h
===================================================================
--- branches/libc-0.6/src/emx/include/InnoTekLIBC/fork.h	(revision 2784)
+++ branches/libc-0.6/src/emx/include/InnoTekLIBC/fork.h	(revision 2786)
@@ -575,6 +575,6 @@
 /** Indicates that the module is an executable. */
 #define __LIBC_FORKMODULE_FLAGS_EXECUTABLE      0x00000001
-/** Indicates that the module was dynamically loaded. */
-#define __LIBC_FORKMODULE_FLAGS_DYNAMIC         0x00010000
+/** Indicates that the module already has been deregistered (libc termination). */
+#define __LIBC_FORKMODULE_FLAGS_DEREGISTERED    0x00010000
 /** @} */
 
@@ -606,4 +606,16 @@
  */
 int __libc_ForkRegisterModule(__LIBC_PFORKMODULE pModule, int fExecutable);
+
+
+/**
+ * Deregister a forkable module. Called by dll0.
+ *
+ * The call links pModule out of the list of forkable modules
+ * which is maintained in the process block.
+ *
+ * @param   pModule     Pointer to the fork module structure for the
+ *                      module which is to registered.
+ */
+void __libc_ForkDeregisterModule(__LIBC_PFORKMODULE pModule);
 
 
Index: branches/libc-0.6/src/emx/src/lib/libc.def
===================================================================
--- branches/libc-0.6/src/emx/src/lib/libc.def	(revision 2784)
+++ branches/libc-0.6/src/emx/src/lib/libc.def	(revision 2786)
@@ -1953,2 +1953,4 @@
     "__std_getdirents" @1951
     "___libc_Back_ioFileControlStandard" @1952
+    "___libc_Back_termDll" @1953
+    "___libc_ForkDeregisterModule" @1954
Index: branches/libc-0.6/src/emx/src/lib/libc.smak
===================================================================
--- branches/libc-0.6/src/emx/src/lib/libc.smak	(revision 2784)
+++ branches/libc-0.6/src/emx/src/lib/libc.smak	(revision 2786)
@@ -75,5 +75,5 @@
 include mklib.smak
 
-					
+
 #------------------------------------------------------
 # Now define the rules for building libc$(VERSION).dll
@@ -178,6 +178,6 @@
 endef
 $(foreach o,$(LIBC.OBJS.COMMON),$(eval $(call def_libc_omf_dep,$(o),$(patsubst %.obj,%.o,$(o)))))
-							
-			
+
+
 # LIBC linking.
 $(LIBC.DLL): $(LIBC.OBJS) $(LIBC.LIBS) $(LIBC.DEF) $(LIBC.DEPS)
@@ -188,5 +188,5 @@
 		$(filter %.lib,$(LIBC.DEPS)) \
 		-L$.omf -lgcc -lgcc_eh $(LIBC.STUBARG)
-ifeq ($(MODE),opt)	
+ifeq ($(MODE),opt)
 	cp $@ $(@:.dll=.dbg)
 	lxlite $(LXLITE.FLAGS) $(subst /,\\,$@)
@@ -194,5 +194,5 @@
 
 # LIBC .def file
-$(LIBC.DEF): $(LIBC.LIBS) src/lib/libc.def
+$(LIBC.DEF): $(LIBC.LIBS) src/lib/libc.def version.smak
 	$(call RM,$@)
 	@$(call FECHO,$@,LIBRARY libc$(VH)$(VM)$(VL) INITINSTANCE TERMINSTANCE)
@@ -239,5 +239,5 @@
 		$(filter %.lib,$(LIBC.LOG.DEPS)) \
 		-L$.omf -lgcc -lgcc_eh $(LIBC.STUBARG)
-	echo $@		
+	echo $@
 
 # LIBC.PRF - Profiling version of libcXY.dll
@@ -249,5 +249,5 @@
 		$(filter %.lib,$(LIBC.PRF.DEPS)) \
 		-L$.omf-prof -L$.omf -lgcc -lgcc_eh $(LIBC.STUBARG)
-	echo $@		
+	echo $@
 
 # LIBC.PREF .def file
@@ -260,5 +260,5 @@
 	@$(call FECHO,$@,"")
 
-	
+
 # LIBC.ELH - Electric Heap version of libcXY.dll
 $(LIBC.ELH.DLL): $(LIBC.ELH.OBJS) $(LIBC.ELH.LIBS) $(LIBC.ELH.DEF) $(LIBC.ELH.DEPS)
@@ -269,5 +269,5 @@
 		$(filter %.lib,$(LIBC.ELH.DEPS)) \
 		-L$.omf -lgcc -lgcc_eh $(LIBC.STUBARG)
-	echo $@		
+	echo $@
 
 # LIBC.ELH .def file
@@ -280,5 +280,5 @@
 	@$(call FECHO,$@,"")
 	@$(call FECHO,$@,"IMPORTS");
-	@$(call FECHO,$@,"  kHeapDbgException              = KLIBDBG.kHeapDbgException")	
+	@$(call FECHO,$@,"  kHeapDbgException              = KLIBDBG.kHeapDbgException")
 	@$(call FECHO,$@,"  __std_calloc                   = KLIBDBG._gcc__std_calloc")
 	@$(call FECHO,$@,"  __um_default_alloc             = KLIBDBG._gcc__um_default_alloc")
@@ -344,5 +344,5 @@
 	@$(call FECHO,$@,"  __um_tiled_heap                = KLIBDBG._gcc__um_tiled_heap")
 
-					
+
 # Put libc into the big picture. (semihacks)
 DO.HELP.MODULES += $(call ECHO,    libc-dll - The dynamic C library (.dll))$(NL)
@@ -380,13 +380,13 @@
 	if test -f $@; then rm -f $@ || (unlock $(subst /,\\,$@) && rm -f $@); fi
 	cp $< $@
-	
+
 $(INS)lib/$(notdir $(LIBC.DLL:.dll=.map)): $(LIBC.DLL:.dll=.map)
 	$(call CP,$<,$@)
-	
+
 $(INS)lib/$(notdir $(LIBC.LOG.DLL)): $(LIBC.LOG.DLL)
 	$(call CP,$<,$@)
 $(INS)lib/$(notdir $(LIBC.LOG.DLL).map): $(LIBC.LOG.DLL).map
 	$(call CP,$<,$@)
-						
+
 $(INS)lib/$(notdir $(LIBC.PRF.DLL)): $(LIBC.PRF.DLL)
 	$(call CP,$<,$@)
@@ -440,5 +440,5 @@
 $.omf-prof/src/lib/startup/dllinit.obj: $.aout-prof/src/lib/startup/dllinit.o
 	$(DO.EMXOMF)
-			
+
 # Forget temporary variables
 include comend.smak
@@ -452,9 +452,9 @@
 #
 # Forwarder DLLs for backward compatibility.
-#			
+#
 $.omf/fwdstub.obj: src/lib/fwdstub.asm
 	$(call DO.COMPILE.asm)
-	
-## libc06b4						
+
+## libc06b4
 #$.omf/libc06b4.dll: src/lib/libc06b4.def $.omf/fwdstub.obj $.omf/libc_dll.lib
 #	gcc -Zomf -o $@ -nostdlib -Zdll $^
@@ -464,5 +464,5 @@
 #libc-dll: $.omf/libc06b4.dll
 
-## libc06b5						
+## libc06b5
 #$.omf/libc06b5.dll: src/lib/libc06b5.def $.omf/fwdstub.obj $.omf/libc_dll.lib
 #	gcc -Zomf -o $@ -nostdlib -Zdll  $^
Index: branches/libc-0.6/src/emx/src/lib/misc/atexit.c
===================================================================
--- branches/libc-0.6/src/emx/src/lib/misc/atexit.c	(revision 2784)
+++ branches/libc-0.6/src/emx/src/lib/misc/atexit.c	(revision 2786)
@@ -4,5 +4,5 @@
  * LIBC atexit().
  *
- * Copyright (c) 2005 knut st. osmundsen <bird@anduin.net>
+ * Copyright (c) 2005-2006 knut st. osmundsen <bird@anduin.net>
  *
  *
@@ -30,7 +30,13 @@
 *******************************************************************************/
 #include "libc-alias.h"
+#ifdef __OS2__
+# define INCL_DOSMODULEMGR
+# define INCL_ERRORS
+# define INCL_FSMACROS
+# include <os2emx.h>
+#endif
 #include <stdlib.h>
 #include <InnoTekLIBC/atexit.h>
-#include <386/builtin.h>
+#include <sys/builtin.h>
 #include <emx/umalloc.h>
 #define __LIBC_LOG_GROUP __LIBC_LOG_GRP_INITTERM
@@ -41,5 +47,5 @@
 {
     LIBCLOG_ENTER("pfnCallback=%p\n", (void *)pfnCallback);
-    __LIBC_PATEXIT pCur = __libc_atexit_new();
+    __LIBC_PATEXIT pCur = __libc_atexit_new((void *)pfnCallback);
     if (pCur)
     {
@@ -57,7 +63,27 @@
  * @returns Pointer to new entry.
  * @returns NULL on failure.
+ *
+ * @param   pvCallback      The callback address.
+ *                          This used to initialize the __LIBC_AT_EXIT::hmod field.
  */
-__LIBC_PATEXIT __libc_atexit_new(void)
+__LIBC_PATEXIT __libc_atexit_new(void *pvCallback)
 {
+    /*
+     * Try find the module this callback belongs to.
+     */
+    uintptr_t   hmod = 0;
+    /** @todo generalize DosQueryModFromEIP and let the (loader) backend do that kind of job. */
+#ifdef __OS2__
+    HMODULE     hmodOS2;
+    ULONG       iObj, offObj;
+    FS_VAR_SAVE_LOAD();
+    APIRET rc = DosQueryModFromEIP(&hmodOS2, &iObj, 0, NULL, &offObj, (uintptr_t)pvCallback);
+    if (rc == NO_ERROR)
+        hmod = hmodOS2;
+    FS_RESTORE();
+#else
+    /** @todo port to NT and other target platforms. */
+#endif
+
     /*
      * Search existing chunks.
@@ -72,4 +98,5 @@
             if (__atomic_cmpxchg32(&pChunk->c, i + 1, i))
             {
+                pChunk->a[i].hmod = hmod;
                 pChunk->a[i].enmType = __LIBC_ATEXITTYPE_TRANS;
                 return &pChunk->a[i];
@@ -84,5 +111,7 @@
     if (!pChunk)
         return NULL;
+    /** @todo There is a chance that the exit list order could be screwed up here if two threads get here at the same time. (bird, 2006-08-21) */
     pChunk->c = 1;
+    pChunk->a[0].hmod = hmod;
     pChunk->a[0].enmType = __LIBC_ATEXITTYPE_TRANS;
     do
@@ -94,2 +123,29 @@
 }
 
+
+/**
+ * Invalidate all atexit and on_exit callback for a
+ * module which is being unloaded.
+ *
+ * @param   hmod        The module handle.
+ */
+void __libc_atexit_unload(uintptr_t hmod)
+{
+    /*
+     * Search existing chunks.
+     * (This is made simple by us not freeing anything.)
+     */
+    __LIBC_PATEXITCHUNK pChunk;
+    for (pChunk = __libc_gAtExitHead; pChunk; pChunk = pChunk->pNext)
+    {
+        uint32_t i = pChunk->c;
+        while (i-- > 0)
+        {
+            if (    pChunk->a[i].hmod == hmod
+                &&  pChunk->a[i].enmType > __LIBC_ATEXITTYPE_FREE
+                &&  pChunk->a[i].enmType < __LIBC_ATEXITTYPE_TRANS)
+                pChunk->a[i].enmType = __LIBC_ATEXITTYPE_UNLOADED;
+        }
+    }
+}
+
Index: branches/libc-0.6/src/emx/src/lib/misc/on_exit.c
===================================================================
--- branches/libc-0.6/src/emx/src/lib/misc/on_exit.c	(revision 2784)
+++ branches/libc-0.6/src/emx/src/lib/misc/on_exit.c	(revision 2786)
@@ -4,5 +4,5 @@
  * LIBC on_exit().
  *
- * Copyright (c) 2005 knut st. osmundsen <bird@anduin.net>
+ * Copyright (c) 2005-2006 knut st. osmundsen <bird@anduin.net>
  *
  *
@@ -39,5 +39,5 @@
 {
     LIBCLOG_ENTER("pfnCallback=%p pvUser=%p\n", (void *)pfnCallback, pvUser);
-    __LIBC_PATEXIT pCur = __libc_atexit_new();
+    __LIBC_PATEXIT pCur = __libc_atexit_new((void *)pfnCallback);
     if (pCur)
     {
Index: branches/libc-0.6/src/emx/src/lib/startup/386/dll0.s
===================================================================
--- branches/libc-0.6/src/emx/src/lib/startup/386/dll0.s	(revision 2784)
+++ branches/libc-0.6/src/emx/src/lib/startup/386/dll0.s	(revision 2786)
@@ -50,13 +50,25 @@
 #endif
 
+#if defined(NOFORK)
+#define FLAG_NOFORK     4
+#else
+#define FLAG_NOFORK     0
+#endif
+
+    /*
+     * The DLL Entry Point.
+     */
 __text:
     cmpl    $0, 8(%esp)
-    jz      do_common_init
-    jmp     do_initterm
+    je      do_init
+    cmpl    $1, 8(%esp)
+    je      do_term
+    jmp     _DLL_InitTerm
+
 
     /*
-     * Nonstandard: we're doing some early initializations.
+     * DLL Initialization.
      */
-do_common_init:
+do_init:
 #if !defined(NOFORK) && !defined(NOUNIX)
     pushl   $0
@@ -65,13 +77,11 @@
     addl    $8, %esp
     cmpl    $0, %eax
-    je      do_init
-    jg      do_fork
-
-    /* return failure (eax < 0). */
+    je      do_init_not_forking
+    jg      do_return_success   /* we're forking; no init. */
+    /* else: eax < 0 - failure. */
+do_return_failure:
     xorl    %eax, %eax
     ret
-
-    /* return success - we're forking; no init. */
-do_fork:
+do_return_success:
     xorl    %eax, %eax
     inc     %al
@@ -79,24 +89,60 @@
 
     /* normal dll init. */
-do_init:
+do_init_not_forking:
 #endif
+    /* call __init_dll() */
     pushl   4(%esp)
-    pushl   $(FLAG_HIGHMEM + FLAG_NOUNIX)
+    pushl   $(FLAG_HIGHMEM + FLAG_NOUNIX + FLAG_NOFORK)
     call    ___init_dll
     add     $8, %esp
     orl     %eax, %eax
-    jz      do_initterm
+    jnz     do_return_failure
+
+    /* call _DLL_InitTerm */
+    cld
+    pushl   8(%esp)
+    pushl   8(%esp)
+    call    _DLL_InitTerm
+    add     $8, %esp
+    orl     %eax, %eax
+    jnz     do_return_success
+
+    /* _DLL_InitTerm failed, undo the module registration. */
+#if !defined(NOFORK) && !defined(NOUNIX)
+    pushl   $ForkModule
+    call    ___libc_ForkRegisterModule
+    addl    $4, %esp
+#endif
+    jmp     do_return_failure;
+
+
     /*
-     * __init_dll() failed - fail the dll loading.
+     * DLL Termination.
      */
-    xorl    %eax, %eax
+do_term:
+    /* do init term first */
+    cld
+    pushl   8(%esp)
+    pushl   8(%esp)
+    call    _DLL_InitTerm
+    add     $8, %esp
+    orl     %eax, %eax
+    jnz     do_term_dll
     ret
 
-    /*
-     * Standard initialization.
-     */
-do_initterm:
-    cld
-    jmp     _DLL_InitTerm
+do_term_dll:
+    /* call __libc_Back_termDll() */
+    pushl   4(%esp)
+    pushl   $(FLAG_HIGHMEM + FLAG_NOUNIX + FLAG_NOFORK)
+    call    ___libc_Back_termDll
+    add     $8, %esp
+
+    /* call __libc_ForkDeregisterModule */
+#if !defined(NOFORK) && !defined(NOUNIX)
+    pushl   $ForkModule
+    call    ___libc_ForkDeregisterModule
+    addl    $4, %esp
+#endif
+    jmp     do_return_success
 
 
Index: branches/libc-0.6/src/emx/src/lib/sys/b_processWait.c
===================================================================
--- branches/libc-0.6/src/emx/src/lib/sys/b_processWait.c	(revision 2784)
+++ branches/libc-0.6/src/emx/src/lib/sys/b_processWait.c	(revision 2786)
@@ -124,5 +124,5 @@
 /** Number of known child processes. */
 static volatile unsigned    gcChildren;
-/** Total number of born child processes. */
+/** Total number of born child processes. (For statistical purposes only.) */
 static volatile unsigned    gcBirths;
 /** Total number of died child processes. */
@@ -171,5 +171,5 @@
          * Create the fmutex.
          */
-        _fmutex_checked_create(&gmtxWait, 0);
+        _fmutex_checked_create2(&gmtxWait, _FMC_MUST_COMPLETE, "b_processWait.c: gmtxWait");
         int rc = DosCreateEventSemEx(NULL, (PHEV)&ghevWait, 0, TRUE);
         if (rc)
@@ -194,8 +194,9 @@
 void __libc_back_processWaitNotifyTerm(void)
 {
+    __atomic_xchg(&gfTerminate, 1);
+
+    /* kill the thread - this is probably a waste of time in the exitlist handler... */
     if (gtidThread)
     {
-        /* kill the thread - this is probably a waste of time in the exitlist handler... */
-        __atomic_xchg(&gfTerminate, 1);
         DosKillThread(gtidThread);
         gtidThread = 0;
@@ -223,7 +224,11 @@
      * Kill all known decendants.
      */
+    /** @todo We should really reparent them to the init process, but only the session manager can do that. */
     PWAITCHILD pChild = gpChildrenHead;
     if (pChild)
     {
+        gpChildrenHead = NULL;
+        gcChildren = 0;
+
         /*
          * Kill the process tree so we don't end up having childrens
@@ -268,9 +273,5 @@
     int rc = _fmutex_request(&gmtxWait, fNoInterrupts ? _FMR_IGNINT : 0);
     if (!rc)
-    {
-        ULONG cIgnore = 0;
-        DosEnterMustComplete(&cIgnore);
         return 0;
-    }
     LIBC_ASSERTM_FAILED("_fmutex_request -> %d\n", rc);
     return -__libc_native2errno(rc);
@@ -284,6 +285,4 @@
     int rc = _fmutex_release(&gmtxWait);
     LIBC_ASSERTM(!rc, "_fmutex_release -> %d\n", rc);
-    ULONG cIgnore = 0;
-    DosExitMustComplete(&cIgnore);
     rc = rc;
 }
@@ -652,4 +651,6 @@
     if (!gtidThread)
     {
+        gfTerminate = 0;
+
         /*
          * Create the internal thread for dealing with waiting.
Index: branches/libc-0.6/src/emx/src/lib/sys/b_termDll.c
===================================================================
--- branches/libc-0.6/src/emx/src/lib/sys/b_termDll.c	(revision 2786)
+++ branches/libc-0.6/src/emx/src/lib/sys/b_termDll.c	(revision 2786)
@@ -0,0 +1,58 @@
+/* $Id: $ */
+/** @file
+ *
+ * kUNIX - Termination of a DLL.
+ *
+ * Copyright (c) 2006 knut st. osmundsen <bird@innotek.de>
+ *
+ *
+ * This file is part of kLIBC.
+ *
+ * kLIBC 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.
+ *
+ * kLIBC 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 kLIBC; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <InnoTekLIBC/backend.h>
+#include <InnoTekLIBC/atexit.h>
+#define __LIBC_LOG_GROUP __LIBC_LOG_GRP_INITTERM
+#include <InnoTekLIBC/logstrict.h>
+
+
+
+/**
+ * Called by dll0.s when a dll is being unloaded.
+ *
+ * @param   fFlags  Bit 0: If set the application is open to put the default heap in high memory.
+ *                         If clear the application veto against putting the default heap in high memory.
+ *                  Bit 1: If set some of the unixness of LIBC is disabled.
+ *                         If clear all unix like features are enabled.
+ *                  Bit 2: If set the dll is registered for forking.
+ *                         If clear the dll isn't registered for forking.
+ *
+ * @param   hmod    The module handle.
+ */
+/** @todo 0.7: #defines for the flags! klibc/startup.h? */
+void __libc_Back_termDll(unsigned fFlags, uintptr_t hmod)
+{
+    LIBCLOG_ENTER("fFlag=%#x hmod=%x\n", fFlags, hmod);
+    __libc_atexit_unload(hmod);
+    LIBCLOG_RETURN_VOID();
+}
+
+
Index: branches/libc-0.6/src/emx/src/lib/sys/libcfork.c
===================================================================
--- branches/libc-0.6/src/emx/src/lib/sys/libcfork.c	(revision 2784)
+++ branches/libc-0.6/src/emx/src/lib/sys/libcfork.c	(revision 2786)
@@ -314,5 +314,5 @@
 
     /*
-     * Install exceptionhandler.
+     * Install an exceptionhandler.
      */
     DosGetInfoBlocks(&pTib, &pPib);
@@ -346,11 +346,10 @@
 
     /*
-     * Check if dynamically loaded or not.
-     */
-    /** @todo! */
-
-    /*
      * Register the module.
-     */
+     * (We're hoping for OS/2 to indirectly serialize this... not properly verified yet.)
+     */
+    if (pModule->fFlags & __LIBC_FORKMODULE_FLAGS_DEREGISTERED)
+        LIBCLOG_MSG("__LIBC_FORKMODULE_FLAGS_DEREGISTERED!\n");
+    pModule->fFlags &= ~__LIBC_FORKMODULE_FLAGS_DEREGISTERED;
     pModule->pNext = NULL;
     if (pProcess->pvModuleHead)
@@ -433,4 +432,74 @@
     pTib->tib_pexchain = XcptRegRec.prev_structure;
     LIBCLOG_RETURN_INT(1);
+}
+
+
+/**
+ * Deregister a forkable module. Called by dll0.
+ *
+ * The call links pModule out of the list of forkable modules
+ * which is maintained in the process block.
+ *
+ * @param   pModule     Pointer to the fork module structure for the
+ *                      module which is to registered.
+ */
+void __libc_ForkDeregisterModule(__LIBC_PFORKMODULE pModule)
+{
+    LIBCLOG_ENTER("pModule=%p:{.uVersion=%#x, .pfnAtFork=%p, .papParent1=%p, .papChild1=%p, .pvDataSegBase=%p, .pvDataSegEnd=%p, .fFlags=%#x, .pNext=%p}\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);
+
+    /*
+     * Don't deregister modules which has already been deregistered.
+     * Don't deregister if we're in shutting down the process (waste of time and
+     * SPM might already be shut down).
+     */
+    if (pModule->fFlags & __LIBC_FORKMODULE_FLAGS_DEREGISTERED)
+        LIBCLOG_RETURN_MSG_VOID( "ret void (__LIBC_FORKMODULE_FLAGS_DEREGISTERD)\n");
+    if (!__libc_GpFIBLIS)
+        LIBCLOG_RETURN_MSG_VOID( "ret void (__libc_GpFIBLIS)\n");
+    if (fibIsInExit())
+        LIBCLOG_RETURN_MSG_VOID( "ret void (fibIsInExit)\n");
+
+    /*
+     * Find the SPM process.
+     */
+    __LIBC_PSPMPROCESS pProcess = __libc_spmSelf();
+    if (!pProcess)
+    {
+        LIBC_ASSERTM_FAILED("can't find the process! weird!\n");
+        LIBCLOG_RETURN_VOID();
+    }
+
+    /*
+     * Deregister the module.
+     * (We're hoping for OS/2 to indirectly serialize this... not properly verified yet.)
+     */
+    if ((__LIBC_PFORKMODULE)pProcess->pvModuleHead == pModule)
+    {
+        pProcess->pvModuleHead = pModule->pNext;
+        if (!pModule->pNext)
+            pProcess->ppvModuleTail = &pProcess->pvModuleHead;
+    }
+    else
+    {
+        __LIBC_PFORKMODULE pPrev = (__LIBC_PFORKMODULE)pProcess->pvModuleHead;
+        while (pPrev && pPrev->pNext != pModule)
+            pPrev = pPrev->pNext;
+        if (!pPrev)
+        {
+            LIBC_ASSERTM_FAILED("can't find the module! weird!\n");
+            LIBCLOG_RETURN_VOID();
+        }
+
+        pPrev->pNext = pModule->pNext;
+        if (!pModule->pNext)
+            pProcess->ppvModuleTail = (void **)&pPrev->pNext;
+    }
+
+    pModule->pNext = NULL;
+    pModule->fFlags |= __LIBC_FORKMODULE_FLAGS_DEREGISTERED;
+
+    LIBCLOG_RETURN_VOID();
 }
 
@@ -1280,4 +1349,10 @@
     /*
      * Open and process the buffer on first call.
+     *
+     * Because of DosLoadModuleEx processing, we can end up here in while
+     * processing the initial fork buffer. This means that if any of those
+     * DLLS tries to talk to the parent at __LIBC_FORK_EXEC_CHILD time the
+     * fork() will fail. Fortunately, this is very unlikely. (Otherwise, we
+     * should've delayed those pfnAtFork() calls.)
      */
     if (pForkHandle->enmStage == __LIBC_FORK_STAGE_EXEC)
@@ -1289,4 +1364,5 @@
             LIBCLOG_RETURN_INT(rc);
         }
+        pForkHandle->enmStage = __LIBC_FORK_STAGE_INIT_CHILD;
         rc = forkBthBufferProcess(pForkHandle, __LIBC_FORK_CTX_CHILD, NULL);
         if (rc < 0)
@@ -1295,7 +1371,4 @@
             LIBCLOG_RETURN_INT(rc);
         }
-
-        /* next stage */
-        pForkHandle->enmStage = __LIBC_FORK_STAGE_INIT_CHILD;
     }
 
