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