[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:
Timo Kreuzer 2024-05-11 07:03:12 +02:00
parent f1b60c66f0
commit 04e0dc4a7a
568 changed files with 115483 additions and 0 deletions

View 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;
}

View 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"

View 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);
}

View 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);
}

View 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>();
}

View 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());
}

View 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);
}

View 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;
}

View 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;
});
}

View 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),
&parameter.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);
}