Copyright 2010 Red Hat Inc., contributed by Dave Korn. How the C runtime handles startup and termination. -------------------------------------------------- This file documents the processes involved in starting up and shutting down a Cygwin executable. The responsibility is divided between code that is statically linked into each Cygwin-based DLL or executable as part of the C runtime, and code in the Cygwin DLL itself that co-operates with it. The runtime library code lives in the winsup/cygwin/lib directory, and a little of it is in winsup/cygwin/include/cygwin/cygwin_dll.h Process overall startup sequence. ================================= Overall process startup (and indeed termination) is under the control of the underlying Windows OS. The details of the Win32 CreateProcess API and the underlying NT Native API ZwCreateProcess calls are far more complex (and unknown, since proprietary) than we need go into here; the important details are that the process address space is first created, then an initial thread is spawned that performs DLL initialisation, calling the DllMain functions of all statically-linked DLLs in load order. This thread is also serialised under the Windows OS global loader lock, and DllMain functions are very limited in what they can do as a consequence; to help deal with this, cygwin wraps the user's DllMain function and defers calling it until runtime. Once the DLLs have been initialised, the initial thread then performs C runtime setup and calls into the executable's main() function. Entry sequence for Cygwin-based DLLs. ===================================== In the compiler's LINK_SPEC, a -e option sets the entry point (what Windows regards as DllMain) to __cygwin_dll_entry@12. This is defined in include/cygwin/cygwin_dll.h. The user's DllMain function, if any, is called from within this function - directly in the case of thread attach/detach notifications and process detach, but indirectly at process attach time via cygwin_attach_dll in lib/cygwin_attach_dll.c, which calls the CRT common code _cygwin_crt0_common and then hands off to the Cygwin DLL at dll_dllcrt0. The CRT common code doesn't call the user DllMain at once; it caches a pointer to it in the 'main' member of the DLL's per_process struct. __cygwin_dll_entry@12 -> cygwin_attach_dll -> (_cygwin_crt0_common) -> dll_dllcrt0 -> (DllMain?maybe?) dll_dllcrt0 is in dll_init.cc sets up exception handler, ensures cygwin DLL is at least partially initialised, allocates a new entry for the DLL chain, and either calls the 'main' function (via dll::init) before returning to the OS loader, or defers doing so until dll_crt0_1 runs dlls.dll_list::init() during the application's startup sequence, depending on whether Cygwin DLL was fully initialised yet or not. In general statically linked DLLs will defer, while dlopen'd DLLs will run at once. The Cygwin DLL runs the dependent DLL's ctors immediately prior to making the call, whether immediate or deferred. Entry sequence for Cygwin-based executables. ============================================ The entry point is the windows standard entrypoint, WinMainCRTStartup, aliased to mainCRTStartup, defined in crt0.c. It aligns the stack, sets the x87 fpu cw, and hands off to cygwin_crt0 in lib/cygwin_crt0.c, which calls the CRT common init code in _cygwin_crt0_common and heads off into the DLL, never to return from _dll_crt0. mainCRTStartup -> cygwin_crt0 -> (_cygwin_crt0_common) -> _dll_crt0 -> dll_crt0_1 -> (n*DllMain?maybe?) -> main -> (__main) -> cygwin_exit This is a wrapper that does some fork-related stack sorting out then hands off to dll_crt0_1, which completes all Cygwin DLL initialisation, runs any deferred DllMain calls, and jumps into the application, returning via the termination routines. Post-entry construction. ======================== The compiler automatically inserts a hidden call to __main at the start of the user's main() function. During startup, DLL constructors are run in dll:init() immediately prior to calling that DLL's DllMain function (not in a forkee, though; once is enough). In __main, all statically-loaded DLL ctors are now complete, so we queue an atexit call to dll_global_dtors, then run the application's ctors and queue an atexit call to do_global_dtors. Process overall termination sequence. ===================================== The program termination sequence can begin in one of the following ways: - by returning from main() - by calling exit(), _Exit() or _exit() - by calling abort() (this can be implicit, such as when an unhandled C++ exception is thrown, or when an SEH exception is raised and not trapped, or an unhandled signal terminates the program). Unload sequence for Cygwin-based DLLS. ====================================== _cygwin_dll_entry@12 -> (DllMain) -> cygwin_detach_dll -> dll_list::detach -> (remove_dll_atexit) -> (dll::run_dtors) When a DLL is unloaded, whether as a result of dlclose() calling FreeLibrary(), or when then entire process is terminating, the OS arranges to call the DLL's DllMain function with a DLL_PROCESS_DETACH notification. As during the entry sequence, this is also wrapped by _cygwin_dll_entry(), although there is in this case no need to defer calling the user's DllMain hook; it is called at once. If no error is indicated, the dll is then detached from Cygwin's internal tracking list, and any atexit functions it has registered are run and cancelled from the atexit list. Finally any static destructors are run. Exit sequence for Cygwin-based executables. ============================================ This diagram illustrates the code paths, listed above, by which the main executable can terminate: +-------------->-- exception handling --->----------------------------+ | | +-------------->--------- abort --------->--- stdio cleanup ----------+ | | +-------------->-- direct or via _Exit -->-------------------+ | | | | +-------------->----------+ | | | V stdio cleanup, V V main -> dll_crt0_1 -> cygwin_exit -> exit -> atexit funcs -> _exit -> do_exit -> pinfo::exit -> ExitProcess -> END. Returning from main() transfers control back to dll_crt0_1(), which passes the return value to cygwin_exit(); this is the same as calling exit(), which is an export name alias for cygwin_exit() anyway. cygwin_exit() calls the real exit() function in newlib, which runs the atexit functions and shuts down stdio before exiting via _exit(), which immediately passes the exit status through to do_exit(). If exiting via abort(), stdio is cleaned up, but no atexit functions are run. All the termination sequences end up in do_exit(), which takes care of POSIXy stuff like process group and child signalling, tty disconnection, etc. This finally passes control to pinfo::exit(), which takes care of indicating the correct overall exit status and then gives control to the OS process shutdown routine, ExitProcess(). During ExitProcess(), all the statically-linked DLLs in the application are terminated, by calling their DllMain functions with the DLL_PROCESS_DETACH notification. Static object destruction. ========================== Static object destruction for any statically-linked DLLs, or any dlopen()ed DLLs that have still not been dlclose()d by termination time, is handled in dll_global_dtors(). As the description above makes clear, this relies on the atexit functions being run, and so only takes place during a graceful exit, and not in the case of termination via _exit(), _Exit(), abort() or through an unhandled signal or exception. The destructors are run before stdio has been terminated, and in reverse of DLL load order.