OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <malloc.h> | 5 #include <malloc.h> |
6 #include <new.h> | 6 #include <new.h> |
7 #include <windows.h> | 7 #include <windows.h> |
8 | 8 |
9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
10 | 10 |
11 // This shim make it possible to perform additional checks on allocations | 11 // This shim make it possible to perform additional checks on allocations |
12 // before passing them to the Heap functions. | 12 // before passing them to the Heap functions. |
13 | 13 |
14 // new_mode behaves similarly to MSVC's _set_new_mode. | 14 // Heap functions are stripped from libcmt.lib using the prep_libc.py |
15 // If flag is 0 (default), calls to malloc will behave normally. | 15 // for each object file stripped, we re-implement them here to allow us to |
16 // If flag is 1, calls to malloc will behave like calls to new, | 16 // perform additional checks: |
17 // and the std_new_handler will be invoked on failure. | 17 // 1. Enforcing the maximum size that can be allocated to 2Gb. |
18 // Can be set by calling _set_new_mode(). | 18 // 2. Calling new_handler if malloc fails. |
19 static int new_mode = 0; | 19 |
| 20 extern "C" { |
| 21 // We set this to 1 because part of the CRT uses a check of _crtheap != 0 |
| 22 // to test whether the CRT has been initialized. Once we've ripped out |
| 23 // the allocators from libcmt, we need to provide this definition so that |
| 24 // the rest of the CRT is still usable. |
| 25 // heapinit.c |
| 26 void* _crtheap = reinterpret_cast<void*>(1); |
| 27 } |
20 | 28 |
21 namespace { | 29 namespace { |
22 | 30 |
23 // This is a simple allocator based on the windows heap. | |
24 const size_t kWindowsPageSize = 4096; | 31 const size_t kWindowsPageSize = 4096; |
25 const size_t kMaxWindowsAllocation = INT_MAX - kWindowsPageSize; | 32 const size_t kMaxWindowsAllocation = INT_MAX - kWindowsPageSize; |
26 static HANDLE win_heap; | 33 int new_mode = 0; |
27 | 34 |
28 // VS2013 crt uses the process heap as its heap, so we do the same here. | 35 // VS2013 crt uses the process heap as its heap, so we do the same here. |
29 // See heapinit.c in VS CRT sources. | 36 // See heapinit.c in VS CRT sources. |
30 bool win_heap_init() { | 37 bool win_heap_init() { |
31 win_heap = GetProcessHeap(); | 38 // Set the _crtheap global here. THis allows us to offload most of the |
32 if (win_heap == NULL) | 39 // memory management to the CRT, except the functions we need to shim. |
| 40 _crtheap = GetProcessHeap(); |
| 41 if (_crtheap == NULL) |
33 return false; | 42 return false; |
34 | 43 |
35 ULONG enable_lfh = 2; | 44 ULONG enable_lfh = 2; |
36 // NOTE: Setting LFH may fail. Vista already has it enabled. | 45 // NOTE: Setting LFH may fail. Vista already has it enabled. |
37 // And under the debugger, it won't use LFH. So we | 46 // And under the debugger, it won't use LFH. So we |
38 // ignore any errors. | 47 // ignore any errors. |
39 HeapSetInformation(win_heap, HeapCompatibilityInformation, &enable_lfh, | 48 HeapSetInformation(_crtheap, HeapCompatibilityInformation, &enable_lfh, |
40 sizeof(enable_lfh)); | 49 sizeof(enable_lfh)); |
41 | 50 |
42 return true; | 51 return true; |
43 } | 52 } |
44 | 53 |
45 void* win_heap_malloc(size_t size) { | 54 void* win_heap_malloc(size_t size) { |
46 if (size < kMaxWindowsAllocation) | 55 if (size < kMaxWindowsAllocation) |
47 return HeapAlloc(win_heap, 0, size); | 56 return HeapAlloc(_crtheap, 0, size); |
48 return NULL; | 57 return NULL; |
49 } | 58 } |
50 | 59 |
51 void win_heap_free(void* size) { | 60 void win_heap_free(void* size) { |
52 HeapFree(win_heap, 0, size); | 61 HeapFree(_crtheap, 0, size); |
53 } | 62 } |
54 | 63 |
55 void* win_heap_realloc(void* ptr, size_t size) { | 64 void* win_heap_realloc(void* ptr, size_t size) { |
56 if (!ptr) | 65 if (!ptr) |
57 return win_heap_malloc(size); | 66 return win_heap_malloc(size); |
58 if (!size) { | 67 if (!size) { |
59 win_heap_free(ptr); | 68 win_heap_free(ptr); |
60 return NULL; | 69 return NULL; |
61 } | 70 } |
62 if (size < kMaxWindowsAllocation) | 71 if (size < kMaxWindowsAllocation) |
63 return HeapReAlloc(win_heap, 0, ptr, size); | 72 return HeapReAlloc(_crtheap, 0, ptr, size); |
64 return NULL; | 73 return NULL; |
65 } | 74 } |
66 | 75 |
67 size_t win_heap_msize(void* ptr) { | 76 void win_heap_term() { |
68 return HeapSize(win_heap, 0, ptr); | 77 _crtheap = NULL; |
69 } | 78 } |
70 | 79 |
71 void* win_heap_memalign(size_t alignment, size_t size) { | |
72 // Reserve enough space to ensure we can align and set aligned_ptr[-1] to the | |
73 // original allocation for use with win_heap_memalign_free() later. | |
74 size_t allocation_size = size + (alignment - 1) + sizeof(void*); | |
75 | |
76 // Check for overflow. Alignment and size are checked in allocator_shim. | |
77 if (size >= allocation_size || alignment >= allocation_size) { | |
78 return NULL; | |
79 } | |
80 | |
81 // Since we're directly calling the allocator function, before OOM handling, | |
82 // we need to NULL check to ensure the allocation succeeded. | |
83 void* ptr = win_heap_malloc(allocation_size); | |
84 if (!ptr) | |
85 return ptr; | |
86 | |
87 char* aligned_ptr = static_cast<char*>(ptr) + sizeof(void*); | |
88 aligned_ptr += | |
89 alignment - reinterpret_cast<uintptr_t>(aligned_ptr) & (alignment - 1); | |
90 | |
91 reinterpret_cast<void**>(aligned_ptr)[-1] = ptr; | |
92 return aligned_ptr; | |
93 } | |
94 | |
95 void win_heap_memalign_free(void* ptr) { | |
96 if (ptr) | |
97 win_heap_free(static_cast<void**>(ptr)[-1]); | |
98 } | |
99 | |
100 void win_heap_term() { | |
101 win_heap = NULL; | |
102 } | |
103 | |
104 } // namespace | |
105 | |
106 // Call the new handler, if one has been set. | 80 // Call the new handler, if one has been set. |
107 // Returns true on successfully calling the handler, false otherwise. | 81 // Returns true on successfully calling the handler, false otherwise. |
108 inline bool call_new_handler(bool nothrow, size_t size) { | 82 inline bool call_new_handler(bool nothrow, size_t size) { |
109 // Get the current new handler. | 83 // Get the current new handler. |
110 _PNH nh = _query_new_handler(); | 84 _PNH nh = _query_new_handler(); |
111 #if defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS | 85 #if defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS |
112 if (!nh) | 86 if (!nh) |
113 return false; | 87 return false; |
114 // Since exceptions are disabled, we don't really know if new_handler | 88 // Since exceptions are disabled, we don't really know if new_handler |
115 // failed. Assume it will abort if it fails. | 89 // failed. Assume it will abort if it fails. |
116 return nh(size); | 90 return nh(size); |
117 #else | 91 #else |
118 #error "Exceptions in allocator shim are not supported!" | 92 #error "Exceptions in allocator shim are not supported!" |
119 #endif // defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS | 93 #endif // defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS |
120 return false; | 94 return false; |
121 } | 95 } |
122 | 96 |
| 97 // Implement a C++ style allocation, which always calls the new_handler |
| 98 // on failure. |
| 99 inline void* generic_cpp_alloc(size_t size, bool nothrow) { |
| 100 void* ptr; |
| 101 for (;;) { |
| 102 ptr = malloc(size); |
| 103 if (ptr) |
| 104 return ptr; |
| 105 if (!call_new_handler(nothrow, size)) |
| 106 break; |
| 107 } |
| 108 return ptr; |
| 109 } |
| 110 |
| 111 } // namespace |
| 112 |
| 113 // new.cpp |
| 114 void* operator new(size_t size) { |
| 115 return generic_cpp_alloc(size, false); |
| 116 } |
| 117 |
| 118 // delete.cpp |
| 119 void operator delete(void* p) throw() { |
| 120 free(p); |
| 121 } |
| 122 |
| 123 // new2.cpp |
| 124 void* operator new[](size_t size) { |
| 125 return generic_cpp_alloc(size, false); |
| 126 } |
| 127 |
| 128 // delete2.cpp |
| 129 void operator delete[](void* p) throw() { |
| 130 free(p); |
| 131 } |
| 132 |
| 133 // newopnt.cpp |
| 134 void* operator new(size_t size, const std::nothrow_t& nt) { |
| 135 return generic_cpp_alloc(size, true); |
| 136 } |
| 137 |
| 138 // newaopnt.cpp |
| 139 void* operator new[](size_t size, const std::nothrow_t& nt) { |
| 140 return generic_cpp_alloc(size, true); |
| 141 } |
| 142 |
| 143 // This function behaves similarly to MSVC's _set_new_mode. |
| 144 // If flag is 0 (default), calls to malloc will behave normally. |
| 145 // If flag is 1, calls to malloc will behave like calls to new, |
| 146 // and the std_new_handler will be invoked on failure. |
| 147 // Returns the previous mode. |
| 148 // new_mode.cpp |
| 149 int _set_new_mode(int flag) throw() { |
| 150 int old_mode = new_mode; |
| 151 new_mode = flag; |
| 152 return old_mode; |
| 153 } |
| 154 |
| 155 // new_mode.cpp |
| 156 int _query_new_mode() { |
| 157 return new_mode; |
| 158 } |
| 159 |
123 extern "C" { | 160 extern "C" { |
124 | 161 // malloc.c |
125 void* malloc(size_t size) { | 162 void* malloc(size_t size) { |
126 void* ptr; | 163 void* ptr; |
127 for (;;) { | 164 for (;;) { |
128 ptr = win_heap_malloc(size); | 165 ptr = win_heap_malloc(size); |
129 if (ptr) | 166 if (ptr) |
130 return ptr; | 167 return ptr; |
131 | 168 |
132 if (!new_mode || !call_new_handler(true, size)) | 169 if (!new_mode || !call_new_handler(true, size)) |
133 break; | 170 break; |
134 } | 171 } |
135 return ptr; | 172 return ptr; |
136 } | 173 } |
137 | 174 |
| 175 // free.c |
138 void free(void* p) { | 176 void free(void* p) { |
139 win_heap_free(p); | 177 win_heap_free(p); |
140 return; | 178 return; |
141 } | 179 } |
142 | 180 |
| 181 // realloc.c |
143 void* realloc(void* ptr, size_t size) { | 182 void* realloc(void* ptr, size_t size) { |
144 // Webkit is brittle for allocators that return NULL for malloc(0). The | 183 // Webkit is brittle for allocators that return NULL for malloc(0). The |
145 // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure | 184 // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure |
146 // to call malloc for this case. | 185 // to call malloc for this case. |
147 if (!ptr) | 186 if (!ptr) |
148 return malloc(size); | 187 return malloc(size); |
149 | 188 |
150 void* new_ptr; | 189 void* new_ptr; |
151 for (;;) { | 190 for (;;) { |
152 new_ptr = win_heap_realloc(ptr, size); | 191 new_ptr = win_heap_realloc(ptr, size); |
153 | 192 |
154 // Subtle warning: NULL return does not alwas indicate out-of-memory. If | 193 // Subtle warning: NULL return does not alwas indicate out-of-memory. If |
155 // the requested new size is zero, realloc should free the ptr and return | 194 // the requested new size is zero, realloc should free the ptr and return |
156 // NULL. | 195 // NULL. |
157 if (new_ptr || !size) | 196 if (new_ptr || !size) |
158 return new_ptr; | 197 return new_ptr; |
159 if (!new_mode || !call_new_handler(true, size)) | 198 if (!new_mode || !call_new_handler(true, size)) |
160 break; | 199 break; |
161 } | 200 } |
162 return new_ptr; | 201 return new_ptr; |
163 } | 202 } |
164 | 203 |
165 | 204 // heapinit.c |
166 size_t _msize(void* p) { | 205 intptr_t _get_heap_handle() { |
167 return win_heap_msize(p); | 206 return reinterpret_cast<intptr_t>(_crtheap); |
168 } | 207 } |
169 | 208 |
170 intptr_t _get_heap_handle() { | 209 // heapinit.c |
171 return reinterpret_cast<intptr_t>(win_heap); | |
172 } | |
173 | |
174 // The CRT heap initialization stub. | |
175 int _heap_init() { | 210 int _heap_init() { |
176 return win_heap_init() ? 1 : 0; | 211 return win_heap_init() ? 1 : 0; |
177 } | 212 } |
178 | 213 |
179 // The CRT heap cleanup stub. | 214 // heapinit.c |
180 void _heap_term() { | 215 void _heap_term() { |
181 win_heap_term(); | 216 win_heap_term(); |
182 } | 217 } |
183 | 218 |
184 // We set this to 1 because part of the CRT uses a check of _crtheap != 0 | 219 // calloc.c |
185 // to test whether the CRT has been initialized. Once we've ripped out | 220 void* calloc(size_t n, size_t elem_size) { |
186 // the allocators from libcmt, we need to provide this definition so that | 221 // Overflow check. |
187 // the rest of the CRT is still usable. | 222 const size_t size = n * elem_size; |
188 void* _crtheap = reinterpret_cast<void*>(1); | 223 if (elem_size != 0 && size / elem_size != n) |
189 | |
190 // Provide support for aligned memory through Windows only _aligned_malloc(). | |
191 void* _aligned_malloc(size_t size, size_t alignment) { | |
192 // _aligned_malloc guarantees parameter validation, so do so here. These | |
193 // checks are somewhat stricter than _aligned_malloc() since we're effectively | |
194 // using memalign() under the hood. | |
195 if (size == 0U || (alignment & (alignment - 1)) != 0U || | |
196 (alignment % sizeof(void*)) != 0U) | |
197 return NULL; | 224 return NULL; |
198 | 225 |
199 void* ptr; | 226 void* result = malloc(size); |
200 for (;;) { | 227 if (result != NULL) { |
201 ptr = win_heap_memalign(alignment, size); | 228 memset(result, 0, size); |
202 | |
203 if (ptr) { | |
204 return ptr; | |
205 } | |
206 | |
207 if (!new_mode || !call_new_handler(true, size)) | |
208 break; | |
209 } | 229 } |
210 return ptr; | 230 return result; |
211 } | 231 } |
212 | 232 |
213 void _aligned_free(void* p) { | 233 // recalloc.c |
214 // Pointers allocated with win_heap_memalign() MUST be freed via | 234 void* _recalloc(void* p, size_t n, size_t elem_size) { |
215 // win_heap_memalign_free() since the aligned pointer is not the real one. | 235 if (!p) |
216 win_heap_memalign_free(p); | 236 return calloc(n, elem_size); |
| 237 |
| 238 // This API is a bit odd. |
| 239 // Note: recalloc only guarantees zeroed memory when p is NULL. |
| 240 // Generally, calls to malloc() have padding. So a request |
| 241 // to malloc N bytes actually malloc's N+x bytes. Later, if |
| 242 // that buffer is passed to recalloc, we don't know what N |
| 243 // was anymore. We only know what N+x is. As such, there is |
| 244 // no way to know what to zero out. |
| 245 const size_t size = n * elem_size; |
| 246 if (elem_size != 0 && size / elem_size != n) |
| 247 return NULL; |
| 248 return realloc(p, size); |
217 } | 249 } |
218 | 250 |
219 #include "generic_allocators.cc" | 251 // calloc_impl.c |
| 252 void* _calloc_impl(size_t n, size_t size) { |
| 253 return calloc(n, size); |
| 254 } |
| 255 |
| 256 #ifndef NDEBUG |
| 257 #undef malloc |
| 258 #undef free |
| 259 #undef calloc |
| 260 |
| 261 static int error_handler(int reportType) { |
| 262 switch (reportType) { |
| 263 case 0: // _CRT_WARN |
| 264 __debugbreak(); |
| 265 return 0; |
| 266 |
| 267 case 1: // _CRT_ERROR |
| 268 __debugbreak(); |
| 269 return 0; |
| 270 |
| 271 case 2: // _CRT_ASSERT |
| 272 __debugbreak(); |
| 273 return 0; |
| 274 } |
| 275 char* p = NULL; |
| 276 *p = '\0'; |
| 277 return 0; |
| 278 } |
| 279 |
| 280 int _CrtDbgReport(int reportType, |
| 281 const char*, |
| 282 int, |
| 283 const char*, |
| 284 const char*, |
| 285 ...) { |
| 286 return error_handler(reportType); |
| 287 } |
| 288 |
| 289 int _CrtDbgReportW(int reportType, |
| 290 const wchar_t*, |
| 291 int, |
| 292 const wchar_t*, |
| 293 const wchar_t*, |
| 294 ...) { |
| 295 return error_handler(reportType); |
| 296 } |
| 297 |
| 298 int _CrtSetReportMode(int, int) { |
| 299 return 0; |
| 300 } |
| 301 |
| 302 void* _malloc_dbg(size_t size, int, const char*, int) { |
| 303 return malloc(size); |
| 304 } |
| 305 |
| 306 void* _realloc_dbg(void* ptr, size_t size, int, const char*, int) { |
| 307 return realloc(ptr, size); |
| 308 } |
| 309 |
| 310 void _free_dbg(void* ptr, int) { |
| 311 free(ptr); |
| 312 } |
| 313 |
| 314 void* _calloc_dbg(size_t n, size_t size, int, const char*, int) { |
| 315 return calloc(n, size); |
| 316 } |
| 317 #endif // NDEBUG |
220 | 318 |
221 } // extern C | 319 } // extern C |
OLD | NEW |