summaryrefslogtreecommitdiff
path: root/miniany/libc-freestanding.c
diff options
context:
space:
mode:
Diffstat (limited to 'miniany/libc-freestanding.c')
-rw-r--r--miniany/libc-freestanding.c174
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;
+}