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
is_managed_app()34 static bool __cdecl is_managed_app() throw() { return false; }
35
try_cor_exit_process(UINT const)36 static void __cdecl try_cor_exit_process(UINT const) throw() { }
37
38 // This function never returns. It causes the process to exit.
exit_or_terminate_process(UINT const return_code)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.
is_managed_app()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
try_cor_exit_process(UINT const return_code)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.
should_call_terminate_process()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.
exit_or_terminate_process(UINT const return_code)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
atexit_exception_filter(unsigned long const _exception_code)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
__acrt_initialize_thread_local_exit_callback(void * encoded_null)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.
_register_thread_local_exe_atexit_callback(_In_ _tls_callback_type const _Callback)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
common_exit(int const return_code,_crt_exit_cleanup_mode const cleanup_mode,_crt_exit_return_mode const return_mode)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
_is_c_termination_complete()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
exit(int const return_code)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
_exit(int const return_code)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
_Exit(int const return_code)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
quick_exit(int const return_code)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
_cexit()310 extern "C" void __cdecl _cexit()
311 {
312 common_exit(0, _crt_exit_full_cleanup, _crt_exit_return_to_caller);
313 }
314
_c_exit()315 extern "C" void __cdecl _c_exit()
316 {
317 common_exit(0, _crt_exit_no_cleanup, _crt_exit_return_to_caller);
318 }
319