Chromium Code Reviews| 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..626b7f2b8505f069545bd41068e41465e91cda6a 100644 |
| --- a/base/allocator/allocator_shim_win.cc |
| +++ b/base/allocator/allocator_shim_win.cc |
| @@ -11,32 +11,42 @@ |
| // 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; |
| +HANDLE win_heap; |
|
cpu_(ooo_6.6-7.5)
2015/01/22 21:11:40
: (
|
| +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 +54,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 +70,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 +95,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 +173,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 +202,56 @@ 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); |
| } |
| -// 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); |
| - |
| -// 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) |
| +// 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; |
| - void* ptr; |
| - for (;;) { |
| - ptr = win_heap_memalign(alignment, size); |
| - |
| - if (ptr) { |
| - return ptr; |
| - } |
| - |
| - if (!new_mode || !call_new_handler(true, size)) |
| - break; |
| + void* result = malloc(size); |
| + if (result != NULL) { |
| + memset(result, 0, size); |
| } |
| - return ptr; |
| + return result; |
| } |
| -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); |
| +// 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); |
| } |
| -#include "generic_allocators.cc" |
| +// calloc_impl.c |
| +void* _calloc_impl(size_t n, size_t size) { |
| + return calloc(n, size); |
| +} |
| } // extern C |