mirror of
https://github.com/reactos/reactos.git
synced 2025-07-31 09:01:37 +00:00
[UCRT] Import Microsoft.Windows.SDK.CRTSource version 10.0.22621.3
Imported from https://www.nuget.org/packages/Microsoft.Windows.SDK.CRTSource/10.0.22621.3 License: MIT
This commit is contained in:
parent
f1b60c66f0
commit
04e0dc4a7a
568 changed files with 115483 additions and 0 deletions
114
sdk/lib/ucrt/startup/abort.cpp
Normal file
114
sdk/lib/ucrt/startup/abort.cpp
Normal file
|
@ -0,0 +1,114 @@
|
|||
/***
|
||||
*abort.c - abort a program by raising SIGABRT
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
*Purpose:
|
||||
* defines abort() - print a message and raise SIGABRT.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <corecrt_internal.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define _INIT_ABORT_BEHAVIOR _WRITE_ABORT_MSG
|
||||
#else
|
||||
#define _INIT_ABORT_BEHAVIOR _CALL_REPORTFAULT
|
||||
#endif
|
||||
|
||||
extern "C" unsigned int __abort_behavior = _INIT_ABORT_BEHAVIOR;
|
||||
|
||||
/***
|
||||
*void abort() - abort the current program by raising SIGABRT
|
||||
*
|
||||
*Purpose:
|
||||
* print out an abort message and raise the SIGABRT signal. If the user
|
||||
* hasn't defined an abort handler routine, terminate the program
|
||||
* with exit status of 3 without cleaning up.
|
||||
*
|
||||
* Multi-thread version does not raise SIGABRT -- this isn't supported
|
||||
* under multi-thread.
|
||||
*
|
||||
*Entry:
|
||||
* None.
|
||||
*
|
||||
*Exit:
|
||||
* Does not return.
|
||||
*
|
||||
*Uses:
|
||||
*
|
||||
*Exceptions:
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
extern "C" void __cdecl abort()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (__abort_behavior & _WRITE_ABORT_MSG)
|
||||
{
|
||||
__acrt_report_runtime_error(L"abort() has been called");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Check if the user installed a handler for SIGABRT.
|
||||
* We need to read the user handler atomically in the case
|
||||
* another thread is aborting while we change the signal
|
||||
* handler.
|
||||
*/
|
||||
__crt_signal_handler_t const sigabrt_action = __acrt_get_sigabrt_handler();
|
||||
if (sigabrt_action != SIG_DFL)
|
||||
{
|
||||
raise(SIGABRT);
|
||||
}
|
||||
|
||||
/* If there is no user handler for SIGABRT or if the user
|
||||
* handler returns, then exit from the program anyway
|
||||
*/
|
||||
|
||||
if (__abort_behavior & _CALL_REPORTFAULT)
|
||||
{
|
||||
#if defined _M_ARM || defined _M_ARM64 || defined _M_ARM64EC || defined _UCRT_ENCLAVE_BUILD
|
||||
__fastfail(FAST_FAIL_FATAL_APP_EXIT);
|
||||
#else
|
||||
if (IsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE))
|
||||
__fastfail(FAST_FAIL_FATAL_APP_EXIT);
|
||||
|
||||
__acrt_call_reportfault(_CRT_DEBUGGER_ABORT, STATUS_FATAL_APP_EXIT, EXCEPTION_NONCONTINUABLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* If we don't want to call ReportFault, then we call _exit(3), which is the
|
||||
* same as invoking the default handler for SIGABRT
|
||||
*/
|
||||
|
||||
|
||||
_exit(3);
|
||||
}
|
||||
|
||||
/***
|
||||
*unsigned int _set_abort_behavior(unsigned int, unsigned int) - set the behavior on abort
|
||||
*
|
||||
*Purpose:
|
||||
*
|
||||
*Entry:
|
||||
* unsigned int flags - the flags we want to set
|
||||
* unsigned int mask - mask the flag values
|
||||
*
|
||||
*Exit:
|
||||
* Return the old behavior flags
|
||||
*
|
||||
*Exceptions:
|
||||
* None
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
extern "C" unsigned int __cdecl _set_abort_behavior(unsigned int flags, unsigned int mask)
|
||||
{
|
||||
unsigned int oldflags = __abort_behavior;
|
||||
__abort_behavior = oldflags & (~mask) | flags & mask;
|
||||
return oldflags;
|
||||
}
|
75
sdk/lib/ucrt/startup/argv_data.cpp
Normal file
75
sdk/lib/ucrt/startup/argv_data.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// argv_data.cpp
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// This file defines the global data that stores the command line with which the
|
||||
// program was executed, along with the parsed arguments (if the arguments were
|
||||
// parsed), and the accessors for the global data.
|
||||
//
|
||||
#include <corecrt_internal.h>
|
||||
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
||||
// Note: In general, either the narrow or wide string variables will be set,
|
||||
// but not both. These get initialized by the CRT startup sequence before any
|
||||
// user code is executed. There are cases where any or all of the pointers may
|
||||
// be null during execution. Do not assume that they are non-null.
|
||||
|
||||
int __argc = 0; // The number of arguments in __argv or __wargv
|
||||
char** __argv = nullptr; // The arguments as narrow strings
|
||||
wchar_t** __wargv = nullptr; // The arguments as wide strings
|
||||
char* _pgmptr = nullptr; // The name of the program as a narrow string
|
||||
wchar_t* _wpgmptr = nullptr; // The name of the program as a wide string
|
||||
char* _acmdln = nullptr; // The raw command line as a narrow string
|
||||
wchar_t* _wcmdln = nullptr; // The raw command line as a wide string
|
||||
|
||||
_BEGIN_SECURE_CRT_DEPRECATION_DISABLE
|
||||
|
||||
int* __cdecl __p___argc() { return &__argc; }
|
||||
char*** __cdecl __p___argv() { return &__argv; }
|
||||
wchar_t*** __cdecl __p___wargv() { return &__wargv; }
|
||||
char** __cdecl __p__pgmptr() { return &_pgmptr; }
|
||||
wchar_t** __cdecl __p__wpgmptr() { return &_wpgmptr; }
|
||||
char** __cdecl __p__acmdln() { return &_acmdln; }
|
||||
wchar_t** __cdecl __p__wcmdln() { return &_wcmdln; }
|
||||
|
||||
errno_t __cdecl _get_wpgmptr(wchar_t** const result)
|
||||
{
|
||||
_VALIDATE_RETURN_ERRCODE(result != nullptr, EINVAL);
|
||||
_VALIDATE_RETURN_ERRCODE(_wpgmptr != nullptr, EINVAL);
|
||||
|
||||
*result = _wpgmptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t __cdecl _get_pgmptr(char** const result)
|
||||
{
|
||||
_VALIDATE_RETURN_ERRCODE(result != nullptr, EINVAL);
|
||||
_VALIDATE_RETURN_ERRCODE(_pgmptr != nullptr, EINVAL);
|
||||
*result = _pgmptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_END_SECURE_CRT_DEPRECATION_DISABLE
|
||||
|
||||
|
||||
|
||||
bool __cdecl __acrt_initialize_command_line()
|
||||
{
|
||||
_acmdln = GetCommandLineA();
|
||||
_wcmdln = GetCommandLineW();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool __cdecl __acrt_uninitialize_command_line(bool const /* terminating */)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // extern "C"
|
404
sdk/lib/ucrt/startup/argv_parsing.cpp
Normal file
404
sdk/lib/ucrt/startup/argv_parsing.cpp
Normal file
|
@ -0,0 +1,404 @@
|
|||
/***
|
||||
*stdargv.c - standard & wildcard _setargv routine
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
*Purpose:
|
||||
* processes program command line, with or without wildcard expansion
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <corecrt_internal.h>
|
||||
#include <corecrt_internal_traits.h>
|
||||
#include <limits.h>
|
||||
#include <mbstring.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
|
||||
// In the function below, we need to ensure that we've initialized the mbc table
|
||||
// before we start performing character transformations.
|
||||
static void do_locale_initialization(char) throw() { __acrt_initialize_multibyte(); }
|
||||
static void do_locale_initialization(wchar_t) throw() { /* no-op */ }
|
||||
|
||||
static char* get_command_line(char) throw() { return _acmdln; }
|
||||
static wchar_t* get_command_line(wchar_t) throw() { return _wcmdln; }
|
||||
|
||||
static char**& get_argv(char) throw() { return __argv; }
|
||||
static wchar_t**& get_argv(wchar_t) throw() { return __wargv; }
|
||||
|
||||
static errno_t expand_argv_wildcards(
|
||||
_In_z_ char** const argv,
|
||||
_Out_ _Deref_post_z_ char*** const expanded_argv) throw()
|
||||
{
|
||||
return __acrt_expand_narrow_argv_wildcards(argv, expanded_argv);
|
||||
}
|
||||
|
||||
static errno_t expand_argv_wildcards(
|
||||
_In_z_ wchar_t** const argv,
|
||||
_Out_ _Deref_post_z_ wchar_t*** const expanded_argv) throw()
|
||||
{
|
||||
return __acrt_expand_wide_argv_wildcards(argv, expanded_argv);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***
|
||||
*static void parse_cmdline(cmdstart, argv, args, argument_count, character_count)
|
||||
*
|
||||
*Purpose:
|
||||
* Parses the command line and sets up the argv[] array.
|
||||
* On entry, cmdstart should point to the command line,
|
||||
* argv should point to memory for the argv array, args
|
||||
* points to memory to place the text of the arguments.
|
||||
* If these are nullptr, then no storing (only counting)
|
||||
* is done. On exit, *argument_count has the number of
|
||||
* arguments (plus one for a final nullptr argument),
|
||||
* and *character_count has the number of bytes used in the buffer
|
||||
* pointed to by args.
|
||||
*
|
||||
*Entry:
|
||||
* Character *cmdstart - pointer to command line of the form
|
||||
* <progname><nul><args><nul>
|
||||
* Character **argv - where to build argv array; nullptr means don't
|
||||
* build array
|
||||
* Character *args - where to place argument text; nullptr means don't
|
||||
* store text
|
||||
*
|
||||
*Exit:
|
||||
* no return value
|
||||
* int *argument_count - returns number of argv entries created
|
||||
* int *character_count - number of characters used in args buffer
|
||||
*
|
||||
*Exceptions:
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
// should_copy_another_character helper functions
|
||||
// should_copy_another_character is *ONLY* checking for DBCS lead bytes to see if there
|
||||
// might be a following trail byte. This works because the callers are only concerned
|
||||
// about escaped quote sequences and other codepages aren't using those quotes.
|
||||
static bool __cdecl should_copy_another_character(char const c) throw()
|
||||
{
|
||||
// This is OK for UTF-8 as a quote is never a trail byte.
|
||||
return _ismbblead(c) != 0;
|
||||
}
|
||||
|
||||
static bool __cdecl should_copy_another_character(wchar_t) throw()
|
||||
{
|
||||
// This is OK for UTF-16 as a quote is never part of a surrogate pair.
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Character>
|
||||
static void __cdecl parse_command_line(
|
||||
Character* cmdstart,
|
||||
Character** argv,
|
||||
Character* args,
|
||||
size_t* argument_count,
|
||||
size_t* character_count
|
||||
) throw()
|
||||
{
|
||||
*character_count = 0;
|
||||
*argument_count = 1; // We'll have at least the program name
|
||||
|
||||
Character c;
|
||||
int copy_character; /* 1 = copy char to *args */
|
||||
unsigned numslash; /* num of backslashes seen */
|
||||
|
||||
/* first scan the program name, copy it, and count the bytes */
|
||||
Character* p = cmdstart;
|
||||
if (argv)
|
||||
*argv++ = args;
|
||||
|
||||
// A quoted program name is handled here. The handling is much
|
||||
// simpler than for other arguments. Basically, whatever lies
|
||||
// between the leading double-quote and next one, or a terminal null
|
||||
// character is simply accepted. Fancier handling is not required
|
||||
// because the program name must be a legal NTFS/HPFS file name.
|
||||
// Note that the double-quote characters are not copied, nor do they
|
||||
// contribute to character_count.
|
||||
bool in_quotes = false;
|
||||
do
|
||||
{
|
||||
if (*p == '"')
|
||||
{
|
||||
in_quotes = !in_quotes;
|
||||
c = *p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
++*character_count;
|
||||
if (args)
|
||||
*args++ = *p;
|
||||
|
||||
c = *p++;
|
||||
|
||||
if (should_copy_another_character(c))
|
||||
{
|
||||
++*character_count;
|
||||
if (args)
|
||||
*args++ = *p; // Copy 2nd byte too
|
||||
++p; // skip over trail byte
|
||||
}
|
||||
}
|
||||
while (c != '\0' && (in_quotes || (c != ' ' && c != '\t')));
|
||||
|
||||
if (c == '\0')
|
||||
{
|
||||
p--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (args)
|
||||
*(args - 1) = '\0';
|
||||
}
|
||||
|
||||
in_quotes = false;
|
||||
|
||||
// Loop on each argument
|
||||
for (;;)
|
||||
{
|
||||
if (*p)
|
||||
{
|
||||
while (*p == ' ' || *p == '\t')
|
||||
++p;
|
||||
}
|
||||
|
||||
if (*p == '\0')
|
||||
break; // End of arguments
|
||||
|
||||
// Scan an argument:
|
||||
if (argv)
|
||||
*argv++ = args;
|
||||
|
||||
++*argument_count;
|
||||
|
||||
// Loop through scanning one argument:
|
||||
for (;;)
|
||||
{
|
||||
copy_character = 1;
|
||||
|
||||
// Rules:
|
||||
// 2N backslashes + " ==> N backslashes and begin/end quote
|
||||
// 2N + 1 backslashes + " ==> N backslashes + literal "
|
||||
// N backslashes ==> N backslashes
|
||||
numslash = 0;
|
||||
|
||||
while (*p == '\\')
|
||||
{
|
||||
// Count number of backslashes for use below
|
||||
++p;
|
||||
++numslash;
|
||||
}
|
||||
|
||||
if (*p == '"')
|
||||
{
|
||||
// if 2N backslashes before, start/end quote, otherwise
|
||||
// copy literally:
|
||||
if (numslash % 2 == 0)
|
||||
{
|
||||
if (in_quotes && p[1] == '"')
|
||||
{
|
||||
p++; // Double quote inside quoted string
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip first quote char and copy second:
|
||||
copy_character = 0; // Don't copy quote
|
||||
in_quotes = !in_quotes;
|
||||
}
|
||||
}
|
||||
|
||||
numslash /= 2;
|
||||
}
|
||||
|
||||
// Copy slashes:
|
||||
while (numslash--)
|
||||
{
|
||||
if (args)
|
||||
*args++ = '\\';
|
||||
++*character_count;
|
||||
}
|
||||
|
||||
// If at end of arg, break loop:
|
||||
if (*p == '\0' || (!in_quotes && (*p == ' ' || *p == '\t')))
|
||||
break;
|
||||
|
||||
// Copy character into argument:
|
||||
if (copy_character)
|
||||
{
|
||||
if (args)
|
||||
*args++ = *p;
|
||||
|
||||
if (should_copy_another_character(*p))
|
||||
{
|
||||
++p;
|
||||
++*character_count;
|
||||
|
||||
if (args)
|
||||
*args++ = *p;
|
||||
}
|
||||
|
||||
++*character_count;
|
||||
}
|
||||
|
||||
++p;
|
||||
}
|
||||
|
||||
// Null-terminate the argument:
|
||||
if (args)
|
||||
*args++ = '\0'; // Terminate the string
|
||||
|
||||
++*character_count;
|
||||
}
|
||||
|
||||
// We put one last argument in -- a null pointer:
|
||||
if (argv)
|
||||
*argv++ = nullptr;
|
||||
|
||||
++*argument_count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" unsigned char* __cdecl __acrt_allocate_buffer_for_argv(
|
||||
size_t const argument_count,
|
||||
size_t const character_count,
|
||||
size_t const character_size
|
||||
)
|
||||
{
|
||||
if (argument_count >= SIZE_MAX / sizeof(void*))
|
||||
return nullptr;
|
||||
|
||||
if (character_count >= SIZE_MAX / character_size)
|
||||
return nullptr;
|
||||
|
||||
size_t const argument_array_size = argument_count * sizeof(void*);
|
||||
size_t const character_array_size = character_count * character_size;
|
||||
|
||||
if (SIZE_MAX - argument_array_size <= character_array_size)
|
||||
return nullptr;
|
||||
|
||||
size_t const total_size = argument_array_size + character_array_size;
|
||||
__crt_unique_heap_ptr<unsigned char> buffer(_calloc_crt_t(unsigned char, total_size));
|
||||
if (!buffer)
|
||||
return nullptr;
|
||||
|
||||
return buffer.detach();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***
|
||||
*_setargv, __setargv - set up "argc" and "argv" for C programs
|
||||
*
|
||||
*Purpose:
|
||||
* Read the command line and create the argv array for C
|
||||
* programs.
|
||||
*
|
||||
*Entry:
|
||||
* Arguments are retrieved from the program command line,
|
||||
* pointed to by _acmdln.
|
||||
*
|
||||
*Exit:
|
||||
* Returns 0 if successful, -1 if memory allocation failed.
|
||||
* "argv" points to a null-terminated list of pointers to ASCIZ
|
||||
* strings, each of which is an argument from the command line.
|
||||
* "argc" is the number of arguments. The strings are copied from
|
||||
* the environment segment into space allocated on the heap/stack.
|
||||
* The list of pointers is also located on the heap or stack.
|
||||
* _pgmptr points to the program name.
|
||||
*
|
||||
*Exceptions:
|
||||
* Terminates with out of memory error if no memory to allocate.
|
||||
*
|
||||
*******************************************************************************/
|
||||
template <typename Character>
|
||||
static errno_t __cdecl common_configure_argv(_crt_argv_mode const mode) throw()
|
||||
{
|
||||
typedef __crt_char_traits<Character> traits;
|
||||
|
||||
if (mode == _crt_argv_no_arguments)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
_VALIDATE_RETURN_ERRCODE(
|
||||
mode == _crt_argv_expanded_arguments ||
|
||||
mode == _crt_argv_unexpanded_arguments, EINVAL);
|
||||
|
||||
do_locale_initialization(Character());
|
||||
|
||||
|
||||
static Character program_name[MAX_PATH + 1];
|
||||
traits::get_module_file_name(nullptr, program_name, MAX_PATH);
|
||||
traits::set_program_name(&program_name[0]);
|
||||
|
||||
// If there's no command line at all, then use the program name as the
|
||||
// command line to parse, so that argv[0] is initialized with the program
|
||||
// name. (This won't happen when the program is run by cmd.exe, but it
|
||||
// could happen if the program is spawned via some other means.)
|
||||
Character* const raw_command_line = get_command_line(Character());
|
||||
Character* const command_line = raw_command_line == nullptr || raw_command_line[0] == '\0'
|
||||
? program_name
|
||||
: raw_command_line;
|
||||
|
||||
size_t argument_count = 0;
|
||||
size_t character_count = 0;
|
||||
parse_command_line(
|
||||
command_line,
|
||||
static_cast<Character**>(nullptr),
|
||||
static_cast<Character*>(nullptr),
|
||||
&argument_count,
|
||||
&character_count);
|
||||
|
||||
__crt_unique_heap_ptr<unsigned char> buffer(__acrt_allocate_buffer_for_argv(
|
||||
argument_count,
|
||||
character_count,
|
||||
sizeof(Character)));
|
||||
|
||||
_VALIDATE_RETURN_ERRCODE_NOEXC(buffer, ENOMEM);
|
||||
|
||||
Character** const first_argument = reinterpret_cast<Character**>(buffer.get());
|
||||
Character* const first_string = reinterpret_cast<Character*>(buffer.get() + argument_count * sizeof(Character*));
|
||||
|
||||
parse_command_line(command_line, first_argument, first_string, &argument_count, &character_count);
|
||||
|
||||
// If we are not expanding wildcards, then we are done...
|
||||
if (mode == _crt_argv_unexpanded_arguments)
|
||||
{
|
||||
__argc = static_cast<int>(argument_count - 1);
|
||||
get_argv(Character()) = reinterpret_cast<Character**>(buffer.detach());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ... otherwise, we try to do the wildcard expansion:
|
||||
__crt_unique_heap_ptr<Character*> expanded_argv;
|
||||
errno_t const argv_expansion_status = expand_argv_wildcards(first_argument, expanded_argv.get_address_of());
|
||||
if (argv_expansion_status != 0)
|
||||
return argv_expansion_status;
|
||||
|
||||
__argc = [&]()
|
||||
{
|
||||
size_t n = 0;
|
||||
for (auto it = expanded_argv.get(); *it; ++it, ++n) { }
|
||||
return static_cast<int>(n);
|
||||
}();
|
||||
|
||||
get_argv(Character()) = expanded_argv.detach();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" errno_t __cdecl _configure_narrow_argv(_crt_argv_mode const mode)
|
||||
{
|
||||
return common_configure_argv<char>(mode);
|
||||
}
|
||||
|
||||
extern "C" errno_t __cdecl _configure_wide_argv(_crt_argv_mode const mode)
|
||||
{
|
||||
return common_configure_argv<wchar_t>(mode);
|
||||
}
|
391
sdk/lib/ucrt/startup/argv_wildcards.cpp
Normal file
391
sdk/lib/ucrt/startup/argv_wildcards.cpp
Normal file
|
@ -0,0 +1,391 @@
|
|||
/***
|
||||
*wild.c - wildcard expander
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
*Purpose:
|
||||
* expands wildcards in argv
|
||||
*
|
||||
* handles '*' (none or more of any char) and '?' (exactly one char)
|
||||
*
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <corecrt_internal.h>
|
||||
#include <ctype.h>
|
||||
#include <corecrt_internal_traits.h>
|
||||
#include <limits.h>
|
||||
#include <mbstring.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wrl/wrappers/corewrappers.h>
|
||||
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename Character>
|
||||
class argument_list
|
||||
{
|
||||
public:
|
||||
|
||||
argument_list() throw() : _first(nullptr), _last(nullptr), _end(nullptr) { }
|
||||
|
||||
size_t size() const throw() { return _last - _first; }
|
||||
Character** begin() const throw() { return _first; }
|
||||
Character** end() const throw() { return _last; }
|
||||
|
||||
errno_t append(Character* const element) throw()
|
||||
{
|
||||
errno_t const expand_status = expand_if_necessary();
|
||||
if (expand_status != 0)
|
||||
{
|
||||
_free_crt(element);
|
||||
return expand_status;
|
||||
}
|
||||
|
||||
*_last++ = element;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Character** detach() throw()
|
||||
{
|
||||
_last = nullptr;
|
||||
_end = nullptr;
|
||||
|
||||
Character** const first = _first;
|
||||
_first = nullptr;
|
||||
return first;
|
||||
}
|
||||
|
||||
~argument_list() throw()
|
||||
{
|
||||
for (auto it = _first; it != _last; ++it)
|
||||
_free_crt(*it);
|
||||
|
||||
_free_crt(_first);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
argument_list(argument_list const&) throw(); // not implemented
|
||||
argument_list& operator=(argument_list const&) throw(); // not implemented
|
||||
|
||||
errno_t expand_if_necessary() throw()
|
||||
{
|
||||
// If there is already room for more elements, just return:
|
||||
if (_last != _end)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// If the list has not yet had an array allocated for it, allocate one:
|
||||
if (!_first)
|
||||
{
|
||||
size_t const initial_count = 4;
|
||||
|
||||
_first = _calloc_crt_t(Character*, initial_count).detach();
|
||||
if (!_first)
|
||||
return ENOMEM;
|
||||
|
||||
_last = _first;
|
||||
_end = _first + initial_count;
|
||||
return 0;
|
||||
}
|
||||
// Otherwise, double the size of the array:
|
||||
else
|
||||
{
|
||||
size_t const old_count = _end - _first;
|
||||
if (old_count > SIZE_MAX / 2)
|
||||
return ENOMEM;
|
||||
|
||||
size_t const new_count = old_count * 2;
|
||||
__crt_unique_heap_ptr<Character*> new_array(_recalloc_crt_t(Character*, _first, new_count));
|
||||
if (!new_array)
|
||||
return ENOMEM;
|
||||
|
||||
_first = new_array.detach();
|
||||
_last = _first + old_count;
|
||||
_end = _first + new_count;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Character** _first;
|
||||
Character** _last;
|
||||
Character** _end;
|
||||
};
|
||||
}
|
||||
|
||||
_Check_return_
|
||||
static char*
|
||||
previous_character(_In_reads_z_(current - first + 1) char* const first,
|
||||
_In_z_ char* const current) throw()
|
||||
{
|
||||
return reinterpret_cast<char*>(_mbsdec(
|
||||
reinterpret_cast<unsigned char*>(first),
|
||||
reinterpret_cast<unsigned char*>(current)));
|
||||
}
|
||||
|
||||
static wchar_t* previous_character(_In_reads_(0) wchar_t*, _In_reads_(0) wchar_t* const current) throw()
|
||||
{
|
||||
return current - 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename Character>
|
||||
static errno_t copy_and_add_argument_to_buffer(
|
||||
_In_z_ Character const* const file_name,
|
||||
_In_z_ Character const* const directory,
|
||||
size_t const directory_length,
|
||||
argument_list<Character>& buffer
|
||||
) throw()
|
||||
{
|
||||
typedef __crt_char_traits<Character> traits;
|
||||
|
||||
size_t const file_name_count = traits::tcslen(file_name) + 1;
|
||||
if (file_name_count > SIZE_MAX - directory_length)
|
||||
return ENOMEM;
|
||||
|
||||
size_t const required_count = directory_length + file_name_count + 1;
|
||||
__crt_unique_heap_ptr<Character> argument_buffer(_calloc_crt_t(Character, required_count));
|
||||
|
||||
if (directory_length > 0)
|
||||
{
|
||||
_ERRCHECK(traits::tcsncpy_s(argument_buffer.get(), required_count, directory, directory_length));
|
||||
}
|
||||
|
||||
_ERRCHECK(traits::tcsncpy_s(
|
||||
argument_buffer.get() + directory_length,
|
||||
required_count - directory_length,
|
||||
file_name,
|
||||
file_name_count));
|
||||
|
||||
return buffer.append(argument_buffer.detach());
|
||||
}
|
||||
|
||||
|
||||
static wchar_t * get_wide(__crt_internal_win32_buffer<wchar_t> * const dest, char * const source)
|
||||
{
|
||||
errno_t const cvt1 = __acrt_mbs_to_wcs_cp(
|
||||
source,
|
||||
*dest,
|
||||
__acrt_get_utf8_acp_compatibility_codepage()
|
||||
);
|
||||
|
||||
if (cvt1 != 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dest->data();
|
||||
};
|
||||
|
||||
static wchar_t * get_wide(__crt_internal_win32_buffer<wchar_t> *, wchar_t * const source)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
static char * get_file_name(__crt_internal_win32_buffer<char> * const dest, wchar_t * const source)
|
||||
{
|
||||
errno_t const cvt = __acrt_wcs_to_mbs_cp(
|
||||
source,
|
||||
*dest,
|
||||
__acrt_get_utf8_acp_compatibility_codepage()
|
||||
);
|
||||
|
||||
if (cvt != 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dest->data();
|
||||
}
|
||||
|
||||
static wchar_t * get_file_name(__crt_internal_win32_buffer<wchar_t> *, wchar_t * const source)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
template <typename Character>
|
||||
static errno_t expand_argument_wildcards(
|
||||
Character* const argument,
|
||||
Character* const wildcard,
|
||||
argument_list<Character>& buffer
|
||||
) throw()
|
||||
{
|
||||
typedef __crt_char_traits<Character> traits;
|
||||
typedef typename traits::win32_find_data_type find_data_type;
|
||||
|
||||
auto const is_directory_separator = [](Character const c) { return c == '/' || c == '\\' || c == ':'; };
|
||||
|
||||
// Find the first slash or colon before the wildcard:
|
||||
Character* it = wildcard;
|
||||
while (it != argument && !is_directory_separator(*it))
|
||||
{
|
||||
it = previous_character(argument, it);
|
||||
}
|
||||
|
||||
// If we found a colon that can't form a drive name (e.g. it can't be 'D:'),
|
||||
// then just add the argument as-is (we don't know how to expand it):
|
||||
if (*it == ':' && it != argument + 1)
|
||||
{
|
||||
return copy_and_add_argument_to_buffer(argument, static_cast<Character*>(nullptr), 0, buffer);
|
||||
}
|
||||
|
||||
size_t const directory_length = is_directory_separator(*it)
|
||||
? it - argument + 1 // it points to the separator, so add 1 to include it.
|
||||
: 0;
|
||||
|
||||
// Try to begin the find operation:
|
||||
WIN32_FIND_DATAW findFileDataW;
|
||||
__crt_internal_win32_buffer<wchar_t> wide_file_name;
|
||||
|
||||
__crt_findfile_handle const find_handle(::FindFirstFileExW(
|
||||
get_wide(&wide_file_name, argument),
|
||||
FindExInfoStandard,
|
||||
&findFileDataW,
|
||||
FindExSearchNameMatch,
|
||||
nullptr,
|
||||
0));
|
||||
|
||||
// If the find operation failed, there was no match, so just add the argument:
|
||||
if (find_handle.get() == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return copy_and_add_argument_to_buffer(argument, static_cast<Character*>(nullptr), 0, buffer);
|
||||
}
|
||||
|
||||
size_t const old_argument_count = buffer.size();
|
||||
|
||||
do
|
||||
{
|
||||
__crt_internal_win32_buffer<Character> character_buffer;
|
||||
Character* const file_name = get_file_name(&character_buffer, findFileDataW.cFileName);
|
||||
// Skip . and ..:
|
||||
if (file_name[0] == '.' && file_name[1] == '\0')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file_name[0] == '.' && file_name[1] == '.' && file_name[2] == '\0')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
errno_t const add_status = copy_and_add_argument_to_buffer(file_name, argument, directory_length, buffer);
|
||||
if (add_status != 0)
|
||||
{
|
||||
return add_status;
|
||||
}
|
||||
}
|
||||
while (::FindNextFileW(find_handle.get(), &findFileDataW));
|
||||
|
||||
// If we didn't add any arguments to the buffer, then we're done:
|
||||
size_t const new_argument_count = buffer.size();
|
||||
if (old_argument_count == new_argument_count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If we did add new arguments, let's helpfully sort them:
|
||||
qsort(
|
||||
buffer.begin() + old_argument_count,
|
||||
new_argument_count - old_argument_count,
|
||||
sizeof(Character*),
|
||||
[](void const* lhs, void const* rhs) -> int
|
||||
{
|
||||
if (lhs < rhs) { return -1; }
|
||||
if (lhs > rhs) { return 1; }
|
||||
return 0;
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
template <typename Character>
|
||||
static errno_t common_expand_argv_wildcards(Character** const argv, Character*** const result) throw()
|
||||
{
|
||||
typedef __crt_char_traits<Character> traits;
|
||||
|
||||
_VALIDATE_RETURN_ERRCODE(result != nullptr, EINVAL);
|
||||
*result = nullptr;
|
||||
|
||||
argument_list<Character> expansion_buffer;
|
||||
for (Character** it = argv; *it != nullptr; ++it)
|
||||
{
|
||||
Character const wildcard_characters[] = { '*', '?', '\0' };
|
||||
Character* const wildcard = traits::tcspbrk(*it, wildcard_characters);
|
||||
|
||||
// If no wildcard characters were found in the argument string, just
|
||||
// append it to the list and continue on. Otherwise, do the expansion:
|
||||
if (!wildcard)
|
||||
{
|
||||
errno_t const append_status = copy_and_add_argument_to_buffer(
|
||||
*it,
|
||||
static_cast<Character*>(nullptr),
|
||||
0,
|
||||
expansion_buffer);
|
||||
|
||||
if (append_status != 0)
|
||||
return append_status;
|
||||
}
|
||||
else
|
||||
{
|
||||
errno_t const expand_status = expand_argument_wildcards(*it, wildcard, expansion_buffer);
|
||||
if (expand_status != 0)
|
||||
return expand_status;
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we've accumulated the expanded arguments into the expansion
|
||||
// buffer, we want to re-pack them in the form used by the argv parser,
|
||||
// in a single array, with everything "concatenated" together.
|
||||
size_t const argument_count = expansion_buffer.size() + 1;
|
||||
size_t character_count = 0;
|
||||
for (auto it = expansion_buffer.begin(); it != expansion_buffer.end(); ++it)
|
||||
character_count += traits::tcslen(*it) + 1;
|
||||
|
||||
__crt_unique_heap_ptr<unsigned char> expanded_argv(__acrt_allocate_buffer_for_argv(
|
||||
argument_count,
|
||||
character_count,
|
||||
sizeof(Character)));
|
||||
|
||||
if (!expanded_argv)
|
||||
return -1;
|
||||
|
||||
Character** const argument_first = reinterpret_cast<Character**>(expanded_argv.get());
|
||||
Character* const character_first = reinterpret_cast<Character*>(
|
||||
expanded_argv.get() +
|
||||
argument_count * sizeof(Character*));
|
||||
|
||||
Character** argument_it = argument_first;
|
||||
Character* character_it = character_first;
|
||||
for (auto it = expansion_buffer.begin(); it != expansion_buffer.end(); ++it)
|
||||
{
|
||||
size_t const count = traits::tcslen(*it) + 1;
|
||||
|
||||
_ERRCHECK(traits::tcsncpy_s(
|
||||
character_it,
|
||||
character_count - (character_it - character_first),
|
||||
*it,
|
||||
count));
|
||||
|
||||
*argument_it++ = character_it;
|
||||
character_it += count;
|
||||
}
|
||||
|
||||
*result = reinterpret_cast<Character**>(expanded_argv.detach());
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" errno_t __acrt_expand_narrow_argv_wildcards(char** const argv, char*** const result)
|
||||
{
|
||||
return common_expand_argv_wildcards(argv, result);
|
||||
}
|
||||
|
||||
extern "C" errno_t __acrt_expand_wide_argv_wildcards(wchar_t** const argv, wchar_t*** const result)
|
||||
{
|
||||
return common_expand_argv_wildcards(argv, result);
|
||||
}
|
111
sdk/lib/ucrt/startup/argv_winmain.cpp
Normal file
111
sdk/lib/ucrt/startup/argv_winmain.cpp
Normal file
|
@ -0,0 +1,111 @@
|
|||
/***
|
||||
*wincmdln.c - process command line for WinMain
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
*Purpose:
|
||||
* Prepare command line to be passed to [w]WinMain.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <corecrt_internal.h>
|
||||
#include <mbstring.h>
|
||||
|
||||
|
||||
|
||||
// In the function below, we need to ensure that we've initialized the mbc table
|
||||
// before we start performing character transformations.
|
||||
static void do_locale_initialization(unsigned char) throw() { __acrt_initialize_multibyte(); }
|
||||
static void do_locale_initialization(wchar_t) throw() { /* no-op */ }
|
||||
|
||||
static unsigned char* get_command_line(unsigned char) throw()
|
||||
{
|
||||
return reinterpret_cast<unsigned char *>(_acmdln);
|
||||
}
|
||||
|
||||
static wchar_t* get_command_line(wchar_t) throw() { return _wcmdln; }
|
||||
|
||||
|
||||
// should_copy_another_character is *ONLY* checking for DBCS lead bytes to see if there
|
||||
// might be a following trail byte. This works because the callers are only concerned
|
||||
// about escaped quote sequences and other codepages aren't using those quotes.
|
||||
static bool __cdecl should_copy_another_character(unsigned char const c) throw()
|
||||
{
|
||||
// This is OK for UTF-8 as a quote is never a trail byte.
|
||||
return _ismbblead(c) != 0;
|
||||
}
|
||||
|
||||
static bool __cdecl should_copy_another_character(wchar_t) throw()
|
||||
{
|
||||
// This is OK for UTF-16 as a quote is never part of a surrogate pair.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***
|
||||
*_[w]wincmdln
|
||||
*
|
||||
*Purpose:
|
||||
* Extract the command line tail to be passed to WinMain.
|
||||
*
|
||||
* Be warned! This code was originally implemented by the NT group and
|
||||
* has remained pretty much unchanged since 12-91. It should be changed
|
||||
* only with extreme care since there are undoubtedly many apps which
|
||||
* depend on its historical behavior.
|
||||
*
|
||||
*Entry:
|
||||
* The global variable _[a|w]cmdln is set to point at the complete
|
||||
* command line.
|
||||
*
|
||||
*Exit:
|
||||
* Returns a pointer to the command line tail.
|
||||
*
|
||||
*Exceptions:
|
||||
*
|
||||
*******************************************************************************/
|
||||
template <typename Character>
|
||||
static Character* __cdecl common_wincmdln() throw()
|
||||
{
|
||||
do_locale_initialization(Character());
|
||||
|
||||
static Character empty_string[] = { '\0' };
|
||||
|
||||
Character* command_line = get_command_line(Character()) == nullptr
|
||||
? empty_string
|
||||
: get_command_line(Character());
|
||||
|
||||
// Skip past the program name (the first token in the command line) and
|
||||
// check for and handle a quoted program name:
|
||||
bool in_double_quotes = false;
|
||||
while (*command_line > ' ' || (*command_line != '\0' && in_double_quotes))
|
||||
{
|
||||
// Toggle the in_double_quotes flag if the current character is '"'
|
||||
if (*command_line == '"')
|
||||
in_double_quotes = !in_double_quotes;
|
||||
|
||||
if (should_copy_another_character(*command_line))
|
||||
++command_line;
|
||||
|
||||
++command_line;
|
||||
}
|
||||
|
||||
// Skip past any whitespace preceding the next token:
|
||||
while (*command_line != '\0' && *command_line <= ' ')
|
||||
++command_line;
|
||||
|
||||
return command_line;
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" char* __cdecl _get_narrow_winmain_command_line()
|
||||
{ // Need to use unsigned char so that we correctly handle ASCII characters
|
||||
// above 127, in particular the comparison to ' ' (space - 0x20).
|
||||
return reinterpret_cast<char *>(common_wincmdln<unsigned char>());
|
||||
}
|
||||
|
||||
extern "C" wchar_t* __cdecl _get_wide_winmain_command_line()
|
||||
{
|
||||
return common_wincmdln<wchar_t>();
|
||||
}
|
444
sdk/lib/ucrt/startup/assert.cpp
Normal file
444
sdk/lib/ucrt/startup/assert.cpp
Normal file
|
@ -0,0 +1,444 @@
|
|||
/***
|
||||
*assert.c - Display a message and abort
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
*Purpose:
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <corecrt_internal.h>
|
||||
#include <corecrt_internal_stdio.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#undef NDEBUG
|
||||
#define _ASSERT_OK
|
||||
#include <assert.h>
|
||||
|
||||
// Assertion string components:
|
||||
#define MAXLINELEN 64 /* max length for line in message box */
|
||||
#define ASSERTBUFSZ (MAXLINELEN * 9) /* 9 lines in message box */
|
||||
|
||||
// Format of stderr for assertions:
|
||||
//
|
||||
// Assertion failed: <expression>, file c:\test\mytest\bar.c, line 69
|
||||
//
|
||||
|
||||
|
||||
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(assert_format, "Assertion failed: %Ts, file %Ts, line %d\n")
|
||||
|
||||
// Enclaves only support assertions sent to the debugger.
|
||||
// This mode could also be enabled for normal apps as well.
|
||||
|
||||
#ifdef _UCRT_ENCLAVE_BUILD
|
||||
|
||||
template <typename Character>
|
||||
__declspec(noreturn) static void __cdecl common_assert_to_debug(
|
||||
Character const* const expression,
|
||||
Character const* const file_name,
|
||||
unsigned const line_number
|
||||
) throw()
|
||||
{
|
||||
using traits = __crt_char_traits<Character>;
|
||||
|
||||
Character assert_buffer[ASSERTBUFSZ];
|
||||
if (traits::sntprintf_s(assert_buffer, _countof(assert_buffer), _countof(assert_buffer), get_assert_format(Character()), expression, file_name, line_number) < 0)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
traits::output_debug_string(assert_buffer);
|
||||
abort();
|
||||
}
|
||||
|
||||
template <typename Character>
|
||||
static void __cdecl common_assert(
|
||||
Character const* const expression,
|
||||
Character const* const file_name,
|
||||
unsigned const line_number,
|
||||
void* const
|
||||
) throw()
|
||||
{
|
||||
common_assert_to_debug(expression, file_name, line_number);
|
||||
}
|
||||
|
||||
#else /* ^^^ _UCRT_ENCLAVE_BUILD ^^^ // vvv !_UCRT_ENCLAVE_BUILD vvv */
|
||||
|
||||
// Format of MessageBox for assertions:
|
||||
//
|
||||
// ================= Microsft Visual C++ Debug Library ================
|
||||
//
|
||||
// Assertion Failed!
|
||||
//
|
||||
// Program: c:\test\mytest\foo.exe
|
||||
// File: c:\test\mytest\bar.c
|
||||
// Line: 69
|
||||
//
|
||||
// Expression: <expression>
|
||||
//
|
||||
// For information on how your program can cause an assertion
|
||||
// failure, see the Visual C++ documentation on asserts
|
||||
//
|
||||
// (Press Retry to debug the application - JIT must be enabled)
|
||||
//
|
||||
// ===================================================================
|
||||
|
||||
|
||||
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(banner_text, "Microsoft Visual C++ Runtime Library")
|
||||
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(box_intro, "Assertion failed!")
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(program_intro, "Program: ")
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(file_intro, "File: ")
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(line_intro, "Line: ")
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(expression_intro, "Expression: ")
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(info_intro, "For information on how your program can cause an assertion\nfailure, see the Visual C++ documentation on asserts")
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(help_intro, "(Press Retry to debug the application - JIT must be enabled)")
|
||||
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(dot_dot_dot, "...")
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(newline, "\n")
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(double_newline, "\n\n")
|
||||
|
||||
_GENERATE_TCHAR_STRING_FUNCTIONS(program_name_unknown_text, "<program name unknown>")
|
||||
|
||||
/***
|
||||
*_assert() - Display a message and abort
|
||||
*
|
||||
*Purpose:
|
||||
* The assert macro calls this routine if the assert expression is
|
||||
* true. By placing the assert code in a subroutine instead of within
|
||||
* the body of the macro, programs that call assert multiple times will
|
||||
* save space.
|
||||
*
|
||||
*Entry:
|
||||
*
|
||||
*Exit:
|
||||
*
|
||||
*Exceptions:
|
||||
*
|
||||
*******************************************************************************/
|
||||
static void __cdecl common_assert_to_stderr_direct(char const*, char const*, unsigned) throw()
|
||||
{
|
||||
// No action for narrow strings
|
||||
}
|
||||
|
||||
static void __cdecl common_assert_to_stderr_direct(
|
||||
wchar_t const* const expression,
|
||||
wchar_t const* const file_name,
|
||||
unsigned const line_number
|
||||
) throw()
|
||||
{
|
||||
HANDLE const stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
#pragma warning(suppress:__WARNING_REDUNDANT_POINTER_TEST) // 28922
|
||||
if (stderr_handle == INVALID_HANDLE_VALUE || stderr_handle == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetFileType(stderr_handle) != FILE_TYPE_CHAR)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wchar_t assert_buffer[ASSERTBUFSZ];
|
||||
#pragma warning(suppress:__WARNING_BANNED_API_USAGE) // 28719
|
||||
if (swprintf(assert_buffer, _countof(assert_buffer), get_assert_format(wchar_t()), expression, file_name, line_number) < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD const assert_buffer_length = static_cast<DWORD>(wcslen(assert_buffer));
|
||||
DWORD characters_written = 0;
|
||||
if (WriteConsoleW(stderr_handle, assert_buffer, assert_buffer_length, &characters_written, nullptr) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
template <typename Character>
|
||||
__declspec(noreturn) static void __cdecl common_assert_to_stderr(
|
||||
Character const* const expression,
|
||||
Character const* const file_name,
|
||||
unsigned const line_number
|
||||
) throw()
|
||||
{
|
||||
using traits = __crt_char_traits<Character>;
|
||||
|
||||
// Try to write directly to the console. This is only supported for wide
|
||||
// character strings. If we have a narrow character string or the write
|
||||
// fails, we fall back to call through stdio.
|
||||
common_assert_to_stderr_direct(expression, file_name, line_number);
|
||||
|
||||
// If stderr does not yet have a buffer, set it to use single character
|
||||
// buffering to avoid dynamic allocation of a stream buffer:
|
||||
if (!__crt_stdio_stream(stderr).has_any_buffer())
|
||||
{
|
||||
setvbuf(stderr, nullptr, _IONBF, 0);
|
||||
}
|
||||
|
||||
traits::ftprintf(stderr, get_assert_format(Character()), expression, file_name, line_number);
|
||||
fflush(stderr);
|
||||
abort();
|
||||
}
|
||||
|
||||
template <typename Character>
|
||||
static void __cdecl common_assert_to_message_box_build_string(
|
||||
Character* const assert_buffer,
|
||||
size_t const assert_buffer_count,
|
||||
Character const* const expression,
|
||||
Character const* const file_name,
|
||||
unsigned const line_number,
|
||||
void* const return_address
|
||||
) throw()
|
||||
{
|
||||
using traits = __crt_char_traits<Character>;
|
||||
|
||||
// Line 1: Box introduction line:
|
||||
_ERRCHECK(traits::tcscpy_s(assert_buffer, assert_buffer_count, get_box_intro(Character())));
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_double_newline(Character())));
|
||||
|
||||
// Line 2: Program line:
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_program_intro(Character())));
|
||||
|
||||
Character program_name[_MAX_PATH + 1]{};
|
||||
|
||||
HMODULE asserting_module = nullptr;
|
||||
if (!GetModuleHandleExW(
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
||||
static_cast<wchar_t const*>(return_address),
|
||||
&asserting_module))
|
||||
{
|
||||
asserting_module = nullptr;
|
||||
}
|
||||
|
||||
#ifdef CRTDLL
|
||||
// If the assert came from within the CRT DLL, report it as having come
|
||||
// from the EXE instead:
|
||||
if (asserting_module == reinterpret_cast<HMODULE>(&__ImageBase))
|
||||
{
|
||||
asserting_module = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!traits::get_module_file_name(asserting_module, program_name, static_cast<DWORD>(_countof(program_name))))
|
||||
{
|
||||
_ERRCHECK(traits::tcscpy_s(program_name, _countof(program_name), get_program_name_unknown_text(Character())));
|
||||
}
|
||||
|
||||
Character* pchProg = program_name;
|
||||
if (program_intro_count + traits::tcslen(program_name) + newline_length > MAXLINELEN)
|
||||
{
|
||||
pchProg += (program_intro_count + traits::tcslen(program_name) + newline_length) - MAXLINELEN;
|
||||
// Only replace first (sizeof(Character) * dot_dot_dot_length) bytes to ellipsis:
|
||||
_ERRCHECK(memcpy_s(
|
||||
pchProg,
|
||||
sizeof(Character) * ((MAX_PATH + 1) - (pchProg - program_name)),
|
||||
get_dot_dot_dot(Character()),
|
||||
sizeof(Character) * dot_dot_dot_length
|
||||
));
|
||||
}
|
||||
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, pchProg));
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_newline(Character())));
|
||||
|
||||
// Line 3: File line
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_file_intro(Character())));
|
||||
|
||||
if (file_intro_count + traits::tcslen(file_name) + newline_length > MAXLINELEN)
|
||||
{
|
||||
size_t const ffn = MAXLINELEN - file_intro_count - newline_length;
|
||||
|
||||
size_t p = 0;
|
||||
size_t len = 0;
|
||||
Character const* pch = file_name;
|
||||
for (len = traits::tcslen(file_name), p = 1;
|
||||
pch[len - p] != '\\' && pch[len - p] != '/' && p < len;
|
||||
p++)
|
||||
{
|
||||
}
|
||||
|
||||
// Trim the path and file name so that they fit, using up to 2/3 of
|
||||
// the maximum number of characters for the path and the remaining
|
||||
// 1/3 for the file name:
|
||||
if ((ffn - ffn / 3) < (len - p) && ffn / 3 > p)
|
||||
{
|
||||
// The path is too long. Use the first part of the path and the
|
||||
// full file name:
|
||||
_ERRCHECK(traits::tcsncat_s(assert_buffer, assert_buffer_count, pch, ffn - dot_dot_dot_length - p));
|
||||
_ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, get_dot_dot_dot(Character())));
|
||||
_ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, pch + len - p));
|
||||
}
|
||||
else if (ffn - ffn / 3 > len - p)
|
||||
{
|
||||
// The file name is too long. Use the full path and the first
|
||||
// and last part of the file name, with a ... in between:
|
||||
p = p / 2;
|
||||
_ERRCHECK(traits::tcsncat_s(assert_buffer, assert_buffer_count, pch, ffn - dot_dot_dot_length - p));
|
||||
_ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, get_dot_dot_dot(Character())));
|
||||
_ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, pch + len - p));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Both are too long. Use the first part of the path and the
|
||||
// first and last part of the file name, with ...s in between:
|
||||
_ERRCHECK(traits::tcsncat_s(assert_buffer, assert_buffer_count, pch, ffn - ffn / 3 - dot_dot_dot_length));
|
||||
_ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, get_dot_dot_dot(Character())));
|
||||
_ERRCHECK(traits::tcsncat_s(assert_buffer, assert_buffer_count, pch + len - p, ffn / 6 - 1));
|
||||
_ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, get_dot_dot_dot(Character())));
|
||||
_ERRCHECK(traits::tcscat_s (assert_buffer, assert_buffer_count, pch + len - (ffn / 3 - ffn / 6 - 2)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Plenty of room on the line; just append the full path and file name:
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, file_name));
|
||||
}
|
||||
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_newline(Character())));
|
||||
|
||||
// Line 4: Line Number line:
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_line_intro(Character())));
|
||||
_ERRCHECK(traits::itot_s(
|
||||
line_number,
|
||||
assert_buffer + traits::tcslen(assert_buffer),
|
||||
assert_buffer_count - traits::tcslen(assert_buffer),
|
||||
10));
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_double_newline(Character())));
|
||||
|
||||
// Line 5: Message line:
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_expression_intro(Character())));
|
||||
|
||||
size_t const characters_used =
|
||||
traits::tcslen(assert_buffer) +
|
||||
2 * double_newline_length +
|
||||
info_intro_length +
|
||||
help_intro_count;
|
||||
|
||||
if (characters_used + traits::tcslen(expression) > assert_buffer_count)
|
||||
{
|
||||
size_t const characters_to_write = assert_buffer_count - (characters_used + dot_dot_dot_length);
|
||||
_ERRCHECK(traits::tcsncat_s(
|
||||
assert_buffer,
|
||||
assert_buffer_count,
|
||||
expression,
|
||||
characters_to_write));
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_dot_dot_dot(Character())));
|
||||
}
|
||||
else
|
||||
{
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, expression));
|
||||
}
|
||||
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_double_newline(Character())));
|
||||
|
||||
// Info line:
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_info_intro(Character())));
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_double_newline(Character())));
|
||||
|
||||
// Help line:
|
||||
_ERRCHECK(traits::tcscat_s(assert_buffer, assert_buffer_count, get_help_intro(Character())));
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename Character>
|
||||
static void __cdecl common_assert_to_message_box(
|
||||
Character const* const expression,
|
||||
Character const* const file_name,
|
||||
unsigned const line_number,
|
||||
void* const return_address
|
||||
) throw()
|
||||
{
|
||||
using traits = __crt_char_traits<Character>;
|
||||
|
||||
Character assert_buffer[ASSERTBUFSZ]{};
|
||||
common_assert_to_message_box_build_string(
|
||||
assert_buffer,
|
||||
_countof(assert_buffer),
|
||||
expression,
|
||||
file_name,
|
||||
line_number,
|
||||
return_address);
|
||||
|
||||
int const action = traits::show_message_box(
|
||||
assert_buffer,
|
||||
get_banner_text(Character()),
|
||||
MB_TASKMODAL | MB_ICONHAND | MB_ABORTRETRYIGNORE | MB_SETFOREGROUND);
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case IDABORT: // Abort the program:
|
||||
{
|
||||
raise(SIGABRT);
|
||||
|
||||
// We won't usually get here, but it's possible that a user-registered
|
||||
// abort handler returns, so exit the program immediately. Note that
|
||||
// even though we are "aborting," we do not call abort() because we do
|
||||
// not want to invoke Watson (the user has already had an opportunity
|
||||
// to debug the error and chose not to).
|
||||
_exit(3);
|
||||
}
|
||||
case IDRETRY: // Break into the debugger then return control to caller
|
||||
{
|
||||
__debugbreak();
|
||||
return;
|
||||
}
|
||||
case IDIGNORE: // Return control to caller
|
||||
{
|
||||
return;
|
||||
}
|
||||
default: // This should not happen; treat as fatal error:
|
||||
{
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Character>
|
||||
static void __cdecl common_assert(
|
||||
Character const* const expression,
|
||||
Character const* const file_name,
|
||||
unsigned const line_number,
|
||||
void* const return_address
|
||||
) throw()
|
||||
{
|
||||
using traits = __crt_char_traits<Character>;
|
||||
|
||||
int const current_error_mode = _set_error_mode(_REPORT_ERRMODE);
|
||||
if (current_error_mode == _OUT_TO_STDERR)
|
||||
{
|
||||
return common_assert_to_stderr(expression, file_name, line_number);
|
||||
}
|
||||
|
||||
if (current_error_mode == _OUT_TO_DEFAULT && _query_app_type() == _crt_console_app)
|
||||
{
|
||||
return common_assert_to_stderr(expression, file_name, line_number);
|
||||
}
|
||||
|
||||
return common_assert_to_message_box(expression, file_name, line_number, return_address);
|
||||
}
|
||||
|
||||
#endif /* _UCRT_ENCLAVE_BUILD */
|
||||
|
||||
extern "C" void __cdecl _assert(
|
||||
char const* const expression,
|
||||
char const* const file_name,
|
||||
unsigned const line_number
|
||||
) throw()
|
||||
{
|
||||
return common_assert(expression, file_name, line_number, _ReturnAddress());
|
||||
}
|
||||
|
||||
extern "C" void __cdecl _wassert(
|
||||
wchar_t const* const expression,
|
||||
wchar_t const* const file_name,
|
||||
unsigned const line_number
|
||||
)
|
||||
{
|
||||
return common_assert(expression, file_name, line_number, _ReturnAddress());
|
||||
}
|
313
sdk/lib/ucrt/startup/exit.cpp
Normal file
313
sdk/lib/ucrt/startup/exit.cpp
Normal file
|
@ -0,0 +1,313 @@
|
|||
//
|
||||
// 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" extern _onexit_table_t __acrt_atexit_table;
|
||||
extern "C" extern _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();
|
||||
}
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
extern "C" void __cdecl _exit(int const return_code)
|
||||
{
|
||||
common_exit(return_code, _crt_exit_no_cleanup, _crt_exit_terminate_process);
|
||||
}
|
||||
|
||||
extern "C" void __cdecl _Exit(int const return_code)
|
||||
{
|
||||
common_exit(return_code, _crt_exit_no_cleanup, _crt_exit_terminate_process);
|
||||
}
|
||||
|
||||
extern "C" void __cdecl quick_exit(int const return_code)
|
||||
{
|
||||
common_exit(return_code, _crt_exit_quick_cleanup, _crt_exit_terminate_process);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
46
sdk/lib/ucrt/startup/initterm.cpp
Normal file
46
sdk/lib/ucrt/startup/initterm.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// initterm.cpp
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// _initterm and _initterm_e functions used during dynamic initialization.
|
||||
//
|
||||
#include <corecrt_internal.h>
|
||||
|
||||
|
||||
|
||||
// Calls each function in [first, last). [first, last) must be a valid range of
|
||||
// function pointers. Each function is called, in order.
|
||||
extern "C" void __cdecl _initterm(_PVFV* const first, _PVFV* const last)
|
||||
{
|
||||
for (_PVFV* it = first; it != last; ++it)
|
||||
{
|
||||
if (*it == nullptr)
|
||||
continue;
|
||||
|
||||
(**it)();
|
||||
}
|
||||
}
|
||||
|
||||
// Calls each function in [first, last). [first, last) must be a valid range of
|
||||
// function pointers. Each function must return zero on success, nonzero on
|
||||
// failure. If any function returns nonzero, iteration stops immediately and
|
||||
// the nonzero value is returned. Otherwise all functions are called and zero
|
||||
// is returned.
|
||||
//
|
||||
// If a nonzero value is returned, it is expected to be one of the runtime error
|
||||
// values (_RT_{NAME}, defined in the internal header files).
|
||||
extern "C" int __cdecl _initterm_e(_PIFV* const first, _PIFV* const last)
|
||||
{
|
||||
for (_PIFV* it = first; it != last; ++it)
|
||||
{
|
||||
if (*it == nullptr)
|
||||
continue;
|
||||
|
||||
int const result = (**it)();
|
||||
if (result != 0)
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
231
sdk/lib/ucrt/startup/onexit.cpp
Normal file
231
sdk/lib/ucrt/startup/onexit.cpp
Normal file
|
@ -0,0 +1,231 @@
|
|||
//
|
||||
// onexit.cpp
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// The _onexit registry, which stores pointers to functions to be called when
|
||||
// the program terminates or, when the CRT is statically linked into a DLL, when
|
||||
// the DLL is unloaded.
|
||||
//
|
||||
// When the CRT is statically linked into an EXE or a DLL, this registry is used
|
||||
// to hold the on-exit functions for the module (EXE or DLL) into which it is
|
||||
// linked.
|
||||
//
|
||||
// When the dynamic CRT is used, this object is part of the AppCRT DLL and this
|
||||
// registry stores the on-exit functions for the VCRuntime. If the EXE for the
|
||||
// process uses this VCRuntime, then this registry also stores the on-exit
|
||||
// functions for that EXE. If a DLL uses the dynamic CRT, then that DLL has its
|
||||
// own registry, defined in the statically-linked VCStartup library.
|
||||
//
|
||||
#include <corecrt_internal.h>
|
||||
|
||||
|
||||
|
||||
// The global atexit and at_quick_exit registries
|
||||
extern "C" _onexit_table_t __acrt_atexit_table{};
|
||||
extern "C" _onexit_table_t __acrt_at_quick_exit_table{};
|
||||
|
||||
|
||||
|
||||
enum : size_t
|
||||
{
|
||||
initial_table_count = 32,
|
||||
minimum_table_increment = 4,
|
||||
maximum_table_increment = 512
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Registers a function to be executed on exit. This function modifies the global
|
||||
// onexit table.
|
||||
extern "C" int __cdecl _crt_atexit(_PVFV const function)
|
||||
{
|
||||
return _register_onexit_function(&__acrt_atexit_table, reinterpret_cast<_onexit_t>(function));
|
||||
}
|
||||
|
||||
extern "C" int __cdecl _crt_at_quick_exit(_PVFV const function)
|
||||
{
|
||||
return _register_onexit_function(&__acrt_at_quick_exit_table, reinterpret_cast<_onexit_t>(function));
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" int __cdecl _initialize_onexit_table(_onexit_table_t* const table)
|
||||
{
|
||||
if (!table)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If the table has already been initialized, do not do anything. Note that
|
||||
// this handles both the case where the table was value initialized and where
|
||||
// the table was initialized with encoded null pointers.
|
||||
if (table->_first != table->_end)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
_PVFV* const encoded_nullptr = __crt_fast_encode_pointer(nullptr);
|
||||
|
||||
table->_first = encoded_nullptr;
|
||||
table->_last = encoded_nullptr;
|
||||
table->_end = encoded_nullptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Appends the given 'function' to the given onexit 'table'. Returns 0 on
|
||||
// success; returns -1 on failure. In general, failures are considered fatal
|
||||
// in calling code.
|
||||
extern "C" int __cdecl _register_onexit_function(_onexit_table_t* const table, _onexit_t const function)
|
||||
{
|
||||
return __acrt_lock_and_call(__acrt_select_exit_lock(), [&]
|
||||
{
|
||||
if (!table)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
_PVFV* first = __crt_fast_decode_pointer(table->_first);
|
||||
_PVFV* last = __crt_fast_decode_pointer(table->_last);
|
||||
_PVFV* end = __crt_fast_decode_pointer(table->_end);
|
||||
|
||||
// If there is no room for the new entry, reallocate a larger table:
|
||||
if (last == end)
|
||||
{
|
||||
size_t const old_count = end - first;
|
||||
|
||||
size_t const increment = old_count > maximum_table_increment ? maximum_table_increment : old_count;
|
||||
|
||||
// First, try to double the capacity of the table:
|
||||
size_t new_count = old_count + increment;
|
||||
if (new_count == 0)
|
||||
{
|
||||
new_count = initial_table_count;
|
||||
}
|
||||
|
||||
_PVFV* new_first = nullptr;
|
||||
if (new_count >= old_count)
|
||||
{
|
||||
new_first = _recalloc_crt_t(_PVFV, first, new_count).detach();
|
||||
}
|
||||
|
||||
// If that didn't work, try to allocate a smaller increment:
|
||||
if (new_first == nullptr)
|
||||
{
|
||||
new_count = old_count + minimum_table_increment;
|
||||
new_first = _recalloc_crt_t(_PVFV, first, new_count).detach();
|
||||
}
|
||||
|
||||
if (new_first == nullptr)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
first = new_first;
|
||||
last = new_first + old_count;
|
||||
end = new_first + new_count;
|
||||
|
||||
// The "additional" storage obtained from recalloc is sero-initialized.
|
||||
// The array holds encoded function pointers, so we need to fill the
|
||||
// storage with encoded nullptrs:
|
||||
_PVFV const encoded_nullptr = __crt_fast_encode_pointer(nullptr);
|
||||
for (auto it = last; it != end; ++it)
|
||||
{
|
||||
*it = encoded_nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
*last++ = reinterpret_cast<_PVFV>(__crt_fast_encode_pointer(function));
|
||||
|
||||
table->_first = __crt_fast_encode_pointer(first);
|
||||
table->_last = __crt_fast_encode_pointer(last);
|
||||
table->_end = __crt_fast_encode_pointer(end);
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This function executes a table of _onexit()/atexit() functions. The
|
||||
// terminators are executed in reverse order, to give the required LIFO
|
||||
// execution order. If the table is uninitialized, this function has no
|
||||
// effect. After executing the terminators, this function resets the table
|
||||
// so that it is uninitialized. Returns 0 on success; -1 on failure.
|
||||
extern "C" int __cdecl _execute_onexit_table(_onexit_table_t* const table)
|
||||
{
|
||||
return __acrt_lock_and_call(__acrt_select_exit_lock(), [&]
|
||||
{
|
||||
if (!table)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
_PVFV* first = __crt_fast_decode_pointer(table->_first);
|
||||
_PVFV* last = __crt_fast_decode_pointer(table->_last);
|
||||
if (!first || first == reinterpret_cast<_PVFV*>(-1))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This loop calls through caller-provided function pointers. We must
|
||||
// save and reset the global state mode before calling them, to maintain
|
||||
// proper mode nesting. (These calls to caller-provided function pointers
|
||||
// are the only non-trivial calls, so we can do this once for the entire
|
||||
// loop.)
|
||||
{
|
||||
__crt_state_management::scoped_global_state_reset saved_state;
|
||||
|
||||
_PVFV const encoded_nullptr = __crt_fast_encode_pointer(nullptr);
|
||||
|
||||
_PVFV* saved_first = first;
|
||||
_PVFV* saved_last = last;
|
||||
for (;;)
|
||||
{
|
||||
// Find the last valid function pointer to call:
|
||||
while (--last >= first && *last == encoded_nullptr)
|
||||
{
|
||||
// Keep going backwards
|
||||
}
|
||||
|
||||
if (last < first)
|
||||
{
|
||||
// There are no more valid entries in the list; we are done:
|
||||
break;
|
||||
}
|
||||
|
||||
// Store the function pointer and mark it as visited in the list:
|
||||
_PVFV const function = __crt_fast_decode_pointer(*last);
|
||||
*last = encoded_nullptr;
|
||||
|
||||
function();
|
||||
|
||||
_PVFV* const new_first = __crt_fast_decode_pointer(table->_first);
|
||||
_PVFV* const new_last = __crt_fast_decode_pointer(table->_last);
|
||||
|
||||
// Reset iteration if either the begin or end pointer has changed:
|
||||
if (new_first != saved_first || new_last != saved_last)
|
||||
{
|
||||
first = saved_first = new_first;
|
||||
last = saved_last = new_last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first != reinterpret_cast<_PVFV*>(-1))
|
||||
{
|
||||
_free_crt(first);
|
||||
}
|
||||
|
||||
_PVFV* const encoded_nullptr = __crt_fast_encode_pointer(nullptr);
|
||||
|
||||
table->_first = encoded_nullptr;
|
||||
table->_last = encoded_nullptr;
|
||||
table->_end = encoded_nullptr;
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
278
sdk/lib/ucrt/startup/thread.cpp
Normal file
278
sdk/lib/ucrt/startup/thread.cpp
Normal file
|
@ -0,0 +1,278 @@
|
|||
//
|
||||
// thread.cpp
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// The _beginthread(), _beginthreadex(), _endthread(), and _endthreadex().
|
||||
//
|
||||
// There are several key differences in behavior between _beginthread() and
|
||||
// _beginthreadex():
|
||||
//
|
||||
// * _beginthreadex() takes three additional parameters, which are passed on to
|
||||
// CreateThread(): a security descriptor for the new thread, the initial
|
||||
// thread state (running or asleep), and an optional out parameter to which
|
||||
// the thread id of the newly created thread will be stored.
|
||||
//
|
||||
// * The procedure passed to _beginthread() must be __cdecl and has no return
|
||||
// code. The routine passed to _beginthreadex() must be __stdcall and must
|
||||
// return a return code, which will be used as the thread exit code.
|
||||
// Likewise, _endthread() takes no parameter and always returns a thread exit
|
||||
// code of 0 if the thread exits without error, whereas _endthreadex() takes
|
||||
// an exit code.
|
||||
//
|
||||
// * _endthread() calls CloseHandle() on the handle returned from CreateThread().
|
||||
// Note that this means that a caller should not use this handle, since it is
|
||||
// possible that the thread will have terminated and the handle will have been
|
||||
// closed by the time that _beginthread() returns.
|
||||
//
|
||||
// _endthreadex() does not call CloseHandle() to close the handle: the caller
|
||||
// of _beginthreadex() is required to close the handle.
|
||||
//
|
||||
// * _beginthread() returns -1 on failure. _beginthreadex() returns zero on
|
||||
// failure (just as CreateThread() does).
|
||||
//
|
||||
#include <corecrt_internal.h>
|
||||
#include <process.h>
|
||||
#include <roapi.h>
|
||||
|
||||
// In some compilation models, the compiler is able to detect that the return
|
||||
// statement at the end of thread_start is unreachable. We cannot suppress the
|
||||
// warning locally because it is a backend warning.
|
||||
#pragma warning(disable: 4702) // unreachable code
|
||||
#pragma warning(disable: 4984) // 'if constexpr' is a C++17 language extension
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
struct thread_parameter_free_policy
|
||||
{
|
||||
void operator()(__acrt_thread_parameter* const parameter) throw()
|
||||
{
|
||||
if (!parameter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameter->_thread_handle)
|
||||
{
|
||||
CloseHandle(parameter->_thread_handle);
|
||||
}
|
||||
|
||||
if (parameter->_module_handle)
|
||||
{
|
||||
FreeLibrary(parameter->_module_handle);
|
||||
}
|
||||
|
||||
_free_crt(parameter);
|
||||
}
|
||||
};
|
||||
|
||||
using unique_thread_parameter = __crt_unique_heap_ptr<
|
||||
__acrt_thread_parameter,
|
||||
thread_parameter_free_policy>;
|
||||
}
|
||||
|
||||
template <typename ThreadProcedure, bool Ex>
|
||||
static unsigned long WINAPI thread_start(void* const parameter) throw()
|
||||
{
|
||||
if (!parameter)
|
||||
{
|
||||
ExitThread(GetLastError());
|
||||
}
|
||||
|
||||
__acrt_thread_parameter* const context = static_cast<__acrt_thread_parameter*>(parameter);
|
||||
|
||||
__acrt_getptd()->_beginthread_context = context;
|
||||
|
||||
if (__acrt_get_begin_thread_init_policy() == begin_thread_init_policy_ro_initialize)
|
||||
{
|
||||
context->_initialized_apartment = __acrt_RoInitialize(RO_INIT_MULTITHREADED) == S_OK;
|
||||
}
|
||||
|
||||
__try
|
||||
{
|
||||
ThreadProcedure const procedure = reinterpret_cast<ThreadProcedure>(context->_procedure);
|
||||
if constexpr (Ex)
|
||||
{
|
||||
_endthreadex(procedure(context->_context));
|
||||
}
|
||||
else
|
||||
{
|
||||
procedure(context->_context);
|
||||
_endthreadex(0);
|
||||
}
|
||||
}
|
||||
__except (_seh_filter_exe(GetExceptionCode(), GetExceptionInformation()))
|
||||
{
|
||||
// Execution should never reach here:
|
||||
_exit(GetExceptionCode());
|
||||
}
|
||||
|
||||
// This return statement will never be reached. All execution paths result
|
||||
// in the thread or process exiting.
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static __acrt_thread_parameter* __cdecl create_thread_parameter(
|
||||
void* const procedure,
|
||||
void* const context
|
||||
) throw()
|
||||
{
|
||||
unique_thread_parameter parameter(_calloc_crt_t(__acrt_thread_parameter, 1).detach());
|
||||
if (!parameter)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
parameter.get()->_procedure = procedure;
|
||||
parameter.get()->_context = context;
|
||||
|
||||
// Attempt to bump the reference count of the module in which the user's
|
||||
// thread procedure is defined, to ensure that the module will stay loaded
|
||||
// as long as the thread is executing. We will release this HMDOULE when
|
||||
// the thread procedure returns or _endthreadex is called.
|
||||
GetModuleHandleExW(
|
||||
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
||||
reinterpret_cast<LPCWSTR>(procedure),
|
||||
¶meter.get()->_module_handle);
|
||||
|
||||
return parameter.detach();
|
||||
}
|
||||
|
||||
extern "C" uintptr_t __cdecl _beginthread(
|
||||
_beginthread_proc_type const procedure,
|
||||
unsigned int const stack_size,
|
||||
void* const context
|
||||
)
|
||||
{
|
||||
_VALIDATE_RETURN(procedure != nullptr, EINVAL, reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE));
|
||||
|
||||
unique_thread_parameter parameter(create_thread_parameter(procedure, context));
|
||||
if (!parameter)
|
||||
{
|
||||
return reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
|
||||
// We create the new thread in a suspended state so that we can update
|
||||
// the parameter structure with the thread handle. The newly created
|
||||
// thread is responsible for closing this handle.
|
||||
DWORD thread_id{};
|
||||
HANDLE const thread_handle = CreateThread(
|
||||
nullptr,
|
||||
stack_size,
|
||||
thread_start<_beginthread_proc_type, false>,
|
||||
parameter.get(),
|
||||
CREATE_SUSPENDED,
|
||||
&thread_id);
|
||||
|
||||
if (!thread_handle)
|
||||
{
|
||||
__acrt_errno_map_os_error(GetLastError());
|
||||
return reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
|
||||
parameter.get()->_thread_handle = thread_handle;
|
||||
|
||||
// Now we can start the thread...
|
||||
if (ResumeThread(thread_handle) == static_cast<DWORD>(-1))
|
||||
{
|
||||
__acrt_errno_map_os_error(GetLastError());
|
||||
return reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
|
||||
// If we successfully created the thread, the thread now owns its parameter:
|
||||
parameter.detach();
|
||||
|
||||
return reinterpret_cast<uintptr_t>(thread_handle);
|
||||
}
|
||||
|
||||
extern "C" uintptr_t __cdecl _beginthreadex(
|
||||
void* const security_descriptor,
|
||||
unsigned int const stack_size,
|
||||
_beginthreadex_proc_type const procedure,
|
||||
void* const context,
|
||||
unsigned int const creation_flags,
|
||||
unsigned int* const thread_id_result
|
||||
)
|
||||
{
|
||||
_VALIDATE_RETURN(procedure != nullptr, EINVAL, 0);
|
||||
|
||||
unique_thread_parameter parameter(create_thread_parameter(procedure, context));
|
||||
if (!parameter)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD thread_id;
|
||||
HANDLE const thread_handle = CreateThread(
|
||||
reinterpret_cast<LPSECURITY_ATTRIBUTES>(security_descriptor),
|
||||
stack_size,
|
||||
thread_start<_beginthreadex_proc_type, true>,
|
||||
parameter.get(),
|
||||
creation_flags,
|
||||
&thread_id);
|
||||
|
||||
if (!thread_handle)
|
||||
{
|
||||
__acrt_errno_map_os_error(GetLastError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (thread_id_result)
|
||||
{
|
||||
*thread_id_result = thread_id;
|
||||
}
|
||||
|
||||
// If we successfully created the thread, the thread now owns its parameter:
|
||||
parameter.detach();
|
||||
|
||||
return reinterpret_cast<uintptr_t>(thread_handle);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void __cdecl common_end_thread(unsigned int const return_code) throw()
|
||||
{
|
||||
__acrt_ptd* const ptd = __acrt_getptd_noexit();
|
||||
if (!ptd)
|
||||
{
|
||||
ExitThread(return_code);
|
||||
}
|
||||
|
||||
__acrt_thread_parameter* const parameter = ptd->_beginthread_context;
|
||||
if (!parameter)
|
||||
{
|
||||
ExitThread(return_code);
|
||||
}
|
||||
|
||||
if (parameter->_initialized_apartment)
|
||||
{
|
||||
__acrt_RoUninitialize();
|
||||
}
|
||||
|
||||
if (parameter->_thread_handle != INVALID_HANDLE_VALUE && parameter->_thread_handle != nullptr)
|
||||
{
|
||||
CloseHandle(parameter->_thread_handle);
|
||||
}
|
||||
|
||||
if (parameter->_module_handle != INVALID_HANDLE_VALUE && parameter->_module_handle != nullptr)
|
||||
{
|
||||
FreeLibraryAndExitThread(parameter->_module_handle, return_code);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExitThread(return_code);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __cdecl _endthread()
|
||||
{
|
||||
return common_end_thread(0);
|
||||
}
|
||||
|
||||
extern "C" void __cdecl _endthreadex(unsigned int const return_code)
|
||||
{
|
||||
return common_end_thread(return_code);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue