diff options
Diffstat (limited to 'miniany/libc-freestanding.c')
-rw-r--r-- | miniany/libc-freestanding.c | 174 |
1 files changed, 173 insertions, 1 deletions
diff --git a/miniany/libc-freestanding.c b/miniany/libc-freestanding.c index fdec615..2bcdf0c 100644 --- a/miniany/libc-freestanding.c +++ b/miniany/libc-freestanding.c @@ -62,7 +62,8 @@ enum { enum { SYSCALL_EXIT = 1, SYSCALL_READ = 3, - SYSCALL_WRITE = 4 + SYSCALL_WRITE = 4, + SYSCALL_BRK = 45 }; enum { @@ -71,6 +72,10 @@ enum { int errno; +enum { + ENOMEM = 1 +}; + int syscall1( int id, int arg0 ) { int retval; @@ -245,3 +250,170 @@ void exit( int status ) { syscall1( SYSCALL_EXIT, status ); } + +int system_brk( char *addr ) +{ + return syscall1( SYSCALL_BRK, (int )addr ); +} + +char *current_brk = 0; + +int brk( char *addr ) +{ + char *p; + + p = (char *)system_brk( addr ); + if( (int)p < 0 ) { + return -1; + } + if( p == current_brk ) { + errno = ENOMEM; + return -1; + } + + current_brk = p; + + return 0; +} + +char *sbrk( int inc ) +{ + char *old_brk; + int res; + + if( current_brk == 0 ) { + brk( 0 ); + } + + if( inc == 0 ) { + return current_brk; + } + + old_brk = current_brk; + + res = brk( ( char *)current_brk + inc ); + if( res == -1 ) { + return (char *)-1; + } + + return old_brk; +} + +struct mem_header { + int size; + int used; + struct mem_header *next; + int align; +}; + +struct mem_header *mem_head = NULL; +struct mem_header *mem_tail = NULL; +char *initial_brk = NULL; + +int mem_align( int n ) +{ + return ( n + sizeof( int ) - 1 ) & ~( sizeof( int ) - 1 ); +} + +char *malloc( int size ) +{ + int total_size; + char *ptr; + struct mem_header *header; + + total_size = mem_align( sizeof( struct mem_header ) + size ); + + header = mem_head; + while( header != NULL ) { + if( !header->used && header->size >= size ) { + header->used = 1; + return (char *)( header + 1 ); + } + header = header->next; + } + + if( initial_brk == NULL ) { + initial_brk = sbrk( 0 ); + } + + ptr = sbrk( total_size ); + if( ptr == (char *)-1 ) { + return NULL; + } + + header = (struct mem_header *)ptr; + header->size = size; + header->used = 1; + header->next = NULL; + + if( mem_head == NULL ) { + mem_head = header; + } + if( mem_tail != NULL ) { + mem_tail->next = header; + } + mem_tail = header; + + return (char *)( header + 1 ); +} + +void free( char *ptr ) +{ + struct mem_header *header, *tmp; + char *actual_brk; + + if( ptr == NULL ) { + putstring( "warning: freeing a NULL pointer\n" ); + return; + } + + header = (struct mem_header *)ptr - 1; + if( !header->used ) { + putstring( "warning: double free in linked list detected\n" ); + return; + } + header->used = 0; + + actual_brk = sbrk( 0 ); + if( (char *)ptr + header->size == actual_brk ) { + tmp = mem_head; + while( tmp != NULL ) { + if( tmp->next == mem_tail ) { + tmp->next = NULL; + mem_tail = tmp; + } + tmp = tmp->next; + } + if( mem_head == mem_tail ) { + mem_head = NULL; + mem_tail = NULL; + } + sbrk( - header->size - sizeof( struct mem_header ) ); + } +} + +char *memcpy( char *d, char *s, int len ) +{ + char *p; + char *q; + + p = s; + q = d; + while( len-- ) { + *q = *p; + q++; + p++; + } + + return d; +} + +char *strdup( char *s ) +{ + char *c; + + c = malloc( strlen( s ) + 1 ); + memcpy( c, s, strlen( s ) + 1 ); + + return c; +} |