1 /* ----------------------------------------------------------------------------
2 Copyright (c) 2018-2021, Microsoft Research, Daan Leijen
3 This is free software; you can redistribute it and/or modify it under the
4 terms of the MIT license. A copy of the license can be found in the file
5 "LICENSE" at the root of this distribution.
6 -----------------------------------------------------------------------------*/
7 
8 #if !defined(MI_IN_ALLOC_C)
9 #error "this file should be included from 'alloc.c' (so aliases can work)"
10 #endif
11 
12 #if defined(MI_MALLOC_OVERRIDE) && defined(_WIN32) && !(defined(MI_SHARED_LIB) && defined(_DLL))
13 #error "It is only possible to override "malloc" on Windows when building as a DLL (and linking the C runtime as a DLL)"
14 #endif
15 
16 #if defined(MI_MALLOC_OVERRIDE) && !(defined(_WIN32))
17 
18 #if defined(__APPLE__)
19 mi_decl_externc void   vfree(void* p);
20 mi_decl_externc size_t malloc_size(const void* p);
21 mi_decl_externc size_t malloc_good_size(size_t size);
22 #endif
23 
24 // helper definition for C override of C++ new
25 typedef struct mi_nothrow_s { int _tag; } mi_nothrow_t;
26 
27 // ------------------------------------------------------
28 // Override system malloc
29 // ------------------------------------------------------
30 
31 #if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) && !defined(MI_VALGRIND)
32   // gcc, clang: use aliasing to alias the exported function to one of our `mi_` functions
33   #if (defined(__GNUC__) && __GNUC__ >= 9)
34     #pragma GCC diagnostic ignored "-Wattributes"  // or we get warnings that nodiscard is ignored on a forward
35     #define MI_FORWARD(fun)      __attribute__((alias(#fun), used, visibility("default"), copy(fun)));
36   #else
37     #define MI_FORWARD(fun)      __attribute__((alias(#fun), used, visibility("default")));
38   #endif
39   #define MI_FORWARD1(fun,x)      MI_FORWARD(fun)
40   #define MI_FORWARD2(fun,x,y)    MI_FORWARD(fun)
41   #define MI_FORWARD3(fun,x,y,z)  MI_FORWARD(fun)
42   #define MI_FORWARD0(fun,x)      MI_FORWARD(fun)
43   #define MI_FORWARD02(fun,x,y)   MI_FORWARD(fun)
44 #else
45   // otherwise use forwarding by calling our `mi_` function
46   #define MI_FORWARD1(fun,x)      { return fun(x); }
47   #define MI_FORWARD2(fun,x,y)    { return fun(x,y); }
48   #define MI_FORWARD3(fun,x,y,z)  { return fun(x,y,z); }
49   #define MI_FORWARD0(fun,x)      { fun(x); }
50   #define MI_FORWARD02(fun,x,y)   { fun(x,y); }
51 #endif
52 
53 #if defined(__APPLE__) && defined(MI_SHARED_LIB_EXPORT) && defined(MI_OSX_INTERPOSE)
54   // define MI_OSX_IS_INTERPOSED as we should not provide forwarding definitions for
55   // functions that are interposed (or the interposing does not work)
56   #define MI_OSX_IS_INTERPOSED
57 
58   // use interposing so `DYLD_INSERT_LIBRARIES` works without `DYLD_FORCE_FLAT_NAMESPACE=1`
59   // See: <https://books.google.com/books?id=K8vUkpOXhN4C&pg=PA73>
60   struct mi_interpose_s {
61     const void* replacement;
62     const void* target;
63   };
64   #define MI_INTERPOSE_FUN(oldfun,newfun) { (const void*)&newfun, (const void*)&oldfun }
65   #define MI_INTERPOSE_MI(fun)            MI_INTERPOSE_FUN(fun,mi_##fun)
66 
67   __attribute__((used)) static struct mi_interpose_s _mi_interposes[]  __attribute__((section("__DATA, __interpose"))) =
68   {
69     MI_INTERPOSE_MI(malloc),
70     MI_INTERPOSE_MI(calloc),
71     MI_INTERPOSE_MI(realloc),
72     MI_INTERPOSE_MI(strdup),
73     MI_INTERPOSE_MI(strndup),
74     MI_INTERPOSE_MI(realpath),
75     MI_INTERPOSE_MI(posix_memalign),
76     MI_INTERPOSE_MI(reallocf),
77     MI_INTERPOSE_MI(valloc),
78     MI_INTERPOSE_MI(malloc_size),
79     MI_INTERPOSE_MI(malloc_good_size),
80     MI_INTERPOSE_MI(aligned_alloc),
81     #ifdef MI_OSX_ZONE
82     // we interpose malloc_default_zone in alloc-override-osx.c so we can use mi_free safely
83     MI_INTERPOSE_MI(free),
84     MI_INTERPOSE_FUN(vfree,mi_free),
85     #else
86     // sometimes code allocates from default zone but deallocates using plain free :-( (like NxHashResizeToCapacity <https://github.com/nneonneo/osx-10.9-opensource/blob/master/objc4-551.1/runtime/hashtable2.mm>)
87     MI_INTERPOSE_FUN(free,mi_cfree), // use safe free that checks if pointers are from us
88     MI_INTERPOSE_FUN(vfree,mi_cfree),
89     #endif
90   };
91 
92   #ifdef __cplusplus
93   extern "C" {
94     void  _ZdlPv(void* p);   // delete
95     void  _ZdaPv(void* p);   // delete[]
96     void  _ZdlPvm(void* p, size_t n);  // delete
97     void  _ZdaPvm(void* p, size_t n);  // delete[]
98     void* _Znwm(size_t n);  // new
99     void* _Znam(size_t n);  // new[]
100     void* _ZnwmRKSt9nothrow_t(size_t n, mi_nothrow_t tag); // new nothrow
101     void* _ZnamRKSt9nothrow_t(size_t n, mi_nothrow_t tag); // new[] nothrow
102   }
103   __attribute__((used)) static struct mi_interpose_s _mi_cxx_interposes[]  __attribute__((section("__DATA, __interpose"))) =
104   {
105     MI_INTERPOSE_FUN(_ZdlPv,mi_free),
106     MI_INTERPOSE_FUN(_ZdaPv,mi_free),
107     MI_INTERPOSE_FUN(_ZdlPvm,mi_free_size),
108     MI_INTERPOSE_FUN(_ZdaPvm,mi_free_size),
109     MI_INTERPOSE_FUN(_Znwm,mi_new),
110     MI_INTERPOSE_FUN(_Znam,mi_new),
111     MI_INTERPOSE_FUN(_ZnwmRKSt9nothrow_t,mi_new_nothrow),
112     MI_INTERPOSE_FUN(_ZnamRKSt9nothrow_t,mi_new_nothrow),
113   };
114   #endif // __cplusplus
115 
116 #elif defined(_MSC_VER)
117   // cannot override malloc unless using a dll.
118   // we just override new/delete which does work in a static library.
119 #else
120   // On all other systems forward to our API
malloc(size_t size)121   void* malloc(size_t size)              MI_FORWARD1(mi_malloc, size)
122   void* calloc(size_t size, size_t n)    MI_FORWARD2(mi_calloc, size, n)
123   void* realloc(void* p, size_t newsize) MI_FORWARD2(mi_realloc, p, newsize)
124   void  free(void* p)                    MI_FORWARD0(mi_free, p)
125 #endif
126 
127 #if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__)
128 #pragma GCC visibility push(default)
129 #endif
130 
131 // ------------------------------------------------------
132 // Override new/delete
133 // This is not really necessary as they usually call
134 // malloc/free anyway, but it improves performance.
135 // ------------------------------------------------------
136 #ifdef __cplusplus
137   // ------------------------------------------------------
138   // With a C++ compiler we override the new/delete operators.
139   // see <https://en.cppreference.com/w/cpp/memory/new/operator_new>
140   // ------------------------------------------------------
141   #include <new>
142 
143   #ifndef MI_OSX_IS_INTERPOSED
144     void operator delete(void* p) noexcept              MI_FORWARD0(mi_free,p)
145     void operator delete[](void* p) noexcept            MI_FORWARD0(mi_free,p)
146 
147     void* operator new(std::size_t n) noexcept(false)   MI_FORWARD1(mi_new,n)
148     void* operator new[](std::size_t n) noexcept(false) MI_FORWARD1(mi_new,n)
149 
150     void* operator new  (std::size_t n, const std::nothrow_t& tag) noexcept { MI_UNUSED(tag); return mi_new_nothrow(n); }
151     void* operator new[](std::size_t n, const std::nothrow_t& tag) noexcept { MI_UNUSED(tag); return mi_new_nothrow(n); }
152 
153     #if (__cplusplus >= 201402L || _MSC_VER >= 1916)
delete(void * p,std::size_t n)154     void operator delete  (void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n)
155     void operator delete[](void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n)
156     #endif
157   #endif
158 
159   #if (__cplusplus > 201402L && defined(__cpp_aligned_new)) && (!defined(__GNUC__) || (__GNUC__ > 5))
160   void operator delete  (void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
161   void operator delete[](void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
delete(void * p,std::size_t n,std::align_val_t al)162   void operator delete  (void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); };
163   void operator delete[](void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); };
164 
new(std::size_t n,std::align_val_t al)165   void* operator new( std::size_t n, std::align_val_t al)   noexcept(false) { return mi_new_aligned(n, static_cast<size_t>(al)); }
noexcept(false)166   void* operator new[]( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast<size_t>(al)); }
new(std::size_t n,std::align_val_t al,const std::nothrow_t &)167   void* operator new  (std::size_t n, std::align_val_t al, const std::nothrow_t&) noexcept { return mi_new_aligned_nothrow(n, static_cast<size_t>(al)); }
168   void* operator new[](std::size_t n, std::align_val_t al, const std::nothrow_t&) noexcept { return mi_new_aligned_nothrow(n, static_cast<size_t>(al)); }
169   #endif
170 
171 #elif (defined(__GNUC__) || defined(__clang__))
172   // ------------------------------------------------------
173   // Override by defining the mangled C++ names of the operators (as
174   // used by GCC and CLang).
175   // See <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling>
176   // ------------------------------------------------------
177 
178   void _ZdlPv(void* p)            MI_FORWARD0(mi_free,p) // delete
179   void _ZdaPv(void* p)            MI_FORWARD0(mi_free,p) // delete[]
180   void _ZdlPvm(void* p, size_t n) MI_FORWARD02(mi_free_size,p,n)
181   void _ZdaPvm(void* p, size_t n) MI_FORWARD02(mi_free_size,p,n)
182   void _ZdlPvSt11align_val_t(void* p, size_t al)            { mi_free_aligned(p,al); }
183   void _ZdaPvSt11align_val_t(void* p, size_t al)            { mi_free_aligned(p,al); }
184   void _ZdlPvmSt11align_val_t(void* p, size_t n, size_t al) { mi_free_size_aligned(p,n,al); }
185   void _ZdaPvmSt11align_val_t(void* p, size_t n, size_t al) { mi_free_size_aligned(p,n,al); }
186 
187   #if (MI_INTPTR_SIZE==8)
188     void* _Znwm(size_t n)                             MI_FORWARD1(mi_new,n)  // new 64-bit
189     void* _Znam(size_t n)                             MI_FORWARD1(mi_new,n)  // new[] 64-bit
190     void* _ZnwmRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); }
191     void* _ZnamRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); }
192     void* _ZnwmSt11align_val_t(size_t n, size_t al)   MI_FORWARD2(mi_new_aligned, n, al)
193     void* _ZnamSt11align_val_t(size_t n, size_t al)   MI_FORWARD2(mi_new_aligned, n, al)
194     void* _ZnwmSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
195     void* _ZnamSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
196   #elif (MI_INTPTR_SIZE==4)
197     void* _Znwj(size_t n)                             MI_FORWARD1(mi_new,n)  // new 64-bit
198     void* _Znaj(size_t n)                             MI_FORWARD1(mi_new,n)  // new[] 64-bit
199     void* _ZnwjRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); }
200     void* _ZnajRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); }
201     void* _ZnwjSt11align_val_t(size_t n, size_t al)   MI_FORWARD2(mi_new_aligned, n, al)
202     void* _ZnajSt11align_val_t(size_t n, size_t al)   MI_FORWARD2(mi_new_aligned, n, al)
203     void* _ZnwjSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
204     void* _ZnajSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
205   #else
206     #error "define overloads for new/delete for this platform (just for performance, can be skipped)"
207   #endif
208 #endif // __cplusplus
209 
210 // ------------------------------------------------------
211 // Further Posix & Unix functions definitions
212 // ------------------------------------------------------
213 
214 #ifdef __cplusplus
215 extern "C" {
216 #endif
217 
218 #ifndef MI_OSX_IS_INTERPOSED
219   // Forward Posix/Unix calls as well
reallocf(void * p,size_t newsize)220   void*  reallocf(void* p, size_t newsize) MI_FORWARD2(mi_reallocf,p,newsize)
221   size_t malloc_size(const void* p)        MI_FORWARD1(mi_usable_size,p)
222   #if !defined(__ANDROID__) && !defined(__FreeBSD__)
223   size_t malloc_usable_size(void *p)       MI_FORWARD1(mi_usable_size,p)
224   #else
225   size_t malloc_usable_size(const void *p) MI_FORWARD1(mi_usable_size,p)
226   #endif
227 
228   // No forwarding here due to aliasing/name mangling issues
229   void*  valloc(size_t size)               { return mi_valloc(size); }
vfree(void * p)230   void   vfree(void* p)                    { mi_free(p); }
malloc_good_size(size_t size)231   size_t malloc_good_size(size_t size)     { return mi_malloc_good_size(size); }
posix_memalign(void ** p,size_t alignment,size_t size)232   int    posix_memalign(void** p, size_t alignment, size_t size) { return mi_posix_memalign(p, alignment, size); }
233 
234 
235   // `aligned_alloc` is only available when __USE_ISOC11 is defined.
236   // Note: Conda has a custom glibc where `aligned_alloc` is declared `static inline` and we cannot
237   // override it, but both _ISOC11_SOURCE and __USE_ISOC11 are undefined in Conda GCC7 or GCC9.
238   // Fortunately, in the case where `aligned_alloc` is declared as `static inline` it
239   // uses internally `memalign`, `posix_memalign`, or `_aligned_malloc` so we  can avoid overriding it ourselves.
240   #if __USE_ISOC11
aligned_alloc(size_t alignment,size_t size)241   void* aligned_alloc(size_t alignment, size_t size) { return mi_aligned_alloc(alignment, size); }
242   #endif
243 #endif
244 
245 // no forwarding here due to aliasing/name mangling issues
cfree(void * p)246 void  cfree(void* p)                                    { mi_free(p); }
pvalloc(size_t size)247 void* pvalloc(size_t size)                              { return mi_pvalloc(size); }
reallocarray(void * p,size_t count,size_t size)248 void* reallocarray(void* p, size_t count, size_t size)  { return mi_reallocarray(p, count, size); }
memalign(size_t alignment,size_t size)249 void* memalign(size_t alignment, size_t size)           { return mi_memalign(alignment, size); }
_aligned_malloc(size_t alignment,size_t size)250 void* _aligned_malloc(size_t alignment, size_t size)    { return mi_aligned_alloc(alignment, size); }
251 
252 #if defined(__GLIBC__) && defined(__linux__)
253   // forward __libc interface (needed for glibc-based Linux distributions)
__libc_malloc(size_t size)254   void* __libc_malloc(size_t size)                      MI_FORWARD1(mi_malloc,size)
255   void* __libc_calloc(size_t count, size_t size)        MI_FORWARD2(mi_calloc,count,size)
256   void* __libc_realloc(void* p, size_t size)            MI_FORWARD2(mi_realloc,p,size)
257   void  __libc_free(void* p)                            MI_FORWARD0(mi_free,p)
258   void  __libc_cfree(void* p)                           MI_FORWARD0(mi_free,p)
259 
260   void* __libc_valloc(size_t size)                      { return mi_valloc(size); }
__libc_pvalloc(size_t size)261   void* __libc_pvalloc(size_t size)                     { return mi_pvalloc(size); }
__libc_memalign(size_t alignment,size_t size)262   void* __libc_memalign(size_t alignment, size_t size)  { return mi_memalign(alignment,size); }
__posix_memalign(void ** p,size_t alignment,size_t size)263   int   __posix_memalign(void** p, size_t alignment, size_t size) { return mi_posix_memalign(p,alignment,size); }
264 #endif
265 
266 #ifdef __cplusplus
267 }
268 #endif
269 
270 #if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__)
271 #pragma GCC visibility pop
272 #endif
273 
274 #endif // MI_MALLOC_OVERRIDE && !_WIN32
275