mirror of
https://github.com/reactos/reactos.git
synced 2025-04-21 04:37:15 +00:00
318 lines
9.9 KiB
C++
318 lines
9.9 KiB
C++
//
|
|
// exit.cpp
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// The exit() implementation
|
|
//
|
|
#include <corecrt_internal.h>
|
|
#include <eh.h>
|
|
#include <process.h>
|
|
|
|
static long c_termination_complete = FALSE;
|
|
|
|
extern "C" _onexit_table_t __acrt_atexit_table;
|
|
extern "C" _onexit_table_t __acrt_at_quick_exit_table;
|
|
|
|
// thread_local atexit dtor handling. The APPCRT exports a function to set the
|
|
// callback function. The exe main function will call this to set the callback
|
|
// function so exit() can invoke destructors for thread-storage objects owned
|
|
// by the main thread.
|
|
static _tls_callback_type thread_local_exit_callback_func;
|
|
|
|
|
|
// CRT_REFACTOR TODO This needs to be declared somewhere more accessible and we
|
|
// need to clean up the static CRT exit coordination.
|
|
#if !defined CRTDLL && defined _DEBUG
|
|
extern "C" bool __cdecl __scrt_uninitialize_crt(bool is_terminating, bool from_exit);
|
|
#endif
|
|
|
|
|
|
// Enclaves have no support for managed apps
|
|
#ifdef _UCRT_ENCLAVE_BUILD
|
|
|
|
static bool __cdecl is_managed_app() throw() { return false; }
|
|
|
|
static void __cdecl try_cor_exit_process(UINT const) throw() { }
|
|
|
|
// This function never returns. It causes the process to exit.
|
|
static void __cdecl exit_or_terminate_process(UINT const return_code) throw()
|
|
{
|
|
TerminateProcess(GetCurrentProcess(), return_code);
|
|
}
|
|
|
|
#else /* ^^^ _UCRT_ENCLAVE_BUILD ^^^ // vvv !_UCRT_ENCLAVE_BUILD vvv */
|
|
|
|
typedef void (WINAPI* exit_process_pft)(UINT);
|
|
|
|
// CRT_REFACTOR TODO This is duplicated in the VCStartup utility_desktop.cpp.
|
|
static bool __cdecl is_managed_app() throw()
|
|
{
|
|
PIMAGE_DOS_HEADER const dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(GetModuleHandleW(nullptr));
|
|
if (dos_header == nullptr)
|
|
return false;
|
|
|
|
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
|
|
return false;
|
|
|
|
PIMAGE_NT_HEADERS const pe_header = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
|
reinterpret_cast<BYTE*>(dos_header) + dos_header->e_lfanew);
|
|
|
|
if (pe_header->Signature != IMAGE_NT_SIGNATURE)
|
|
return false;
|
|
|
|
if (pe_header->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
|
|
return false;
|
|
|
|
// prefast assumes we are overrunning __ImageBase
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 26000)
|
|
|
|
if (pe_header->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
|
|
return false;
|
|
|
|
#pragma warning(pop)
|
|
|
|
if (pe_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress == 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void __cdecl try_cor_exit_process(UINT const return_code) throw()
|
|
{
|
|
__crt_unique_hmodule mscoree;
|
|
if (!GetModuleHandleExW(0, L"mscoree.dll", mscoree.get_address_of()))
|
|
return;
|
|
|
|
auto const cor_exit_process = __crt_get_proc_address<exit_process_pft>(mscoree.get(), "CorExitProcess");
|
|
if (!cor_exit_process)
|
|
return;
|
|
|
|
cor_exit_process(return_code);
|
|
}
|
|
|
|
|
|
|
|
// For Windows Store apps, starting in Windows 10, we use TerminateProcess
|
|
// instead of ExitProcess. ExitProcess will allow threads to run during
|
|
// termination in an unordered fashion. This has lead to problems in various
|
|
// apps. TerminateProcess does not allow threads to run while the process is
|
|
// being torn down. See also CoreApplication::Run, which does the same.
|
|
//
|
|
// For non-Windows Store app processes, we continue to call ExitProcess, for
|
|
// compatibility with the legacy runtimes.
|
|
static bool __cdecl should_call_terminate_process() throw()
|
|
{
|
|
if (__acrt_get_process_end_policy() == process_end_policy_exit_process)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// If application verifier is running, we still want to call ExitProcess,
|
|
// to enable tools that require DLLs to be unloaded cleanly at process exit
|
|
// to do their work.
|
|
if (__acrt_app_verifier_enabled())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// This function never returns. It causes the process to exit.
|
|
static void __cdecl exit_or_terminate_process(UINT const return_code) throw()
|
|
{
|
|
if (should_call_terminate_process())
|
|
{
|
|
TerminateProcess(GetCurrentProcess(), return_code);
|
|
}
|
|
|
|
try_cor_exit_process(return_code);
|
|
|
|
// If that returned, then the exe for this process is not managed or we
|
|
// failed to exit via a call to CorExitProcess. Exit the normal way:
|
|
ExitProcess(return_code);
|
|
}
|
|
|
|
#endif /* _UCRT_ENCLAVE_BUILD */
|
|
|
|
|
|
static int __cdecl atexit_exception_filter(unsigned long const _exception_code) throw()
|
|
{
|
|
if (_exception_code == ('msc' | 0xE0000000))
|
|
{
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
|
|
|
|
extern "C" void __cdecl __acrt_initialize_thread_local_exit_callback(void * encoded_null)
|
|
{
|
|
thread_local_exit_callback_func = reinterpret_cast<_tls_callback_type>(encoded_null);
|
|
}
|
|
|
|
// Register the dynamic TLS dtor callback. Called from the vcstartup library
|
|
// as part of the EXE common_main to allow the acrt to call back into the scrt.
|
|
extern "C" void __cdecl _register_thread_local_exe_atexit_callback(_In_ _tls_callback_type const _Callback)
|
|
{
|
|
// Can only set the callback once.
|
|
if (thread_local_exit_callback_func != __crt_fast_encode_pointer(nullptr))
|
|
{
|
|
terminate();
|
|
}
|
|
|
|
thread_local_exit_callback_func = __crt_fast_encode_pointer(_Callback);
|
|
}
|
|
|
|
|
|
|
|
static void __cdecl common_exit(
|
|
int const return_code,
|
|
_crt_exit_cleanup_mode const cleanup_mode,
|
|
_crt_exit_return_mode const return_mode
|
|
) throw()
|
|
{
|
|
// First, check to see if we're loaded in a managed app. If we are, try to
|
|
// call CorExitProcess to let the CLR handle the process termination. If
|
|
// the call to CorExitProcess is successful, then it will call back through
|
|
// this function with a return mode of _crt_exit_return_to_caller, at which
|
|
// point we will run the C termination routines. It will then terminate the
|
|
// process itself and not return.
|
|
if (return_mode == _crt_exit_terminate_process && is_managed_app())
|
|
{
|
|
try_cor_exit_process(return_code);
|
|
}
|
|
|
|
// Run the C termination:
|
|
bool crt_uninitialization_required = false;
|
|
|
|
__acrt_lock_and_call(__acrt_select_exit_lock(), [&]
|
|
{
|
|
static bool c_exit_complete = false;
|
|
if (c_exit_complete)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_InterlockedExchange(&c_termination_complete, TRUE);
|
|
|
|
__try
|
|
{
|
|
if (cleanup_mode == _crt_exit_full_cleanup)
|
|
{
|
|
|
|
// If this module has any dynamically initialized
|
|
// __declspec(thread) variables, then we invoke their
|
|
// destruction for the primary thread. All thread_local
|
|
// destructors are sequenced before any atexit calls or static
|
|
// object destructors (3.6.3/1)
|
|
if (thread_local_exit_callback_func != __crt_fast_encode_pointer(nullptr))
|
|
{
|
|
(__crt_fast_decode_pointer(thread_local_exit_callback_func))(nullptr, DLL_PROCESS_DETACH, nullptr);
|
|
}
|
|
|
|
_execute_onexit_table(&__acrt_atexit_table);
|
|
}
|
|
else if (cleanup_mode == _crt_exit_quick_cleanup)
|
|
{
|
|
_execute_onexit_table(&__acrt_at_quick_exit_table);
|
|
}
|
|
}
|
|
__except (atexit_exception_filter(GetExceptionCode()))
|
|
{
|
|
terminate();
|
|
}
|
|
__endtry
|
|
|
|
#ifndef CRTDLL
|
|
// When the CRT is statically linked, we are responsible for executing
|
|
// the terminators here, because the CRT code is present in this module.
|
|
// When the CRT DLLs are used, the terminators will be executed when
|
|
// the CRT DLLs are unloaded, after the call to ExitProcess.
|
|
if (cleanup_mode == _crt_exit_full_cleanup)
|
|
{
|
|
_initterm(__xp_a, __xp_z);
|
|
}
|
|
|
|
_initterm(__xt_a, __xt_z);
|
|
#endif // CRTDLL
|
|
|
|
if (return_mode == _crt_exit_terminate_process)
|
|
{
|
|
c_exit_complete = true;
|
|
crt_uninitialization_required = true;
|
|
}
|
|
});
|
|
|
|
// Do NOT try to uninitialize the CRT while holding one of its locks.
|
|
if (crt_uninitialization_required)
|
|
{
|
|
// If we are about to terminate the process, if the debug CRT is linked
|
|
// statically into this module and this module is an EXE, we need to
|
|
// ensure that we fully and correctly uninitialize the CRT so that the
|
|
// debug on-exit() checks (e.g. debug heap leak detection) have a chance
|
|
// to run.
|
|
//
|
|
// We do not need to uninitialize the CRT when it is statically linked
|
|
// into a DLL because its DllMain will be called for DLL_PROCESS_DETACH
|
|
// and we can uninitialize the CRT there.
|
|
//
|
|
// We never need to uninitialize the retail CRT during exit() because
|
|
// the process is about to terminate.
|
|
#if !CRTDLL && _DEBUG
|
|
__scrt_uninitialize_crt(true, true);
|
|
#endif
|
|
}
|
|
|
|
if (return_mode == _crt_exit_terminate_process)
|
|
{
|
|
exit_or_terminate_process(return_code);
|
|
}
|
|
}
|
|
|
|
extern "C" int __cdecl _is_c_termination_complete()
|
|
{
|
|
return static_cast<int>(__crt_interlocked_read(&c_termination_complete));
|
|
}
|
|
|
|
|
|
|
|
extern "C" void __cdecl exit(int const return_code)
|
|
{
|
|
common_exit(return_code, _crt_exit_full_cleanup, _crt_exit_terminate_process);
|
|
UNREACHABLE;
|
|
}
|
|
|
|
extern "C" void __cdecl _exit(int const return_code)
|
|
{
|
|
common_exit(return_code, _crt_exit_no_cleanup, _crt_exit_terminate_process);
|
|
UNREACHABLE;
|
|
}
|
|
|
|
extern "C" void __cdecl _Exit(int const return_code)
|
|
{
|
|
common_exit(return_code, _crt_exit_no_cleanup, _crt_exit_terminate_process);
|
|
UNREACHABLE;
|
|
}
|
|
|
|
extern "C" void __cdecl quick_exit(int const return_code)
|
|
{
|
|
common_exit(return_code, _crt_exit_quick_cleanup, _crt_exit_terminate_process);
|
|
UNREACHABLE;
|
|
}
|
|
|
|
extern "C" void __cdecl _cexit()
|
|
{
|
|
common_exit(0, _crt_exit_full_cleanup, _crt_exit_return_to_caller);
|
|
}
|
|
|
|
extern "C" void __cdecl _c_exit()
|
|
{
|
|
common_exit(0, _crt_exit_no_cleanup, _crt_exit_return_to_caller);
|
|
}
|