microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.19.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/allocator/mimalloc-sys/mimalloc/src/prim/osx/alloc-override-zone.c

461lines · modecode

1/* ----------------------------------------------------------------------------
2Copyright (c) 2018-2022, Microsoft Research, Daan Leijen
3This is free software; you can redistribute it and/or modify it under the
4terms 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#include "mimalloc.h"
9#include "mimalloc/internal.h"
10
11#if defined(MI_MALLOC_OVERRIDE)
12
13#if !defined(__APPLE__)
14#error "this file should only be included on macOS"
15#endif
16
17/* ------------------------------------------------------
18 Override system malloc on macOS
19 This is done through the malloc zone interface.
20 It seems to be most robust in combination with interposing
21 though or otherwise we may get zone errors as there are could
22 be allocations done by the time we take over the
23 zone.
24------------------------------------------------------ */
25
26#include <AvailabilityMacros.h>
27#include <malloc/malloc.h>
28#include <string.h> // memset
29#include <stdlib.h>
30
31#ifdef __cplusplus
32extern "C" {
33#endif
34
35#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
36// only available from OSX 10.6
37extern malloc_zone_t* malloc_default_purgeable_zone(void) __attribute__((weak_import));
38#endif
39
40/* ------------------------------------------------------
41 malloc zone members
42------------------------------------------------------ */
43
44static size_t zone_size(malloc_zone_t* zone, const void* p) {
45 MI_UNUSED(zone);
46 if (!mi_is_in_heap_region(p)){ return 0; } // not our pointer, bail out
47 return mi_usable_size(p);
48}
49
50static void* zone_malloc(malloc_zone_t* zone, size_t size) {
51 MI_UNUSED(zone);
52 return mi_malloc(size);
53}
54
55static void* zone_calloc(malloc_zone_t* zone, size_t count, size_t size) {
56 MI_UNUSED(zone);
57 return mi_calloc(count, size);
58}
59
60static void* zone_valloc(malloc_zone_t* zone, size_t size) {
61 MI_UNUSED(zone);
62 return mi_malloc_aligned(size, _mi_os_page_size());
63}
64
65static void zone_free(malloc_zone_t* zone, void* p) {
66 MI_UNUSED(zone);
67 mi_cfree(p);
68}
69
70static void* zone_realloc(malloc_zone_t* zone, void* p, size_t newsize) {
71 MI_UNUSED(zone);
72 return mi_realloc(p, newsize);
73}
74
75static void* zone_memalign(malloc_zone_t* zone, size_t alignment, size_t size) {
76 MI_UNUSED(zone);
77 return mi_malloc_aligned(size,alignment);
78}
79
80static void zone_destroy(malloc_zone_t* zone) {
81 MI_UNUSED(zone);
82 // todo: ignore for now?
83}
84
85static unsigned zone_batch_malloc(malloc_zone_t* zone, size_t size, void** ps, unsigned count) {
86 size_t i;
87 for (i = 0; i < count; i++) {
88 ps[i] = zone_malloc(zone, size);
89 if (ps[i] == NULL) break;
90 }
91 return i;
92}
93
94static void zone_batch_free(malloc_zone_t* zone, void** ps, unsigned count) {
95 for(size_t i = 0; i < count; i++) {
96 zone_free(zone, ps[i]);
97 ps[i] = NULL;
98 }
99}
100
101static size_t zone_pressure_relief(malloc_zone_t* zone, size_t size) {
102 MI_UNUSED(zone); MI_UNUSED(size);
103 mi_collect(false);
104 return 0;
105}
106
107static void zone_free_definite_size(malloc_zone_t* zone, void* p, size_t size) {
108 MI_UNUSED(size);
109 zone_free(zone,p);
110}
111
112static boolean_t zone_claimed_address(malloc_zone_t* zone, void* p) {
113 MI_UNUSED(zone);
114 return mi_is_in_heap_region(p);
115}
116
117
118/* ------------------------------------------------------
119 Introspection members
120------------------------------------------------------ */
121
122static kern_return_t intro_enumerator(task_t task, void* p,
123 unsigned type_mask, vm_address_t zone_address,
124 memory_reader_t reader,
125 vm_range_recorder_t recorder)
126{
127 // todo: enumerate all memory
128 MI_UNUSED(task); MI_UNUSED(p); MI_UNUSED(type_mask); MI_UNUSED(zone_address);
129 MI_UNUSED(reader); MI_UNUSED(recorder);
130 return KERN_SUCCESS;
131}
132
133static size_t intro_good_size(malloc_zone_t* zone, size_t size) {
134 MI_UNUSED(zone);
135 return mi_good_size(size);
136}
137
138static boolean_t intro_check(malloc_zone_t* zone) {
139 MI_UNUSED(zone);
140 return true;
141}
142
143static void intro_print(malloc_zone_t* zone, boolean_t verbose) {
144 MI_UNUSED(zone); MI_UNUSED(verbose);
145 mi_stats_print(NULL);
146}
147
148static void intro_log(malloc_zone_t* zone, void* p) {
149 MI_UNUSED(zone); MI_UNUSED(p);
150 // todo?
151}
152
153static void intro_force_lock(malloc_zone_t* zone) {
154 MI_UNUSED(zone);
155 // todo?
156}
157
158static void intro_force_unlock(malloc_zone_t* zone) {
159 MI_UNUSED(zone);
160 // todo?
161}
162
163static void intro_statistics(malloc_zone_t* zone, malloc_statistics_t* stats) {
164 MI_UNUSED(zone);
165 // todo...
166 stats->blocks_in_use = 0;
167 stats->size_in_use = 0;
168 stats->max_size_in_use = 0;
169 stats->size_allocated = 0;
170}
171
172static boolean_t intro_zone_locked(malloc_zone_t* zone) {
173 MI_UNUSED(zone);
174 return false;
175}
176
177
178/* ------------------------------------------------------
179 At process start, override the default allocator
180------------------------------------------------------ */
181
182#if defined(__GNUC__) && !defined(__clang__)
183#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
184#endif
185
186#if defined(__clang__)
187#pragma clang diagnostic ignored "-Wc99-extensions"
188#endif
189
190static malloc_introspection_t mi_introspect = {
191 .enumerator = &intro_enumerator,
192 .good_size = &intro_good_size,
193 .check = &intro_check,
194 .print = &intro_print,
195 .log = &intro_log,
196 .force_lock = &intro_force_lock,
197 .force_unlock = &intro_force_unlock,
198#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) && !defined(__ppc__)
199 .statistics = &intro_statistics,
200 .zone_locked = &intro_zone_locked,
201#endif
202};
203
204static malloc_zone_t mi_malloc_zone = {
205 // note: even with designators, the order is important for C++ compilation
206 //.reserved1 = NULL,
207 //.reserved2 = NULL,
208 .size = &zone_size,
209 .malloc = &zone_malloc,
210 .calloc = &zone_calloc,
211 .valloc = &zone_valloc,
212 .free = &zone_free,
213 .realloc = &zone_realloc,
214 .destroy = &zone_destroy,
215 .zone_name = "mimalloc",
216 .batch_malloc = &zone_batch_malloc,
217 .batch_free = &zone_batch_free,
218 .introspect = &mi_introspect,
219#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) && !defined(__ppc__)
220 #if defined(MAC_OS_X_VERSION_10_14) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14)
221 .version = 10,
222 #else
223 .version = 9,
224 #endif
225 // switch to version 9+ on OSX 10.6 to support memalign.
226 .memalign = &zone_memalign,
227 .free_definite_size = &zone_free_definite_size,
228 #if defined(MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
229 .pressure_relief = &zone_pressure_relief,
230 #endif
231 #if defined(MAC_OS_X_VERSION_10_14) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14)
232 .claimed_address = &zone_claimed_address,
233 #endif
234#else
235 .version = 4,
236#endif
237};
238
239#ifdef __cplusplus
240}
241#endif
242
243
244#if defined(MI_OSX_INTERPOSE) && defined(MI_SHARED_LIB_EXPORT)
245
246// ------------------------------------------------------
247// Override malloc_xxx and malloc_zone_xxx api's to use only
248// our mimalloc zone. Since even the loader uses malloc
249// on macOS, this ensures that all allocations go through
250// mimalloc (as all calls are interposed).
251// The main `malloc`, `free`, etc calls are interposed in `alloc-override.c`,
252// Here, we also override macOS specific API's like
253// `malloc_zone_calloc` etc. see <https://github.com/aosm/libmalloc/blob/master/man/malloc_zone_malloc.3>
254// ------------------------------------------------------
255
256static inline malloc_zone_t* mi_get_default_zone(void)
257{
258 static bool init;
259 if mi_unlikely(!init) {
260 init = true;
261 malloc_zone_register(&mi_malloc_zone); // by calling register we avoid a zone error on free (see <http://eatmyrandom.blogspot.com/2010/03/mallocfree-interception-on-mac-os-x.html>)
262 }
263 return &mi_malloc_zone;
264}
265
266mi_decl_externc int malloc_jumpstart(uintptr_t cookie);
267mi_decl_externc void _malloc_fork_prepare(void);
268mi_decl_externc void _malloc_fork_parent(void);
269mi_decl_externc void _malloc_fork_child(void);
270
271
272static malloc_zone_t* mi_malloc_create_zone(vm_size_t size, unsigned flags) {
273 MI_UNUSED(size); MI_UNUSED(flags);
274 return mi_get_default_zone();
275}
276
277static malloc_zone_t* mi_malloc_default_zone (void) {
278 return mi_get_default_zone();
279}
280
281static malloc_zone_t* mi_malloc_default_purgeable_zone(void) {
282 return mi_get_default_zone();
283}
284
285static void mi_malloc_destroy_zone(malloc_zone_t* zone) {
286 MI_UNUSED(zone);
287 // nothing.
288}
289
290static kern_return_t mi_malloc_get_all_zones (task_t task, memory_reader_t mr, vm_address_t** addresses, unsigned* count) {
291 MI_UNUSED(task); MI_UNUSED(mr);
292 if (addresses != NULL) *addresses = NULL;
293 if (count != NULL) *count = 0;
294 return KERN_SUCCESS;
295}
296
297static const char* mi_malloc_get_zone_name(malloc_zone_t* zone) {
298 return (zone == NULL ? mi_malloc_zone.zone_name : zone->zone_name);
299}
300
301static void mi_malloc_set_zone_name(malloc_zone_t* zone, const char* name) {
302 MI_UNUSED(zone); MI_UNUSED(name);
303}
304
305static int mi_malloc_jumpstart(uintptr_t cookie) {
306 MI_UNUSED(cookie);
307 return 1; // or 0 for no error?
308}
309
310static void mi__malloc_fork_prepare(void) {
311 // nothing
312}
313static void mi__malloc_fork_parent(void) {
314 // nothing
315}
316static void mi__malloc_fork_child(void) {
317 // nothing
318}
319
320static void mi_malloc_printf(const char* fmt, ...) {
321 MI_UNUSED(fmt);
322}
323
324static bool zone_check(malloc_zone_t* zone) {
325 MI_UNUSED(zone);
326 return true;
327}
328
329static malloc_zone_t* zone_from_ptr(const void* p) {
330 MI_UNUSED(p);
331 return mi_get_default_zone();
332}
333
334static void zone_log(malloc_zone_t* zone, void* p) {
335 MI_UNUSED(zone); MI_UNUSED(p);
336}
337
338static void zone_print(malloc_zone_t* zone, bool b) {
339 MI_UNUSED(zone); MI_UNUSED(b);
340}
341
342static void zone_print_ptr_info(void* p) {
343 MI_UNUSED(p);
344}
345
346static void zone_register(malloc_zone_t* zone) {
347 MI_UNUSED(zone);
348}
349
350static void zone_unregister(malloc_zone_t* zone) {
351 MI_UNUSED(zone);
352}
353
354// use interposing so `DYLD_INSERT_LIBRARIES` works without `DYLD_FORCE_FLAT_NAMESPACE=1`
355// See: <https://books.google.com/books?id=K8vUkpOXhN4C&pg=PA73>
356struct mi_interpose_s {
357 const void* replacement;
358 const void* target;
359};
360#define MI_INTERPOSE_FUN(oldfun,newfun) { (const void*)&newfun, (const void*)&oldfun }
361#define MI_INTERPOSE_MI(fun) MI_INTERPOSE_FUN(fun,mi_##fun)
362#define MI_INTERPOSE_ZONE(fun) MI_INTERPOSE_FUN(malloc_##fun,fun)
363__attribute__((used)) static const struct mi_interpose_s _mi_zone_interposes[] __attribute__((section("__DATA, __interpose"))) =
364{
365
366 MI_INTERPOSE_MI(malloc_create_zone),
367 MI_INTERPOSE_MI(malloc_default_purgeable_zone),
368 MI_INTERPOSE_MI(malloc_default_zone),
369 MI_INTERPOSE_MI(malloc_destroy_zone),
370 MI_INTERPOSE_MI(malloc_get_all_zones),
371 MI_INTERPOSE_MI(malloc_get_zone_name),
372 MI_INTERPOSE_MI(malloc_jumpstart),
373 MI_INTERPOSE_MI(malloc_printf),
374 MI_INTERPOSE_MI(malloc_set_zone_name),
375 MI_INTERPOSE_MI(_malloc_fork_child),
376 MI_INTERPOSE_MI(_malloc_fork_parent),
377 MI_INTERPOSE_MI(_malloc_fork_prepare),
378
379 MI_INTERPOSE_ZONE(zone_batch_free),
380 MI_INTERPOSE_ZONE(zone_batch_malloc),
381 MI_INTERPOSE_ZONE(zone_calloc),
382 MI_INTERPOSE_ZONE(zone_check),
383 MI_INTERPOSE_ZONE(zone_free),
384 MI_INTERPOSE_ZONE(zone_from_ptr),
385 MI_INTERPOSE_ZONE(zone_log),
386 MI_INTERPOSE_ZONE(zone_malloc),
387 MI_INTERPOSE_ZONE(zone_memalign),
388 MI_INTERPOSE_ZONE(zone_print),
389 MI_INTERPOSE_ZONE(zone_print_ptr_info),
390 MI_INTERPOSE_ZONE(zone_realloc),
391 MI_INTERPOSE_ZONE(zone_register),
392 MI_INTERPOSE_ZONE(zone_unregister),
393 MI_INTERPOSE_ZONE(zone_valloc)
394};
395
396
397#else
398
399// ------------------------------------------------------
400// hook into the zone api's without interposing
401// This is the official way of adding an allocator but
402// it seems less robust than using interpose.
403// ------------------------------------------------------
404
405static inline malloc_zone_t* mi_get_default_zone(void)
406{
407 // The first returned zone is the real default
408 malloc_zone_t** zones = NULL;
409 unsigned count = 0;
410 kern_return_t ret = malloc_get_all_zones(0, NULL, (vm_address_t**)&zones, &count);
411 if (ret == KERN_SUCCESS && count > 0) {
412 return zones[0];
413 }
414 else {
415 // fallback
416 return malloc_default_zone();
417 }
418}
419
420#if defined(__clang__)
421__attribute__((constructor(101))) // highest priority
422#else
423__attribute__((constructor)) // priority level is not supported by gcc
424#endif
425__attribute__((used))
426static void _mi_macos_override_malloc(void) {
427 malloc_zone_t* purgeable_zone = NULL;
428
429 #if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
430 // force the purgeable zone to exist to avoid strange bugs
431 if (malloc_default_purgeable_zone) {
432 purgeable_zone = malloc_default_purgeable_zone();
433 }
434 #endif
435
436 // Register our zone.
437 // thomcc: I think this is still needed to put us in the zone list.
438 malloc_zone_register(&mi_malloc_zone);
439 // Unregister the default zone, this makes our zone the new default
440 // as that was the last registered.
441 malloc_zone_t *default_zone = mi_get_default_zone();
442 // thomcc: Unsure if the next test is *always* false or just false in the
443 // cases I've tried. I'm also unsure if the code inside is needed. at all
444 if (default_zone != &mi_malloc_zone) {
445 malloc_zone_unregister(default_zone);
446
447 // Reregister the default zone so free and realloc in that zone keep working.
448 malloc_zone_register(default_zone);
449 }
450
451 // Unregister, and re-register the purgeable_zone to avoid bugs if it occurs
452 // earlier than the default zone.
453 if (purgeable_zone != NULL) {
454 malloc_zone_unregister(purgeable_zone);
455 malloc_zone_register(purgeable_zone);
456 }
457
458}
459#endif // MI_OSX_INTERPOSE
460
461#endif // MI_MALLOC_OVERRIDE