d6d9a40361e134db10ed997762f44bc2d123563b
[tpg/acess2.git] / Usermode / Libraries / libc.so_src / heap.c
1 /*\r
2  * Acess2 C Library\r
3  * - By John Hodge (thePowersGang)\r
4  *\r
5  * heap.c\r
6  * - Dynamic Memory (malloc/free)\r
7  */\r
8 #include <acess/sys.h>\r
9 #include <stdlib.h>\r
10 #include <string.h>\r
11 #include "lib.h"\r
12 \r
13 #if 0\r
14 # define DEBUGS(s...)   _SysDebug(s)\r
15 #else\r
16 # define DEBUGS(s...)   do{}while(0)\r
17 #endif\r
18 \r
19 // === Constants ===\r
20 #define MAGIC   0xACE55051      //AcessOS1\r
21 #define MAGIC_FREE      (~MAGIC)\r
22 #define BLOCK_SIZE      16      //Minimum\r
23 #define HEAP_INIT_SIZE  0x10000\r
24 #define DEBUG_HEAP      1\r
25 \r
26 typedef unsigned int Uint;\r
27 \r
28 // === TYPES ===\r
29 typedef struct {\r
30         uint32_t        magic;\r
31         size_t  size;\r
32         // - \r
33         #if DEBUG_HEAP\r
34         void    *Caller;\r
35         size_t  RealSize;\r
36         #endif\r
37         char    data[];\r
38 }       heap_head;\r
39 typedef struct {\r
40         heap_head       *header;\r
41         uint32_t        magic;\r
42 }       heap_foot;\r
43 \r
44 // === HELPERS ===\r
45 static inline heap_head *NEXT_HEAD(heap_head *ptr)\r
46 {\r
47         return (void*)((char*)ptr + ptr->size);\r
48 }\r
49 static inline heap_foot *THIS_FOOT(heap_head *ptr)\r
50 {\r
51         return (void*)((char*)ptr + ptr->size - sizeof(heap_foot));\r
52 }\r
53 static inline heap_foot *PREV_FOOT(heap_head *ptr)\r
54 {\r
55         return (void*)((char*)ptr - sizeof(heap_foot));\r
56 }\r
57 static inline size_t CALC_BLOCK_SIZE(size_t bytes)\r
58 {\r
59         size_t ret;\r
60         ret = bytes + sizeof(heap_head) + sizeof(heap_foot) + BLOCK_SIZE - 1;\r
61         ret = (ret/BLOCK_SIZE)*BLOCK_SIZE;      //Round up to block size\r
62         return ret;\r
63 }\r
64 \r
65 // === LOCAL VARIABLES ===\r
66 static heap_head        *_heap_start = NULL;\r
67 static heap_head        *_heap_end = NULL;\r
68 static const heap_head  _heap_zero_allocation;\r
69 \r
70 // === PROTOTYPES ===\r
71 EXPORT void     *malloc(size_t bytes);\r
72 void    *_malloc(size_t bytes, void *owner);\r
73 EXPORT void     *calloc(size_t bytes, size_t count);\r
74 EXPORT void     free(void *mem);\r
75 EXPORT void     *realloc(void *mem, size_t bytes);\r
76 EXPORT void     *sbrk(int increment);\r
77 LOCAL void      *extendHeap(int bytes);\r
78 static void     *FindHeapBase();\r
79 LOCAL uint      brk(uintptr_t newpos);\r
80 EXPORT int      Heap_Validate(int bDump);\r
81 \r
82 //Code\r
83 \r
84 /**\r
85  \fn EXPORT void *malloc(size_t bytes)\r
86  \brief Allocates memory from the heap space\r
87  \param bytes   Integer - Size of buffer to return\r
88  \return Pointer to buffer\r
89 */\r
90 EXPORT void *malloc(size_t bytes)\r
91 {\r
92         return _malloc(bytes, __builtin_return_address(0));\r
93 }\r
94 \r
95 void *_malloc(size_t bytes, void *owner)\r
96 {\r
97         size_t  closestMatch = 0;\r
98         void    *bestMatchAddr = 0;\r
99 \r
100         // Initialise Heap\r
101         if(_heap_start == NULL)\r
102         {\r
103                 _heap_start = sbrk(0);\r
104                 _heap_end = _heap_start;\r
105                 extendHeap(HEAP_INIT_SIZE);\r
106         }\r
107 \r
108         // Zero bytes, return a random area (in .rodata)\r
109         if( bytes == 0 )\r
110                 return (void*)_heap_zero_allocation.data;       \r
111 \r
112         // Calculate the required block size\r
113         size_t bestSize = CALC_BLOCK_SIZE(bytes);\r
114         \r
115         heap_head       *curBlock;\r
116         for(curBlock = _heap_start; curBlock < _heap_end; curBlock = NEXT_HEAD(curBlock))\r
117         {\r
118                 //_SysDebug(" malloc: curBlock = 0x%x, curBlock->magic = 0x%x\n", curBlock, curBlock->magic);\r
119                 if(curBlock->magic == MAGIC_FREE)\r
120                 {\r
121                         // Perfect match, nice\r
122                         if(curBlock->size == bestSize)\r
123                                 break;\r
124                         // Check if this is a tighter match than the last free block\r
125                         if(bestSize < curBlock->size && (curBlock->size < closestMatch || closestMatch == 0)) {\r
126                                 closestMatch = curBlock->size;\r
127                                 bestMatchAddr = curBlock;\r
128                         }\r
129                 }\r
130                 else if(curBlock->magic != MAGIC)\r
131                 {\r
132                         //Corrupt Heap\r
133                         Heap_Validate(1);\r
134                         _SysDebug("malloc: Corrupt Heap when called by %p\n", owner);\r
135                         exit(128);\r
136                         return NULL;\r
137                 }\r
138         }\r
139         \r
140         if(curBlock < _heap_start) {\r
141                 Heap_Validate(1);\r
142                 _SysDebug("malloc: Heap underrun for some reason\n");\r
143                 exit(128);\r
144                 return NULL;\r
145         }\r
146         \r
147         //Found a perfect match\r
148         if(curBlock < _heap_end) {\r
149                 curBlock->magic = MAGIC;\r
150                 curBlock->RealSize = bytes;\r
151                 curBlock->Caller = owner;\r
152                 return curBlock->data;\r
153         }\r
154         \r
155         //Out of Heap Space\r
156         if(!closestMatch) {\r
157                 curBlock = extendHeap(bestSize);        //Allocate more\r
158                 if(curBlock == NULL) {\r
159                         _SysDebug("malloc: Out of Heap Space\n");\r
160                         return NULL;\r
161                 }\r
162                 curBlock->magic = MAGIC;\r
163                 curBlock->RealSize = bytes;\r
164                 curBlock->Caller = owner;\r
165                 DEBUGS("malloc(0x%x) = %p (extend) 0x%x", bytes, curBlock->data, bestSize);\r
166                 return curBlock->data;\r
167         }\r
168         \r
169         heap_head *besthead = (void*)bestMatchAddr;\r
170         \r
171         // Do we need to split this block?\r
172         if(closestMatch - bestSize > BLOCK_SIZE) {\r
173                 heap_foot       *foot;\r
174                 \r
175                 // Block we're going to return\r
176                 curBlock = (heap_head*)bestMatchAddr;\r
177                 curBlock->magic = MAGIC;\r
178                 curBlock->size = bestSize;\r
179                 foot = THIS_FOOT(curBlock);\r
180                 foot->header = curBlock;\r
181                 foot->magic = MAGIC;\r
182 \r
183                 // Remaining parts of the block\r
184                 curBlock = (heap_head*)(bestMatchAddr + bestSize);\r
185                 curBlock->magic = MAGIC_FREE;\r
186                 curBlock->size = closestMatch - bestSize;\r
187                 foot = THIS_FOOT(curBlock);\r
188                 foot->header = curBlock;\r
189         \r
190                 // Mark return as used  \r
191                 DEBUGS("malloc(0x%x) = %p (split) 0x%x", bytes, besthead->data, bestSize);\r
192         }\r
193         else {\r
194                 // No need to split\r
195                 DEBUGS("malloc(0x%x) = %p (reuse) 0x%x", bytes, besthead->data, besthead->size);\r
196         }\r
197         \r
198         besthead->magic = MAGIC;\r
199         besthead->RealSize = bytes;\r
200         besthead->Caller = owner;\r
201         return besthead->data;\r
202 }\r
203 \r
204 /**\r
205  * \fn EXPORT void *calloc(size_t bytes, size_t count)\r
206  * \brief Allocate and zero a block of memory\r
207  * \param __nmemb       Number of memeber elements\r
208  * \param __size        Size of one element\r
209  */\r
210 EXPORT void *calloc(size_t __nmemb, size_t __size)\r
211 {\r
212         void    *ret = _malloc(__size*__nmemb, __builtin_return_address(0));\r
213         if(!ret)        return NULL;\r
214         memset(ret, 0, __size*__nmemb);\r
215         return ret;\r
216 }\r
217 \r
218 /**\r
219  \fn EXPORT void free(void *mem)\r
220  \brief Free previously allocated memory\r
221  \param mem     Pointer - Memory to free\r
222 */\r
223 EXPORT void free(void *mem)\r
224 {\r
225         heap_head       *head = (heap_head*)mem - 1;\r
226 \r
227         // Free of NULL or the zero allocation does nothing\r
228         if(!mem || mem == _heap_zero_allocation.data)\r
229                 return ;\r
230         \r
231         // Sanity check the head address\r
232         if(head->magic != MAGIC) {\r
233                 if( head->magic != MAGIC_FREE ) {\r
234                         _SysDebug("Double free of %p", mem);\r
235                         Heap_Validate(1);\r
236                         exit(0);\r
237                 }\r
238                 else {\r
239                         _SysDebug("Free of invalid pointer %p", mem);\r
240                         Heap_Validate(1);\r
241                         exit(0);\r
242                 }\r
243                 return;\r
244         }\r
245         \r
246         head->magic = MAGIC_FREE;\r
247         DEBUGS("free(%p) : 0x%x bytes", mem, head->size);\r
248         \r
249         // Unify Right\r
250         if( NEXT_HEAD(head) < _heap_end )\r
251         {\r
252                 heap_head       *nextHead = NEXT_HEAD(head);\r
253                 // Is the next block free?\r
254                 if(nextHead->magic == MAGIC_FREE)\r
255                 {\r
256                         head->size += nextHead->size;   // Amalgamate\r
257                         THIS_FOOT(head)->header = head;\r
258                         nextHead->magic = 0;    //For Security\r
259                 }\r
260         }\r
261         \r
262         // Unify Left\r
263         if( head > _heap_start )\r
264         {\r
265                 heap_foot *prevFoot = PREV_FOOT(head);\r
266                 if( prevFoot->magic != MAGIC )\r
267                 {\r
268                         Heap_Validate(1);\r
269                         exit(1);\r
270                 }\r
271                 \r
272                 heap_head *prevHead = prevFoot->header;\r
273                 if(prevHead->magic == MAGIC_FREE)\r
274                 {\r
275                         // Amalgamate\r
276                         prevHead->size += head->size;\r
277                         THIS_FOOT(prevHead)->header = prevHead;\r
278                         // For Security\r
279                         head->magic = 0;\r
280                         prevFoot->magic = 0;\r
281                         prevFoot->header = NULL;\r
282                 }\r
283         }\r
284 }\r
285 \r
286 /**\r
287  \fn EXPORT void *realloc(void *oldPos, size_t bytes)\r
288  \brief Reallocate a block of memory\r
289  \param bytes   Integer - Size of new buffer\r
290  \param oldPos  Pointer - Old Buffer\r
291  \return Pointer to new buffer\r
292 */\r
293 EXPORT void *realloc(void *oldPos, size_t bytes)\r
294 {\r
295         if(oldPos == NULL || oldPos == _heap_zero_allocation.data) {\r
296                 return _malloc(bytes, __builtin_return_address(0));\r
297         }\r
298 \r
299         size_t  reqd_size = CALC_BLOCK_SIZE(bytes);     \r
300 \r
301         // Check for free space within the block\r
302         // - oldPos points to just after the header, so -1 gives the header\r
303         heap_head *head = (heap_head*)oldPos - 1;\r
304         if( head->size >= reqd_size ) {\r
305                 head->RealSize = bytes;\r
306                 return oldPos;\r
307         }\r
308         \r
309         // Check for free space after the block\r
310         heap_head *nexthead = NEXT_HEAD(head);\r
311         if( nexthead && nexthead->magic == MAGIC_FREE && head->size + nexthead->size >= reqd_size )\r
312         {\r
313                 // Split next block\r
314                 if( head->size + nexthead->size > reqd_size )\r
315                 {\r
316                         size_t newblocksize = nexthead->size - (reqd_size - head->size);\r
317                         head->size = reqd_size;\r
318                         \r
319                         nexthead = NEXT_HEAD(head);\r
320                         nexthead->size = newblocksize;\r
321                         nexthead->magic = MAGIC_FREE;\r
322                         THIS_FOOT(nexthead)->header = nexthead;\r
323                 }\r
324                 else\r
325                 {\r
326                         head->size = reqd_size;\r
327                 }\r
328                 THIS_FOOT(head)->magic = MAGIC;\r
329                 THIS_FOOT(head)->header = head;\r
330                 head->RealSize = bytes;\r
331                 return oldPos;\r
332         }\r
333         \r
334         // TODO: Should I check for free before too? What about before AND after?\r
335         \r
336         // Allocate new memory\r
337         void *ret = _malloc(bytes, __builtin_return_address(0));\r
338         if(ret == NULL)\r
339                 return NULL;\r
340         \r
341         //Copy Old Data\r
342         size_t copy_size = head->size-sizeof(heap_head)-sizeof(heap_foot);\r
343         if( copy_size > bytes )\r
344                 copy_size = bytes;\r
345         memcpy(ret, oldPos, bytes);\r
346         free(oldPos);\r
347         \r
348         //Return\r
349         return ret;\r
350 }\r
351 \r
352 /**\r
353  * \fn LOCAL void *extendHeap(int bytes)\r
354  * \brief Create a new block at the end of the heap area\r
355  * \param bytes Integer - Size reqired\r
356  * \return Pointer to last free block\r
357  */\r
358 \r
359 LOCAL void *extendHeap(int bytes)\r
360 {\r
361         heap_head       *head = _heap_end;\r
362         heap_foot       *foot;\r
363         \r
364         //Expand Memory Space  (Use foot as a temp pointer)\r
365         foot = sbrk(bytes);\r
366         if(foot == (void*)-1)\r
367                 return NULL;\r
368         \r
369         //Create New Block\r
370         // Header\r
371         head->magic = MAGIC_FREE;       //Unallocated\r
372         head->size = bytes;\r
373         // Footer\r
374         foot = THIS_FOOT(head);\r
375         foot->header = head;\r
376         foot->magic = MAGIC;\r
377         \r
378         //Combine with previous block if nessasary\r
379         if(_heap_end != _heap_start && ((heap_foot*)((uintptr_t)_heap_end-sizeof(heap_foot)))->magic == MAGIC) {\r
380                 heap_head       *tmpHead = ((heap_foot*)((uintptr_t)_heap_end-sizeof(heap_foot)))->header;\r
381                 if(tmpHead->magic == MAGIC_FREE) {\r
382                         tmpHead->size += bytes;\r
383                         foot->header = tmpHead;\r
384                         head = tmpHead;\r
385                 }\r
386         }\r
387         \r
388         _heap_end = (void*) ((uintptr_t)foot+sizeof(heap_foot));\r
389         return head;\r
390 }\r
391 \r
392 /**\r
393  \fn EXPORT void *sbrk(int increment)\r
394  \brief Increases the program's memory space\r
395  \param count   Integer - Size of heap increase\r
396  \return Pointer to start of new region\r
397 */\r
398 EXPORT void *sbrk(int increment)\r
399 {\r
400         static uintptr_t oldEnd = 0;\r
401         static uintptr_t curEnd = 0;\r
402 \r
403         //_SysDebug("sbrk: (increment=%i)", increment);\r
404 \r
405         if (curEnd == 0) {\r
406                 oldEnd = curEnd = (uintptr_t)FindHeapBase();\r
407                 //_SysAllocate(curEnd); // Allocate the first page\r
408         }\r
409 \r
410         //_SysDebug(" sbrk: oldEnd = 0x%x", oldEnd);\r
411         if (increment == 0)     return (void *) curEnd;\r
412 \r
413         oldEnd = curEnd;\r
414 \r
415         // Single Page\r
416         if( (curEnd & 0xFFF) && (curEnd & 0xFFF) + increment < 0x1000 )\r
417         {\r
418                 //if( curEnd & 0xFFF == 0 )\r
419                 //{\r
420                 //      if( !_SysAllocate(curEnd) )\r
421                 //      {\r
422                 //              _SysDebug("sbrk - Error allocating memory");\r
423                 //              return (void*)-1;\r
424                 //      }\r
425                 //}\r
426                 curEnd += increment;\r
427                 //_SysDebug("sbrk: RETURN %p (single page, no alloc)", (void *) oldEnd);\r
428                 return (void *)oldEnd;\r
429         }\r
430 \r
431         increment -= curEnd & 0xFFF;\r
432         curEnd += 0xFFF;        curEnd &= ~0xFFF;\r
433         while( increment > 0 )\r
434         {\r
435                 if( !_SysAllocate(curEnd) )\r
436                 {\r
437                         // Error?\r
438                         _SysDebug("sbrk - Error allocating memory");\r
439                         return (void*)-1;\r
440                 }\r
441                 increment -= 0x1000;\r
442                 curEnd += 0x1000;\r
443         }\r
444 \r
445         //_SysDebug("sbrk: RETURN %p", (void *) oldEnd);\r
446         return (void *) oldEnd;\r
447 }\r
448 \r
449 /**\r
450  * \fn EXPORT int IsHeap(void *ptr)\r
451  */\r
452 EXPORT int IsHeap(void *ptr)\r
453 {\r
454         if( ptr == _heap_zero_allocation.data )\r
455                 return 1;\r
456         #if 0\r
457         heap_head       *head;\r
458         heap_foot       *foot;\r
459         #endif\r
460         if( (uintptr_t)ptr < (uintptr_t)_heap_start )   return 0;\r
461         if( (uintptr_t)ptr > (uintptr_t)_heap_end )     return 0;\r
462         \r
463         #if 0\r
464         head = (void*)((Uint)ptr - 4);\r
465         if( head->magic != MAGIC )      return 0;\r
466         foot = (void*)( (Uint)ptr + head->size - sizeof(heap_foot) );\r
467         if( foot->magic != MAGIC )      return 0;\r
468         #endif\r
469         return 1;\r
470 }\r
471 \r
472 // === STATIC FUNCTIONS ===\r
473 /**\r
474  * Does the job of brk(0)\r
475  */\r
476 static void *FindHeapBase()\r
477 {\r
478         #if 0\r
479         #define MAX             0xC0000000      // Address\r
480         #define THRESHOLD       512     // Pages\r
481         uint    addr;\r
482         uint    stretch = 0;\r
483         uint64_t        tmp;\r
484         \r
485         // Scan address space\r
486         for(addr = 0;\r
487                 addr < MAX;\r
488                 addr += 0x1000\r
489                 )\r
490         {\r
491                 tmp = _SysGetPhys(addr);\r
492                 if( tmp != 0 ) {\r
493                         stretch = 0;\r
494                 } else {\r
495                         stretch ++;\r
496                         if(stretch > THRESHOLD)\r
497                         {\r
498                                 return (void*)( addr - stretch*0x1000 );\r
499                         }\r
500                 }\r
501                 //__asm__ __volatile__ (\r
502                 //      "push %%ebx;mov %%edx,%%ebx;int $0xAC;pop %%ebx"\r
503                 //      ::"a"(256),"d"("%x"),"c"(addr));\r
504         }\r
505         \r
506         return NULL;\r
507         #else\r
508         return (void*)0x00900000;\r
509         #endif\r
510 }\r
511 \r
512 LOCAL uint brk(uintptr_t newpos)\r
513 {\r
514         static uintptr_t        curpos;\r
515         uint    pages;\r
516         uint    ret = curpos;\r
517          int    delta;\r
518         \r
519         _SysDebug("brk: (newpos=0x%x)", newpos);\r
520         \r
521         // Find initial position\r
522         if(curpos == 0) curpos = (uintptr_t)FindHeapBase();\r
523         \r
524         // Get Current Position\r
525         if(newpos == 0) return curpos;\r
526         \r
527         if(newpos < curpos)     return newpos;\r
528         \r
529         delta = newpos - curpos;\r
530         _SysDebug(" brk: delta = 0x%x", delta);\r
531         \r
532         // Do we need to add pages\r
533         if(curpos & 0xFFF && (curpos & 0xFFF) + delta < 0x1000)\r
534                 return curpos += delta;\r
535         \r
536         // Page align current position\r
537         if(curpos & 0xFFF)      delta -= 0x1000 - (curpos & 0xFFF);\r
538         curpos = (curpos + 0xFFF) & ~0xFFF;\r
539         \r
540         // Allocate Pages\r
541         pages = (delta + 0xFFF) >> 12;\r
542         while(pages--)\r
543         {\r
544                 _SysAllocate(curpos);\r
545                 curpos += 0x1000;\r
546                 delta -= 0x1000;\r
547         }\r
548         \r
549         // Bring the current position to exactly what we want\r
550         curpos -= ((delta + 0xFFF) & ~0xFFF) - delta;\r
551         \r
552         return ret;     // Return old curpos\r
553 }\r
554 \r
555 int Heap_Validate(int bDump)\r
556 {\r
557          int    rv = 0;\r
558         heap_head *cur = _heap_start;\r
559         while( cur < (heap_head*)_heap_end && rv == 0 )\r
560         {\r
561                 if( cur->magic == MAGIC ) {\r
562                         if( bDump ) {\r
563                                 _SysDebug("Used block %p[0x%x] - ptr=%p,Owner=%p,Size=0x%x",\r
564                                         cur, cur->size, cur->data,\r
565                                         cur->Caller, cur->RealSize\r
566                                         );\r
567                         }\r
568                 }\r
569                 else if( cur->magic == MAGIC_FREE ) {\r
570                         if( bDump ) {\r
571                                 _SysDebug("Free block %p[0x%x]", cur, cur->size, cur->data);\r
572                         }\r
573                 }\r
574                 else {\r
575                         _SysDebug("Block %p bad magic (0x%x)", cur, cur->magic);\r
576                         rv = 1;\r
577                         break ;\r
578                 }\r
579                 heap_foot *foot = THIS_FOOT(cur);\r
580                 if( foot->magic != MAGIC ) {\r
581                         _SysDebug("- %p: Foot magic clobbered (0x%x!=0x%x)", cur, foot->magic, MAGIC);\r
582                         rv = 1;\r
583                 }\r
584                 if( foot->header != cur ) {\r
585                         _SysDebug("- %p: Head pointer clobbered (%p)", cur, foot->header);\r
586                         rv = 1;\r
587                 }\r
588         \r
589                 if( rv && !bDump ) {\r
590                         _SysDebug("%s block %p[0x%x] - ptr=%p,Owner=%p,Size=0x%x",\r
591                                 (cur->magic == MAGIC ? "Used":"Free"),\r
592                                 cur, cur->size, cur->data,\r
593                                 cur->Caller, cur->RealSize\r
594                                 );\r
595                 }       \r
596         \r
597                 cur = NEXT_HEAD(cur);\r
598         }\r
599         if( rv && !bDump ) {\r
600                 _SysDebug("- Caller: %p", __builtin_return_address(0));\r
601                 exit(1);\r
602         }\r
603         return 0;\r
604 }\r
605 \r

UCC git Repository :: git.ucc.asn.au