Commit c8ab401b authored by Jaromil's avatar Jaromil
Browse files

new internal lightweight memory manager (lwmem)

cleanup from old 16bit umm memmanager and added config options to
activate "lw" as internal memory manager (limited to 8MB)
parent c2617447
......@@ -28,7 +28,7 @@ SOURCES := \
lua_functions.o lua_modules.o lualibs_detected.o lua_shims.o \
json.o json_strbuf.o json_fpconv.o \
encoding.o cmsgpack.o \
umm_malloc.o zen_memory.o \
zen_memory.o lwmem.c \
zen_io.o zen_parse.o repl.o zen_config.o \
zen_octet.o zen_ecp.o zen_ecp2.o zen_big.o \
zen_fp12.o zen_random.o zen_hash.o \
......
......@@ -357,10 +357,6 @@ int main(int argc, char **argv) {
}
#endif /* POSIX */
// report experimental memory manager
// if((strcmp(conffile,"umm")==0) && zen_heap) {
// lua_gc(L, LUA_GCCOLLECT, 0);
// }
zen_teardown(Z);
return EXIT_SUCCESS;
}
......
This diff is collapsed.
/**
* \file lwmem.c
* \brief Lightweight dynamic memory manager
*/
/*
* Copyright (c) 2019 Tilen MAJERLE
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* This file is part of LwMEM - Lightweight dynamic memory manager library.
*
* Author: Tilen MAJERLE <tilen@majerle.eu>
* Version: v1.2.0
*/
#ifndef LWMEM_HDR_H
#define LWMEM_HDR_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <string.h>
/**
* \defgroup LWMEM Lightweight dynamic memory manager
* \brief Lightweight dynamic memory manager
* \{
*/
/* --- Memory unique part starts --- */
/**
* \brief Memory function/typedef prefix string
*
* It is used to change function names in zero time to easily re-use same library between applications.
* Use `#define LWMEM_PREF(x) my_prefix_ ## x` to change all function names to (for example) `my_prefix_lwmem_init`
*
* \note Modification of this macro must be done in header and source file aswell
*/
#define LWMEM_PREF(x) lwmem_ ## x
/* --- Memory unique part ends --- */
/**
* \brief Memory region descriptor
*/
typedef struct {
void* start_addr; /*!< Region start address */
size_t size; /*!< Size of region in units of bytes */
} LWMEM_PREF(region_t);
size_t LWMEM_PREF(assignmem)(const LWMEM_PREF(region_t)* regions, const size_t len);
void * LWMEM_PREF(malloc)(const size_t size);
void * LWMEM_PREF(calloc)(const size_t nitems, const size_t size);
void * LWMEM_PREF(realloc)(void* const ptr, const size_t size);
unsigned char LWMEM_PREF(realloc_s)(void** const ptr, const size_t size);
void LWMEM_PREF(free)(void* const ptr);
void LWMEM_PREF(free_s)(void** const ptr);
#if defined(LWMEM_DEV) && !__DOXYGEN__
unsigned char lwmem_debug_create_regions(LWMEM_PREF(region_t)** regs_out, size_t count, size_t size);
void lwmem_debug_save_state(void);
void lwmem_debug_restore_to_saved(void);
void lwmem_debug_print(unsigned char print_alloc, unsigned char print_free);
#endif /* defined(LWMEM_DEV) && !__DOXYGEN__ */
#undef LWMEM_PREF
/**
* \}
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LWMEM_HDR_H */
/* ----------------------------------------------------------------------------
* dbglog.h - A set of macros that cleans up code that needs to produce debug
* or log information.
*
* Many embedded systems still put a premium on code space and therefore need
* a way to conditionally compile in debug code. Yes, it can lead to code that
* runs differently depending on whether the debug code is cmpiled in or not
* but you need to be able to evaluate the tradeoff.
*
* See copyright notice in LICENSE.TXT
* ----------------------------------------------------------------------------
* NOTE WELL that this file may be included multiple times - this allows you
* to set the trace level #define DBGLOG_LEVEL x
*
* To update which of the DBGLOG macros are compiled in, you must redefine the
* DBGLOG_LEVEL macro and the inlcude the dbglog.h file again, like this:
*
* #undef DBGLOG_LEVEL
* #define DBGLOG_LEVEL 6
* #include "dbglog/dbglog.txt"
*
* To handle multiple inclusion, we need to first undefine any macros we define
* so that the compiler does not warn us that we are changing a macro.
* ----------------------------------------------------------------------------
* The DBGLOG_LEVEL and DBGLOG_FUNCTION should be defined BEFORE this
* file is included or else the following defaults are used:
*
* #define DBGLOG_LEVEL 0
* #define DBGLOG_FUNCTION printf
* ----------------------------------------------------------------------------
* There are macros to handle the following decreasing levels of detail:
*
* 6 = TRACE
* 5 = DEBUG
* 4 = CRITICAL
* 3 = ERROR
* 2 = WARNING
* 1 = INFO
* 0 = FORCE - The DBGLOG_FUNCTION is always compiled in and is called only when
* the first parameter to the macro is non-0
* ----------------------------------------------------------------------------
*/
#undef DBGLOG_TRACE
#undef DBGLOG_DEBUG
#undef DBGLOG_CRITICAL
#undef DBGLOG_ERROR
#undef DBGLOG_WARNING
#undef DBGLOG_INFO
#undef DBGLOG_FORCE
#include <jutils.h>
/* ------------------------------------------------------------------------- */
#if DBGLOG_LEVEL >= 6
# define DBGLOG_TRACE(format, ...) DBGLOG_FUNCTION(0, format, ## __VA_ARGS__)
#else
# define DBGLOG_TRACE(format, ...)
#endif
#if DBGLOG_LEVEL >= 5
# define DBGLOG_DEBUG(format, ...) DBGLOG_FUNCTION(0, format, ## __VA_ARGS__)
#else
# define DBGLOG_DEBUG(format, ...)
#endif
#if DBGLOG_LEVEL >= 4
# define DBGLOG_CRITICAL(format, ...) DBGLOG_FUNCTION(0, format, ## __VA_ARGS__)
#else
# define DBGLOG_CRITICAL(format, ...)
#endif
#if DBGLOG_LEVEL >= 3
# define DBGLOG_ERROR(format, ...) DBGLOG_FUNCTION(0, format, ## __VA_ARGS__)
#else
# define DBGLOG_ERROR(format, ...)
#endif
#if DBGLOG_LEVEL >= 2
# define DBGLOG_WARNING(format, ...) DBGLOG_FUNCTION(0, format, ## __VA_ARGS__)
#else
# define DBGLOG_WARNING(format, ...)
#endif
#if DBGLOG_LEVEL >= 1
# define DBGLOG_INFO(format, ...) DBGLOG_FUNCTION(0, format, ## __VA_ARGS__)
#else
# define DBGLOG_INFO(format, ...)
#endif
#define DBGLOG_FORCE(force, format, ...) {if(force) {DBGLOG_FUNCTION(0, format, ## __VA_ARGS__);}}
This diff is collapsed.
/* ----------------------------------------------------------------------------
* umm_malloc.h - a memory allocator for embedded systems (microcontrollers)
*
* See copyright notice in LICENSE.TXT
* ----------------------------------------------------------------------------
*/
#ifndef UMM_MALLOC_H
#define UMM_MALLOC_H
/* ------------------------------------------------------------------------ */
void umm_init( void*, size_t );
void *umm_malloc( size_t size );
void *umm_calloc( size_t num, size_t size );
void *umm_realloc( void *ptr, size_t size );
void umm_free( void *ptr );
/* ------------------------------------------------------------------------ */
#endif /* UMM_MALLOC_H */
/*
* Configuration for umm_malloc
*/
#ifndef _UMM_MALLOC_CFG_H
#define _UMM_MALLOC_CFG_H
/*
* There are a number of defines you can set at compile time that affect how
* the memory allocator will operate.
* You can set them in your config file umm_malloc_cfg.h.
* In GNU C, you also can set these compile time defines like this:
*
* -D UMM_TEST_MAIN
*
* Set this if you want to compile in the test suite
*
* -D UMM_BEST_FIT (defualt)
*
* Set this if you want to use a best-fit algorithm for allocating new
* blocks
*
* -D UMM_FIRST_FIT
*
* Set this if you want to use a first-fit algorithm for allocating new
* blocks
*
* -D UMM_DBG_LOG_LEVEL=n
*
* Set n to a value from 0 to 6 depending on how verbose you want the debug
* log to be
*
* ----------------------------------------------------------------------------
*
* Support for this library in a multitasking environment is provided when
* you add bodies to the UMM_CRITICAL_ENTRY and UMM_CRITICAL_EXIT macros
* (see below)
*
* ----------------------------------------------------------------------------
*/
#include <zenroom.h>
/* Start addresses and the size of the heap */
//extern umm_block *umm_heap;
//#define UMM_MALLOC_CFG_HEAP_ADDR umm_heap
//#define UMM_MALLOC_CFG_HEAP_SIZE MAX_HEAP
/* A couple of macros to make packing structures less compiler dependent */
#define UMM_H_ATTPACKPRE
#define UMM_H_ATTPACKSUF __attribute__((__packed__))
#define UMM_BEST_FIT
#undef UMM_FIRST_FIT
/*
* -D UMM_INFO :
*
* Enables a dup of the heap contents and a function to return the total
* heap size that is unallocated - note this is not the same as the largest
* unallocated block on the heap!
*/
typedef struct UMM_HEAP_INFO_t {
unsigned short int totalEntries;
unsigned short int usedEntries;
unsigned short int freeEntries;
unsigned short int totalBlocks;
unsigned short int usedBlocks;
unsigned short int freeBlocks;
unsigned short int maxFreeContiguousBlocks;
}
UMM_HEAP_INFO;
void *umm_info( void *ptr );
size_t umm_free_heap_size( void );
/*
* A couple of macros to make it easier to protect the memory allocator
* in a multitasking system. You should set these macros up to use whatever
* your system uses for this purpose. You can disable interrupts entirely, or
* just disable task switching - it's up to you
*
* NOTE WELL that these macros MUST be allowed to nest, because umm_free() is
* called from within umm_malloc()
*/
#define UMM_CRITICAL_ENTRY()
#define UMM_CRITICAL_EXIT()
/*
* -D UMM_INTEGRITY_CHECK :
*
* Enables heap integrity check before any heap operation. It affects
* performance, but does NOT consume extra memory.
*
* If integrity violation is detected, the message is printed and user-provided
* callback is called: `UMM_HEAP_CORRUPTION_CB()`
*
* Note that not all buffer overruns are detected: each buffer is aligned by
* 4 bytes, so there might be some trailing "extra" bytes which are not checked
* for corruption.
*/
#define UMM_INTEGRITY_CHECK
#ifdef UMM_INTEGRITY_CHECK
int umm_integrity_check( void );
# define INTEGRITY_CHECK() umm_integrity_check()
extern void umm_corruption(void);
# define UMM_HEAP_CORRUPTION_CB() printf( "Heap Corruption!" )
#else
# define INTEGRITY_CHECK() 0
#endif
/*
* -D UMM_POISON :
*
* Enables heap poisoning: add predefined value (poison) before and after each
* allocation, and check before each heap operation that no poison is
* corrupted.
*
* Other than the poison itself, we need to store exact user-requested length
* for each buffer, so that overrun by just 1 byte will be always noticed.
*
* Customizations:
*
* UMM_POISON_SIZE_BEFORE:
* Number of poison bytes before each block, e.g. 2
* UMM_POISON_SIZE_AFTER:
* Number of poison bytes after each block e.g. 2
* UMM_POISONED_BLOCK_LEN_TYPE
* Type of the exact buffer length, e.g. `short`
*
* NOTE: each allocated buffer is aligned by 4 bytes. But when poisoning is
* enabled, actual pointer returned to user is shifted by
* `(sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE)`.
* It's your responsibility to make resulting pointers aligned appropriately.
*
* If poison corruption is detected, the message is printed and user-provided
* callback is called: `UMM_HEAP_CORRUPTION_CB()`
*/
#define UMM_POISON_CHECK
#define UMM_POISON_SIZE_BEFORE 4
#define UMM_POISON_SIZE_AFTER 4
#define UMM_POISONED_BLOCK_LEN_TYPE short
#ifdef UMM_POISON_CHECK
void *umm_poison_malloc( size_t size );
void *umm_poison_calloc( size_t num, size_t size );
void *umm_poison_realloc( void *ptr, size_t size );
void umm_poison_free( void *ptr );
int umm_poison_check( void );
# define POISON_CHECK() umm_poison_check()
#else
# define POISON_CHECK() 0
#endif
#endif /* _UMM_MALLOC_CFG_H */
/* poisoning (UMM_POISON_CHECK) {{{ */
#if defined(UMM_POISON_CHECK)
#define POISON_BYTE (0xa5)
/*
* Yields a size of the poison for the block of size `s`.
* If `s` is 0, returns 0.
*/
static size_t poison_size(size_t s) {
return(s ? (UMM_POISON_SIZE_BEFORE +
sizeof(UMM_POISONED_BLOCK_LEN_TYPE) +
UMM_POISON_SIZE_AFTER)
: 0);
}
/*
* Print memory contents starting from given `ptr`
*/
static void dump_mem ( const unsigned char *ptr, size_t len ) {
while (len--) {
DBGLOG_ERROR(" 0x%.2x", (unsigned int)(*ptr++));
}
}
/*
* Put poison data at given `ptr` and `poison_size`
*/
static void put_poison( unsigned char *ptr, size_t poison_size ) {
memset(ptr, POISON_BYTE, poison_size);
}
/*
* Check poison data at given `ptr` and `poison_size`. `where` is a pointer to
* a string, either "before" or "after", meaning, before or after the block.
*
* If poison is there, returns 1.
* Otherwise, prints the appropriate message, and returns 0.
*/
static int check_poison( const unsigned char *ptr, size_t poison_size,
const char *where) {
size_t i;
int ok = 1;
for (i = 0; i < poison_size; i++) {
if (ptr[i] != POISON_BYTE) {
ok = 0;
break;
}
}
if (!ok) {
DBGLOG_ERROR( "No poison %s block at: 0x%lx, actual data:", where, (unsigned long)ptr);
dump_mem(ptr, poison_size);
DBGLOG_ERROR( "\n" );
}
return ok;
}
/*
* Check if a block is properly poisoned. Must be called only for non-free
* blocks.
*/
static int check_poison_block( umm_block *pblock ) {
int ok = 1;
if (pblock->header.used.next & UMM_FREELIST_MASK) {
DBGLOG_ERROR( "check_poison_block is called for free block 0x%lx\n", (unsigned long)pblock);
} else {
/* the block is used; let's check poison */
unsigned char *pc = (unsigned char *)pblock->body.data;
unsigned char *pc_cur;
pc_cur = pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE);
if (!check_poison(pc_cur, UMM_POISON_SIZE_BEFORE, "before")) {
ok = 0;
goto clean;
}
pc_cur = pc + *((UMM_POISONED_BLOCK_LEN_TYPE *)pc) - UMM_POISON_SIZE_AFTER;
if (!check_poison(pc_cur, UMM_POISON_SIZE_AFTER, "after")) {
ok = 0;
goto clean;
}
}
clean:
return ok;
}
/*
* Takes a pointer returned by actual allocator function (`umm_malloc` or
* `umm_realloc`), puts appropriate poison, and returns adjusted pointer that
* should be returned to the user.
*
* `size_w_poison` is a size of the whole block, including a poison.
*/
static void *get_poisoned( unsigned char *ptr, size_t size_w_poison ) {
if (size_w_poison != 0 && ptr != NULL) {
/* Poison beginning and the end of the allocated chunk */
put_poison(ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE),
UMM_POISON_SIZE_BEFORE);
put_poison(ptr + size_w_poison - UMM_POISON_SIZE_AFTER,
UMM_POISON_SIZE_AFTER);
/* Put exact length of the user's chunk of memory */
*(UMM_POISONED_BLOCK_LEN_TYPE *)ptr = (UMM_POISONED_BLOCK_LEN_TYPE)size_w_poison;
/* Return pointer at the first non-poisoned byte */
return ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE;
} else {
return ptr;
}
}
/*
* Takes "poisoned" pointer (i.e. pointer returned from `get_poisoned()`),
* and checks that the poison of this particular block is still there.
*
* Returns unpoisoned pointer, i.e. actual pointer to the allocated memory.
*/
static void *get_unpoisoned( unsigned char *ptr ) {
if (ptr != NULL) {
unsigned short int c;
ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE);
/* Figure out which block we're in. Note the use of truncated division... */
c = (((char *)ptr)-(char *)(&(umm_heap[0])))/sizeof(umm_block);
check_poison_block(&UMM_BLOCK(c));
}
return ptr;
}
/* }}} */
/* ------------------------------------------------------------------------ */
void *umm_poison_malloc( size_t size ) {
void *ret;
size += poison_size(size);
ret = umm_malloc( size );
ret = get_poisoned(ret, size);
return ret;
}
/* ------------------------------------------------------------------------ */
void *umm_poison_calloc( size_t num, size_t item_size ) {
void *ret;
size_t size = item_size * num;
size += poison_size(size);
ret = umm_malloc(size);
if (NULL != ret)
memset(ret, 0x00, size);
ret = get_poisoned(ret, size);
return ret;
}
/* ------------------------------------------------------------------------ */
void *umm_poison_realloc( void *ptr, size_t size ) {
void *ret;
ptr = get_unpoisoned(ptr);
size += poison_size(size);
ret = umm_realloc( ptr, size );
ret = get_poisoned(ret, size);
return ret;
}
/* ------------------------------------------------------------------------ */
void umm_poison_free( void *ptr ) {
ptr = get_unpoisoned(ptr);
umm_free( ptr );
}
/*
* Iterates through all blocks in the heap, and checks poison for all used
* blocks.
*/
int umm_poison_check(void) {
int ok = 1;
unsigned short int cur;
if (umm_heap == NULL) {
umm_init();
}
/* Now iterate through the blocks list */
cur = UMM_NBLOCK(0) & UMM_BLOCKNO_MASK;
while( UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK ) {
if ( !(UMM_NBLOCK(cur) & UMM_FREELIST_MASK) ) {
/* This is a used block (not free), so, check its poison */
ok = check_poison_block(&UMM_BLOCK(cur));
if (!ok){
break;
}
}
cur = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK;
}
return ok;
}
/* ------------------------------------------------------------------------ */
#endif
......@@ -69,12 +69,13 @@ extern void set_color(int on);
#include <stb_c_lexer.h>
typedef enum { NIL, VERBOSE, COLOR, SECCOMP,RNGSEED } zconf;
typedef enum { NIL, VERBOSE, COLOR, SECCOMP, RNGSEED, MEMMGR } zconf;
static zconf curconf = NIL;
int zconf_seccomp = 0;
char *zconf_rngseed_str = NULL;
int zconf_rngseed_len = 0;