Reactos
at master 318 lines 10 kB view raw
1// 2// exit.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// The exit() implementation 7// 8#include <corecrt_internal.h> 9#include <eh.h> 10#include <process.h> 11 12static long c_termination_complete = FALSE; 13 14extern "C" _onexit_table_t __acrt_atexit_table; 15extern "C" _onexit_table_t __acrt_at_quick_exit_table; 16 17// thread_local atexit dtor handling. The APPCRT exports a function to set the 18// callback function. The exe main function will call this to set the callback 19// function so exit() can invoke destructors for thread-storage objects owned 20// by the main thread. 21static _tls_callback_type thread_local_exit_callback_func; 22 23 24// CRT_REFACTOR TODO This needs to be declared somewhere more accessible and we 25// need to clean up the static CRT exit coordination. 26#if !defined CRTDLL && defined _DEBUG 27 extern "C" bool __cdecl __scrt_uninitialize_crt(bool is_terminating, bool from_exit); 28#endif 29 30 31// Enclaves have no support for managed apps 32#ifdef _UCRT_ENCLAVE_BUILD 33 34static bool __cdecl is_managed_app() throw() { return false; } 35 36static void __cdecl try_cor_exit_process(UINT const) throw() { } 37 38// This function never returns. It causes the process to exit. 39static void __cdecl exit_or_terminate_process(UINT const return_code) throw() 40{ 41 TerminateProcess(GetCurrentProcess(), return_code); 42} 43 44#else /* ^^^ _UCRT_ENCLAVE_BUILD ^^^ // vvv !_UCRT_ENCLAVE_BUILD vvv */ 45 46typedef void (WINAPI* exit_process_pft)(UINT); 47 48// CRT_REFACTOR TODO This is duplicated in the VCStartup utility_desktop.cpp. 49static bool __cdecl is_managed_app() throw() 50{ 51 PIMAGE_DOS_HEADER const dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(GetModuleHandleW(nullptr)); 52 if (dos_header == nullptr) 53 return false; 54 55 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) 56 return false; 57 58 PIMAGE_NT_HEADERS const pe_header = reinterpret_cast<PIMAGE_NT_HEADERS>( 59 reinterpret_cast<BYTE*>(dos_header) + dos_header->e_lfanew); 60 61 if (pe_header->Signature != IMAGE_NT_SIGNATURE) 62 return false; 63 64 if (pe_header->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) 65 return false; 66 67 // prefast assumes we are overrunning __ImageBase 68 #pragma warning(push) 69 #pragma warning(disable: 26000) 70 71 if (pe_header->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) 72 return false; 73 74 #pragma warning(pop) 75 76 if (pe_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress == 0) 77 return false; 78 79 return true; 80} 81 82static void __cdecl try_cor_exit_process(UINT const return_code) throw() 83{ 84 __crt_unique_hmodule mscoree; 85 if (!GetModuleHandleExW(0, L"mscoree.dll", mscoree.get_address_of())) 86 return; 87 88 auto const cor_exit_process = __crt_get_proc_address<exit_process_pft>(mscoree.get(), "CorExitProcess"); 89 if (!cor_exit_process) 90 return; 91 92 cor_exit_process(return_code); 93} 94 95 96 97// For Windows Store apps, starting in Windows 10, we use TerminateProcess 98// instead of ExitProcess. ExitProcess will allow threads to run during 99// termination in an unordered fashion. This has lead to problems in various 100// apps. TerminateProcess does not allow threads to run while the process is 101// being torn down. See also CoreApplication::Run, which does the same. 102// 103// For non-Windows Store app processes, we continue to call ExitProcess, for 104// compatibility with the legacy runtimes. 105static bool __cdecl should_call_terminate_process() throw() 106{ 107 if (__acrt_get_process_end_policy() == process_end_policy_exit_process) 108 { 109 return false; 110 } 111 112 // If application verifier is running, we still want to call ExitProcess, 113 // to enable tools that require DLLs to be unloaded cleanly at process exit 114 // to do their work. 115 if (__acrt_app_verifier_enabled()) 116 { 117 return false; 118 } 119 120 return true; 121} 122 123 124 125// This function never returns. It causes the process to exit. 126static void __cdecl exit_or_terminate_process(UINT const return_code) throw() 127{ 128 if (should_call_terminate_process()) 129 { 130 TerminateProcess(GetCurrentProcess(), return_code); 131 } 132 133 try_cor_exit_process(return_code); 134 135 // If that returned, then the exe for this process is not managed or we 136 // failed to exit via a call to CorExitProcess. Exit the normal way: 137 ExitProcess(return_code); 138} 139 140#endif /* _UCRT_ENCLAVE_BUILD */ 141 142 143static int __cdecl atexit_exception_filter(unsigned long const _exception_code) throw() 144{ 145 if (_exception_code == ('msc' | 0xE0000000)) 146 { 147 return EXCEPTION_EXECUTE_HANDLER; 148 } 149 150 return EXCEPTION_CONTINUE_SEARCH; 151} 152 153 154 155extern "C" void __cdecl __acrt_initialize_thread_local_exit_callback(void * encoded_null) 156{ 157 thread_local_exit_callback_func = reinterpret_cast<_tls_callback_type>(encoded_null); 158} 159 160// Register the dynamic TLS dtor callback. Called from the vcstartup library 161// as part of the EXE common_main to allow the acrt to call back into the scrt. 162extern "C" void __cdecl _register_thread_local_exe_atexit_callback(_In_ _tls_callback_type const _Callback) 163{ 164 // Can only set the callback once. 165 if (thread_local_exit_callback_func != __crt_fast_encode_pointer(nullptr)) 166 { 167 terminate(); 168 } 169 170 thread_local_exit_callback_func = __crt_fast_encode_pointer(_Callback); 171} 172 173 174 175static void __cdecl common_exit( 176 int const return_code, 177 _crt_exit_cleanup_mode const cleanup_mode, 178 _crt_exit_return_mode const return_mode 179 ) throw() 180{ 181 // First, check to see if we're loaded in a managed app. If we are, try to 182 // call CorExitProcess to let the CLR handle the process termination. If 183 // the call to CorExitProcess is successful, then it will call back through 184 // this function with a return mode of _crt_exit_return_to_caller, at which 185 // point we will run the C termination routines. It will then terminate the 186 // process itself and not return. 187 if (return_mode == _crt_exit_terminate_process && is_managed_app()) 188 { 189 try_cor_exit_process(return_code); 190 } 191 192 // Run the C termination: 193 bool crt_uninitialization_required = false; 194 195 __acrt_lock_and_call(__acrt_select_exit_lock(), [&] 196 { 197 static bool c_exit_complete = false; 198 if (c_exit_complete) 199 { 200 return; 201 } 202 203 _InterlockedExchange(&c_termination_complete, TRUE); 204 205 __try 206 { 207 if (cleanup_mode == _crt_exit_full_cleanup) 208 { 209 210 // If this module has any dynamically initialized 211 // __declspec(thread) variables, then we invoke their 212 // destruction for the primary thread. All thread_local 213 // destructors are sequenced before any atexit calls or static 214 // object destructors (3.6.3/1) 215 if (thread_local_exit_callback_func != __crt_fast_encode_pointer(nullptr)) 216 { 217 (__crt_fast_decode_pointer(thread_local_exit_callback_func))(nullptr, DLL_PROCESS_DETACH, nullptr); 218 } 219 220 _execute_onexit_table(&__acrt_atexit_table); 221 } 222 else if (cleanup_mode == _crt_exit_quick_cleanup) 223 { 224 _execute_onexit_table(&__acrt_at_quick_exit_table); 225 } 226 } 227 __except (atexit_exception_filter(GetExceptionCode())) 228 { 229 terminate(); 230 } 231 __endtry 232 233 #ifndef CRTDLL 234 // When the CRT is statically linked, we are responsible for executing 235 // the terminators here, because the CRT code is present in this module. 236 // When the CRT DLLs are used, the terminators will be executed when 237 // the CRT DLLs are unloaded, after the call to ExitProcess. 238 if (cleanup_mode == _crt_exit_full_cleanup) 239 { 240 _initterm(__xp_a, __xp_z); 241 } 242 243 _initterm(__xt_a, __xt_z); 244 #endif // CRTDLL 245 246 if (return_mode == _crt_exit_terminate_process) 247 { 248 c_exit_complete = true; 249 crt_uninitialization_required = true; 250 } 251 }); 252 253 // Do NOT try to uninitialize the CRT while holding one of its locks. 254 if (crt_uninitialization_required) 255 { 256 // If we are about to terminate the process, if the debug CRT is linked 257 // statically into this module and this module is an EXE, we need to 258 // ensure that we fully and correctly uninitialize the CRT so that the 259 // debug on-exit() checks (e.g. debug heap leak detection) have a chance 260 // to run. 261 // 262 // We do not need to uninitialize the CRT when it is statically linked 263 // into a DLL because its DllMain will be called for DLL_PROCESS_DETACH 264 // and we can uninitialize the CRT there. 265 // 266 // We never need to uninitialize the retail CRT during exit() because 267 // the process is about to terminate. 268 #if !CRTDLL && _DEBUG 269 __scrt_uninitialize_crt(true, true); 270 #endif 271 } 272 273 if (return_mode == _crt_exit_terminate_process) 274 { 275 exit_or_terminate_process(return_code); 276 } 277} 278 279extern "C" int __cdecl _is_c_termination_complete() 280{ 281 return static_cast<int>(__crt_interlocked_read(&c_termination_complete)); 282} 283 284 285 286extern "C" void __cdecl exit(int const return_code) 287{ 288 common_exit(return_code, _crt_exit_full_cleanup, _crt_exit_terminate_process); 289 UNREACHABLE; 290} 291 292extern "C" void __cdecl _exit(int const return_code) 293{ 294 common_exit(return_code, _crt_exit_no_cleanup, _crt_exit_terminate_process); 295 UNREACHABLE; 296} 297 298extern "C" void __cdecl _Exit(int const return_code) 299{ 300 common_exit(return_code, _crt_exit_no_cleanup, _crt_exit_terminate_process); 301 UNREACHABLE; 302} 303 304extern "C" void __cdecl quick_exit(int const return_code) 305{ 306 common_exit(return_code, _crt_exit_quick_cleanup, _crt_exit_terminate_process); 307 UNREACHABLE; 308} 309 310extern "C" void __cdecl _cexit() 311{ 312 common_exit(0, _crt_exit_full_cleanup, _crt_exit_return_to_caller); 313} 314 315extern "C" void __cdecl _c_exit() 316{ 317 common_exit(0, _crt_exit_no_cleanup, _crt_exit_return_to_caller); 318}