#ifdef HAVE_CONFIG_H # include "config.h" #endif /* HAVE_CONFIG_H */ #include #include #include #include #include #ifdef TCC_MEMORY_DEBUG # undef tcc_free # undef tcc_malloc # undef tcc_mallocz # undef tcc_realloc # undef tcc_strdup #endif /* TCC_MEMORY_DEBUG */ static void * default_reallocator(void *ptr, size_t size) { void *ptr1; if (size == 0) { free(ptr); return (NULL); } ptr1 = realloc(ptr, size); if (ptr1 == NULL) { fprintf(stderr, "memory full\n"); exit(EXIT_FAILURE); } return (ptr1); } static TCCReallocFunc *reallocator = default_reallocator; void tcc_set_realloc(TCCReallocFunc *my_realloc) { if (my_realloc == NULL) { reallocator = default_reallocator; return; } reallocator = my_realloc; } void tcc_free(void *ptr) { reallocator(ptr, 0); } void * tcc_malloc(size_t size) { return (reallocator(NULL, size)); } void * tcc_mallocz(size_t size) { void *ptr; ptr = tcc_malloc(size); if (ptr == NULL) { return (NULL); } memset(ptr, 0, size); return (ptr); } void * tcc_realloc(void *ptr, size_t size) { return (reallocator(ptr, size)); } char * tcc_strdup(const char *str) { char *ptr; ptr = tcc_malloc(strlen(str) + 1); strcpy(ptr, str); return (ptr); } #ifdef TCC_MEMORY_DEBUG # define MEMORY_DEBUG_MAGIC1 0xFEEDDEB1 # define MEMORY_DEBUG_MAGIC2 0xFEEDDEB2 # define MEMORY_DEBUG_MAGIC3 0xFEEDDEB3 # define MEMORY_DEBUG_FILE_LEN 40 # define MEMORY_DEBUG_CHECK3(hdr) \ ((MemoryDebugHeader *)((uint8_t *)hdr + header->size))->magic3 # define MEMORY_USER_PTR(hdr) \ ((uint8_t *)header + offsetof(MemoryDebugHeader, magic3)) # define MEMORY_HEADER_PTR(ptr) \ (MemoryDebugHeader *)((uint8_t *)ptr - offsetof(MemoryDebugHeader, magic3)) typedef struct MemoryDebugHeader { uint32_t magic1; size_t size; struct MemoryDebugHeader *prev; struct MemoryDebugHeader *next; int linenum; char filename[MEMORY_DEBUG_FILE_LEN + 1]; uint32_t magic2; __attribute__((aligned(16))) uint8_t magic3[4]; } MemoryDebugHeader; TCC_SEM(static mem_sem); static MemoryDebugHeader *mem_debug_chain; static size_t mem_cur_size; size_t mem_max_size; static int nb_states; static MemoryDebugHeader * malloc_check(void *ptr, const char *msg) { MemoryDebugHeader *header; header = MEMORY_HEADER_PTR(ptr); if (header->magic1 != MEMORY_DEBUG_MAGIC1 || header->magic2 != MEMORY_DEBUG_MAGIC2 || read32le(MEMORY_DEBUG_CHECK3(header)) != MEMORY_DEBUG_MAGIC3 || header->size == (size_t)-1) { fprintf(stderr, "%s check failed\n", msg); if (header->magic1 == MEMORY_DEBUG_MAGIC1) { fprintf(stderr, "%s:%u: block allocated here.\n", header->filename, header->linenum); } exit(EXIT_FAILURE); } return (header); } void tcc_free_debug(void *ptr) { MemoryDebugHeader *header; if (ptr == NULL) { return; } header = malloc_check(ptr, "tcc_free"); WAIT_SEM(&mem_sem); mem_cur_size -= header->size; header->size = (size_t)-1; if (header->next) { header->next->prev = header->prev; } if (header->prev) { header->prev->next = header->next; } if (header == mem_debug_chain) { mem_debug_chain = header->next; } POST_SEM(&mem_sem); tcc_free(header); } void * tcc_malloc_debug(size_t size, const char *file, int line) { int ofs; MemoryDebugHeader *header; if (size == 0) { return (NULL); } header = tcc_malloc(sizeof(MemoryDebugHeader) + size); header->magic1 = MEMORY_DEBUG_MAGIC1; header->magic2 = MEMORY_DEBUG_MAGIC2; header->size = size; write32le(MEMORY_DEBUG_CHECK3(header), MEMORY_DEBUG_MAGIC3); header->linenum = line; ofs = strlen(file) - MEMORY_DEBUG_FILE_LEN; strncpy(header->filename, file + (ofs > 0 ? ofs : 0), MEMORY_DEBUG_FILE_LEN); header->filename[MEMORY_DEBUG_FILE_LEN] = 0; WAIT_SEM(&mem_sem); header->next = mem_debug_chain; header->prev = NULL; if (header->next) { header->next->prev = header; } mem_debug_chain = header; mem_cur_size += size; if (mem_cur_size > mem_max_size) { mem_max_size = mem_cur_size; } POST_SEM(&mem_sem); return (MEMORY_USER_PTR(header)); } void * tcc_mallocz_debug(size_t size, const char *file, int line) { void *ptr; ptr = tcc_malloc_debug(size, file, line); if (ptr == NULL) { return (NULL); } memset(ptr, 0, size); return (ptr); } void * tcc_realloc_debug(void *ptr, size_t size, const char *file, int line) { MemoryDebugHeader *header; int mem_debug_chain_update; mem_debug_chain_update = 0; if (ptr == NULL) { return (tcc_malloc_debug(size, file, line)); } if (size == 0) { tcc_free_debug(ptr); return (NULL); } header = malloc_check(ptr, "tcc_realloc"); WAIT_SEM(&mem_sem); mem_cur_size -= header->size; mem_debug_chain_update = (header == mem_debug_chain); header = tcc_realloc(header, sizeof(MemoryDebugHeader) + size); header->size = size; write32le(MEMORY_DEBUG_CHECK3(header), MEMORY_DEBUG_MAGIC3); if (header->next) { header->next->prev = header; } if (header->prev) { header->prev->next = header; } if (mem_debug_chain_update) { mem_debug_chain = header; } mem_cur_size += size; if (mem_cur_size > mem_max_size) { mem_max_size = mem_cur_size; } POST_SEM(&mem_sem); return (MEMORY_USER_PTR(header)); } char * tcc_strdup_debug(const char *str, const char *file, int line) { char *ptr; ptr = tcc_malloc_debug(strlen(str) + 1, file, line); strcpy(ptr, str); return (ptr); } void tcc_memcheck(int d) { MemoryDebugHeader *header; WAIT_SEM(&mem_sem); nb_states += d; if (0 == nb_states && mem_cur_size) { header = mem_debug_chain; fflush(stdout); fprintf(stderr, "MEM_DEBUG: mem_leak= %d bytes, mem_max_size= %d bytes\n", mem_cur_size, mem_max_size); while (header) { fprintf(stderr, "%s:%u: error: %u bytes leaked\n", header->filename, header->linenum, header->size); header = header->next; } fflush(stderr); mem_cur_size = 0; mem_max_size = 0; mem_debug_chain = NULL; #if TCC_MEMORY_DEBUG-0 == 2 exit(2); #endif /* TCC_MEMORY_DEBUG == 2 */ } POST_SEM(&mem_sem); } #endif /* TCC_MEMORY_DEBUG */