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

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