Index: base/allocator/allocator_shim_win.cc |
diff --git a/base/allocator/allocator_shim_win.cc b/base/allocator/allocator_shim_win.cc |
index 0ebaa6b54c7e20baa29d7a2fab7e0d35120e75e3..a1473e5fd29f104f5759ade6982ca40a72848e96 100644 |
--- a/base/allocator/allocator_shim_win.cc |
+++ b/base/allocator/allocator_shim_win.cc |
@@ -11,32 +11,41 @@ |
// This shim make it possible to perform additional checks on allocations |
// before passing them to the Heap functions. |
-// new_mode behaves similarly to MSVC's _set_new_mode. |
-// If flag is 0 (default), calls to malloc will behave normally. |
-// If flag is 1, calls to malloc will behave like calls to new, |
-// and the std_new_handler will be invoked on failure. |
-// Can be set by calling _set_new_mode(). |
-static int new_mode = 0; |
+// Heap functions are stripped from libcmt.lib using the prep_libc.py |
+// for each object file stripped, we re-implement them here to allow us to |
+// perform additional checks: |
+// 1. Enforcing the maximum size that can be allocated to 2Gb. |
+// 2. Calling new_handler if malloc fails. |
+ |
+extern "C" { |
+// We set this to 1 because part of the CRT uses a check of _crtheap != 0 |
+// to test whether the CRT has been initialized. Once we've ripped out |
+// the allocators from libcmt, we need to provide this definition so that |
+// the rest of the CRT is still usable. |
+// heapinit.c |
+void* _crtheap = reinterpret_cast<void*>(1); |
+} |
namespace { |
-// This is a simple allocator based on the windows heap. |
const size_t kWindowsPageSize = 4096; |
const size_t kMaxWindowsAllocation = INT_MAX - kWindowsPageSize; |
-static HANDLE win_heap; |
+int new_mode = 0; |
// VS2013 crt uses the process heap as its heap, so we do the same here. |
// See heapinit.c in VS CRT sources. |
bool win_heap_init() { |
- win_heap = GetProcessHeap(); |
- if (win_heap == NULL) |
+ // Set the _crtheap global here. THis allows us to offload most of the |
+ // memory management to the CRT, except the functions we need to shim. |
+ _crtheap = GetProcessHeap(); |
+ if (_crtheap == NULL) |
return false; |
ULONG enable_lfh = 2; |
// NOTE: Setting LFH may fail. Vista already has it enabled. |
// And under the debugger, it won't use LFH. So we |
// ignore any errors. |
- HeapSetInformation(win_heap, HeapCompatibilityInformation, &enable_lfh, |
+ HeapSetInformation(_crtheap, HeapCompatibilityInformation, &enable_lfh, |
sizeof(enable_lfh)); |
return true; |
@@ -44,12 +53,12 @@ bool win_heap_init() { |
void* win_heap_malloc(size_t size) { |
if (size < kMaxWindowsAllocation) |
- return HeapAlloc(win_heap, 0, size); |
+ return HeapAlloc(_crtheap, 0, size); |
return NULL; |
} |
void win_heap_free(void* size) { |
- HeapFree(win_heap, 0, size); |
+ HeapFree(_crtheap, 0, size); |
} |
void* win_heap_realloc(void* ptr, size_t size) { |
@@ -60,49 +69,14 @@ void* win_heap_realloc(void* ptr, size_t size) { |
return NULL; |
} |
if (size < kMaxWindowsAllocation) |
- return HeapReAlloc(win_heap, 0, ptr, size); |
+ return HeapReAlloc(_crtheap, 0, ptr, size); |
return NULL; |
} |
-size_t win_heap_msize(void* ptr) { |
- return HeapSize(win_heap, 0, ptr); |
-} |
- |
-void* win_heap_memalign(size_t alignment, size_t size) { |
- // Reserve enough space to ensure we can align and set aligned_ptr[-1] to the |
- // original allocation for use with win_heap_memalign_free() later. |
- size_t allocation_size = size + (alignment - 1) + sizeof(void*); |
- |
- // Check for overflow. Alignment and size are checked in allocator_shim. |
- if (size >= allocation_size || alignment >= allocation_size) { |
- return NULL; |
- } |
- |
- // Since we're directly calling the allocator function, before OOM handling, |
- // we need to NULL check to ensure the allocation succeeded. |
- void* ptr = win_heap_malloc(allocation_size); |
- if (!ptr) |
- return ptr; |
- |
- char* aligned_ptr = static_cast<char*>(ptr) + sizeof(void*); |
- aligned_ptr += |
- alignment - reinterpret_cast<uintptr_t>(aligned_ptr) & (alignment - 1); |
- |
- reinterpret_cast<void**>(aligned_ptr)[-1] = ptr; |
- return aligned_ptr; |
-} |
- |
-void win_heap_memalign_free(void* ptr) { |
- if (ptr) |
- win_heap_free(static_cast<void**>(ptr)[-1]); |
-} |
- |
void win_heap_term() { |
- win_heap = NULL; |
+ _crtheap = NULL; |
} |
-} // namespace |
- |
// Call the new handler, if one has been set. |
// Returns true on successfully calling the handler, false otherwise. |
inline bool call_new_handler(bool nothrow, size_t size) { |
@@ -120,8 +94,71 @@ inline bool call_new_handler(bool nothrow, size_t size) { |
return false; |
} |
-extern "C" { |
+// Implement a C++ style allocation, which always calls the new_handler |
+// on failure. |
+inline void* generic_cpp_alloc(size_t size, bool nothrow) { |
+ void* ptr; |
+ for (;;) { |
+ ptr = malloc(size); |
+ if (ptr) |
+ return ptr; |
+ if (!call_new_handler(nothrow, size)) |
+ break; |
+ } |
+ return ptr; |
+} |
+ |
+} // namespace |
+ |
+// new.cpp |
+void* operator new(size_t size) { |
+ return generic_cpp_alloc(size, false); |
+} |
+ |
+// delete.cpp |
+void operator delete(void* p) throw() { |
+ free(p); |
+} |
+ |
+// new2.cpp |
+void* operator new[](size_t size) { |
+ return generic_cpp_alloc(size, false); |
+} |
+ |
+// delete2.cpp |
+void operator delete[](void* p) throw() { |
+ free(p); |
+} |
+// newopnt.cpp |
+void* operator new(size_t size, const std::nothrow_t& nt) { |
+ return generic_cpp_alloc(size, true); |
+} |
+ |
+// newaopnt.cpp |
+void* operator new[](size_t size, const std::nothrow_t& nt) { |
+ return generic_cpp_alloc(size, true); |
+} |
+ |
+// This function behaves similarly to MSVC's _set_new_mode. |
+// If flag is 0 (default), calls to malloc will behave normally. |
+// If flag is 1, calls to malloc will behave like calls to new, |
+// and the std_new_handler will be invoked on failure. |
+// Returns the previous mode. |
+// new_mode.cpp |
+int _set_new_mode(int flag) throw() { |
+ int old_mode = new_mode; |
+ new_mode = flag; |
+ return old_mode; |
+} |
+ |
+// new_mode.cpp |
+int _query_new_mode() { |
+ return new_mode; |
+} |
+ |
+extern "C" { |
+// malloc.c |
void* malloc(size_t size) { |
void* ptr; |
for (;;) { |
@@ -135,11 +172,13 @@ void* malloc(size_t size) { |
return ptr; |
} |
+// free.c |
void free(void* p) { |
win_heap_free(p); |
return; |
} |
+// realloc.c |
void* realloc(void* ptr, size_t size) { |
// Webkit is brittle for allocators that return NULL for malloc(0). The |
// realloc(0, 0) code path does not guarantee a non-NULL return, so be sure |
@@ -162,60 +201,119 @@ void* realloc(void* ptr, size_t size) { |
return new_ptr; |
} |
- |
-size_t _msize(void* p) { |
- return win_heap_msize(p); |
-} |
- |
+// heapinit.c |
intptr_t _get_heap_handle() { |
- return reinterpret_cast<intptr_t>(win_heap); |
+ return reinterpret_cast<intptr_t>(_crtheap); |
} |
-// The CRT heap initialization stub. |
+// heapinit.c |
int _heap_init() { |
return win_heap_init() ? 1 : 0; |
} |
-// The CRT heap cleanup stub. |
+// heapinit.c |
void _heap_term() { |
win_heap_term(); |
} |
-// We set this to 1 because part of the CRT uses a check of _crtheap != 0 |
-// to test whether the CRT has been initialized. Once we've ripped out |
-// the allocators from libcmt, we need to provide this definition so that |
-// the rest of the CRT is still usable. |
-void* _crtheap = reinterpret_cast<void*>(1); |
+// calloc.c |
+void* calloc(size_t n, size_t elem_size) { |
+ // Overflow check. |
+ const size_t size = n * elem_size; |
+ if (elem_size != 0 && size / elem_size != n) |
+ return NULL; |
-// Provide support for aligned memory through Windows only _aligned_malloc(). |
-void* _aligned_malloc(size_t size, size_t alignment) { |
- // _aligned_malloc guarantees parameter validation, so do so here. These |
- // checks are somewhat stricter than _aligned_malloc() since we're effectively |
- // using memalign() under the hood. |
- if (size == 0U || (alignment & (alignment - 1)) != 0U || |
- (alignment % sizeof(void*)) != 0U) |
+ void* result = malloc(size); |
+ if (result != NULL) { |
+ memset(result, 0, size); |
+ } |
+ return result; |
+} |
+ |
+// recalloc.c |
+void* _recalloc(void* p, size_t n, size_t elem_size) { |
+ if (!p) |
+ return calloc(n, elem_size); |
+ |
+ // This API is a bit odd. |
+ // Note: recalloc only guarantees zeroed memory when p is NULL. |
+ // Generally, calls to malloc() have padding. So a request |
+ // to malloc N bytes actually malloc's N+x bytes. Later, if |
+ // that buffer is passed to recalloc, we don't know what N |
+ // was anymore. We only know what N+x is. As such, there is |
+ // no way to know what to zero out. |
+ const size_t size = n * elem_size; |
+ if (elem_size != 0 && size / elem_size != n) |
return NULL; |
+ return realloc(p, size); |
+} |
- void* ptr; |
- for (;;) { |
- ptr = win_heap_memalign(alignment, size); |
+// calloc_impl.c |
+void* _calloc_impl(size_t n, size_t size) { |
+ return calloc(n, size); |
+} |
- if (ptr) { |
- return ptr; |
- } |
+#ifndef NDEBUG |
+#undef malloc |
+#undef free |
+#undef calloc |
- if (!new_mode || !call_new_handler(true, size)) |
- break; |
+static int error_handler(int reportType) { |
+ switch (reportType) { |
+ case 0: // _CRT_WARN |
+ __debugbreak(); |
+ return 0; |
+ |
+ case 1: // _CRT_ERROR |
+ __debugbreak(); |
+ return 0; |
+ |
+ case 2: // _CRT_ASSERT |
+ __debugbreak(); |
+ return 0; |
} |
- return ptr; |
+ char* p = NULL; |
+ *p = '\0'; |
+ return 0; |
} |
-void _aligned_free(void* p) { |
- // Pointers allocated with win_heap_memalign() MUST be freed via |
- // win_heap_memalign_free() since the aligned pointer is not the real one. |
- win_heap_memalign_free(p); |
+int _CrtDbgReport(int reportType, |
+ const char*, |
+ int, |
+ const char*, |
+ const char*, |
+ ...) { |
+ return error_handler(reportType); |
} |
-#include "generic_allocators.cc" |
+int _CrtDbgReportW(int reportType, |
+ const wchar_t*, |
+ int, |
+ const wchar_t*, |
+ const wchar_t*, |
+ ...) { |
+ return error_handler(reportType); |
+} |
+ |
+int _CrtSetReportMode(int, int) { |
+ return 0; |
+} |
+ |
+void* _malloc_dbg(size_t size, int, const char*, int) { |
+ return malloc(size); |
+} |
+ |
+void* _realloc_dbg(void* ptr, size_t size, int, const char*, int) { |
+ return realloc(ptr, size); |
+} |
+ |
+void _free_dbg(void* ptr, int) { |
+ free(ptr); |
+} |
+ |
+void* _calloc_dbg(size_t n, size_t size, int, const char*, int) { |
+ return calloc(n, size); |
+} |
+#endif // NDEBUG |
} // extern C |