Implement & link __osMalloc

This commit is contained in:
Cuyler36
2025-05-25 20:22:56 -04:00
parent 95991ba293
commit 2b61a6760d
6 changed files with 662 additions and 54 deletions
+1 -1
View File
@@ -371,7 +371,7 @@ config.libs = [
"progress_category": "library",
"src_dir": "src/static",
"objects": [
Object(NonMatching, "libc64/__osMalloc.c"),
Object(Matching, "libc64/__osMalloc.c"),
Object(Matching, "libc64/aprintf.c"),
Object(Matching, "libc64/math64.c"),
Object(Matching, "libc64/qrand.c"),
+40 -49
View File
@@ -7,62 +7,53 @@
#include "libultra/osMesg.h"
#include "libultra/ultratypes.h"
struct ArenaNode;
#ifdef __cplusplus
extern "C" {
#endif // !__cplusplus
typedef struct Arena {
/* 0x00 */ struct ArenaNode* head;
/* 0x04 */ void* start;
/* 0x08 */ OSMesgQueue lockQueue;
/* 0x20 */ u8 unk_20;
/* 0x21 */ u8 isInit;
/* 0x22 */ u8 flag;
/* 0x24 */ u8 pad[0x2C - 0x24];
} Arena; // size = 0x2C
typedef struct ArenaNode {
typedef struct OSMemBlock_ {
/* 0x00 */ s16 magic;
/* 0x02 */ s16 isFree;
/* 0x04 */ u32 size;
/* 0x08 */ struct ArenaNode* next;
/* 0x0C */ struct ArenaNode* prev;
/* 0x10 */ const char* filename;
/* 0x02 */ s16 free;
/* 0x04 */ s32 size;
/* 0x08 */ struct OSMemBlock_* next;
/* 0x0C */ struct OSMemBlock_* prev;
/* 0x10 */ const char* file;
/* 0x14 */ s32 line;
/* 0x18 */ OSId threadId;
/* 0x1C */ Arena* arena;
/* 0x1C */ struct OSArena_* arena;
/* 0x20 */ OSTime time;
/* 0x28 */ u8 unk_28[0x30 - 0x28]; // probably padding
} ArenaNode; // size = 0x30
/* 0x28 */ u8 pad[0x30 - 0x28];
} OSMemBlock; // size = 0x30
void setDebugInfo(ArenaNode*, const char*, s32, Arena*);
void arena_lock_init(Arena*);
void arena_lock(Arena*);
void arena_unlock(Arena*);
ArenaNode* get_block_next(ArenaNode*);
ArenaNode* get_block_prev(ArenaNode*);
ArenaNode* search_last_block(Arena*);
void __osMallocInit(Arena*, void*, u32);
void __osMallocAddBlock(Arena*, void*, s32);
void destroy_all_block(Arena*);
void __osMallocCleanup(Arena*);
BOOL __osMallocIsInitialized(Arena*);
void __osMalloc_FreeBlockTest(Arena*, ArenaNode*);
void* __osMallocALign_NoLock(Arena*, u32, u32);
void* __osMalloc_NoLock(Arena*, u32);
void* __osMallocAlign(Arena*, u32, u32);
void* __osMalloc(Arena*, u32);
void* __osMallocR(Arena*, u32);
void __osFree_NoLock(Arena*, void*);
void __osFree(Arena*, void*);
void* __osRealloc(Arena*, void*, u32);
int __osAnalayzeArena(Arena*, u32*);
void __osGetFreeArena(Arena*, u32*, u32*, u32*);
u32 __osGetTotalFreeSize(Arena*);
u32 __osGetFreeSize(Arena*);
u32 __osGetMemBlockSize(Arena*, void*);
void __osDisplayArena(Arena*);
int __osCheckArena(Arena*);
extern int __osMallocIsInitalized(Arena*);
typedef struct OSArena_ {
/* 0x00 */ OSMemBlock* head;
/* 0x04 */ u8* base;
/* 0x08 */ OSMessageQueue lockQueue;
/* 0x20 */ u8 _28;
/* 0x21 */ u8 initialized;
/* 0x22 */ u8 flags;
} OSArena; // size = 0x2C
extern void __osMallocInit(OSArena* arena, u8* base, s32 size);
extern void __osMallocAddBlock(OSArena* arena, u8* base, s32 size);
extern void __osMallocCleanup(OSArena* arena);
extern BOOL __osMallocIsInitalized(OSArena* arena);
extern void* __osMallocAlign(OSArena* arena, u32 size, u32 align);
extern void* __osMalloc(OSArena* arena, u32 size);
extern void* __osMallocR(OSArena* arena, u32 size);
extern void __osFree(OSArena* arena, void* ptr);
extern void* __osRealloc(OSArena* arena, void* ptr, u32 size);
extern void __osGetFreeArena(OSArena* arena, u32* max_free_block_size, u32* free_blocks_size, u32* used_blocks_size);
extern u32 __osGetTotalFreeSize(OSArena* arena);
extern u32 __osGetFreeSize(OSArena* arena);
extern s32 __osGetMemBlockSize(OSArena* arena, void* ptr);
extern void __osDisplayArena(OSArena* arena);
extern int __osCheckArena(OSArena* arena);
extern int __osMalloc_FreeBlockTest_Enable;
#ifdef __cplusplus
}
#endif // !__cplusplus
#endif
+1 -1
View File
@@ -8,7 +8,7 @@
extern "C" {
#endif
extern Arena zelda_arena;
extern OSArena zelda_arena;
extern void* zelda_malloc_align(size_t,u32);
extern void* zelda_malloc(size_t);
+1 -1
View File
@@ -1,6 +1,6 @@
#include "m_malloc.h"
Arena zelda_arena;
OSArena zelda_arena;
extern void* zelda_malloc_align(size_t size, u32 align) {
return __osMallocAlign(&zelda_arena, size, align);
+618 -1
View File
@@ -1 +1,618 @@
int __osMalloc_FreeBlockTest_Enable;
#include "libc64/__osMalloc.h"
#include <dolphin/os.h>
#include "libultra/libultra.h"
#include "terminal.h"
#define OS_MALLOC_MAGIC (s16)'ss'
int __osMalloc_FreeBlockTest_Enable = FALSE;
static void setDebugInfo(OSMemBlock* block, const char* file, s32 line, OSArena* arena) {
block->file = file;
block->line = line;
block->threadId = osGetThreadId(NULL);
block->arena = arena;
block->time = osGetTime();
}
static void arena_lock_init(OSArena* arena) {
static OSMesg arena_lock_msg;
osCreateMesgQueue(&arena->lockQueue, &arena_lock_msg, OS_MESG_BLOCK);
}
static void arena_lock(OSArena* arena) {
osSendMesg(&arena->lockQueue, NULL, OS_MESG_BLOCK);
}
static void arena_unlock(OSArena* arena) {
osRecvMesg(&arena->lockQueue, NULL, OS_MESG_BLOCK);
}
static OSMemBlock* get_block_next(OSMemBlock* block) {
// @BUG - this shouldn't check for block->next != NULL
if (block->next != NULL && (block->next == NULL || block->next->magic != OS_MALLOC_MAGIC)) {
OSReport(VT_COL(RED, WHITE) "緊急事態!メモリリーク発見! (block=%08x)\n" VT_RST, block->next);
OSPanic(__FILE__, 133, "");
block->next = NULL; // @BUG - OSPanic halts CPU execution, this is pointless
}
return block->next;
}
static OSMemBlock* get_block_prev(OSMemBlock* block) {
// @BUG - this shouldn't check for block->prev != NULL
if (block->prev != NULL && (block->prev == NULL || block->prev->magic != OS_MALLOC_MAGIC)) {
OSReport(VT_COL(RED, WHITE) "緊急事態!メモリリーク発見! (block=%08x)\n" VT_RST, block->prev);
OSPanic(__FILE__, 144, "");
block->prev = NULL; // @BUG - OSPanic halts CPU execution, this is pointless
}
return block->prev;
}
static OSMemBlock* search_last_block(OSArena* arena) {
OSMemBlock* block = NULL;
if (arena != NULL) {
OSMemBlock* current = arena->head;
if (current != NULL && current->magic == OS_MALLOC_MAGIC) {
while (current != NULL) {
block = current;
current = get_block_next(current);
}
}
}
return block;
}
extern void __osMallocInit(OSArena* arena, u8* base, s32 size) {
bzero(arena, sizeof(OSArena));
arena_lock_init(arena);
__osMallocAddBlock(arena, base, size);
arena->initialized = TRUE;
}
extern void __osMallocAddBlock(OSArena* arena, u8* base, s32 size) {
s32 align_size;
OSMemBlock* block;
OSMemBlock* last;
if (base != NULL) {
block = (OSMemBlock*)ALIGN_NEXT((u32)base, 32);
align_size = ALIGN_PREV(size - ((u32)block - (u32)base), 32);
if (align_size > (int)sizeof(OSMemBlock)) {
memset(block, 0xAB, align_size);
block->next = NULL;
block->prev = NULL;
block->size = align_size - sizeof(OSMemBlock);
block->free = TRUE;
block->magic = OS_MALLOC_MAGIC;
arena_lock(arena);
last = search_last_block(arena);
if (last == NULL) {
arena->head = block;
arena->base = base;
} else {
block->prev = last;
last->next = block;
}
arena_unlock(arena);
}
}
}
static void destroy_all_block(OSArena* arena) {
OSMemBlock* block;
OSMemBlock* next;
arena_lock(arena);
block = arena->head;
while (block != NULL) {
next = get_block_next(block);
memset(block, 0xAB, block->size + sizeof(OSMemBlock));
block = next;
}
arena_unlock(arena);
}
extern void __osMallocCleanup(OSArena* arena) {
destroy_all_block(arena);
bzero(arena, sizeof(OSArena));
}
extern BOOL __osMallocIsInitalized(OSArena* arena) {
return arena->initialized;
}
static void __osMalloc_FreeBlockTest(OSArena* arena, OSMemBlock* block) {
if (__osMalloc_FreeBlockTest_Enable) {
u32* s = (u32*)((u8*)block + sizeof(OSMemBlock));
u32* e = (u32*)((u8*)block + sizeof(OSMemBlock) + block->size);
u32* p;
for (p = s; p < e; p++) {
u32 v = *p;
if (v != (u32)'\xAB\xAB\xAB\xAB' && v != (u32)'\xEF\xEF\xEF\xEF') {
OSReport(VT_COL(RED, WHITE) "緊急事態!メモリリーク検出! (block=%08x s=%08x e=%08x p=%08x)\n" VT_RST, block, s, e, p);
__osDisplayArena(arena);
OSPanic(__FILE__, 300, "");
break;
}
}
}
}
static void* __osMallocAlign_NoLock(OSArena* arena, u32 size, u32 align) {
OSMemBlock* aligned_block;
OSMemBlock* new_next;
int alignment_bytes;
OSMemBlock* block;
u8* data_p = NULL;
OSMemBlock* next;
u32 full_size;
u32 mask;
int remain;
size = ALIGN_NEXT(size, 32);
full_size = ALIGN_NEXT(size, 32) + sizeof(OSMemBlock);
if (align <= 16) {
align = 16;
} else if (align <= 32) {
align = 32;
} else if (align <= 64) {
align = 64;
} else if (align <= 128) {
align = 128;
} else if (align <= 256) {
align = 256;
} else if (align <= 1024) {
align = 1024;
} else {
align = 8;
}
block = arena->head;
mask = align - 1;
while (block != NULL) {
if (block->free) {
remain = ((u32)block + sizeof(OSMemBlock)) & mask;
alignment_bytes = remain == 0 ? 0 : align - remain;
aligned_block = (OSMemBlock*)((u32)block + alignment_bytes);
if (block->size - alignment_bytes >= size) {
if (arena->flags & 0x4) {
__osMalloc_FreeBlockTest(arena, block);
}
if (block != aligned_block) {
memmove(aligned_block, block, sizeof(OSMemBlock));
block = aligned_block;
block->size -= alignment_bytes;
if (block->prev != NULL) {
block->prev->next = block;
block->prev->size += alignment_bytes; // do not orphan alignment bytes
} else {
arena->head = block;
}
if (block->next != NULL) {
block->next->prev = block;
}
}
if (block->size > full_size) {
new_next = (OSMemBlock*)((u32)block + full_size);
new_next->next = get_block_next(block);
new_next->prev = block;
new_next->size = block->size - full_size;
new_next->free = TRUE;
new_next->magic = OS_MALLOC_MAGIC;
block->next = new_next;
block->size = size;
next = get_block_next(new_next);
if (next != NULL) {
next->prev = new_next;
}
}
block->free = FALSE;
setDebugInfo(block, NULL, 0, arena);
data_p = (u8*)block + sizeof(OSMemBlock);
if (arena->flags & 0x1) {
memset(data_p, 0xCD, size);
}
break;
}
}
block = get_block_next(block);
}
return data_p;
}
static void* __osMalloc_NoLock(OSArena* arena, u32 size) {
return __osMallocAlign_NoLock(arena, size, 0);
}
extern void* __osMallocAlign(OSArena* arena, u32 size, u32 align) {
void* ret;
arena_lock(arena);
ret = __osMallocAlign_NoLock(arena, size, align);
arena_unlock(arena);
return ret;
}
extern void* __osMalloc(OSArena* arena, u32 size) {
return __osMallocAlign(arena, size, 0);
}
extern void* __osMallocR(OSArena* arena, u32 size) {
OSMemBlock* block;
OSMemBlock* next;
OSMemBlock* n;
u8* ret = NULL;
u32 full_size;
size = ALIGN_NEXT(size, 32);
full_size = ALIGN_NEXT(size, 32) + sizeof(OSMemBlock);
arena_lock(arena);
block = search_last_block(arena);
while (block != NULL) {
if (block->free && block->size >= size) {
if (arena->flags & 0x4) {
__osMalloc_FreeBlockTest(arena, block);
}
if (block->size > full_size) {
next = (OSMemBlock*)((u32)block + block->size - size);
next->next = get_block_next(block);
next->prev = block;
next->size = size;
next->magic = OS_MALLOC_MAGIC;
block->next = next;
block->size -= full_size;
n = get_block_next(next);
if (n != NULL) {
n->prev = next;
}
block = next;
}
block->free = FALSE;
setDebugInfo(block, NULL, 0, arena);
ret = (u8*)block + sizeof(OSMemBlock);
if (arena->flags & 0x1) {
memset(ret, 0xCD, size);
}
break;
}
block = get_block_prev(block);
}
arena_unlock(arena);
return ret;
}
static void __osFree_NoLock(OSArena* arena, void* ptr) {
OSMemBlock* block = (OSMemBlock*)((u32)ptr - sizeof(OSMemBlock));
OSMemBlock* next;
OSMemBlock* prev;
OSMemBlock* temp;
if (ptr != NULL) {
if (block == NULL || block->magic != OS_MALLOC_MAGIC) {
OSReport(VT_COL(RED, WHITE) "__osFree:不正解放(%08x)\n" VT_RST, ptr); // __osFree: irregular deallocation
OSPanic(__FILE__, 738, "");
return;
}
if (block->free) {
OSReport(VT_COL(RED, WHITE) "__osFree:二重解放(%08x)\n" VT_RST, ptr); // __osFree: double deallocation
OSPanic(__FILE__, 743, "");
return;
}
if (block->arena != arena && arena != NULL) {
OSReport(VT_COL(RED, WHITE) "__osFree:確保時と違う方法で解放しようとした (%08x:%08x)\n" VT_RST, arena, block->arena); // __osFree: attempt to release memory in a different arena from where it was allocated
OSPanic(__FILE__, 750, "");
return;
}
next = get_block_next(block);
prev = get_block_prev(block);
block->free = TRUE;
setDebugInfo(block, NULL, 0, arena);
if (arena->flags & 0x2) {
memset((u8*)block + sizeof(OSMemBlock), 0xEF, block->size);
}
if (next == (OSMemBlock*)((u32)block + sizeof(OSMemBlock) + block->size)) {
if (next->free) {
temp = get_block_next(next);
if (temp != NULL) {
temp->prev = block;
}
block->size += next->size + sizeof(OSMemBlock);
if (arena->flags & 0x2) {
memset(next, 0xEF, sizeof(OSMemBlock));
}
block->next = temp;
next = temp;
}
}
if (prev != NULL && prev->free && block == (OSMemBlock*)((u32)prev + sizeof(OSMemBlock) + prev->size)) {
if (next != NULL) {
next->prev = prev;
}
prev->next = next;
prev->size += block->size + sizeof(OSMemBlock);
if (arena->flags & 0x2) {
memset(block, 0xEF, sizeof(OSMemBlock));
}
}
}
}
extern void __osFree(OSArena* arena, void* ptr) {
arena_lock(arena);
__osFree_NoLock(arena, ptr);
arena_unlock(arena);
}
// @fabricated, most likely suspect from DnM+'s symbol map
static void* __osFree_NoLock_DEBUG(OSArena* arena, void* ptr) {
OSReport(VT_COL(RED, WHITE) "__osFree:不正解放(%08x) [%s:%d ]\n" VT_RST);
OSReport(VT_COL(RED, WHITE) "__osFree:二重解放(%08x) [%s:%d ]\n" VT_RST);
}
#pragma force_active on
extern void* __osRealloc(OSArena* arena, void* ptr, u32 size) {
void* new_ptr;
OSMemBlock* orig_block;
OSMemBlock* next;
OSMemBlock* temp;
OSMemBlock* new_next;
u32 full_size;
u32 need_size;
orig_block = (OSMemBlock*)((u32)ptr - sizeof(OSMemBlock));
size = ALIGN_NEXT(size, 32);
full_size = ALIGN_NEXT(size, 32) + sizeof(OSMemBlock);
arena_lock(arena);
if (ptr == NULL) {
ptr = __osMalloc_NoLock(arena, size);
} else if (size == 0) {
__osFree_NoLock(arena, ptr);
ptr = NULL;
} else if (size != orig_block->size) {
if (size > orig_block->size) {
next = get_block_next(orig_block);
need_size = size - orig_block->size;
if (next == (OSMemBlock*)((u32)orig_block + sizeof(OSMemBlock) + orig_block->size) && next->free && next->size >= need_size) {
OSMemBlock* new_next = (OSMemBlock*)((u32)next + need_size);
next->size -= need_size;
temp = get_block_next(next);
if (temp != NULL) {
temp->prev = new_next;
}
orig_block->next = new_next;
orig_block->size = size;
memmove(new_next, next, sizeof(OSMemBlock));
} else {
new_ptr = __osMalloc_NoLock(arena, size);
if (new_ptr != NULL) {
memmove(new_ptr, ptr, orig_block->size);
__osFree_NoLock(arena, ptr);
}
ptr = new_ptr;
}
} else if (size < orig_block->size) {
next = get_block_next(orig_block);
if (next != NULL && next->free) {
OSMemBlock* new_next = (OSMemBlock*)((u32)orig_block + full_size);
*new_next = *next;
new_next->size += orig_block->size - size;
orig_block->next = new_next;
orig_block->size = size;
temp = get_block_next(new_next);
if (temp != NULL) {
temp->prev = new_next;
}
} else if (size + sizeof(OSMemBlock) < orig_block->size) {
new_next = (OSMemBlock*)((u32)orig_block + full_size);
new_next->next = get_block_next(orig_block);
new_next->prev = orig_block;
new_next->size = orig_block->size - full_size;
new_next->free = TRUE;
new_next->magic = OS_MALLOC_MAGIC;
orig_block->next = new_next;
orig_block->size = size;
temp = get_block_next(new_next);
if (temp != NULL) {
temp->prev = new_next;
}
} else {
ptr = NULL;
}
}
}
arena_unlock(arena);
return ptr;
}
#pragma force_active reset
static int __osAnalyzeArena(OSArena* arena, u32* ptr) {
OSMemBlock* block;
u32 max_free_block_size = 0;
u32 free_blocks_size = 0;
u32 free_blocks = 0;
u32 used_blocks_size = 0;
u32 used_blocks = 0;
if (arena == NULL || ptr == NULL) {
return -1;
}
arena_lock(arena);
for (block = arena->head; block != NULL; block = get_block_next(block)) {
if (block->free) {
free_blocks++;
free_blocks_size += block->size;
if (max_free_block_size < block->size) {
max_free_block_size = block->size;
}
} else {
used_blocks++;
used_blocks_size += block->size;
}
}
ptr[0] = max_free_block_size;
ptr[1] = free_blocks_size;
ptr[2] = free_blocks;
ptr[3] = used_blocks_size;
ptr[4] = used_blocks;
arena_unlock(arena);
return 0;
}
extern void __osGetFreeArena(OSArena* arena, u32* max_free_block_size, u32* free_blocks_size, u32* used_blocks_size) {
u32 data[5];
if (__osAnalyzeArena(arena, data) == 0) {
if (max_free_block_size != NULL) {
*max_free_block_size = data[0];
}
if (free_blocks_size != NULL) {
*free_blocks_size = data[1];
}
if (used_blocks_size != NULL) {
*used_blocks_size = data[3];
}
}
}
extern u32 __osGetTotalFreeSize(OSArena* arena) {
u32 total_free_size;
__osGetFreeArena(arena, NULL, &total_free_size, NULL);
return total_free_size;
}
#pragma force_active on
extern u32 __osGetFreeSize(OSArena* arena) {
u32 free_size;
__osGetFreeArena(arena, &free_size, NULL, NULL);
return free_size;
}
#pragma force_active reset
extern s32 __osGetMemBlockSize(OSArena* arena, void* ptr) {
OSMemBlock* block;
if (ptr == NULL) {
return -1;
}
block = (OSMemBlock*)((u32)ptr - sizeof(OSMemBlock));
if (block != NULL && block->magic == OS_MALLOC_MAGIC) {
return block->size;
}
return -1;
}
extern void __osDisplayArena(OSArena* arena) {
OSMemBlock* block;
OSMemBlock* next;
u32 max_free;
u32 total_free;
u32 total_used;
if (__osMallocIsInitalized(arena)) {
arena_lock(arena);
max_free = 0;
total_free = 0;
total_used = 0;
OSReport("アリーナの内容 (0x%08x)\n", arena);
OSReport("メモリブロック範囲 status サイズ [時刻 s ms us ns: TID:src:行]\n");
block = arena->head;
while (block != NULL) {
if (block != NULL && block->magic == OS_MALLOC_MAGIC) {
next = block->next;
OSReport("%08x-%08x%c %s %08x", (u32)block, (u32)block + sizeof(OSMemBlock) + block->size, next == NULL ? '$' : (next->prev != block ? '!' : ' '), block->free ? "空き" : "確保", block->size);
if (!block->free) {
OSReport(" [%016llu:%2d:%s:%d]", OSTicksToMicroseconds((u64)block->time * 1000), block->threadId, block->file != NULL ? block->file : "**NULL**", block->line);
}
OSReport("\n");
if (block->free) {
total_free += block->size;
if (max_free < block->size) {
max_free = block->size;
}
} else {
total_used += block->size;
}
} else {
OSReport("%08x Block Invalid\n", block);
next = NULL;
}
block = next;
}
OSReport("確保ブロックサイズの合計 0x%08x バイト\n", total_used);
OSReport("空きブロックサイズの合計 0x%08x バイト\n", total_free);
OSReport("最大空きブロックサイズ 0x%08x バイト\n", max_free);
arena_unlock(arena);
}
}
#pragma force_active on
extern int __osCheckArena(OSArena* arena) {
int ret = FALSE;
OSMemBlock* block;
arena_lock(arena);
block = arena->head;
while (block != NULL) {
if (block == NULL || block->magic != OS_MALLOC_MAGIC) {
OSReport(VT_COL(RED, WHITE) "おおっと!! (%08x %08x)\n" VT_RST, block, block->magic);
OSPanic(__FILE__, 1307, "");
ret = TRUE;
break;
}
block = get_block_next(block);
}
arena_unlock(arena);
return ret;
}
#pragma force_active reset
+1 -1
View File
@@ -1,7 +1,7 @@
#include "libc64/__osMalloc.h"
#include "libc64/malloc.h"
Arena malloc_arena;
OSArena malloc_arena;
extern void* malloc(size_t size) {
return __osMalloc(&malloc_arena, size);