1 /*
2 Z88DK Z80 Macro Assembler
3
4 Alloc library.
5
6 Copyright (C) Gunther Strube, InterLogic 1993-99
7 Copyright (C) Paulo Custodio, 2011-2020
8 License: The Artistic License 2.0, http://www.perlfoundation.org/artistic_license_2_0
9 Repository: https://github.com/z88dk/z88dk
10 */
11
12 #include "alloc.h"
13 #include "dbg.h"
14 #include "init.h"
15 #include "types.h"
16 #include "utlist.h"
17 #include <stddef.h>
18
19 /*-----------------------------------------------------------------------------
20 * Memory Block - allocated before the actual buffer requested by the user
21 * keeps linked list of all allocated blocks to be freed at exit
22 *----------------------------------------------------------------------------*/
23 #define FENCE_SIZE MAX( sizeof(long), sizeof(void*) )
24 #define FENCE_SIGN 0xAA
25 #define MEMBLOCK_SIGN 0x5A5A5A5A
26
27 typedef struct MemBlock {
28 struct MemBlock *next, *prev; /* Double-linked list of blocks */
29
30 int signature; /* contains MEMBLOCK_SIGN to assure we found a block */
31 destructor_t destructor; /* desctructor function called by m_free_() to
32 destroy children */
33 struct {
34 bool in_collection :1; /* true if part of collection, deleted last */
35 bool destroy_atextit :1; /* true to shut up memory leak warning */
36 } flags;
37
38 size_t client_size; /* size requested by client */
39 const char *file; /* source where allocated */
40 int lineno; /* line number where allocated */
41
42 char fence[FENCE_SIZE]; /* fence to detect underflow */
43
44 /* client data starts here with client_size bytes + FENCE_SIZE fence */
45
46 } MemBlock;
47
48 /*-----------------------------------------------------------------------------
49 * Global data
50 *----------------------------------------------------------------------------*/
51
52 /* list of all allocated memory */
53 static MemBlock *g_mem_blocks = NULL;
54
55 static char g_fence[ FENCE_SIZE ]; /* keep signature */
56
57 /*-----------------------------------------------------------------------------
58 * Macros to convert addresses
59 *----------------------------------------------------------------------------*/
60
61 /* convert from MemBlock area to client area */
62 #define CLIENT_PTR(block) ((block)->fence + FENCE_SIZE)
63 #define CLIENT_SIZE(block) ((block)->client_size)
64
65 /* convert client block and size to MemBlock and total size */
66 #define BLOCK_PTR(ptr) ((MemBlock *) (((char*) (ptr)) \
67 - offsetof(struct MemBlock, fence) - FENCE_SIZE))
68 #define BLOCK_SIZE(size) ((size) + sizeof(MemBlock) + FENCE_SIZE)
69
70 /* address of both fences */
71 #define START_FENCE_PTR(block) ((block)->fence)
72 #define END_FENCE_PTR(block) (CLIENT_PTR(block) + (block)->client_size)
73
74 /*-----------------------------------------------------------------------------
75 * Initialize and terminate functions
76 *----------------------------------------------------------------------------*/
DEFINE_init_module()77 DEFINE_init_module()
78 {
79 memset( g_fence, FENCE_SIGN, FENCE_SIZE );
80 }
81
82 /* return next block to free; search backwards, first not in collection,
83 then in collection, return NULL at the end */
next_to_free(void)84 static MemBlock *next_to_free(void)
85 {
86 MemBlock *block;
87 int count;
88
89 /* not in collection */
90 DL_FOREACH(g_mem_blocks, block) {
91 check_node(block);
92 if (!block->flags.in_collection)
93 return block;
94 }
95
96 /* all the others */
97 DL_FOREACH(g_mem_blocks, block) {
98 check_node(block);
99 return block;
100 }
101
102 DL_COUNT(g_mem_blocks, block, count);
103 check(count == 0, "%d blocks not freed", count);
104
105 error:
106 return NULL;
107 }
108
DEFINE_dtor_module()109 DEFINE_dtor_module()
110 {
111 MemBlock *block, *block_count;
112 void *memptr;
113 int count1, count2;
114
115 /* delete all existing blocks in reverse order */
116 while ( (block = next_to_free()) != NULL ) {
117 DL_COUNT(g_mem_blocks, block_count, count1);
118
119 /* skip memory leak warning if declared to destroy at exit */
120 if ( ! block->flags.destroy_atextit )
121 log_warn("memory leak (%u bytes) allocated at %s:%d",
122 (unsigned)block->client_size, block->file ? block->file : "(null)", block->lineno );
123
124 /* delete from g_mem_blocks */
125 memptr = CLIENT_PTR(block);
126 m_free( memptr );
127
128 /* assert that block was freed */
129 DL_COUNT(g_mem_blocks, block_count, count2);
130 check(count2 < count1, "block not freed");
131 }
132 error: ;
133 }
134
m_alloc_init(void)135 void m_alloc_init( void )
136 {
137 init_module();
138 }
139
140 /*-----------------------------------------------------------------------------
141 * Create a new MemBlock, return NULL on out of memory
142 *----------------------------------------------------------------------------*/
new_block(size_t client_size,const char * file,int lineno)143 static MemBlock *new_block( size_t client_size, const char *file, int lineno )
144 {
145 MemBlock *block;
146 size_t block_size;
147
148 /* create memory to hold MemBlock + client area + fence */
149 block_size = BLOCK_SIZE( client_size );
150
151 block = malloc( block_size );
152 check( block,
153 "memory alloc (%u bytes) failed at %s:%d", (unsigned)client_size, file, lineno );
154
155 /* init block */
156 block->signature = MEMBLOCK_SIGN;
157 block->destructor = NULL;
158 block->flags.in_collection = false;
159 block->flags.destroy_atextit = false;
160 block->client_size = client_size;
161 block->file = file;
162 block->lineno = lineno;
163
164 /* fill fences */
165 memset( START_FENCE_PTR( block ), FENCE_SIGN, FENCE_SIZE );
166 memset( END_FENCE_PTR( block ), FENCE_SIGN, FENCE_SIZE );
167
168 /* add to list of blocks in reverse order, so that cleanup is reversed */
169 DL_PREPEND(g_mem_blocks, block);
170
171 return block;
172
173 error:
174 return NULL;
175 }
176
177 /*-----------------------------------------------------------------------------
178 * Find a block via client ptr, return NULL if not found
179 *----------------------------------------------------------------------------*/
find_block_no_warn(void * memptr)180 static MemBlock *find_block_no_warn( void *memptr )
181 {
182 MemBlock *block = BLOCK_PTR( memptr );
183 if ( block->signature == MEMBLOCK_SIGN )
184 return block;
185 else
186 return NULL;
187 }
188
find_block(void * memptr,char * file,int lineno)189 static MemBlock *find_block( void *memptr, char *file, int lineno )
190 {
191 MemBlock *block = find_block_no_warn( memptr );
192 check( block, "memory block not found at %s:%d", file, lineno );
193 return block;
194
195 error:
196 return NULL;
197 }
198
m_is_managed(void * memptr)199 bool m_is_managed( void *memptr )
200 {
201 return find_block_no_warn( memptr ) ? true : false;
202 }
203
204 /*-----------------------------------------------------------------------------
205 * Check block fence, return false on error
206 *----------------------------------------------------------------------------*/
check_fences(MemBlock * block)207 static bool check_fences( MemBlock *block )
208 {
209 /* check fences */
210 check( memcmp( g_fence, START_FENCE_PTR( block ), FENCE_SIZE ) == 0,
211 "buffer underflow, memory allocated at %s:%d", block->file, block->lineno );
212
213 check( memcmp( g_fence, END_FENCE_PTR( block ), FENCE_SIZE ) == 0,
214 "buffer overflow, memory allocated at %s:%d", block->file, block->lineno );
215 return true;
216
217 error:
218 return false;
219 }
220
221 /*-----------------------------------------------------------------------------
222 * malloc, calloc, strdup
223 *----------------------------------------------------------------------------*/
m_alloc(size_t size,int fill,const char * source,const char * file,int lineno)224 static void *m_alloc( size_t size, int fill, const char *source, const char *file, int lineno )
225 {
226 MemBlock *block;
227 void *memptr;
228
229 init_module();
230
231 block = new_block( size, file, lineno );
232 check_mem( block );
233
234 memptr = CLIENT_PTR( block );
235
236 if ( fill >= 0 ) { /* calloc */
237 memset( memptr, fill, size );
238 }
239 else if ( source ) { /* strdup */
240 strcpy( (char *)memptr, source );
241 }
242 else { /* malloc */
243 }
244
245 return memptr;
246
247 error:
248 return NULL;
249 }
250
m_malloc_compat(size_t size)251 void *m_malloc_compat( size_t size )
252 {
253 return m_malloc_(size, __FILE__, __LINE__);
254 }
255
m_malloc_(size_t size,const char * file,int lineno)256 void *m_malloc_( size_t size, const char *file, int lineno )
257 {
258 return m_alloc( size, -1, NULL, file, lineno );
259 }
260
m_calloc_compat(size_t num,size_t size)261 void *m_calloc_compat( size_t num, size_t size )
262 {
263 return m_calloc_( num, size, __FILE__, __LINE__ );
264 }
265
m_calloc_(size_t num,size_t size,const char * file,int lineno)266 void *m_calloc_( size_t num, size_t size, const char *file, int lineno )
267 {
268 return m_alloc( num * size, 0, NULL, file, lineno );
269 }
270
m_strdup_compat(const char * source)271 char *m_strdup_compat(const char *source )
272 {
273 return m_strdup_( source, __FILE__, __LINE__ );
274 }
275
m_strdup_(const char * source,const char * file,int lineno)276 char *m_strdup_(const char *source, const char *file, int lineno )
277 {
278 return (char *)m_alloc( strlen(source) + 1, -1, source, file, lineno );
279 }
280
281 /*-----------------------------------------------------------------------------
282 * realloc
283 *----------------------------------------------------------------------------*/
m_realloc_(void * memptr,size_t size,char * file,int lineno)284 void *m_realloc_( void *memptr, size_t size, char *file, int lineno )
285 {
286 MemBlock *block, *next_block;
287 bool result;
288
289 init_module();
290
291 /* if input is NULL, behave as malloc */
292 if ( memptr == NULL )
293 return m_malloc_( size, file, lineno );
294
295 /* find the block */
296 block = find_block( memptr, file, lineno );
297 check( block, "memory realloc (%u bytes) failed at %s:%d", (unsigned)size, file, lineno );
298
299 /* delete from list as realloc may move block */
300 next_block = block->next; /* remember position */
301 DL_DELETE(g_mem_blocks, block);
302
303 /* check fences */
304 result = check_fences( block );
305 check( result, "memory realloc (%u bytes) failed at %s:%d", (unsigned)size, file, lineno );
306
307 /* reallocate and create new end fence */
308 block = realloc( block, BLOCK_SIZE( size ) );
309 check( block, "memory realloc (%u bytes) failed at %s:%d", (unsigned)size, file, lineno );
310
311 /* update block */
312 block->client_size = size;
313 block->file = file;
314 block->lineno = lineno;
315
316 /* fill end fence */
317 memset( END_FENCE_PTR( block ), FENCE_SIGN, FENCE_SIZE );
318
319 /* add to list at the same location as before */
320 if (next_block == NULL)
321 DL_APPEND(g_mem_blocks, block);
322 else
323 DL_PREPEND_ELEM(g_mem_blocks, next_block, block);
324
325 return CLIENT_PTR( block );
326
327 error:
328 return NULL;
329 }
330
m_realloc_compat(void * memptr,size_t size)331 void *m_realloc_compat( void *memptr, size_t size )
332 {
333 return m_realloc_( memptr, size, __FILE__, __LINE__ );
334 }
335
336 /*-----------------------------------------------------------------------------
337 * free
338 *----------------------------------------------------------------------------*/
m_free_(void * memptr,char * file,int lineno)339 void m_free_( void *memptr, char *file, int lineno )
340 {
341 MemBlock *block = NULL;
342 bool result;
343
344 init_module();
345
346 /* if input is NULL, do nothing */
347 if ( memptr == NULL )
348 return;
349
350 block = find_block( memptr, file, lineno );
351 check( block, "memory free at %s:%d failed", file, lineno );
352
353 /* delete from list to avoid recursion atexit() if overflow */
354 DL_DELETE(g_mem_blocks, block );
355
356 /* check fences */
357 result = check_fences( block );
358 check( result, "memory free at %s:%d failed", file, lineno );
359
360 error:
361 /* delete memory blocks */
362 if ( block ) {
363 if ( block->destructor ) /* destroy children */
364 block->destructor( memptr ); /* user destructor */
365
366 free( block ); /* destroy itself */
367 }
368 }
369
m_free_compat(void * memptr)370 void m_free_compat( void *memptr )
371 {
372 m_free_( memptr, __FILE__, __LINE__ );
373 }
374
375 /*-----------------------------------------------------------------------------
376 * Destructors
377 *----------------------------------------------------------------------------*/
m_set_destructor_(void * memptr,destructor_t destructor,char * file,int lineno)378 void *m_set_destructor_( void *memptr, destructor_t destructor, char *file, int lineno )
379 {
380 MemBlock *block;
381
382 block = find_block( memptr, file, lineno );
383 check( block, "m_set_destructor at %s:%d failed", file, lineno );
384
385 block->destructor = destructor;
386
387 error:
388 return memptr;
389 }
390
m_set_in_collection_(void * memptr,bool in_collection,char * file,int lineno)391 void *m_set_in_collection_( void *memptr, bool in_collection, char *file, int lineno )
392 {
393 MemBlock *block;
394
395 block = find_block( memptr, file, lineno );
396 check( block, "m_%s_in_collection at %s:%d failed", in_collection ? "set" : "clear", file, lineno );
397
398 block->flags.in_collection = in_collection;
399
400 error:
401 return memptr;
402 }
403
m_destroy_atexit_(void * memptr,char * file,int lineno)404 void *m_destroy_atexit_( void *memptr, char *file, int lineno )
405 {
406 MemBlock *block;
407
408 block = find_block( memptr, file, lineno );
409 check( block, "m_destroy_atexit at %s:%d failed", file, lineno );
410
411 block->flags.destroy_atextit = true;
412
413 error:
414 return memptr;
415 }
416