From cb094cfad9251af00ff891282951412728cfa86f Mon Sep 17 00:00:00 2001 From: Anghelo Carvajal Date: Mon, 25 Aug 2025 17:11:44 -0400 Subject: [PATCH] match osmalloc --- include/libc64/os_malloc.h | 18 +++ src/boot/libc64/__osMalloc.c | 235 +++++++++++++++++++++++++++++++++-- src/code/gamealloc.c | 3 + 3 files changed, 248 insertions(+), 8 deletions(-) diff --git a/include/libc64/os_malloc.h b/include/libc64/os_malloc.h index 78cbc233f3..30b54cf270 100644 --- a/include/libc64/os_malloc.h +++ b/include/libc64/os_malloc.h @@ -4,12 +4,23 @@ #include "ultra64.h" #include "stddef.h" +#include "unk.h" +#include "versions.h" + typedef struct ArenaNode { /* 0x0 */ s16 magic; // Should always be 0x7373 /* 0x2 */ s16 isFree; /* 0x4 */ size_t size; /* 0x8 */ struct ArenaNode* next; /* 0xC */ struct ArenaNode* prev; + #if MM_VERSION <= N64_JP_1_1 + /* 0x10 */ s32 unk_10; + /* 0x14 */ s32 unk_14; + /* 0x18 */ OSId unk_18; + /* 0x1C */ void *unk_1C; + /* 0x20 */ OSTime unk_20; + /* 0x28 */ UNK_TYPE1 unk_28[0x8]; + #endif } ArenaNode; // size = 0x10 typedef struct Arena { @@ -31,4 +42,11 @@ void* __osRealloc(Arena* arena, void* ptr, size_t newSize); void __osGetSizes(Arena* arena, size_t* outMaxFree, size_t* outFree, size_t* outAlloc); s32 __osCheckArena(Arena* arena); +#if MM_VERSION <= N64_JP_1_1 +void *__osMallocDebug(Arena *arena, size_t size, s32 arg2, s32 arg3); +void *__osMallocRDebug(Arena *arena, size_t size, s32 arg2, s32 arg3); + +void *__osReallocDebug(Arena* arena, void* ptr, size_t newSize, const char* file, int line); +#endif + #endif diff --git a/src/boot/libc64/__osMalloc.c b/src/boot/libc64/__osMalloc.c index 0cd35afffc..005bb1b7f9 100644 --- a/src/boot/libc64/__osMalloc.c +++ b/src/boot/libc64/__osMalloc.c @@ -19,6 +19,35 @@ #define BLOCK_FREE_MAGIC (0xEF) #define BLOCK_FREE_MAGIC_32 (0xEFEFEFEF) +#if MM_VERSION >= N64_US +#define SET_DEBUG_INFO(node, f, l, a) ((void)0) + +#define CHECK_CORRECT_ARENA(node, arena) (true) + +#define CHECK_ALLOC_FAILURE(arena, ptr) (void)0 +#else +#define SET_DEBUG_INFO(node, f, l, a) \ + { \ + node->unk_10 = (f); \ + node->unk_14 = (l); \ + node->unk_18 = osGetThreadId(NULL); \ + node->unk_1C = (a); \ + node->unk_20 = osGetTime(); \ + } (void) 0 + +#define CHECK_CORRECT_ARENA(node, arena) ((node)->unk_1C == (arena)) + +s32 D_800984A0_unknown = 0; + +#define CHECK_ALLOC_FAILURE(arena, ptr) \ + do { \ + if ((ptr) == NULL) { \ + D_800984A0_unknown++; \ + (arena)->unk20++; \ + } \ + } while (0) +#endif + OSMesg sArenaLockMsg[1]; void __osMallocAddHeap(Arena* arena, void* heap, size_t size); @@ -127,6 +156,105 @@ u8 __osMallocIsInitialized(Arena* arena) { return arena->isInit; } +#if MM_VERSION <= N64_JP_1_1 +void *__osMallocDebug(Arena *arena, size_t size, s32 arg2, s32 arg3) { + ArenaNode *iter; + ArenaNode *newNode; + void *alloc = NULL; + + ArenaImpl_Lock(arena); + + size = ALIGN16(size); + + for (iter = arena->head; iter != NULL; iter = iter->next) { + if (iter->isFree && (iter->size >= size)) { + size_t blockSize = ALIGN16(size) + sizeof(ArenaNode); + + if (blockSize < iter->size) { + ArenaNode* next; + + newNode = (ArenaNode*)((uintptr_t)iter + blockSize); + newNode->next = iter->next; + newNode->prev = iter; + newNode->size = iter->size - blockSize; + newNode->isFree = true; + newNode->magic = NODE_MAGIC; + + iter->next = newNode; + iter->size = size; + + next = newNode->next; + if (next != NULL) { + next->prev = newNode; + } + } + + iter->isFree = false; + + SET_DEBUG_INFO(iter, arg2, arg3, arena); + + alloc = (void*)((uintptr_t)iter + sizeof(ArenaNode)); + break; + } + } + + CHECK_ALLOC_FAILURE(arena, alloc); + + ArenaImpl_Unlock(arena); + + return alloc; +} + +void *__osMallocRDebug(Arena *arena, size_t size, s32 arg2, s32 arg3) { + ArenaNode *iter; + ArenaNode *newNode; + size_t blockSize; + void *alloc = NULL; + + size = ALIGN16(size); + + ArenaImpl_Lock(arena); + + for (iter = ArenaImpl_GetLastBlock(arena); iter != NULL; iter = iter->prev) { + if (iter->isFree && (iter->size >= size)) { + blockSize = ALIGN16(size) + sizeof(ArenaNode); + + if (blockSize < iter->size) { + ArenaNode *next; + + newNode = (ArenaNode*)(((uintptr_t)iter + iter->size) - size); + newNode->next = iter->next; + newNode->prev = iter; + newNode->size = size; + newNode->magic = NODE_MAGIC; + + iter->next = newNode; + iter->size -= blockSize; + + next = newNode->next; + if (next != NULL) { + next->prev = newNode; + } + iter = newNode; + } + + iter->isFree = false; + + SET_DEBUG_INFO(iter, arg2, arg3, arena); + + alloc = (void*)((uintptr_t)iter + sizeof(ArenaNode)); + break; + } + } + + CHECK_ALLOC_FAILURE(arena, alloc); + + ArenaImpl_Unlock(arena); + + return alloc; +} +#endif + /** * Allocates at least \p size bytes of memory using the given \p arena. * The block of memory will be allocated at the start of the first sufficiently large free block. @@ -155,7 +283,7 @@ void* __osMalloc(Arena* arena, size_t size) { // Iterate over the arena looking for a big enough space of memory. while (iter != NULL) { - if (iter->isFree && iter->size >= size) { + if (iter->isFree && (iter->size >= size)) { size_t blockSize = ALIGN16(size) + sizeof(ArenaNode); // If the block is larger than the requested size, then split it and just use the required size of the @@ -180,6 +308,9 @@ void* __osMalloc(Arena* arena, size_t size) { } iter->isFree = false; + + SET_DEBUG_INFO(iter, 0, 0, arena); + alloc = (void*)((uintptr_t)iter + sizeof(ArenaNode)); break; } @@ -187,6 +318,8 @@ void* __osMalloc(Arena* arena, size_t size) { iter = iter->next; } + CHECK_ALLOC_FAILURE(arena, alloc); + ArenaImpl_Unlock(arena); return alloc; @@ -245,12 +378,17 @@ void* __osMallocR(Arena* arena, size_t size) { } iter->isFree = false; + + SET_DEBUG_INFO(iter, 0, 0, arena); + alloc = (void*)((uintptr_t)iter + sizeof(ArenaNode)); break; } iter = iter->prev; } + CHECK_ALLOC_FAILURE(arena, alloc); + ArenaImpl_Unlock(arena); return alloc; @@ -275,16 +413,25 @@ void __osFree(Arena* arena, void* ptr) { ArenaImpl_Lock(arena); + // __osFree:Unauthorized release(%08x)\n + (void) "__osFree:不正解放(%08x)\n"; + // __osFree:Double release(%08x)\n + (void) "__osFree:二重解放(%08x)\n"; + // __osFree:arena(%08x) and __osMallocのarena(%08x) do not match\n + (void) "__osFree:arena(%08x)が__osMallocのarena(%08x)と一致しない\n"; + node = (ArenaNode*)((uintptr_t)ptr - sizeof(ArenaNode)); - if ((ptr != NULL) && (node->magic == NODE_MAGIC) && !node->isFree) { + if ((ptr != NULL) && (node->magic == NODE_MAGIC) && !node->isFree && CHECK_CORRECT_ARENA(node, arena)) { next = node->next; prev = node->prev; node->isFree = true; + SET_DEBUG_INFO(node, 0, 0, arena); + // Checks if the next node is contiguous to the current node and if it isn't currently allocated. Then merge the // two nodes into one. - if ((uintptr_t)next == (uintptr_t)node + sizeof(ArenaNode) + node->size && next->isFree) { + if (((uintptr_t)next == (uintptr_t)node + sizeof(ArenaNode) + node->size) && next->isFree) { ArenaNode* newNext = next->next; if (newNext != NULL) { @@ -312,6 +459,62 @@ void __osFree(Arena* arena, void* ptr) { ArenaImpl_Unlock(arena); } +// TODO +#if MM_VERSION <= N64_JP_1_1 +void __osFreeDebug(Arena* arena, void* ptr, s32 arg2, s32 arg3) { + ArenaNode* node; + ArenaNode* next; + ArenaNode* prev; + + ArenaImpl_Lock(arena); + + // __osFree:Unauthorized release(%08x)\n + (void) "__osFree:不正解放(%08x)\n"; + // __osFree:Double release(%08x)\n + (void) "__osFree:二重解放(%08x)\n"; + // __osFree:arena(%08x) and __osMallocのarena(%08x) do not match\n + (void) "__osFree:arena(%08x)が__osMallocのarena(%08x)と一致しない\n"; + + node = (ArenaNode*)((uintptr_t)ptr - sizeof(ArenaNode)); + + if ((ptr != NULL) && (node->magic == NODE_MAGIC) && !node->isFree && CHECK_CORRECT_ARENA(node, arena)) { + next = node->next; + prev = node->prev; + node->isFree = true; + + SET_DEBUG_INFO(node, arg2, arg3, arena); + + // Checks if the next node is contiguous to the current node and if it isn't currently allocated. Then merge the + // two nodes into one. + if (((uintptr_t)next == (uintptr_t)node + sizeof(ArenaNode) + node->size) && next->isFree) { + ArenaNode* newNext = next->next; + + if (newNext != NULL) { + newNext->prev = node; + } + + node->size += next->size + sizeof(ArenaNode); + + node->next = newNext; + next = newNext; + } + + // Checks if the previous node is contiguous to the current node and if it isn't currently allocated. Then merge + // the two nodes into one. + if ((prev != NULL) && prev->isFree && ((uintptr_t)node == (uintptr_t)prev + sizeof(ArenaNode) + prev->size)) { + if (next != NULL) { + next->prev = prev; + } + + prev->next = next; + prev->size += node->size + sizeof(ArenaNode); + } + } + + ArenaImpl_Unlock(arena); +} +#endif + /** * Reallocates the pointer \p ptr. * \p ptr must be either a pointer previously allocated by `__osMalloc`, `__osMallocR` or `__osRealloc` and @@ -354,18 +557,18 @@ void* __osRealloc(Arena* arena, void* ptr, size_t newSize) { newSize = ALIGN16(newSize); - // Only reallocate the memory if the new size isn't smaller than the actual node size - if ((newSize != node->size) && (node->size < newSize)) { + if (newSize == node->size) { + // Do nothing + } else if (node->size < newSize) { ArenaNode* next = node->next; diff = newSize - node->size; // Checks if the next node is contiguous to the current allocated node and it has enough space to fit the // new requested size - if (((uintptr_t)next == (uintptr_t)node + node->size + sizeof(ArenaNode)) && (next->isFree) && - (next->size >= diff)) { + if (((uintptr_t)next == (uintptr_t)node + node->size + sizeof(ArenaNode)) && next->isFree && (next->size >= diff)) { ArenaNode* next2 = next->next; - next->size = (next->size - diff); + next->size = next->size - diff; if (next2 != NULL) { // Update the previous element of the linked list next2->prev = (void*)((uintptr_t)next + diff); @@ -384,14 +587,24 @@ void* __osRealloc(Arena* arena, void* ptr, size_t newSize) { } ptr = newPtr; } + } else if (newSize < node->size) { + } } + CHECK_ALLOC_FAILURE(arena, ptr); + ArenaImpl_Unlock(arena); return ptr; } +#if MM_VERSION <= N64_JP_1_1 +void *__osReallocDebug(Arena* arena, void* ptr, size_t newSize, const char* file, int line) { + return __osRealloc(arena, ptr, newSize); +} +#endif + /** * Gets the size of the largest free block, the total free space and the total allocated space. * @@ -458,3 +671,9 @@ s32 __osCheckArena(Arena* arena) { return err; } + +#if MM_VERSION <= N64_JP_1_1 +u8 ArenaImpl_GetAllocFailures(Arena* arena) { + return arena->unk20; +} +#endif diff --git a/src/code/gamealloc.c b/src/code/gamealloc.c index 58894324b0..06bd527202 100644 --- a/src/code/gamealloc.c +++ b/src/code/gamealloc.c @@ -5,7 +5,10 @@ void GameAlloc_Log(GameAlloc* this) { GameAllocEntry* iter = this->base.next; + (void)"this = %08x\n"; + while (iter != &this->base) { + (void)"ptr = %08x size = %d\n"; iter = iter->next; } }