* how-startup-shutdown-works.txt: Add new document.
This commit is contained in:
		| @@ -1,3 +1,7 @@ | |||||||
|  | 2010-02-02  Dave Korn  <dave.korn.cygwin@gmail.com> | ||||||
|  |  | ||||||
|  | 	* how-startup-shutdown-works.txt: Add new document. | ||||||
|  |  | ||||||
| 2010-01-29  Corinna Vinschen  <corinna@vinschen.de> | 2010-01-29  Corinna Vinschen  <corinna@vinschen.de> | ||||||
|  |  | ||||||
| 	* sec_auth.cc (lsaauth): Use CYG_LSA_MAGIC as checksum start value to | 	* sec_auth.cc (lsaauth): Use CYG_LSA_MAGIC as checksum start value to | ||||||
|   | |||||||
							
								
								
									
										165
									
								
								winsup/cygwin/how-startup-shutdown-works.txt
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										165
									
								
								winsup/cygwin/how-startup-shutdown-works.txt
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,165 @@ | |||||||
|  | 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. | ||||||
|  |  | ||||||
		Reference in New Issue
	
	Block a user