xref: /reactos/sdk/lib/ucrt/startup/exit.cpp (revision 53d808d2)
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