diff --git a/inc/malloc_km.h b/inc/malloc_km.h new file mode 100644 index 0000000..616610c --- /dev/null +++ b/inc/malloc_km.h @@ -0,0 +1,36 @@ +/* + * PROJECT: Universal C++ RunTime (UCXXRT) + * FILE: malloc_km.h + * DATA: 2022/06/17 + * + * PURPOSE: Universal C++ RunTime + * + * LICENSE: Relicensed under The MIT License from The CC BY 4.0 License + * + * DEVELOPER: MiroKaku (miro.kaku AT Outlook.com) + */ + +#pragma once +#include + + +extern "C" _CRT_HYBRIDPATCHABLE __declspec(noinline) _CRTRESTRICT +void* __cdecl kmalloc( + _In_ _CRT_GUARDOVERFLOW size_t size, + _In_ int pool_type, + _In_ unsigned long tag +); + +extern "C" _CRT_HYBRIDPATCHABLE __declspec(noinline) +void __cdecl kfree( + _Pre_maybenull_ _Post_invalid_ void* block, + _In_ unsigned long tag +); + +extern "C" _CRT_HYBRIDPATCHABLE __declspec(noinline) _CRTRESTRICT +void* __cdecl kcalloc( + _In_ _CRT_GUARDOVERFLOW size_t const count, + _In_ _CRT_GUARDOVERFLOW size_t const size, + _In_ int pool_type, + _In_ unsigned long tag +); diff --git a/inc/malloc_tag.h b/inc/malloc_tag.h deleted file mode 100644 index 5a879f8..0000000 --- a/inc/malloc_tag.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * PROJECT: Universal C++ RunTime (UCXXRT) - * FILE: malloc.h - * DATA: 2021/05/08 - * - * PURPOSE: Universal C++ RunTime - * - * LICENSE: Relicensed under The MIT License from The CC BY 4.0 License - * - * DEVELOPER: MiroKaku (miro.kaku AT Outlook.com) - */ - -#pragma once -#include - -_CRT_BEGIN_C_HEADER - -_CRT_HYBRIDPATCHABLE __declspec(noinline) _CRTRESTRICT -void* __cdecl _malloc_pool_tag( - _In_ _CRT_GUARDOVERFLOW size_t _Size, - _In_ int _Pool, - _In_ unsigned long _Tag -); - -_CRT_HYBRIDPATCHABLE __declspec(noinline) -void __cdecl _free_pool_tag( - _Pre_maybenull_ _Post_invalid_ void* _Block, - _In_ int /*_Pool*/, - _In_ unsigned long _Tag -); - -_CRT_END_C_HEADER diff --git a/inc/new_km.h b/inc/new_km.h new file mode 100644 index 0000000..881d970 --- /dev/null +++ b/inc/new_km.h @@ -0,0 +1,25 @@ +/* + * PROJECT: Universal C++ RunTime (UCXXRT) + * FILE: new_km.h + * DATA: 2022/06/17 + * + * PURPOSE: Universal C++ RunTime + * + * LICENSE: Relicensed under The MIT License from The CC BY 4.0 License + * + * DEVELOPER: MiroKaku (miro.kaku AT Outlook.com) + */ + + // + // Defines the user-defined operator new. + // + +#include + +// user-defined operator new functions +_NODISCARD _VCRT_ALLOCATOR void* __CRTDECL operator new (size_t const size, int pool_type, unsigned long tag); +_NODISCARD _VCRT_ALLOCATOR void* __CRTDECL operator new[](size_t const size, int pool_type, unsigned long tag); + +// user-defined operator deallocation functions +void __CRTDECL operator delete (void* const block, int pool_type, unsigned long tag) noexcept; +void __CRTDECL operator delete[](void* const block, int pool_type, unsigned long tag) noexcept; diff --git a/inc/new_tag.h b/inc/new_tag.h deleted file mode 100644 index 0545ffc..0000000 --- a/inc/new_tag.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * PROJECT: Universal C++ RunTime (UCXXRT) - * FILE: new_user.h - * DATA: 2021/11/09 - * - * PURPOSE: Universal C++ RunTime - * - * LICENSE: Relicensed under The MIT License from The CC BY 4.0 License - * - * DEVELOPER: MiroKaku (miro.kaku AT Outlook.com) - */ - - // - // Defines the user-defined operator new. - // - -#include - -// user-defined operator new functions -_NODISCARD _VCRT_ALLOCATOR void* __CRTDECL operator new(size_t size, POOL_TYPE pool, ULONG tag); -_NODISCARD _VCRT_ALLOCATOR void* __CRTDECL operator new(size_t size, POOL_TYPE pool); -_NODISCARD _VCRT_ALLOCATOR void* __CRTDECL operator new(size_t size, POOL_TYPE pool, std::nothrow_t const&) noexcept; -_NODISCARD _VCRT_ALLOCATOR void* __CRTDECL operator new(size_t size, POOL_TYPE pool, ULONG tag, std::nothrow_t const&) noexcept; -_NODISCARD _VCRT_ALLOCATOR void* __CRTDECL operator new[](size_t size, POOL_TYPE pool, ULONG tag); -_NODISCARD _VCRT_ALLOCATOR void* __CRTDECL operator new[](size_t size, POOL_TYPE pool); -_NODISCARD _VCRT_ALLOCATOR void* __CRTDECL operator new[](size_t size, POOL_TYPE pool, std::nothrow_t const&) noexcept; -_NODISCARD _VCRT_ALLOCATOR void* __CRTDECL operator new[](size_t size, POOL_TYPE pool, ULONG tag, std::nothrow_t const&) noexcept; - -// user-defined operator deallocation functions -void __CRTDECL operator delete(void* block, POOL_TYPE pool, ULONG tag) noexcept; -void __CRTDECL operator delete(void* block, POOL_TYPE pool) noexcept; -void __CRTDECL operator delete(void* block, POOL_TYPE pool, std::nothrow_t const&) noexcept; -void __CRTDECL operator delete(void* block, POOL_TYPE pool, ULONG tag, std::nothrow_t const&) noexcept; -void __CRTDECL operator delete[](void* block, POOL_TYPE pool, ULONG tag) noexcept; -void __CRTDECL operator delete[](void* block, POOL_TYPE pool) noexcept; -void __CRTDECL operator delete[](void* block, POOL_TYPE pool, std::nothrow_t const&) noexcept; -void __CRTDECL operator delete[](void* block, POOL_TYPE pool, ULONG tag, std::nothrow_t const&) noexcept; diff --git a/msvc/ucxxrt.vcxproj b/msvc/ucxxrt.vcxproj index 5800da9..b3c05ad 100644 --- a/msvc/ucxxrt.vcxproj +++ b/msvc/ucxxrt.vcxproj @@ -271,6 +271,7 @@ + @@ -299,6 +300,7 @@ + @@ -342,6 +344,7 @@ + true true @@ -351,8 +354,10 @@ + + diff --git a/msvc/ucxxrt.vcxproj.filters b/msvc/ucxxrt.vcxproj.filters index bbc0d76..c45d9b2 100644 --- a/msvc/ucxxrt.vcxproj.filters +++ b/msvc/ucxxrt.vcxproj.filters @@ -686,6 +686,21 @@ ucxxrt\crt\vcruntime + + ucxxrt\ucrt\heap + + + ucxxrt\ucrt\heap + + + ucxxrt\ucrt\heap + + + ucxxrt\crt\vcruntime + + + ucxxrt\crt\vcruntime + diff --git a/src/crt/vcruntime/delete_km.cpp b/src/crt/vcruntime/delete_km.cpp new file mode 100644 index 0000000..8bf344b --- /dev/null +++ b/src/crt/vcruntime/delete_km.cpp @@ -0,0 +1,45 @@ +/* + * PROJECT: Universal C++ RunTime (UCXXRT) + * FILE: delete_km.cpp + * DATA: 2022/06/17 + * + * PURPOSE: Universal C++ RunTime + * + * LICENSE: Relicensed under The MIT License from The CC BY 4.0 License + * + * DEVELOPER: MiroKaku (miro.kaku AT Outlook.com) + */ + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////// +// delete() Fallback Ordering +// +// +-------------+ +// |delete_scalar<----+-----------------------+ +// +--^----------+ | | +// | | | +// +--+---------+ +--+---------------+ +----+----------------+ +// |delete_array| |delete_scalar_size| |delete_scalar_nothrow| +// +--^----^----+ +------------------+ +---------------------+ +// | | +// | +-------------------+ +// | | +// +--+--------------+ +------+-------------+ +// |delete_array_size| |delete_array_nothrow| +// +-----------------+ +--------------------+ + +_CRT_SECURITYCRITICAL_ATTRIBUTE +void __CRTDECL operator delete(void* const block, int /*pool_type*/, unsigned long tag) noexcept +{ + kfree(block, tag); +} + +_CRT_SECURITYCRITICAL_ATTRIBUTE +void __CRTDECL operator delete[](void* const block, int pool_type, unsigned long tag) noexcept +{ + operator delete(block, pool_type, tag); +} diff --git a/src/crt/vcruntime/new_km.cpp b/src/crt/vcruntime/new_km.cpp new file mode 100644 index 0000000..50b0423 --- /dev/null +++ b/src/crt/vcruntime/new_km.cpp @@ -0,0 +1,64 @@ +/* + * PROJECT: Universal C++ RunTime (UCXXRT) + * FILE: new_km.cpp + * DATA: 2022/06/17 + * + * PURPOSE: Universal C++ RunTime + * + * LICENSE: Relicensed under The MIT License from The CC BY 4.0 License + * + * DEVELOPER: MiroKaku (miro.kaku AT Outlook.com) + */ + +#include +#include +#include + +// Enable the compiler to elide null checks during LTCG +#pragma comment(linker, "/ThrowingNew") + +//////////////////////////////////// +// new() Fallback Ordering +// +// +----------+ +// |new_scalar<---------------+ +// +----^-----+ | +// | | +// +----+-------------+ +----+----+ +// |new_scalar_nothrow| |new_array| +// +------------------+ +----^----+ +// | +// +------------+----+ +// |new_array_nothrow| +// +-----------------+ + +_CRT_SECURITYCRITICAL_ATTRIBUTE +void* __CRTDECL operator new(size_t const size, int pool_type, unsigned long tag) +{ + for (;;) + { + if (void* const block = kmalloc(size, pool_type, tag)) + { + return block; + } + + if (_callnewh(size) == 0) + { + if (size == SIZE_MAX) + { + __scrt_throw_std_bad_array_new_length(); + } + else + { + __scrt_throw_std_bad_alloc(); + } + } + + // The new handler was successful; try to allocate again... + } +} + +void* __CRTDECL operator new[](size_t const size, int pool_type, unsigned long tag) +{ + return operator new(size, pool_type, tag); +} diff --git a/src/ucrt/heap/calloc_km.cpp b/src/ucrt/heap/calloc_km.cpp new file mode 100644 index 0000000..1595811 --- /dev/null +++ b/src/ucrt/heap/calloc_km.cpp @@ -0,0 +1,106 @@ +/* + * PROJECT: Universal C++ RunTime (UCXXRT) + * FILE: calloc_km.cpp + * DATA: 2022/06/17 + * + * PURPOSE: Universal C++ RunTime + * + * LICENSE: Relicensed under The MIT License from The CC BY 4.0 License + * + * DEVELOPER: MiroKaku (miro.kaku AT Outlook.com) + */ + +#include +#include +#include + + +// This function implements the logic of calloc(). +// +// This function must be marked noinline, otherwise calloc and +// _calloc_base will have identical COMDATs, and the linker will fold +// them when calling one from the CRT. This is necessary because calloc +// needs to support users patching in custom implementations. +extern "C" __declspec(noinline) _CRTRESTRICT void* __cdecl _kcalloc_base( + size_t const count, + size_t const size, + int pool_type, + unsigned long tag +) +{ + // Ensure that (count * size) does not overflow + _VALIDATE_RETURN_NOEXC(count == 0 || (_HEAP_MAXREQ / count) >= size, ENOMEM, nullptr); + + // Ensure that we allocate a nonzero block size: + size_t const requested_block_size = count * size; + size_t const actual_block_size = requested_block_size == 0 + ? 1 + : requested_block_size; + + for (;;) + { + void* const block = kmalloc(actual_block_size, pool_type, tag); + + // If allocation succeeded, return the pointer to the new block: + if (block) + { + memset(block, 0, actual_block_size); + return block; + } + + // Otherwise, see if we need to call the new handler, and if so call it. + // If the new handler fails, just return nullptr: + if (_query_new_mode() == 0 || !_callnewh(actual_block_size)) + { + errno = ENOMEM; + return nullptr; + } + + // The new handler was successful; try to allocate aagain... + } +} + + +// This function must be marked noinline, otherwise calloc and +// _calloc_dbg will have identical COMDATs, and the linker will fold +// them when calling one from the CRT. This is necessary because calloc +// needs to support users patching in custom implementations. +extern "C" __declspec(noinline) void* __cdecl _kcalloc_dbg( + size_t const count, + size_t const element_size, + int pool_type, + unsigned long tag, + int const block_use, + char const* const file_name, + int const line_number +) +{ + UNREFERENCED_PARAMETER(block_use); + UNREFERENCED_PARAMETER(file_name); + UNREFERENCED_PARAMETER(line_number); + + return _kcalloc_base(count, element_size, pool_type, tag); +} + + +// Allocates a block of memory of size 'count * size' in the heap. The newly +// allocated block is zero-initialized. If allocation fails, nullptr is +// returned. +// +// This function supports patching and therefore must be marked noinline. +// Both _calloc_dbg and _calloc_base must also be marked noinline +// to prevent identical COMDAT folding from substituting calls to calloc +// with either other function or vice versa. +extern "C" _CRT_HYBRIDPATCHABLE __declspec(noinline) _CRTRESTRICT void* __cdecl kcalloc( + size_t const count, + size_t const size, + int pool_type, + unsigned long tag + ) +{ +#ifdef _DEBUG + return _kcalloc_dbg(count, size, pool_type, tag, _NORMAL_BLOCK, nullptr, 0); +#else + return _kcalloc_base(count, size, pool_type, tag); +#endif +} diff --git a/src/ucrt/heap/free_km.cpp b/src/ucrt/heap/free_km.cpp new file mode 100644 index 0000000..6a66373 --- /dev/null +++ b/src/ucrt/heap/free_km.cpp @@ -0,0 +1,62 @@ +/* + * PROJECT: Universal C++ RunTime (UCXXRT) + * FILE: free_km.cpp + * DATA: 2022/06/17 + * + * PURPOSE: Universal C++ RunTime + * + * LICENSE: Relicensed under The MIT License from The CC BY 4.0 License + * + * DEVELOPER: MiroKaku (miro.kaku AT Outlook.com) + */ + +#include +#include + + +// This function implements the logic of kfree(). It is called directly by the +// kfree() function in the Release CRT, and it is called by the debug heap in the +// Debug CRT. +// +// This function must be marked noinline, otherwise free and +// _kfree_base will have identical COMDATs, and the linker will fold +// them when calling one from the CRT. This is necessary because free +// needs to support users patching in custom implementations. +extern "C" void __declspec(noinline) __cdecl _kfree_base(void* const block, unsigned long tag) +{ + if (block) + { + ExFreePoolWithTag(block, tag); + } +} + + +#ifdef _DEBUG +// This function must be marked noinline, otherwise kfree and +// _kfree_dbg will have identical COMDATs, and the linker will fold +// them when calling one from the CRT. This is necessary because free +// needs to support users patching in custom implementations. +extern "C" __declspec(noinline) void __cdecl _kfree_dbg(void* const block, unsigned long tag, int const block_use) +{ + UNREFERENCED_PARAMETER(block_use); + + return _kfree_base(block, tag); +} +#endif + + +// Frees a block in the heap. The 'block' pointer must either be a null pointer +// or must point to a valid block in the heap. +// +// This function supports patching and therefore must be marked noinline. +// Both _kfree_dbg and _kfree_base must also be marked noinline +// to prevent identical COMDAT folding from substituting calls to free +// with either other function or vice versa. +extern "C" _CRT_HYBRIDPATCHABLE __declspec(noinline) void __cdecl kfree(void* const block, unsigned long tag) +{ +#ifdef _DEBUG + _kfree_dbg(block, tag, _NORMAL_BLOCK); +#else + _kfree_base(block, tag); +#endif +} diff --git a/src/ucrt/heap/malloc_km.cpp b/src/ucrt/heap/malloc_km.cpp new file mode 100644 index 0000000..645288e --- /dev/null +++ b/src/ucrt/heap/malloc_km.cpp @@ -0,0 +1,102 @@ +/* + * PROJECT: Universal C++ RunTime (UCXXRT) + * FILE: malloc_km.cpp + * DATA: 2022/06/17 + * + * PURPOSE: Universal C++ RunTime + * + * LICENSE: Relicensed under The MIT License from The CC BY 4.0 License + * + * DEVELOPER: MiroKaku (miro.kaku AT Outlook.com) + */ + +#include +#include +#include + + +// This function implements the logic of kmalloc(). It is called directly by the +// kmalloc() function in the Release CRT and is called by the debug heap in the +// Debug CRT. +// +// This function must be marked noinline, otherwise malloc and +// _kmalloc_base will have identical COMDATs, and the linker will fold +// them when calling one from the CRT. This is necessary because malloc +// needs to support users patching in custom implementations. +extern "C" __declspec(noinline) _CRTRESTRICT void* __cdecl _kmalloc_base(size_t const size, int pool_type, unsigned long tag) +{ + // Ensure that the requested size is not too large: + _VALIDATE_RETURN_NOEXC(_HEAP_MAXREQ >= size, ENOMEM, nullptr); + + // Ensure we request an allocation of at least one byte: + size_t const actual_size = size == 0 ? 1 : size; + + for (;;) + { +#pragma warning(suppress: 4996) + void* const block = ExAllocatePoolWithTag((POOL_TYPE)pool_type, actual_size, tag); + if (block) + return block; + + // Otherwise, see if we need to call the new handler, and if so call it. + // If the new handler fails, just return nullptr: + if (_query_new_mode() == 0 || !_callnewh(actual_size)) + { + errno = ENOMEM; + return nullptr; + } + + // The new handler was successful; try to allocate again... + } +} + +#ifdef _DEBUG +// These are the allocation functions that allocate, manipulate, and free blocks +// from the debug heap. They are equivalent to the main allocation functions, +// which deal with blocks from the process heap. Most of the debug allocation +// functions accept a block use, file name, and/or line number which are used to +// track where allocations originated. +// +// Documentation comments for these functions describe only the material +// differences between them and the corresponding main allocation functions. + +// This function must be marked noinline, otherwise kmalloc and +// _kmalloc_dbg will have identical COMDATs, and the linker will fold +// them when calling one from the CRT. This is necessary because malloc +// needs to support users patching in custom implementations. +extern "C" __declspec(noinline) void* __cdecl _kmalloc_dbg( + size_t const size, + int pool_type, + unsigned long tag, + int const block_use, + char const* const file_name, + int const line_number +) +{ + UNREFERENCED_PARAMETER(block_use); + UNREFERENCED_PARAMETER(file_name); + UNREFERENCED_PARAMETER(line_number); + + return _kmalloc_base(size, pool_type, tag); +} +#endif + +// Allocates a block of memory of size 'size' bytes in the heap. If allocation +// fails, nullptr is returned. +// +// This function supports patching and therefore must be marked noinline. +// Both _kmalloc_dbg and _kmalloc_base must also be marked noinline +// to prevent identical COMDAT folding from substituting calls to malloc +// with either other function or vice versa. +extern "C" _CRT_HYBRIDPATCHABLE __declspec(noinline) _CRTRESTRICT void* __cdecl kmalloc( + size_t const size, + int pool_type, + unsigned long tag +) +{ +#ifdef _DEBUG + return _kmalloc_dbg(size, pool_type, tag, _NORMAL_BLOCK, nullptr, 0); +#else + return _kmalloc_base(size, pool_type, tag); +#endif +} diff --git a/src/ucxxrt.h b/src/ucxxrt.h index 1cf3fd6..794a251 100644 --- a/src/ucxxrt.h +++ b/src/ucxxrt.h @@ -48,8 +48,8 @@ #endif #include -#include -#include +#include +#include #include