A simple solution is to implement alternative dynamic memory functions using mmap()
.
void* alt_malloc( size_t size )
{
void* mem = mmap( NULL, size + sizeof(size_t),
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ) ;
if( mem != MAP_FAILED )
{
*(size_t*)mem = size ;
}
else
{
mem = NULL ;
}
return mem + sizeof( size_t) ;
}
void* alt_calloc( size_t nitems, size_t size)
{
return alt_malloc( nitems * size ) ;
}
void alt_free( void* mem )
{
if( mem != NULL) munmap( mem, *((size_t*)mem - 1) ) ;
}
void* alt_realloc( void *old_mem, size_t new_size )
{
void* new_mem = alt_malloc( new_size ) ;
if( new_mem != NULL )
{
size_t old_size = *((size_t*)old_mem - 1) ;
size_t copy_size = old_size > new_size ? new_size : old_size ;
memcpy( new_mem, old_mem, copy_size ) ;
alt_free( old_mem ) ;
}
return new_mem ;
}
The following test:
#define ALLOC_SIZE 1024 * 1024
int main()
{
char* test = alt_malloc( ALLOC_SIZE ) ;
memset( test, 'X', ALLOC_SIZE ) ;
printf( "%p : %c %c\n", test, test[0], test[ALLOC_SIZE-1] ) ;
test = alt_realloc( test, ALLOC_SIZE * 2 ) ;
printf( "%p : %c %c\n", test, test[0], test[ALLOC_SIZE-1] ) ;
alt_free( test ) ;
return 0;
}
Outputs:
0x7f102957d008 : X X
0x7f1028ea3008 : X X
Demonstrating that the memset()
covered the extent of the initial block, and that the realloc created a new block and copied the data.
A more efficient, though slightly more complex solution would be to use mmap()
to allocate an alternative heap, and then implement a heap manager operating on that block as an alternative to the standard functions. There is no shortage of heap management examples.
You could for example take the Newlib C library allocator with modified names and implement the sbrk()
syscall (again renamed to prevent clash) using mmap()
to provide memory to the alternate heap.