/* * minimal freestanding C library * * works for IA-32 and Linux, uses old INT 80h software interrupts * for system calls. * */ #include int strlen( char *s ) { char *p; p = s; while( *p != '\0' ) { p++; } return p - s; } int strcmp( char *s1, char *s2 ) { while( ( *s1 != '\0' ) && ( *s2 != '\0' ) && *s1 == *s2 ) { s1++; s2++; } return *s1 - *s2; } int strlcpy( char *d, const char *s, int n ) { int len = 0; while( len < n && s[len] != '\0' ) { d[len] = s[len]; len++; } d[len] = '\0'; while( s[len] != '\0' ) { len++; } if( len >= n ) { d[n-1] = '\0'; } return len; } int strlcat( char *d, char *s, int n ) { int len = 0; char *ss = s; char *dd = d; while( len < n && *dd != '\0' ) { len++; dd++; } if( len == n ) { return len + strlen( s ); } while( len + 1 < n && ( *dd++ = *ss++ ) ) { len++; } *dd = '\0'; return len; } int memcmp( char *s1, char *s2, int n ) { while( n > 0 ) { if( *s1 != *s2 ) { return *s1 - *s2; } n--; s1++; s2++; } return 0; } char *memset( char *d, int c, int n ) { char *s = d; while( n > 0 ) { *s = (char)c; s++; n--; } return d; } int isspace( int c ) { if( c == ' ' || c == '\r' || c == '\n' || c == '\t' ) return 1; return 0; } int isdigit( int c ) { if( c >= '0' && c <= '9' ) return 1; return 0; } int isalpha( int c ) { if( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) ) return 1; return 0; } int isalnum( int c ) { if( isalpha( c ) || isdigit( c ) ) return 1; return 0; } enum { EXIT_SUCCESS = 0, EXIT_FAILURE = 1 }; enum { SYSCALL_EXIT = 1, SYSCALL_READ = 3, SYSCALL_WRITE = 4, SYSCALL_BRK = 45 }; enum { EOF = -1 }; int errno; enum { ENOMEM = 1 }; int syscall1( int id, int arg0 ) { int retval; __asm__ volatile( "\ push %%ebx\n\ mov %1, %%eax\n\ mov %2, %%ebx\n\ int $0x80\n\ mov %%eax, %0\n\ pop %%ebx\n" : "=m"( retval ) : "m"( id ), "m"( arg0 ) : "eax", "ebx" ); if( retval < 0 ) { errno = -retval; return -1; } return retval; } int syscall3( int id, int arg0, int arg1, int arg2 ) { int retval; __asm__ volatile( "\ push %%ebx\n\ push %%ecx\n\ push %%edx\n\ mov %1, %%eax\n\ mov %2, %%ebx\n\ mov %3, %%ecx\n\ mov %4, %%edx\n\ int $0x80\n\ mov %%eax, %0\n\ pop %%edx\n\ pop %%ecx\n\ pop %%ebx\n" : "=m"( retval ) : "m"( id ), "m"( arg0 ), "m"( arg1 ), "m"( arg2 ) : "eax", "ebx", "ecx", "edx" ); if( retval < 0 ) { errno = -retval; return -1; } return retval; } enum { STDIN_FILENO = 0, STDOUT_FILENO = 1, STDERR_FILENO = 2 }; static void print_char( int fd, int c ) { /* gcc 10 and 11 think on -Os, -O1 and -O2 that 's' can be held * in registers, thus clobbering the output? Let's put a * volatile here seems to help. Neither clang, pcc or tcc show * this behaviour.. */ volatile char s[1]; s[0] = c; syscall3( SYSCALL_WRITE, fd, (int)s, 1 ); } static void print_string( char *s ) { syscall3( SYSCALL_WRITE, STDOUT_FILENO, (int)s, strlen( s ) ); } static void print_newline( void ) { print_char( STDOUT_FILENO, '\n' ); } static int read_string( int fd, char *buf, int size ) { return syscall3( SYSCALL_READ, fd, (int)buf, size ); } int puts( char *s ) { print_string( s ); print_newline( ); return 1; } int getchar( ) { int res; char buf[1]; res = read_string( STDIN_FILENO, buf, 1 ); if( res == 0 ) { return EOF; } return buf[0]; } int putchar( int c ) { print_char( STDOUT_FILENO, c ); return c; } static void strreverse( char *s ) { char *end = s + strlen( s ) - 1; while( s < end ) { *s ^= *end; *end ^= *s; *s ^= *end; s++; end--; } } char *itoa( int v, char *s, int base ) { static char digit[] = "0123456789ABCDEF"; int sign = 0; char *p = s; if( base < 2 || base > 16 ) { return NULL; } if( v < 0 ) { v = -v; sign = 1; } do { *p++ = digit[v % base]; } while( ( v /= base ) > 0 ); if( sign ) { *p++ = '-'; } *p = '\0'; strreverse( s ); return s; } int putint( int i ) { char buf[17]; itoa( i, buf, 10 ); print_string( buf ); return i; } int putstring( char *s ) { print_string( s ); return 0; } int putnl( ) { print_newline( ); return 0; } 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 n ) { char *p; char *q; p = s; q = d; while( n > 0 ) { *q = *p; q++; p++; n--; } return d; } char *strdup( char *s ) { char *c; c = malloc( strlen( s ) + 1 ); memcpy( c, s, strlen( s ) + 1 ); return c; }