xref: /oneTBB/src/tbbmalloc_proxy/proxy.cpp (revision 5d8ddb3f)
1 /*
2     Copyright (c) 2005-2021 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #if __unix__ && !__ANDROID__
18 // include <bits/c++config.h> indirectly so that <cstdlib> is not included
19 #include <cstddef>
20 // include <features.h> indirectly so that <stdlib.h> is not included
21 #include <unistd.h>
22 // Working around compiler issue with Anaconda's gcc 7.3 compiler package.
23 // New gcc ported for old libc may provide their inline implementation
24 // of aligned_alloc as required by new C++ standard, this makes it hard to
25 // redefine aligned_alloc here. However, running on systems with new libc
26 // version, it still needs it to be redefined, thus tricking system headers
27 #if defined(__GLIBC_PREREQ)
28 #if !__GLIBC_PREREQ(2, 16) && _GLIBCXX_HAVE_ALIGNED_ALLOC
29 // tell <cstdlib> that there is no aligned_alloc
30 #undef _GLIBCXX_HAVE_ALIGNED_ALLOC
31 // trick <stdlib.h> to define another symbol instead
32 #define aligned_alloc __hidden_redefined_aligned_alloc
33 // Fix the state and undefine the trick
34 #include <cstdlib>
35 #undef aligned_alloc
36 #endif // !__GLIBC_PREREQ(2, 16) && _GLIBCXX_HAVE_ALIGNED_ALLOC
37 #endif // defined(__GLIBC_PREREQ)
38 #include <cstdlib>
39 #endif // __unix__ && !__ANDROID__
40 
41 #include "proxy.h"
42 
43 #include "oneapi/tbb/detail/_config.h"
44 #include "oneapi/tbb/scalable_allocator.h"
45 #include "../tbb/environment.h"
46 
47 #if !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) && !defined(__SUNPRO_CC)
48     #if TBB_USE_EXCEPTIONS
49         #error Compilation settings do not support exception handling. Please do not set TBB_USE_EXCEPTIONS macro or set it to 0.
50     #elif !defined(TBB_USE_EXCEPTIONS)
51         #define TBB_USE_EXCEPTIONS 0
52     #endif
53 #elif !defined(TBB_USE_EXCEPTIONS)
54     #define TBB_USE_EXCEPTIONS 1
55 #endif
56 
57 #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED || _WIN32 && !__TBB_WIN8UI_SUPPORT
58 /*** internal global operator new implementation (Linux, Windows) ***/
59 #include <new>
60 
61 // Synchronization primitives to protect original library pointers and new_handler
62 #include "../tbbmalloc/Synchronize.h"
63 // Use MallocMutex implementation
64 typedef MallocMutex ProxyMutex;
65 
66 // Adds aliasing and copy attributes to function if available
67 #if defined(__has_attribute)
68     #if __has_attribute(__copy__)
69         #define __TBB_ALIAS_ATTR_COPY(name) __attribute__((alias (#name), __copy__(name)))
70     #endif
71 #endif
72 
73 #ifndef __TBB_ALIAS_ATTR_COPY
74     #define __TBB_ALIAS_ATTR_COPY(name) __attribute__((alias (#name)))
75 #endif
76 
77 // In case there is no std::get_new_handler function
78 // which provides synchronized access to std::new_handler
79 #if !__TBB_CPP11_GET_NEW_HANDLER_PRESENT
80 static ProxyMutex new_lock;
81 #endif
82 
83 static inline void* InternalOperatorNew(size_t sz) {
84     void* res = scalable_malloc(sz);
85 #if TBB_USE_EXCEPTIONS
86     while (!res) {
87         std::new_handler handler;
88 #if __TBB_CPP11_GET_NEW_HANDLER_PRESENT
89         handler = std::get_new_handler();
90 #else
91         {
92             ProxyMutex::scoped_lock lock(new_lock);
93             handler = std::set_new_handler(0);
94             std::set_new_handler(handler);
95         }
96 #endif
97         if (handler) {
98             (*handler)();
99         } else {
100             throw std::bad_alloc();
101         }
102         res = scalable_malloc(sz);
103 }
104 #endif /* TBB_USE_EXCEPTIONS */
105     return res;
106 }
107 /*** end of internal global operator new implementation ***/
108 #endif // MALLOC_UNIXLIKE_OVERLOAD_ENABLED || _WIN32 && !__TBB_WIN8UI_SUPPORT
109 
110 #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED
111 
112 #ifndef __THROW
113 #define __THROW
114 #endif
115 
116 /*** service functions and variables ***/
117 #include <string.h> // for memset
118 #include <unistd.h> // for sysconf
119 
120 static long memoryPageSize;
121 
122 static inline void initPageSize()
123 {
124     memoryPageSize = sysconf(_SC_PAGESIZE);
125 }
126 
127 #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED
128 #include <dlfcn.h>
129 #include <malloc.h>    // mallinfo
130 
131 /* __TBB_malloc_proxy used as a weak symbol by libtbbmalloc for:
132    1) detection that the proxy library is loaded
133    2) check that dlsym("malloc") found something different from our replacement malloc
134 */
135 
136 extern "C" void *__TBB_malloc_proxy(size_t) __TBB_ALIAS_ATTR_COPY(malloc);
137 
138 static void *orig_msize;
139 
140 #elif MALLOC_ZONE_OVERLOAD_ENABLED
141 
142 #include "proxy_overload_osx.h"
143 
144 #endif // MALLOC_ZONE_OVERLOAD_ENABLED
145 
146 // Original (i.e., replaced) functions,
147 // they are never changed for MALLOC_ZONE_OVERLOAD_ENABLED.
148 static void *orig_free,
149     *orig_realloc;
150 
151 #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED
152 #define ZONE_ARG
153 #define PREFIX(name) name
154 
155 static void *orig_libc_free,
156     *orig_libc_realloc;
157 
158 // We already tried to find ptr to original functions.
159 static std::atomic<bool> origFuncSearched{false};
160 
161 inline void InitOrigPointers()
162 {
163     // race is OK here, as different threads found same functions
164     if (!origFuncSearched.load(std::memory_order_acquire)) {
165         orig_free = dlsym(RTLD_NEXT, "free");
166         orig_realloc = dlsym(RTLD_NEXT, "realloc");
167         orig_msize = dlsym(RTLD_NEXT, "malloc_usable_size");
168         orig_libc_free = dlsym(RTLD_NEXT, "__libc_free");
169         orig_libc_realloc = dlsym(RTLD_NEXT, "__libc_realloc");
170 
171         origFuncSearched.store(true, std::memory_order_release);
172     }
173 }
174 
175 /*** replacements for malloc and the family ***/
176 extern "C" {
177 #elif MALLOC_ZONE_OVERLOAD_ENABLED
178 
179 // each impl_* function has such 1st argument, it's unused
180 #define ZONE_ARG struct _malloc_zone_t *,
181 #define PREFIX(name) impl_##name
182 // not interested in original functions for zone overload
183 inline void InitOrigPointers() {}
184 
185 #endif // MALLOC_UNIXLIKE_OVERLOAD_ENABLED and MALLOC_ZONE_OVERLOAD_ENABLED
186 
187 void *PREFIX(malloc)(ZONE_ARG size_t size) __THROW
188 {
189     return scalable_malloc(size);
190 }
191 
192 void *PREFIX(calloc)(ZONE_ARG size_t num, size_t size) __THROW
193 {
194     return scalable_calloc(num, size);
195 }
196 
197 void PREFIX(free)(ZONE_ARG void *object) __THROW
198 {
199     InitOrigPointers();
200     __TBB_malloc_safer_free(object, (void (*)(void*))orig_free);
201 }
202 
203 void *PREFIX(realloc)(ZONE_ARG void* ptr, size_t sz) __THROW
204 {
205     InitOrigPointers();
206     return __TBB_malloc_safer_realloc(ptr, sz, orig_realloc);
207 }
208 
209 /* The older *NIX interface for aligned allocations;
210    it's formally substituted by posix_memalign and deprecated,
211    so we do not expect it to cause cyclic dependency with C RTL. */
212 void *PREFIX(memalign)(ZONE_ARG size_t alignment, size_t size) __THROW
213 {
214     return scalable_aligned_malloc(size, alignment);
215 }
216 
217 /* valloc allocates memory aligned on a page boundary */
218 void *PREFIX(valloc)(ZONE_ARG size_t size) __THROW
219 {
220     if (! memoryPageSize) initPageSize();
221 
222     return scalable_aligned_malloc(size, memoryPageSize);
223 }
224 
225 #undef ZONE_ARG
226 #undef PREFIX
227 
228 #if MALLOC_UNIXLIKE_OVERLOAD_ENABLED
229 
230 // match prototype from system headers
231 #if __ANDROID__
232 size_t malloc_usable_size(const void *ptr) __THROW
233 #else
234 size_t malloc_usable_size(void *ptr) __THROW
235 #endif
236 {
237     InitOrigPointers();
238     return __TBB_malloc_safer_msize(const_cast<void*>(ptr), (size_t (*)(void*))orig_msize);
239 }
240 
241 int posix_memalign(void **memptr, size_t alignment, size_t size) __THROW
242 {
243     return scalable_posix_memalign(memptr, alignment, size);
244 }
245 
246 /* pvalloc allocates smallest set of complete pages which can hold
247    the requested number of bytes. Result is aligned on page boundary. */
248 void *pvalloc(size_t size) __THROW
249 {
250     if (! memoryPageSize) initPageSize();
251     // align size up to the page size,
252     // pvalloc(0) returns 1 page, see man libmpatrol
253     size = size? ((size-1) | (memoryPageSize-1)) + 1 : memoryPageSize;
254 
255     return scalable_aligned_malloc(size, memoryPageSize);
256 }
257 
258 int mallopt(int /*param*/, int /*value*/) __THROW
259 {
260     return 1;
261 }
262 
263 struct mallinfo mallinfo() __THROW
264 {
265     struct mallinfo m;
266     memset(&m, 0, sizeof(struct mallinfo));
267 
268     return m;
269 }
270 
271 #if __ANDROID__
272 // Android doesn't have malloc_usable_size, provide it to be compatible
273 // with Linux, in addition overload dlmalloc_usable_size() that presented
274 // under Android.
275 size_t dlmalloc_usable_size(const void *ptr) __TBB_ALIAS_ATTR_COPY(malloc_usable_size);
276 #else // __ANDROID__
277 // TODO: consider using __typeof__ to guarantee the correct declaration types
278 // C11 function, supported starting GLIBC 2.16
279 void *aligned_alloc(size_t alignment, size_t size) __TBB_ALIAS_ATTR_COPY(memalign);
280 // Those non-standard functions are exported by GLIBC, and might be used
281 // in conjunction with standard malloc/free, so we must overload them.
282 // Bionic doesn't have them. Not removing from the linker scripts,
283 // as absent entry points are ignored by the linker.
284 
285 void *__libc_malloc(size_t size) __TBB_ALIAS_ATTR_COPY(malloc);
286 void *__libc_calloc(size_t num, size_t size) __TBB_ALIAS_ATTR_COPY(calloc);
287 void *__libc_memalign(size_t alignment, size_t size) __TBB_ALIAS_ATTR_COPY(memalign);
288 void *__libc_pvalloc(size_t size) __TBB_ALIAS_ATTR_COPY(pvalloc);
289 void *__libc_valloc(size_t size) __TBB_ALIAS_ATTR_COPY(valloc);
290 
291 // call original __libc_* to support naive replacement of free via __libc_free etc
292 void __libc_free(void *ptr)
293 {
294     InitOrigPointers();
295     __TBB_malloc_safer_free(ptr, (void (*)(void*))orig_libc_free);
296 }
297 
298 void *__libc_realloc(void *ptr, size_t size)
299 {
300     InitOrigPointers();
301     return __TBB_malloc_safer_realloc(ptr, size, orig_libc_realloc);
302 }
303 #endif // !__ANDROID__
304 
305 } /* extern "C" */
306 
307 /*** replacements for global operators new and delete ***/
308 
309 void* operator new(size_t sz) {
310     return InternalOperatorNew(sz);
311 }
312 void* operator new[](size_t sz) {
313     return InternalOperatorNew(sz);
314 }
315 void operator delete(void* ptr) noexcept {
316     InitOrigPointers();
317     __TBB_malloc_safer_free(ptr, (void (*)(void*))orig_free);
318 }
319 void operator delete[](void* ptr) noexcept {
320     InitOrigPointers();
321     __TBB_malloc_safer_free(ptr, (void (*)(void*))orig_free);
322 }
323 void* operator new(size_t sz, const std::nothrow_t&) noexcept {
324     return scalable_malloc(sz);
325 }
326 void* operator new[](std::size_t sz, const std::nothrow_t&) noexcept {
327     return scalable_malloc(sz);
328 }
329 void operator delete(void* ptr, const std::nothrow_t&) noexcept {
330     InitOrigPointers();
331     __TBB_malloc_safer_free(ptr, (void (*)(void*))orig_free);
332 }
333 void operator delete[](void* ptr, const std::nothrow_t&) noexcept {
334     InitOrigPointers();
335     __TBB_malloc_safer_free(ptr, (void (*)(void*))orig_free);
336 }
337 
338 #endif /* MALLOC_UNIXLIKE_OVERLOAD_ENABLED */
339 #endif /* MALLOC_UNIXLIKE_OVERLOAD_ENABLED || MALLOC_ZONE_OVERLOAD_ENABLED */
340 
341 #ifdef _WIN32
342 #include <windows.h>
343 
344 #if !__TBB_WIN8UI_SUPPORT
345 
346 #include <stdio.h>
347 
348 #include "function_replacement.h"
349 
350 template<typename T, size_t N> // generic function to find length of array
351 inline size_t arrayLength(const T(&)[N]) {
352     return N;
353 }
354 
355 void __TBB_malloc_safer_delete( void *ptr)
356 {
357     __TBB_malloc_safer_free( ptr, NULL );
358 }
359 
360 void* safer_aligned_malloc( size_t size, size_t alignment )
361 {
362     // workaround for "is power of 2 pow N" bug that accepts zeros
363     return scalable_aligned_malloc( size, alignment>sizeof(size_t*)?alignment:sizeof(size_t*) );
364 }
365 
366 // we do not support _expand();
367 void* safer_expand( void *, size_t )
368 {
369     return NULL;
370 }
371 
372 #define __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(CRTLIB)                                             \
373 void (*orig_free_##CRTLIB)(void*);                                                                   \
374 void __TBB_malloc_safer_free_##CRTLIB(void *ptr)                                                     \
375 {                                                                                                    \
376     __TBB_malloc_safer_free( ptr, orig_free_##CRTLIB );                                              \
377 }                                                                                                    \
378                                                                                                      \
379 void (*orig__aligned_free_##CRTLIB)(void*);                                                          \
380 void __TBB_malloc_safer__aligned_free_##CRTLIB(void *ptr)                                            \
381 {                                                                                                    \
382     __TBB_malloc_safer_free( ptr, orig__aligned_free_##CRTLIB );                                     \
383 }                                                                                                    \
384                                                                                                      \
385 size_t (*orig__msize_##CRTLIB)(void*);                                                               \
386 size_t __TBB_malloc_safer__msize_##CRTLIB(void *ptr)                                                 \
387 {                                                                                                    \
388     return __TBB_malloc_safer_msize( ptr, orig__msize_##CRTLIB );                                    \
389 }                                                                                                    \
390                                                                                                      \
391 size_t (*orig__aligned_msize_##CRTLIB)(void*, size_t, size_t);                                       \
392 size_t __TBB_malloc_safer__aligned_msize_##CRTLIB( void *ptr, size_t alignment, size_t offset)       \
393 {                                                                                                    \
394     return __TBB_malloc_safer_aligned_msize( ptr, alignment, offset, orig__aligned_msize_##CRTLIB ); \
395 }                                                                                                    \
396                                                                                                      \
397 void* __TBB_malloc_safer_realloc_##CRTLIB( void *ptr, size_t size )                                  \
398 {                                                                                                    \
399     orig_ptrs func_ptrs = {orig_free_##CRTLIB, orig__msize_##CRTLIB};                                \
400     return __TBB_malloc_safer_realloc( ptr, size, &func_ptrs );                                      \
401 }                                                                                                    \
402                                                                                                      \
403 void* __TBB_malloc_safer__aligned_realloc_##CRTLIB( void *ptr, size_t size, size_t alignment )       \
404 {                                                                                                    \
405     orig_aligned_ptrs func_ptrs = {orig__aligned_free_##CRTLIB, orig__aligned_msize_##CRTLIB};       \
406     return __TBB_malloc_safer_aligned_realloc( ptr, size, alignment, &func_ptrs );                   \
407 }
408 
409 // Only for ucrtbase: substitution for _o_free
410 void (*orig__o_free)(void*);
411 void __TBB_malloc__o_free(void *ptr)
412 {
413     __TBB_malloc_safer_free( ptr, orig__o_free );
414 }
415 // Only for ucrtbase: substitution for _free_base
416 void(*orig__free_base)(void*);
417 void __TBB_malloc__free_base(void *ptr)
418 {
419     __TBB_malloc_safer_free(ptr, orig__free_base);
420 }
421 
422 // Size limit is MAX_PATTERN_SIZE (28) byte codes / 56 symbols per line.
423 // * can be used to match any digit in byte codes.
424 // # followed by several * indicate a relative address that needs to be corrected.
425 // Purpose of the pattern is to mark an instruction bound; it should consist of several
426 // full instructions plus one extra byte code. It's not required for the patterns
427 // to be unique (i.e., it's OK to have same pattern for unrelated functions).
428 // TODO: use hot patch prologues if exist
429 const char* known_bytecodes[] = {
430 #if _WIN64
431 //  "========================================================" - 56 symbols
432     "4883EC284885C974",       // release free()
433     "4883EC284885C975",       // release _msize()
434     "4885C974375348",         // release free() 8.0.50727.42, 10.0
435     "E907000000CCCC",         // release _aligned_msize(), _aligned_free() ucrtbase.dll
436     "C7442410000000008B",     // release free() ucrtbase.dll 10.0.14393.33
437     "E90B000000CCCC",         // release _msize() ucrtbase.dll 10.0.14393.33
438     "48895C24085748",         // release _aligned_msize() ucrtbase.dll 10.0.14393.33
439     "E903000000CCCC",         // release _aligned_msize() ucrtbase.dll 10.0.16299.522
440     "48894C24084883EC28BA",   // debug prologue
441     "4C894424184889542410",   // debug _aligned_msize() 10.0
442     "48894C24084883EC2848",   // debug _aligned_free 10.0
443     "488BD1488D0D#*******E9", // _o_free(), ucrtbase.dll
444  #if __TBB_OVERLOAD_OLD_MSVCR
445     "48895C2408574883EC3049", // release _aligned_msize 9.0
446     "4883EC384885C975",       // release _msize() 9.0
447     "4C8BC1488B0DA6E4040033", // an old win64 SDK
448  #endif
449 #else // _WIN32
450 //  "========================================================" - 56 symbols
451     "8BFF558BEC8B",           // multiple
452     "8BFF558BEC83",           // release free() & _msize() 10.0.40219.325, _msize() ucrtbase.dll
453     "8BFF558BECFF",           // release _aligned_msize ucrtbase.dll
454     "8BFF558BEC51",           // release free() & _msize() ucrtbase.dll 10.0.14393.33
455     "558BEC8B450885C074",     // release _aligned_free 11.0
456     "558BEC837D08000F",       // release _msize() 11.0.51106.1
457     "558BEC837D08007419FF",   // release free() 11.0.50727.1
458     "558BEC8B450885C075",     // release _aligned_msize() 11.0.50727.1
459     "558BEC6A018B",           // debug free() & _msize() 11.0
460     "558BEC8B451050",         // debug _aligned_msize() 11.0
461     "558BEC8B450850",         // debug _aligned_free 11.0
462     "8BFF558BEC6A",           // debug free() & _msize() 10.0.40219.325
463  #if __TBB_OVERLOAD_OLD_MSVCR
464     "6A1868********E8",       // release free() 8.0.50727.4053, 9.0
465     "6A1C68********E8",       // release _msize() 8.0.50727.4053, 9.0
466  #endif
467 #endif // _WIN64/_WIN32
468     NULL
469     };
470 
471 #define __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_ENTRY(CRT_VER,function_name,dbgsuffix) \
472     ReplaceFunctionWithStore( #CRT_VER #dbgsuffix ".dll", #function_name, \
473       (FUNCPTR)__TBB_malloc_safer_##function_name##_##CRT_VER##dbgsuffix, \
474       known_bytecodes, (FUNCPTR*)&orig_##function_name##_##CRT_VER##dbgsuffix );
475 
476 #define __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_ENTRY_NO_FALLBACK(CRT_VER,function_name,dbgsuffix) \
477     ReplaceFunctionWithStore( #CRT_VER #dbgsuffix ".dll", #function_name, \
478       (FUNCPTR)__TBB_malloc_safer_##function_name##_##CRT_VER##dbgsuffix, 0, NULL );
479 
480 #define __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_ENTRY_REDIRECT(CRT_VER,function_name,dest_func,dbgsuffix) \
481     ReplaceFunctionWithStore( #CRT_VER #dbgsuffix ".dll", #function_name, \
482       (FUNCPTR)__TBB_malloc_safer_##dest_func##_##CRT_VER##dbgsuffix, 0, NULL );
483 
484 #define __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_IMPL(CRT_VER,dbgsuffix)                             \
485     if (BytecodesAreKnown(#CRT_VER #dbgsuffix ".dll")) {                                          \
486       __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_ENTRY(CRT_VER,free,dbgsuffix)                         \
487       __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_ENTRY(CRT_VER,_msize,dbgsuffix)                       \
488       __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_ENTRY_NO_FALLBACK(CRT_VER,realloc,dbgsuffix)          \
489       __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_ENTRY(CRT_VER,_aligned_free,dbgsuffix)                \
490       __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_ENTRY(CRT_VER,_aligned_msize,dbgsuffix)               \
491       __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_ENTRY_NO_FALLBACK(CRT_VER,_aligned_realloc,dbgsuffix) \
492     } else                                                                                        \
493         SkipReplacement(#CRT_VER #dbgsuffix ".dll");
494 
495 #define __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_RELEASE(CRT_VER) __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_IMPL(CRT_VER,)
496 #define __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_DEBUG(CRT_VER) __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_IMPL(CRT_VER,d)
497 
498 #define __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL(CRT_VER)     \
499     __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_RELEASE(CRT_VER) \
500     __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_DEBUG(CRT_VER)
501 
502 #if __TBB_OVERLOAD_OLD_MSVCR
503 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr70d);
504 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr70);
505 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr71d);
506 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr71);
507 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr80d);
508 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr80);
509 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr90d);
510 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr90);
511 #endif
512 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr100d);
513 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr100);
514 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr110d);
515 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr110);
516 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr120d);
517 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(msvcr120);
518 __TBB_ORIG_ALLOCATOR_REPLACEMENT_WRAPPER(ucrtbase);
519 
520 /*** replacements for global operators new and delete ***/
521 
522 #if _MSC_VER && !defined(__INTEL_COMPILER)
523 #pragma warning( push )
524 #pragma warning( disable : 4290 )
525 #endif
526 
527 /*** operator new overloads internals (Linux, Windows) ***/
528 
529 void* operator_new(size_t sz) {
530     return InternalOperatorNew(sz);
531 }
532 void* operator_new_arr(size_t sz) {
533     return InternalOperatorNew(sz);
534 }
535 void operator_delete(void* ptr) noexcept {
536     __TBB_malloc_safer_delete(ptr);
537 }
538 #if _MSC_VER && !defined(__INTEL_COMPILER)
539 #pragma warning( pop )
540 #endif
541 
542 void operator_delete_arr(void* ptr) noexcept {
543     __TBB_malloc_safer_delete(ptr);
544 }
545 void* operator_new_t(size_t sz, const std::nothrow_t&) noexcept {
546     return scalable_malloc(sz);
547 }
548 void* operator_new_arr_t(std::size_t sz, const std::nothrow_t&) noexcept {
549     return scalable_malloc(sz);
550 }
551 void operator_delete_t(void* ptr, const std::nothrow_t&) noexcept {
552     __TBB_malloc_safer_delete(ptr);
553 }
554 void operator_delete_arr_t(void* ptr, const std::nothrow_t&) noexcept {
555     __TBB_malloc_safer_delete(ptr);
556 }
557 
558 struct Module {
559     const char *name;
560     bool        doFuncReplacement; // do replacement in the DLL
561 };
562 
563 Module modules_to_replace[] = {
564     {"msvcr100d.dll", true},
565     {"msvcr100.dll", true},
566     {"msvcr110d.dll", true},
567     {"msvcr110.dll", true},
568     {"msvcr120d.dll", true},
569     {"msvcr120.dll", true},
570     {"ucrtbase.dll", true},
571 //    "ucrtbased.dll" is not supported because of problems with _dbg functions
572 #if __TBB_OVERLOAD_OLD_MSVCR
573     {"msvcr90d.dll", true},
574     {"msvcr90.dll", true},
575     {"msvcr80d.dll", true},
576     {"msvcr80.dll", true},
577     {"msvcr70d.dll", true},
578     {"msvcr70.dll", true},
579     {"msvcr71d.dll", true},
580     {"msvcr71.dll", true},
581 #endif
582 #if __TBB_TODO
583     // TODO: Try enabling replacement for non-versioned system binaries below
584     {"msvcrtd.dll", true},
585     {"msvcrt.dll", true},
586 #endif
587     };
588 
589 /*
590 We need to replace following functions:
591 malloc
592 calloc
593 _aligned_malloc
594 _expand (by dummy implementation)
595 ??2@YAPAXI@Z      operator new                         (ia32)
596 ??_U@YAPAXI@Z     void * operator new[] (size_t size)  (ia32)
597 ??3@YAXPAX@Z      operator delete                      (ia32)
598 ??_V@YAXPAX@Z     operator delete[]                    (ia32)
599 ??2@YAPEAX_K@Z    void * operator new(unsigned __int64)   (intel64)
600 ??_V@YAXPEAX@Z    void * operator new[](unsigned __int64) (intel64)
601 ??3@YAXPEAX@Z     operator delete                         (intel64)
602 ??_V@YAXPEAX@Z    operator delete[]                       (intel64)
603 ??2@YAPAXIABUnothrow_t@std@@@Z      void * operator new (size_t sz, const std::nothrow_t&) throw()  (optional)
604 ??_U@YAPAXIABUnothrow_t@std@@@Z     void * operator new[] (size_t sz, const std::nothrow_t&) throw() (optional)
605 
606 and these functions have runtime-specific replacement:
607 realloc
608 free
609 _msize
610 _aligned_realloc
611 _aligned_free
612 _aligned_msize
613 */
614 
615 typedef struct FRData_t {
616     //char *_module;
617     const char *_func;
618     FUNCPTR _fptr;
619     FRR_ON_ERROR _on_error;
620 } FRDATA;
621 
622 FRDATA c_routines_to_replace[] = {
623     { "malloc",  (FUNCPTR)scalable_malloc, FRR_FAIL },
624     { "calloc",  (FUNCPTR)scalable_calloc, FRR_FAIL },
625     { "_aligned_malloc",  (FUNCPTR)safer_aligned_malloc, FRR_FAIL },
626     { "_expand",  (FUNCPTR)safer_expand, FRR_IGNORE },
627 };
628 
629 FRDATA cxx_routines_to_replace[] = {
630 #if _WIN64
631     { "??2@YAPEAX_K@Z", (FUNCPTR)operator_new, FRR_FAIL },
632     { "??_U@YAPEAX_K@Z", (FUNCPTR)operator_new_arr, FRR_FAIL },
633     { "??3@YAXPEAX@Z", (FUNCPTR)operator_delete, FRR_FAIL },
634     { "??_V@YAXPEAX@Z", (FUNCPTR)operator_delete_arr, FRR_FAIL },
635 #else
636     { "??2@YAPAXI@Z", (FUNCPTR)operator_new, FRR_FAIL },
637     { "??_U@YAPAXI@Z", (FUNCPTR)operator_new_arr, FRR_FAIL },
638     { "??3@YAXPAX@Z", (FUNCPTR)operator_delete, FRR_FAIL },
639     { "??_V@YAXPAX@Z", (FUNCPTR)operator_delete_arr, FRR_FAIL },
640 #endif
641     { "??2@YAPAXIABUnothrow_t@std@@@Z", (FUNCPTR)operator_new_t, FRR_IGNORE },
642     { "??_U@YAPAXIABUnothrow_t@std@@@Z", (FUNCPTR)operator_new_arr_t, FRR_IGNORE }
643 };
644 
645 #ifndef UNICODE
646 typedef char unicode_char_t;
647 #define WCHAR_SPEC "%s"
648 #else
649 typedef wchar_t unicode_char_t;
650 #define WCHAR_SPEC "%ls"
651 #endif
652 
653 // Check that we recognize bytecodes that should be replaced by trampolines.
654 // If some functions have unknown prologue patterns, replacement should not be done.
655 bool BytecodesAreKnown(const unicode_char_t *dllName)
656 {
657     const char *funcName[] = {"free", "_msize", "_aligned_free", "_aligned_msize", 0};
658     HMODULE module = GetModuleHandle(dllName);
659 
660     if (!module)
661         return false;
662     for (int i=0; funcName[i]; i++)
663         if (! IsPrologueKnown(dllName, funcName[i], known_bytecodes, module)) {
664             fprintf(stderr, "TBBmalloc: skip allocation functions replacement in " WCHAR_SPEC
665                     ": unknown prologue for function " WCHAR_SPEC "\n", dllName, funcName[i]);
666             return false;
667         }
668     return true;
669 }
670 
671 void SkipReplacement(const unicode_char_t *dllName)
672 {
673 #ifndef UNICODE
674     const char *dllStr = dllName;
675 #else
676     const size_t sz = 128; // all DLL name must fit
677 
678     char buffer[sz];
679     size_t real_sz;
680     char *dllStr = buffer;
681 
682     errno_t ret = wcstombs_s(&real_sz, dllStr, sz, dllName, sz-1);
683     __TBB_ASSERT(!ret, "Dll name conversion failed");
684 #endif
685 
686     for (size_t i=0; i<arrayLength(modules_to_replace); i++)
687         if (!strcmp(modules_to_replace[i].name, dllStr)) {
688             modules_to_replace[i].doFuncReplacement = false;
689             break;
690         }
691 }
692 
693 void ReplaceFunctionWithStore( const unicode_char_t *dllName, const char *funcName, FUNCPTR newFunc, const char ** opcodes, FUNCPTR* origFunc,  FRR_ON_ERROR on_error = FRR_FAIL )
694 {
695     FRR_TYPE res = ReplaceFunction( dllName, funcName, newFunc, opcodes, origFunc );
696 
697     if (res == FRR_OK || res == FRR_NODLL || (res == FRR_NOFUNC && on_error == FRR_IGNORE))
698         return;
699 
700     fprintf(stderr, "Failed to %s function %s in module %s\n",
701             res==FRR_NOFUNC? "find" : "replace", funcName, dllName);
702 
703     // Unable to replace a required function
704     // Aborting because incomplete replacement of memory management functions
705     // may leave the program in an invalid state
706     abort();
707 }
708 
709 void doMallocReplacement()
710 {
711     // Replace functions and keep backup of original code (separate for each runtime)
712 #if __TBB_OVERLOAD_OLD_MSVCR
713     __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL(msvcr70)
714     __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL(msvcr71)
715     __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL(msvcr80)
716     __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL(msvcr90)
717 #endif
718     __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL(msvcr100)
719     __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL(msvcr110)
720     __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL(msvcr120)
721     __TBB_ORIG_ALLOCATOR_REPLACEMENT_CALL_RELEASE(ucrtbase)
722 
723     // Replace functions without storing original code
724     for (size_t j = 0; j < arrayLength(modules_to_replace); j++) {
725         if (!modules_to_replace[j].doFuncReplacement)
726             continue;
727         for (size_t i = 0; i < arrayLength(c_routines_to_replace); i++)
728         {
729             ReplaceFunctionWithStore( modules_to_replace[j].name, c_routines_to_replace[i]._func, c_routines_to_replace[i]._fptr, NULL, NULL,  c_routines_to_replace[i]._on_error );
730         }
731         if ( strcmp(modules_to_replace[j].name, "ucrtbase.dll") == 0 ) {
732             HMODULE ucrtbase_handle = GetModuleHandle("ucrtbase.dll");
733             if (!ucrtbase_handle)
734                 continue;
735             // If _o_free function is present and patchable, redirect it to tbbmalloc as well
736             // This prevents issues with other _o_* functions which might allocate memory with malloc
737             if ( IsPrologueKnown("ucrtbase.dll", "_o_free", known_bytecodes, ucrtbase_handle)) {
738                 ReplaceFunctionWithStore( "ucrtbase.dll", "_o_free", (FUNCPTR)__TBB_malloc__o_free, known_bytecodes, (FUNCPTR*)&orig__o_free,  FRR_FAIL );
739             }
740             // Similarly for _free_base
741             if (IsPrologueKnown("ucrtbase.dll", "_free_base", known_bytecodes, ucrtbase_handle)) {
742                 ReplaceFunctionWithStore("ucrtbase.dll", "_free_base", (FUNCPTR)__TBB_malloc__free_base, known_bytecodes, (FUNCPTR*)&orig__free_base, FRR_FAIL);
743             }
744             // ucrtbase.dll does not export operator new/delete, so skip the rest of the loop.
745             continue;
746         }
747 
748         for (size_t i = 0; i < arrayLength(cxx_routines_to_replace); i++)
749         {
750 #if !_WIN64
751             // in Microsoft* Visual Studio* 2012 and 2013 32-bit operator delete consists of 2 bytes only: short jump to free(ptr);
752             // replacement should be skipped for this particular case.
753             if ( ((strcmp(modules_to_replace[j].name, "msvcr110.dll") == 0) || (strcmp(modules_to_replace[j].name, "msvcr120.dll") == 0)) && (strcmp(cxx_routines_to_replace[i]._func, "??3@YAXPAX@Z") == 0) ) continue;
754             // in Microsoft* Visual Studio* 2013 32-bit operator delete[] consists of 2 bytes only: short jump to free(ptr);
755             // replacement should be skipped for this particular case.
756             if ( (strcmp(modules_to_replace[j].name, "msvcr120.dll") == 0) && (strcmp(cxx_routines_to_replace[i]._func, "??_V@YAXPAX@Z") == 0) ) continue;
757 #endif
758             ReplaceFunctionWithStore( modules_to_replace[j].name, cxx_routines_to_replace[i]._func, cxx_routines_to_replace[i]._fptr, NULL, NULL,  cxx_routines_to_replace[i]._on_error );
759         }
760     }
761 }
762 
763 #endif // !__TBB_WIN8UI_SUPPORT
764 
765 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
766     // Suppress warning for UWP build ('main' signature found without threading model)
767     #pragma warning(push)
768     #pragma warning(disable:4447)
769 #endif
770 
771 extern "C" BOOL WINAPI DllMain( HINSTANCE hInst, DWORD callReason, LPVOID reserved )
772 {
773 
774     if ( callReason==DLL_PROCESS_ATTACH && reserved && hInst ) {
775 #if !__TBB_WIN8UI_SUPPORT
776         if (!tbb::detail::r1::GetBoolEnvironmentVariable("TBB_MALLOC_DISABLE_REPLACEMENT"))
777         {
778             doMallocReplacement();
779         }
780 #endif // !__TBB_WIN8UI_SUPPORT
781     }
782 
783     return TRUE;
784 }
785 
786 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
787     #pragma warning(pop)
788 #endif
789 
790 // Just to make the linker happy and link the DLL to the application
791 extern "C" __declspec(dllexport) void __TBB_malloc_proxy()
792 {
793 
794 }
795 
796 #endif //_WIN32
797