/*\r
-AcessOS Basic LibC\r
-heap.c - Heap Manager\r
-*/\r
+ * Acess2 C Library\r
+ * - By John Hodge (thePowersGang)\r
+ *\r
+ * heap.c\r
+ * - Dynamic Memory (malloc/free)\r
+ */\r
#include <acess/sys.h>\r
#include <stdlib.h>\r
#include <string.h>\r
+#include <assert.h>\r
+#include <stdbool.h>\r
#include "lib.h"\r
\r
#if 0\r
#define MAGIC_FREE (~MAGIC)\r
#define BLOCK_SIZE 16 //Minimum\r
#define HEAP_INIT_SIZE 0x10000\r
+#define DEBUG_HEAP 1\r
\r
typedef unsigned int Uint;\r
\r
typedef struct {\r
uint32_t magic;\r
size_t size;\r
+ // - \r
+ #if DEBUG_HEAP\r
+ void *Caller;\r
+ size_t RealSize;\r
+ #endif\r
char data[];\r
} heap_head;\r
typedef struct {\r
uint32_t magic;\r
} heap_foot;\r
\r
+// === HELPERS ===\r
+static inline heap_head *NEXT_HEAD(heap_head *ptr)\r
+{\r
+ return (void*)((char*)ptr + ptr->size);\r
+}\r
+static inline heap_foot *THIS_FOOT(heap_head *ptr)\r
+{\r
+ return (void*)((char*)ptr + ptr->size - sizeof(heap_foot));\r
+}\r
+static inline heap_foot *PREV_FOOT(heap_head *ptr)\r
+{\r
+ return (void*)((char*)ptr - sizeof(heap_foot));\r
+}\r
+static inline size_t CALC_BLOCK_SIZE(size_t bytes)\r
+{\r
+ size_t ret;\r
+ ret = bytes + sizeof(heap_head) + sizeof(heap_foot) + BLOCK_SIZE - 1;\r
+ ret = (ret/BLOCK_SIZE)*BLOCK_SIZE; //Round up to block size\r
+ return ret;\r
+}\r
+\r
// === LOCAL VARIABLES ===\r
-static void *_heap_start = NULL;\r
-static void *_heap_end = NULL;\r
+static heap_head *_heap_start = NULL;\r
+static heap_head *_heap_end = NULL;\r
+static const heap_head _heap_zero_allocation;\r
\r
// === PROTOTYPES ===\r
EXPORT void *malloc(size_t bytes);\r
+void *_malloc(size_t bytes, void *owner);\r
EXPORT void *calloc(size_t bytes, size_t count);\r
+bool _libc_free(void *mem);\r
EXPORT void free(void *mem);\r
EXPORT void *realloc(void *mem, size_t bytes);\r
EXPORT void *sbrk(int increment);\r
LOCAL void *extendHeap(int bytes);\r
static void *FindHeapBase();\r
LOCAL uint brk(uintptr_t newpos);\r
-LOCAL void Heap_Dump(void);\r
+EXPORT int Heap_Validate(int bDump);\r
\r
//Code\r
\r
*/\r
EXPORT void *malloc(size_t bytes)\r
{\r
- size_t bestSize;\r
+ return _malloc(bytes, __builtin_return_address(0));\r
+}\r
+\r
+void *_malloc(size_t bytes, void *owner)\r
+{\r
size_t closestMatch = 0;\r
void *bestMatchAddr = 0;\r
- heap_head *curBlock;\r
\r
-// _SysDebug("&_heap_start = %p, _heap_start = %p", &_heap_start, _heap_start);\r
// Initialise Heap\r
if(_heap_start == NULL)\r
{\r
_heap_end = _heap_start;\r
extendHeap(HEAP_INIT_SIZE);\r
}\r
+\r
+ // Zero bytes, return a random area (in .rodata)\r
+ if( bytes == 0 )\r
+ return (void*)_heap_zero_allocation.data; \r
+\r
+ // Calculate the required block size\r
+ size_t bestSize = CALC_BLOCK_SIZE(bytes);\r
\r
- curBlock = _heap_start;\r
-// _SysDebug("_heap_start = %p", _heap_start);\r
- \r
- bestSize = bytes + sizeof(heap_head) + sizeof(heap_foot) + BLOCK_SIZE - 1;\r
- bestSize = (bestSize/BLOCK_SIZE)*BLOCK_SIZE; //Round up to block size\r
- \r
- while( (uintptr_t)curBlock < (uintptr_t)_heap_end)\r
+ heap_head *curBlock;\r
+ for(curBlock = _heap_start; curBlock < _heap_end; curBlock = NEXT_HEAD(curBlock))\r
{\r
//_SysDebug(" malloc: curBlock = 0x%x, curBlock->magic = 0x%x\n", curBlock, curBlock->magic);\r
if(curBlock->magic == MAGIC_FREE)\r
{\r
+ // Perfect match, nice\r
if(curBlock->size == bestSize)\r
break;\r
+ // Check if this is a tighter match than the last free block\r
if(bestSize < curBlock->size && (curBlock->size < closestMatch || closestMatch == 0)) {\r
closestMatch = curBlock->size;\r
bestMatchAddr = curBlock;\r
else if(curBlock->magic != MAGIC)\r
{\r
//Corrupt Heap\r
- Heap_Dump();\r
- _SysDebug("malloc: Corrupt Heap\n");\r
+ Heap_Validate(1);\r
+ _SysDebug("malloc: Corrupt Heap when called by %p\n", owner);\r
exit(128);\r
return NULL;\r
}\r
- curBlock = (heap_head*)((uintptr_t)curBlock + curBlock->size);\r
}\r
\r
- if((uintptr_t)curBlock < (uintptr_t)_heap_start) {\r
- Heap_Dump();\r
+ if(curBlock < _heap_start) {\r
+ Heap_Validate(1);\r
_SysDebug("malloc: Heap underrun for some reason\n");\r
exit(128);\r
return NULL;\r
}\r
\r
//Found a perfect match\r
- if((uintptr_t)curBlock < (uintptr_t)_heap_end) {\r
+ if(curBlock < _heap_end) {\r
curBlock->magic = MAGIC;\r
- return (void*)((uintptr_t)curBlock + sizeof(heap_head));\r
+ curBlock->RealSize = bytes;\r
+ curBlock->Caller = owner;\r
+ return curBlock->data;\r
}\r
\r
//Out of Heap Space\r
return NULL;\r
}\r
curBlock->magic = MAGIC;\r
+ curBlock->RealSize = bytes;\r
+ curBlock->Caller = owner;\r
DEBUGS("malloc(0x%x) = %p (extend) 0x%x", bytes, curBlock->data, bestSize);\r
return curBlock->data;\r
}\r
\r
heap_head *besthead = (void*)bestMatchAddr;\r
\r
- //Split Block?\r
+ // Do we need to split this block?\r
if(closestMatch - bestSize > BLOCK_SIZE) {\r
heap_foot *foot;\r
+ \r
+ // Block we're going to return\r
curBlock = (heap_head*)bestMatchAddr;\r
curBlock->magic = MAGIC;\r
curBlock->size = bestSize;\r
- foot = (heap_foot*)(bestMatchAddr + bestSize - sizeof(heap_foot));\r
+ foot = THIS_FOOT(curBlock);\r
foot->header = curBlock;\r
foot->magic = MAGIC;\r
\r
+ // Remaining parts of the block\r
curBlock = (heap_head*)(bestMatchAddr + bestSize);\r
curBlock->magic = MAGIC_FREE;\r
curBlock->size = closestMatch - bestSize;\r
- \r
- foot = (heap_foot*)(bestMatchAddr + closestMatch - sizeof(heap_foot));\r
+ foot = THIS_FOOT(curBlock);\r
foot->header = curBlock;\r
- \r
- besthead->magic = MAGIC; //mark as used\r
+ \r
+ // Mark return as used \r
DEBUGS("malloc(0x%x) = %p (split) 0x%x", bytes, besthead->data, bestSize);\r
- return besthead->data;\r
+ }\r
+ else {\r
+ // No need to split\r
+ DEBUGS("malloc(0x%x) = %p (reuse) 0x%x", bytes, besthead->data, besthead->size);\r
}\r
\r
- //Don't Split the block\r
besthead->magic = MAGIC;\r
- DEBUGS("malloc(0x%x) = %p (reuse) 0x%x", bytes, besthead->data, besthead->size);\r
+ besthead->RealSize = bytes;\r
+ besthead->Caller = owner;\r
return besthead->data;\r
}\r
\r
*/\r
EXPORT void *calloc(size_t __nmemb, size_t __size)\r
{\r
- void *ret = malloc(__size*__nmemb);\r
+ void *ret = _malloc(__size*__nmemb, __builtin_return_address(0));\r
if(!ret) return NULL;\r
memset(ret, 0, __size*__nmemb);\r
return ret;\r
*/\r
EXPORT void free(void *mem)\r
{\r
- heap_head *head = (void*)((intptr_t)mem-sizeof(heap_head));\r
- \r
- // Sanity please!\r
- if(!mem) return;\r
- \r
- if(head->magic != MAGIC) //Valid Heap Address\r
- return;\r
+ if( !_libc_free(mem) ) {\r
+ Heap_Validate(1);\r
+ exit(0);\r
+ }\r
+}\r
+\r
+// Exported for libc++\r
+EXPORT bool _libc_free(void *mem)\r
+{\r
+ heap_head *head = (heap_head*)mem - 1;\r
+\r
+ // Free of NULL or the zero allocation does nothing\r
+ if(!mem || mem == _heap_zero_allocation.data)\r
+ return true;\r
+ \r
+ // Sanity check the head address\r
+ if(head->magic != MAGIC) {\r
+ if( head->magic != MAGIC_FREE ) {\r
+ _SysDebug("Double free of %p by %p", mem, __builtin_return_address(0));\r
+ }\r
+ else {\r
+ _SysDebug("Free of invalid pointer %p by ", mem, __builtin_return_address(0));\r
+ }\r
+ return false;\r
+ }\r
\r
head->magic = MAGIC_FREE;\r
DEBUGS("free(%p) : 0x%x bytes", mem, head->size);\r
\r
- //Unify Right\r
- if((uintptr_t)head + head->size < (uintptr_t)_heap_end)\r
+ // Unify Right\r
+ if( NEXT_HEAD(head) < _heap_end )\r
{\r
- heap_head *nextHead = (heap_head*)((intptr_t)head + head->size);\r
- if(nextHead->magic == MAGIC_FREE) { //Is the next block free\r
- head->size += nextHead->size; //Amalgamate\r
+ heap_head *nextHead = NEXT_HEAD(head);\r
+ // Is the next block free?\r
+ if(nextHead->magic == MAGIC_FREE)\r
+ {\r
+ head->size += nextHead->size; // Amalgamate\r
+ THIS_FOOT(head)->header = head;\r
nextHead->magic = 0; //For Security\r
}\r
}\r
- //Unify Left\r
- if((uintptr_t)head - sizeof(heap_foot) > (uintptr_t)_heap_start)\r
+ \r
+ // Unify Left\r
+ if( head > _heap_start )\r
{\r
- heap_head *prevHead;\r
- heap_foot *prevFoot = (heap_foot *)((intptr_t)head - sizeof(heap_foot));\r
- if(prevFoot->magic == MAGIC) {\r
- prevHead = prevFoot->header;\r
- if(prevHead->magic == MAGIC_FREE) {\r
- prevHead->size += head->size; //Amalgamate\r
- head->magic = 0; //For Security\r
- }\r
+ heap_foot *prevFoot = PREV_FOOT(head);\r
+ if( prevFoot->magic != MAGIC )\r
+ {\r
+ _SysDebug("Heap corruption, previous foot magic invalid");\r
+ Heap_Validate(1);\r
+ return false;\r
+ }\r
+ \r
+ heap_head *prevHead = prevFoot->header;\r
+ if(prevHead->magic == MAGIC_FREE)\r
+ {\r
+ // Amalgamate\r
+ prevHead->size += head->size;\r
+ THIS_FOOT(prevHead)->header = prevHead;\r
+ // For Security\r
+ head->magic = 0;\r
+ prevFoot->magic = 0;\r
+ prevFoot->header = NULL;\r
}\r
}\r
+ \r
+ return true;\r
}\r
\r
/**\r
*/\r
EXPORT void *realloc(void *oldPos, size_t bytes)\r
{\r
- void *ret;\r
- heap_head *head;\r
- \r
- if(oldPos == NULL) {\r
- return malloc(bytes);\r
+ if(oldPos == NULL || oldPos == _heap_zero_allocation.data) {\r
+ return _malloc(bytes, __builtin_return_address(0));\r
+ }\r
+\r
+ size_t reqd_size = CALC_BLOCK_SIZE(bytes); \r
+\r
+ // Check for free space within the block\r
+ // - oldPos points to just after the header, so -1 gives the header\r
+ heap_head *head = (heap_head*)oldPos - 1;\r
+ if( head->size >= reqd_size ) {\r
+ head->RealSize = bytes;\r
+ return oldPos;\r
}\r
\r
- //Check for free space after block\r
- head = (heap_head*)((uintptr_t)oldPos-sizeof(heap_head));\r
+ // Check for free space after the block\r
+ heap_head *nexthead = NEXT_HEAD(head);\r
+ assert( nexthead <= _heap_end );\r
+ if( nexthead != _heap_end && nexthead->magic == MAGIC_FREE && head->size + nexthead->size >= reqd_size )\r
+ {\r
+ // Split next block\r
+ if( head->size + nexthead->size > reqd_size )\r
+ {\r
+ size_t newblocksize = nexthead->size - (reqd_size - head->size);\r
+ head->size = reqd_size;\r
+ \r
+ nexthead = NEXT_HEAD(head);\r
+ nexthead->size = newblocksize;\r
+ nexthead->magic = MAGIC_FREE;\r
+ THIS_FOOT(nexthead)->header = nexthead;\r
+ }\r
+ else\r
+ {\r
+ head->size = reqd_size;\r
+ }\r
+ THIS_FOOT(head)->magic = MAGIC;\r
+ THIS_FOOT(head)->header = head;\r
+ head->RealSize = bytes;\r
+ return oldPos;\r
+ }\r
\r
- //Hack to used free's amagamating algorithym and malloc's splitting\r
- free(oldPos);\r
+ // TODO: Should I check for free before too? What about before AND after?\r
\r
- //Allocate new memory\r
- ret = malloc(bytes);\r
+ // Allocate new memory\r
+ void *ret = _malloc(bytes, __builtin_return_address(0));\r
if(ret == NULL)\r
return NULL;\r
+ heap_head *newhead = (heap_head*)ret - 1;\r
\r
- //Copy Old Data\r
- if(ret != oldPos) {\r
- memcpy(ret, oldPos, head->size-sizeof(heap_head)-sizeof(heap_foot));\r
- }\r
+ // Copy Old Data\r
+ assert( head->size < newhead->size );\r
+ size_t copy_size = head->size-sizeof(heap_head)-sizeof(heap_foot);\r
+ memcpy(ret, oldPos, copy_size);\r
+ free(oldPos);\r
\r
//Return\r
return ret;\r
head->magic = MAGIC_FREE; //Unallocated\r
head->size = bytes;\r
// Footer\r
- foot = _heap_end + bytes - sizeof(heap_foot);\r
+ foot = THIS_FOOT(head);\r
foot->header = head;\r
foot->magic = MAGIC;\r
\r
*/\r
EXPORT int IsHeap(void *ptr)\r
{\r
+ if( ptr == _heap_zero_allocation.data )\r
+ return 1;\r
#if 0\r
heap_head *head;\r
heap_foot *foot;\r
return ret; // Return old curpos\r
}\r
\r
-void Heap_Dump(void)\r
+int Heap_Validate(int bDump)\r
{\r
+ int rv = 0;\r
heap_head *cur = _heap_start;\r
- while( cur < (heap_head*)_heap_end )\r
+ while( cur < (heap_head*)_heap_end && rv == 0 )\r
{\r
if( cur->magic == MAGIC ) {\r
- _SysDebug("Used block %p[0x%x] - ptr=%p", cur, cur->size, cur->data);\r
+ if( bDump ) {\r
+ _SysDebug("Used block %p[0x%x] - ptr=%p,Owner=%p,Size=0x%x",\r
+ cur, cur->size, cur->data,\r
+ cur->Caller, cur->RealSize\r
+ );\r
+ }\r
}\r
else if( cur->magic == MAGIC_FREE ) {\r
- _SysDebug("Free block %p[0x%x] - ptr=%p", cur, cur->size, cur->data);\r
+ if( bDump ) {\r
+ _SysDebug("Free block %p[0x%x]", cur, cur->size, cur->data);\r
+ }\r
}\r
else {\r
_SysDebug("Block %p bad magic (0x%x)", cur, cur->magic);\r
+ rv = 1;\r
break ;\r
}\r
- cur = (void*)( (char*)cur + cur->size );\r
+ heap_foot *foot = THIS_FOOT(cur);\r
+ if( foot->magic != MAGIC ) {\r
+ _SysDebug("- %p: Foot magic clobbered (0x%x!=0x%x)", cur, foot->magic, MAGIC);\r
+ rv = 1;\r
+ }\r
+ if( foot->header != cur ) {\r
+ _SysDebug("- %p: Head pointer clobbered (%p)", cur, foot->header);\r
+ rv = 1;\r
+ }\r
+ \r
+ if( rv && !bDump ) {\r
+ _SysDebug("%s block %p[0x%x] - ptr=%p,Owner=%p,Size=0x%x",\r
+ (cur->magic == MAGIC ? "Used":"Free"),\r
+ cur, cur->size, cur->data,\r
+ cur->Caller, cur->RealSize\r
+ );\r
+ } \r
+ \r
+ cur = NEXT_HEAD(cur);\r
+ }\r
+ if( rv && !bDump ) {\r
+ _SysDebug("- Caller: %p", __builtin_return_address(0));\r
+ exit(1);\r
}\r
+ return 0;\r
}\r
\r