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 12 static long c_termination_complete = FALSE; 13 14 extern "C" _onexit_table_t __acrt_atexit_table; 15 extern "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. 21 static _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 34 static bool __cdecl is_managed_app() throw() { return false; } 35 36 static void __cdecl try_cor_exit_process(UINT const) throw() { } 37 38 // This function never returns. It causes the process to exit. 39 static 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 46 typedef void (WINAPI* exit_process_pft)(UINT); 47 48 // CRT_REFACTOR TODO This is duplicated in the VCStartup utility_desktop.cpp. 49 static 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 82 static 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. 105 static 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. 126 static 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 143 static 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 155 extern "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. 162 extern "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 175 static 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 279 extern "C" int __cdecl _is_c_termination_complete() 280 { 281 return static_cast<int>(__crt_interlocked_read(&c_termination_complete)); 282 } 283 284 285 286 extern "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 292 extern "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 298 extern "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 304 extern "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 310 extern "C" void __cdecl _cexit() 311 { 312 common_exit(0, _crt_exit_full_cleanup, _crt_exit_return_to_caller); 313 } 314 315 extern "C" void __cdecl _c_exit() 316 { 317 common_exit(0, _crt_exit_no_cleanup, _crt_exit_return_to_caller); 318 } 319