Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
alloc.c
Go to the documentation of this file.
1/*
2
3MIT License
4
5Copyright (c) 2025 PCSX-Redux authors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#include "common/libc/alloc.h"
28
29#include <stddef.h>
30#include <stdint.h>
31
33#ifdef ALLOC_DEBUG
35#endif
36
37// TL;DR: this is a simple memory allocator that uses a linked list of
38// empty blocks to keep track of the free memory. Allocated blocks
39// are not tracked, but have a header that contains the size of the block.
40// Memory is always aligned to 8 bytes on 32-bit platforms, and 16 bytes on
41// 64-bit platforms. The allocator is not thread-safe, and is not
42// re-entrant. It is not designed to be used in a multi-threaded environment.
43// The allocator is designed to be used in a way where memory fragmentation
44// is minimal, and where the heap is not resized. The more memory is
45// fragmented, the slower the allocator will be. Last but not least, the
46// memory allocator respects the posix malloc/free/realloc interface, in
47// particular:
48// 1. Allocating 0 bytes will return a valid pointer, which can be freed.
49// 2. Re-allocating a pointer to a smaller size is always guaranteed to
50// succeed and to return the same pointer.
51// 3. Re-allocating a pointer to 0 bytes will behave as if free was called.
52// 4. Re-allocating a NULL pointer will behave like a call to malloc.
53
54// Align to 8 bytes on 32-bit platforms,
55// and 16 bytes on 64-bit platforms.
56#define ALIGN_MASK ((2 * sizeof(void *)) - 1)
57#define ALIGN_TO(x) (((uintptr_t)(x) + ALIGN_MASK) & ~ALIGN_MASK)
58
59extern char __heap_start;
60extern char __stack_start;
61
62// We keep track of the empty spaces in the heap, because
63// it is fungible. Allocated blocks aren't tracked, and
64// they are made so:
65// 1. Their sizes is always stored before the allocated block.
66// 2. They are always big enough to hold one of these empty blocks.
67
68// Note that all of the memory is aligned to one of these blocks.
69// Which means that there's always enough space for one of these
70// blocks between two allocated blocks, if they are not adjacent.
71typedef struct empty_block_ {
73 // The size of the block, in bytes, including this header,
74 // meaning any empty_block should always have a size of
75 // at least 2 * sizeof(void *), except for the end-of-list
76 // marker, which is 0.
77 size_t size;
79
80// This is the header of an allocated block. It ought to be
81// mapped to the empty_block header exactly. We keep it
82// as a separate type to make sure we don't accidentally
83// mix them up, and for readability.
84typedef struct allocated_block_ {
85 uintptr_t dummy;
86 // The size of the block, in bytes, including this header.
87 size_t size;
89
90// In theory, empty_block should be at exactly as big as
91// alignment requirement. Let's assert that.
92_Static_assert(sizeof(empty_block) == (2 * sizeof(void *)), "empty_block is of the wrong size");
93// The same goes with allocated_block.
94_Static_assert(sizeof(allocated_block) == (2 * sizeof(void *)), "allocated_block is of the wrong size");
95
96// Heap metadata struct, registered with the emulator so
97// it can walk the allocator state for the heap viewer.
98static struct {
103 // The marker is here to make sure that the list is always terminated,
104 // so when we completely fill the heap, we don't end up with a NULL pointer
105 // back to the head. It will never fit any allocation, and will always
106 // be the last block in the list.
108} s_heap_metadata = {NULL, NULL, NULL, NULL, NULL};
109
110// Convenience aliases so the rest of the code reads the same.
111#define head s_heap_metadata.head
112#define bottom s_heap_metadata.bottom
113#define top s_heap_metadata.top
114#define maximum_heap_end s_heap_metadata.maximum_heap_end
115#define marker s_heap_metadata.marker
116
117// Enable this to debug the allocator very thoroughly. May be used to
118// detect memory corruption, and other issues.
119#ifdef ALLOC_DEBUG
120#define dprintf ramsyscall_printf
121static void print_block(const empty_block *block) {
122 if (block == NULL) {
123 ramsyscall_printf("NULL\n");
124 } else if (block == &marker) {
125 ramsyscall_printf("marker\n");
126 } else if (block->next == &marker) {
127 ramsyscall_printf("block: %p, size: %u, next: marker\n", block, block->size);
128 } else {
129 ramsyscall_printf("block: %p, size: %u, next: %p\n", block, block->size, block->next);
130 }
131}
132
133static int check_subintegrity(const allocated_block *first, const allocated_block *top_block, size_t size_start,
134 size_t hypothetical_size) {
135 if (first == top_block) {
136 return 0;
137 }
138 ramsyscall_printf("Integrity check: checking sublist from %p to %p, size_start = %u, hypothetical_size: %u\n",
139 first, top_block, size_start, hypothetical_size);
140 const allocated_block *curr = first;
141 size_t size = size_start;
142 while (curr < top_block) {
143 size += curr->size;
144 ramsyscall_printf("Integrity check: checking allocated block at %p (size: %u) - current total = %u\n", curr,
145 curr->size, size);
146 if (curr->size == 0) {
147 ramsyscall_printf("Integrity check failed: curr->size is 0\n");
148 pcsx_debugbreak();
149 return 1;
150 }
151 if (curr->size < sizeof(allocated_block)) {
152 ramsyscall_printf("Integrity check failed: curr->size is too small\n");
153 pcsx_debugbreak();
154 return 1;
155 }
156 if (curr->size % (sizeof(void *) * 2) != 0) {
157 ramsyscall_printf("Integrity check failed: curr->size is not aligned\n");
158 pcsx_debugbreak();
159 return 1;
160 }
161 if (size > hypothetical_size) {
162 ramsyscall_printf("Integrity check failed: size > hypothetical_size\n");
163 pcsx_debugbreak();
164 return 1;
165 }
166 curr = (allocated_block *)((char *)curr + curr->size);
167 }
168 if (size != hypothetical_size) {
169 ramsyscall_printf("Integrity check failed: size != hypothetical_size\n");
170 print_block((empty_block *)first);
171 pcsx_debugbreak();
172 return 1;
173 }
174 return 0;
175}
176
177static void check_integrity() {
178 empty_block *curr = head;
179 if (head != (empty_block *)bottom) {
181 if (check_subintegrity(bottom, last, 0, (last - bottom) * sizeof(empty_block))) return;
182 }
183 while (curr != &marker) {
184 ramsyscall_printf("Integrity check: checking ");
185 print_block(curr);
186 if (curr->next == NULL) {
187 ramsyscall_printf("Integrity check failed: curr->next is NULL\n");
188 print_block(curr);
189 pcsx_debugbreak();
190 return;
191 }
192 if (curr->next == curr) {
193 ramsyscall_printf("Integrity check failed: curr->next is curr\n");
194 print_block(curr);
195 pcsx_debugbreak();
196 return;
197 }
198 if (curr->size == 0) {
199 ramsyscall_printf("Integrity check failed: curr->size is 0\n");
200 print_block(curr);
201 pcsx_debugbreak();
202 return;
203 }
204 if (curr->size < sizeof(empty_block)) {
205 ramsyscall_printf("Integrity check failed: curr->size is too small\n");
206 print_block(curr);
207 pcsx_debugbreak();
208 return;
209 }
210 if ((uintptr_t)curr->next % sizeof(void *) != 0) {
211 ramsyscall_printf("Integrity check failed: curr->next is not aligned\n");
212 print_block(curr);
213 pcsx_debugbreak();
214 return;
215 }
216 if (curr->size % (sizeof(void *) * 2) != 0) {
217 ramsyscall_printf("Integrity check failed: curr->size is not aligned\n");
218 print_block(curr);
219 pcsx_debugbreak();
220 return;
221 }
222 if ((curr > curr->next) && (curr->next != &marker)) {
223 ramsyscall_printf("Integrity check failed: curr > curr->next\n");
224 print_block(curr);
225 pcsx_debugbreak();
226 return;
227 }
228 allocated_block *last = curr->next == &marker ? top : (allocated_block *)curr->next;
229 allocated_block *ptr = (allocated_block *)((char *)curr + curr->size);
230 size_t start_size = curr->size;
231 size_t hypothetical = ((empty_block *)last - curr) * sizeof(empty_block);
232 if (check_subintegrity(ptr, last, start_size, hypothetical)) return;
233 curr = curr->next;
234 }
235 ramsyscall_printf("Integrity check passed\n");
236}
237#else
238#define dprintf(...)
239#define print_block(x)
240#define check_integrity()
241#endif
242
243#ifdef USE_PCSXMSAN
244void *libc_malloc(size_t size) { return pcsx_msanAlloc(size); }
245void libc_free(void *ptr) { pcsx_msanFree(ptr); }
246void *libc_realloc(void *ptr, size_t size) { return pcsx_msanRealloc(ptr, size); }
247#else
248void *libc_malloc(size_t size_) {
249 dprintf("libc_malloc(%u)\n", size_);
250 empty_block *curr = head;
251 empty_block *prev = NULL;
252 empty_block *best_fit = NULL;
253 empty_block *best_fit_prev = NULL;
254
255 // Empty allocations don't really exist here, meaning we will always
256 // return a valid pointer. We want to store the size of the allocation
257 // before the pointer, in an allocated_block.
258 size_t size = ALIGN_TO(size_ + sizeof(allocated_block));
259 dprintf("libc_malloc(%u) -> %u\n", size_, size);
260
261 // If head is NULL, it means we need to initialize the heap. This means
262 // computing the size of the heap, according to the stack pointer.
263 if (curr == NULL) {
264 marker.next = NULL;
265 marker.size = 0;
266 curr = head = (empty_block *)ALIGN_TO((void *)&__heap_start);
267 bottom = (allocated_block *)curr;
268 curr->next = &marker;
269 // We need to compute the size of the heap, according to the stack pointer.
270 // Its size needs to be aligned to the empty_block size.
271 curr->size = ALIGN_TO(((size_t)&__stack_start) - ((size_t)curr) - sizeof(empty_block));
272 top = (allocated_block *)((char *)curr + curr->size);
273 if (pcsx_present()) {
274 pcsx_registerHeapMetadata(&s_heap_metadata);
275 }
276 }
277
278 // Walk the full list of empty blocks, and find the best fit,
279 // keeping track of the previous block. The previous block
280 // may be NULL if the best fit is the first block. In this context,
281 // best fit means the smallest block that is still big enough.
282 size_t curr_size = 0;
283 while ((curr_size != size) && (curr != &marker)) {
284 dprintf("libc_malloc: curr: ");
285 print_block(curr);
286 curr_size = curr->size;
287 // Is the current block even fitting?
288 if (curr_size >= size) {
289 // Yes - is it a new best fit?
290 if ((best_fit == NULL) || (curr_size < best_fit->size)) {
291 best_fit = curr;
292 best_fit_prev = prev;
293 dprintf("libc_malloc: new best fit: ");
294 print_block(best_fit);
295 }
296 }
297 prev = curr;
298 curr = curr->next;
299 }
300
301 // If we didn't find a fitting block, return NULL. This is
302 // the case when the heap is full, and we've ran out of memory.
303 if (best_fit == NULL) {
304 dprintf("libc_malloc(%u) failed\n", size_);
305 return NULL;
306 }
307
308 size_t best_fit_size = best_fit->size;
309
310 // At this point, we have the best fit block. The best fit
311 // block will become the returned pointer. We will mangle
312 // it a bit, right before returning it, but make it now
313 // for readability.
314 allocated_block *ptr = (allocated_block *)best_fit;
315
316 // At this point, we need to update the linked list. There are
317 // two paths:
318 // 1. If the current block is exactly the size we need, we can just
319 // remove it from the list, and link the previous block to the next one.
320 // Note that due to the granularity of the empty blocks, this is always
321 // possible, and we don't have to worry about blocks which may not be
322 // big enough to hold an empty block.
323 // 2. If the current block is bigger than we need, we need to create
324 // a new empty block after what we are allocating.
325 if (best_fit_size == size) {
326 // Case 1: Remove the block from the list.
327 if (best_fit_prev == NULL) {
328 head = best_fit->next;
329 } else {
330 best_fit_prev->next = best_fit->next;
331 }
332 } else {
333 // Case 2: Create a new empty block after what we are allocating.
334 empty_block *new_block = (empty_block *)((char *)best_fit + size);
335 new_block->next = best_fit->next;
336 new_block->size = best_fit_size - size;
337 if (best_fit_prev == NULL) {
338 head = new_block;
339 } else {
340 best_fit_prev->next = new_block;
341 }
342 }
343
344 // Store the size of the allocation before the pointer.
345 void *end = (void *)((char *)ptr + size);
346 if (end > maximum_heap_end) {
347 maximum_heap_end = end;
348 }
349 ptr->size = size;
350 ptr++;
351
352 dprintf("libc_malloc(%u) -> %p\n", size_, ptr);
354 return ptr;
355}
356
357void libc_free(void *ptr_) {
358 dprintf("libc_free(%p)\n", ptr_);
359 // Freeing NULL is a no-op.
360 if (ptr_ == NULL) {
361 return;
362 }
363
364 empty_block *block = (empty_block *)ptr_;
365 block--;
366 size_t size = block->size;
367
368 // Is head pointing to our marker? If that's the case, the
369 // heap was totally full. So freeing this block means
370 // simply re-creating the head.
371 if (head == &marker) {
372 head = block;
373 block->next = &marker;
374 // This should be a no-op, since the size is stored
375 // at the same place as the size of the empty block.
376 // head->size = size;
378 return;
379 }
380
381 // If the head is NULL, this means the user is trying to free
382 // a block that was never allocated. This is undefined behavior,
383 // but we will just ignore it, because it's an easy one, and
384 // it'll be a pain to debug due to the comparison below.
385 if (head == NULL) {
386 return;
387 }
388
389 // If the head is after the block we're freeing, we can just
390 // insert it at the head of the list.
391 if (head > block) {
392 // Now, we need to check if the next block is adjacent to
393 // the block we're freeing. If it is, we can merge them.
394 if (((char *)block + size) == (char *)head) {
395 block->size = head->size + size;
396 block->next = head->next;
397 } else {
398 // Otherwise, we just insert the block at the head of the list.
399 block->next = head;
400 // Same as above, this is a no-op.
401 // block->size = size;
402 }
403 head = block;
405 return;
406 }
407
408 // At this point, we know that the head is before the block we're
409 // freeing. So we need to walk the list until we find the block
410 // that is right before the block we're freeing.
411 empty_block *curr = head;
412 empty_block *next = NULL;
413 dprintf("libc_free: head: %p\n", head);
414 while ((next = curr->next) != &marker) {
415 dprintf("libc_free: curr: ");
416 print_block(curr);
417 dprintf("libc_free: next: ");
418 print_block(next);
419 // Is the next block after the block we're freeing?
420 if (next <= block) {
421 // Nope, we're not there yet.
422 curr = next;
423 continue;
424 }
425
426 // Yes? Insert the block before the next block.
427
428 // At this point, there are three cases and a half.
429 // 1. The current block is adjacent to the block we're freeing.
430 // In this case, we merge the current block with the block we're freeing,
431 // and we need to check if the next block is adjacent to the block we're freeing
432 // too, to merge all three blocks.
433 // 2. The next block is adjacent to the block we're freeing.
434 // In this case, we merge the next block with the block we're freeing.
435 // 3. The block we're freeing is in the middle of the list.
436 // In this case, we just insert the block before the next block.
437
438 // Case 1: The current block is adjacent to the block we're freeing.
439 if (((char *)curr + curr->size) == (char *)block) {
440 curr->size += size;
441 // Now, we need to check if the next block is adjacent to
442 // the block we're freeing. If it is, we can merge them.
443 if (((char *)curr + curr->size) == (char *)next) {
444 curr->size += next->size;
445 curr->next = next->next;
446 }
447 // Case 2: The next block is adjacent to the block we're freeing.
448 } else if (((char *)block + size) == (char *)next) {
449 block->next = next->next;
450 block->size = size + next->size;
451 curr->next = block;
452 } else {
453 // Case 3: The block we're freeing is in the middle of the list.
454 block->next = next;
455 // Same as above, this is a no-op.
456 // block->size = size;
457 curr->next = block;
458 }
460 return;
461 }
462
463 // If we end up here, it means we reached the end of the list,
464 // and the block we're freeing is after the last block.
465 // At this point, the pointer curr is pointing to the last block.
466 // We need to check if the last block is adjacent to the block we're freeing.
467 // If it is, we can merge them.
468 if (((char *)curr + curr->size) == (char *)block) {
469 curr->size += size;
470 } else {
471 // If the last block is not adjacent to the block we're freeing,
472 // we can just insert the block at the end of the list.
473 block->next = &marker;
474 // Same as above, this is a no-op.
475 // block->size = size;
476 curr->next = block;
477 }
479}
480
481void *libc_realloc(void *ptr_, size_t size_) {
482 dprintf("libc_realloc(%p, %u)\n", ptr_, size_);
483 // If the pointer is NULL, we can just call malloc.
484 if (ptr_ == NULL) {
485 dprintf("libc_realloc(%p, %u) -> malloc\n", ptr_, size_);
486 return libc_malloc(size_);
487 }
488
489 // If the size is 0, we can just call free.
490 if (size_ == 0) {
491 dprintf("libc_realloc(%p, %u) -> free\n", ptr_, size_);
492 libc_free(ptr_);
493 return NULL;
494 }
495
496 size_t size = ALIGN_TO(size_ + sizeof(empty_block));
497 dprintf("libc_realloc(%p, %u) -> %u\n", ptr_, size_, size);
498 // Get the current size of the block.
499 empty_block *block = (empty_block *)ptr_;
500 size_t old_size = (--block)->size;
501
502 // If the new size is the same as the old size, we can just return the pointer.
503 if (size == old_size) {
504 dprintf("libc_realloc(%p, %u) -> same\n", ptr_, size_);
505 return ptr_;
506 }
507
508 // Is our memory already completely full?
509 if (head == &marker) {
510 // If we're shrinking the allocation, then we can
511 // create a new empty block after what we are re-allocating,
512 // and re-create our list.
513 if (size < old_size) {
514 empty_block *new_block = (empty_block *)((char *)block + size);
515 new_block->next = &marker;
516 new_block->size = old_size - size;
517 head = new_block;
518 block->size = size;
519 dprintf("libc_realloc(%p, %u) -> %p\n", ptr_, size_, ptr_);
521 return ptr_;
522 }
523 // Otherwise, we're out of luck, and we need to error out
524 // with a NULL pointer signalling we're out of memory.
525 return NULL;
526 }
527
528 // Special case: is the allocated block before the head?
529 if (block < head) {
530 // Are we shrinking?
531 if (size < old_size) {
532 // If we are, we can just create a new empty block after what we are re-allocating.
533 empty_block *new_block = (empty_block *)((char *)block + size);
534 // Is it adjacent to our head?
535 if (head == (empty_block *)((char *)block + size)) {
536 // Yes, we can merge them.
537 new_block->next = head->next;
538 new_block->size = head->size + (old_size - size);
539 } else {
540 // No, we need to create a new empty block after what we are re-allocating.
541 new_block->next = head;
542 new_block->size = old_size - size;
543 }
544 head = new_block;
545 block->size = size;
546 dprintf("libc_realloc(%p, %u) -> %p\n", ptr_, size_, ptr_);
548 return ptr_;
549 }
550 // We are growing. Is the first block adjacent to the block we're re-allocating?
551 // If the first block is adjacent to the block we're re-allocating,
552 // and it has enough space to hold the new size, we can just grow
553 // the allocation.
554 if (((char *)block + old_size) == (char *)head) {
555 size_t delta = size - old_size;
556 if (head->size >= delta) {
557 // If it has exactly the right amount of space, we can just remove
558 // the first block from the list.
559 if (head->size == delta) {
560 head = head->next;
561 } else {
562 // Otherwise, we need to create a new empty block after what we are re-allocating.
563 empty_block *new_block = (empty_block *)((char *)block + size);
564 new_block->next = head->next;
565 new_block->size = head->size - delta;
566 head = new_block;
567 }
568 block->size = size;
569 dprintf("libc_realloc(%p, %u) -> %p\n", ptr_, size_, ptr_);
571 return ptr_;
572 }
573 }
574 } else {
575 // We need to locate where in the list the pointer is. To do this,
576 // we need to walk the list until we find the block that is right before
577 // the block we're re-allocating.
578 empty_block *curr = head;
579 empty_block *next = NULL;
580 while ((next = curr->next) != NULL) {
581 dprintf("libc_realloc: curr: ");
582 print_block(curr);
583 // Is the next block after the block we're re-allocating?
584 if ((next <= block) && (next != &marker)) {
585 // Nope, we're not there yet.
586 curr = next;
587 } else {
588 break;
589 }
590 }
591
592 // Here, curr points to the empty block before the block we're re-allocating,
593 // and next points to the empty block after the block we're re-allocating, or
594 // to marker if we're at the end of the list.
595
596 // Are we shrinking the allocation?
597 if (size < old_size) {
598 // We're going to create a new block at the end of what we are re-allocating.
599 empty_block *new_block = (empty_block *)((char *)block + size);
600 // Is the next block adjacent to the end of our original allocation?
601 if ((next != &marker) && (((char *)block + old_size) == (char *)next)) {
602 // Yes, we can merge them.
603 new_block->next = next->next;
604 new_block->size = old_size - size + next->size;
605 } else {
606 // No. Create a new empty block after what we are re-allocating.
607 new_block->next = next;
608 new_block->size = old_size - size;
609 }
610 curr->next = new_block;
611 block->size = size;
612 dprintf("libc_realloc(%p, %u) -> %p\n", ptr_, size_, ptr_);
614 return ptr_;
615 }
616
617 // If we're growing the allocation, we need to check if the next block
618 // is adjacent to the block we're re-allocating, and if it has enough
619 // space to hold the new size.
620
621 size_t delta = size - old_size;
622 if ((next != &marker) && (((char *)block + old_size) == (char *)next) && (next->size >= delta)) {
623 // If it does, we can just grow the allocation.
624 // Do we have exactly the right amount of space available?
625 if (next->size == delta) {
626 // Yes? Then we can just remove the next block from the list.
627 curr->next = next->next;
628 } else {
629 // No? Then we need to create a new block after what we are re-allocating.
630 empty_block *new_block = (empty_block *)((char *)block + size);
631 new_block->next = next->next;
632 new_block->size = next->size - delta;
633 curr->next = new_block;
634 }
635 block->size = size;
636 dprintf("libc_realloc(%p, %u) -> %p\n", ptr_, size_, ptr_);
638 return ptr_;
639 }
640
641 // Technically at this point, we have one last recourse before going for
642 // allocating memory elsewhere: we could try to probe if the block before
643 // the block we're re-allocating is adjacent to it, and if it is, we could
644 // try to merge it if it has enough space. This would require walking the
645 // list again, or use double-linked lists, and it requires memmove. Pain.
646 // So let's just ignore this case.
647 }
648
649 void *new_ptr = libc_malloc(size_);
650 if (new_ptr == NULL) {
651 dprintf("libc_realloc(%p, %u) -> NULL\n", ptr_, size_);
652 return NULL;
653 }
654 __builtin_memcpy(new_ptr, ptr_, old_size - sizeof(empty_block));
655 libc_free(ptr_);
656 dprintf("libc_realloc(%p, %u) -> %p\n", ptr_, size_, new_ptr);
657 return new_ptr;
658}
659#endif
660
661void *__builtin_new(size_t size) { return libc_malloc(size); }
663// void * operator new(unsigned int);
664void *_Znwj(unsigned int size) { return libc_malloc(size); }
665// void * operator new[](unsigned int);
666void *_Znaj(unsigned int size) { return libc_malloc(size); }
667// void operator delete(void*);
668void _ZdlPv(void *ptr) { libc_free(ptr); }
669// void operator delete[](void*);
670void _ZdaPv(void *ptr) { libc_free(ptr); }
671// void operator delete(void*, unsigned int);
672void _ZdlPvj(void *ptr, unsigned int size) { libc_free(ptr); }
673// void operator delete[](void*, unsigned int);
674void _ZdaPvj(void *ptr, unsigned int size) { libc_free(ptr); }
675
676void *libc_heap_start() { return bottom; }
#define dprintf(...)
Definition alloc.c:238
void * libc_heap_start()
Returns the pointer to the beginning of the heap.
Definition alloc.c:676
void * libc_realloc(void *ptr_, size_t size_)
Re-allocates memory from the heap.
Definition alloc.c:481
void * __builtin_new(size_t size)
Definition alloc.c:661
void _ZdaPvj(void *ptr, unsigned int size)
Definition alloc.c:674
void _ZdaPv(void *ptr)
Definition alloc.c:670
#define ALIGN_TO(x)
Definition alloc.c:57
#define maximum_heap_end
Definition alloc.c:114
#define head
Definition alloc.c:111
void * libc_heap_end()
Returns the pointer to the end of the heap.
Definition alloc.c:677
char __heap_start
#define marker
Definition alloc.c:115
#define print_block(x)
Definition alloc.c:239
#define bottom
Definition alloc.c:112
#define top
Definition alloc.c:113
#define check_integrity()
Definition alloc.c:240
void * libc_malloc(size_t size_)
Allocates memory from the heap.
Definition alloc.c:248
void * _Znwj(unsigned int size)
Definition alloc.c:664
void * _Znaj(unsigned int size)
Definition alloc.c:666
struct allocated_block_ allocated_block
void _ZdlPv(void *ptr)
Definition alloc.c:668
void libc_free(void *ptr_)
Frees memory from the heap.
Definition alloc.c:357
void _ZdlPvj(void *ptr, unsigned int size)
Definition alloc.c:672
char __stack_start
struct empty_block_ empty_block
void __builtin_delete(void *ptr)
Definition alloc.c:662
static int size
Definition string.h:142
volatile uint32_t * ptr
Definition cop0.c:80
ramsyscall_printf("=== e01_kseg1_reads_no_fill ===\n")
Definition alloc.c:84
size_t size
Definition alloc.c:87
uintptr_t dummy
Definition alloc.c:85
Definition alloc.c:71
size_t size
Definition alloc.c:77
struct empty_block_ * next
Definition alloc.c:72