├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── arch ├── arm │ └── linux │ │ ├── _start.s │ │ └── syscall.c ├── i386 │ ├── linux │ │ ├── _start.s │ │ └── syscall.s │ └── osx │ │ ├── start.s │ │ └── syscall.s ├── mips │ └── linux │ │ ├── start.s │ │ └── syscall.c └── x86_64 │ ├── _syscall.s │ ├── linux │ └── _start.s │ ├── netbsd │ ├── note.s │ └── start.s │ └── osx │ └── start.s ├── example └── test.c └── src ├── ctype.c ├── ctype.h ├── errno.c ├── errno.h ├── io.c ├── io.h ├── math ├── ceil.c └── log.c ├── mem.c ├── mem.h ├── mman.c ├── mman.h ├── num.c ├── num.h ├── stdlib.h ├── string.c ├── string.h ├── syscall.h ├── syscalls.c └── syscalls.h /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: false 3 | 4 | compiler: 5 | - clang 6 | - gcc 7 | 8 | os: 9 | - linux 10 | - osx 11 | 12 | script: "cmake . && make" 13 | after_script: bin/test 14 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(lib43) 3 | 4 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 5 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 6 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 7 | link_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) 8 | include_directories(src) 9 | 10 | enable_language(ASM) 11 | set(CMAKE_C_FLAGS "-std=c99 -Os -ffreestanding -fno-stack-protector -nostdlib") 12 | 13 | # OS detection 14 | if (NOT DEFINED OS) 15 | if (APPLE) 16 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static") 17 | set(CMAKE_SKIP_RPATH TRUE) 18 | set(OS "osx") 19 | elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") 20 | set(OS "linux") 21 | elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") 22 | set(OS "netbsd") 23 | else() 24 | message(FATAL_ERROR "Unsupported OS: ${CMAKE_SYSTEM_NAME}") 25 | endif() 26 | endif() 27 | 28 | # CPU detection 29 | if (NOT DEFINED CPU) 30 | if (CMAKE_SYSTEM_PROCESSOR MATCHES "^i[36]86$") 31 | set(CPU "i386") 32 | elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64)$") 33 | set(CPU "x86_64") 34 | elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^mips") 35 | set(CPU "mips") 36 | elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm") 37 | set(CPU "arm") 38 | else() 39 | message(FATAL_ERROR "Unsupported processor: ${CMAKE_SYSTEM_PROCESSOR}") 40 | endif() 41 | endif() 42 | 43 | if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64)" AND CPU MATCHES "i386") 44 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") 45 | set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -m32") 46 | endif() 47 | 48 | file(GLOB_RECURSE LIB43_SOURCE src/*.c) 49 | file(GLOB_RECURSE LIB43_PLATFORM_SOURCE arch/${CPU}/${OS}/*.s arch/${CPU}/${OS}/*.c) 50 | file(GLOB LIB43_ARCH_SOURCE arch/${CPU}/*.s arch/${CPU}/*.c) 51 | add_library(43 STATIC ${LIB43_SOURCE} ${LIB43_PLATFORM_SOURCE} ${LIB43_ARCH_SOURCE}) 52 | 53 | add_executable(test1 example/test.c) 54 | target_link_libraries(test1 43) 55 | set_target_properties(test1 PROPERTIES OUTPUT_NAME "test") 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Ryan Hileman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/lunixbochs/lib43.svg?branch=master)](https://travis-ci.org/lunixbochs/lib43) 2 | 3 | lib43 4 | ==== 5 | 6 | | | linux | osx | netbsd | 7 | |---------|-------|-----|--------| 8 | | x86 | √ | √ | x | 9 | | x86\_64 | √ | √ | √ | 10 | | arm | √ | x | x | 11 | | arm64 | x | x | x | 12 | | mips32 | √ | x | x | 13 | -------------------------------------------------------------------------------- /arch/arm/linux/_start.s: -------------------------------------------------------------------------------- 1 | .text 2 | .global _start 3 | _start: 4 | ldr %r0, [%sp] 5 | add %r1, %sp, #4 6 | sub %lr, %lr, %lr 7 | bl main 8 | push {%r0} 9 | ldr %r1, =stdout 10 | ldr %r0, [%r1] 11 | bl fflush 12 | pop {%r0} 13 | bl _exit 14 | -------------------------------------------------------------------------------- /arch/arm/linux/syscall.c: -------------------------------------------------------------------------------- 1 | #include "syscall.h" 2 | 3 | #define arg abi_long 4 | #define reg(name) register abi_long name __asm__(#name) 5 | arg syscall(arg _1, arg _2, arg _3, arg _4, arg _5, arg _6, int n) { 6 | reg(r7) = n; reg(r0) = _1; reg(r1) = _2; reg(r2) = _3; 7 | reg(r3) = _4; reg(r4) = _5; reg(r5) = _6; 8 | __asm__ __volatile__( 9 | "SVC #0" 10 | :"=r"(r0) 11 | :"r"(r7), "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5) 12 | ); 13 | return r0; 14 | } 15 | -------------------------------------------------------------------------------- /arch/i386/linux/_start.s: -------------------------------------------------------------------------------- 1 | .text 2 | .global _start 3 | _start: 4 | xor %ebp, %ebp 5 | pop %ecx 6 | mov %esp, %eax 7 | and $-16, %esp 8 | push %eax 9 | push %ecx 10 | call main 11 | 12 | push %eax 13 | 14 | push stdout 15 | call fflush 16 | pop %ecx 17 | 18 | call _exit 19 | -------------------------------------------------------------------------------- /arch/i386/linux/syscall.s: -------------------------------------------------------------------------------- 1 | .global syscall 2 | syscall: 3 | push %ebx 4 | push %ecx 5 | push %edx 6 | push %esi 7 | push %edi 8 | push %ebp 9 | 10 | add $28, %esp 11 | pop %ebx 12 | pop %ecx 13 | pop %edx 14 | pop %esi 15 | pop %edi 16 | pop %ebp 17 | pop %eax 18 | int $0x80 19 | 20 | sub $56, %esp 21 | pop %ebp 22 | pop %edi 23 | pop %esi 24 | pop %edx 25 | pop %ecx 26 | pop %ebx 27 | ret 28 | -------------------------------------------------------------------------------- /arch/i386/osx/start.s: -------------------------------------------------------------------------------- 1 | .text 2 | .global start 3 | start: 4 | xor %ebp, %ebp 5 | pop %ecx 6 | mov %esp, %eax 7 | and $-16, %esp 8 | push %eax 9 | push %ecx 10 | call _main 11 | push %eax 12 | 13 | push _stdout 14 | call _fflush 15 | pop %ecx 16 | 17 | call __exit 18 | -------------------------------------------------------------------------------- /arch/i386/osx/syscall.s: -------------------------------------------------------------------------------- 1 | .global _syscall 2 | _syscall: 3 | mov 28(%esp), %eax 4 | int $0x80 5 | ret 6 | -------------------------------------------------------------------------------- /arch/mips/linux/start.s: -------------------------------------------------------------------------------- 1 | .text 2 | .global __start 3 | __start: 4 | subu $fp, $fp, $fp 5 | lui $gp, %hi(_gp) 6 | addi $gp, %lo(_gp) 7 | lw $a0, ($sp) # argc 8 | addu $a1, $sp, 4 # argv 9 | lw $25, %call16(main)($gp) 10 | and $sp, $sp, -8 11 | addi $sp, $sp, -4*6 12 | jalr $25 13 | 14 | lw $4, stdout 15 | lw $25, %call16(fflush)($gp) 16 | jalr $25 17 | 18 | move $a0, $v0 19 | lw $25, %call16(_exit)($gp) 20 | jalr $25 21 | -------------------------------------------------------------------------------- /arch/mips/linux/syscall.c: -------------------------------------------------------------------------------- 1 | #include "syscall.h" 2 | 3 | #define arg abi_long 4 | #define reg(name) register abi_long name __asm__(#name) 5 | arg syscall(arg _1, arg _2, arg _3, arg _4, arg _5, arg _6, int n) { 6 | reg(v0) = n; reg(a0) = _1; reg(a1) = _2; reg(a2) = _3; reg(a3) = _4; 7 | __asm__ __volatile__( 8 | "syscall;" 9 | :"=r"(v0), "=r"(a3) 10 | :"r"(v0), "r"(a0), "r"(a1), "r"(a2), "r"(a3) 11 | ); 12 | if (a3) { 13 | errno = v0; 14 | return -1; 15 | } else { 16 | return v0; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /arch/x86_64/_syscall.s: -------------------------------------------------------------------------------- 1 | .global syscall 2 | .global _syscall 3 | syscall: 4 | _syscall: 5 | mov 8(%rsp), %rax 6 | mov %rcx, %r10 7 | syscall 8 | ret 9 | -------------------------------------------------------------------------------- /arch/x86_64/linux/_start.s: -------------------------------------------------------------------------------- 1 | .text 2 | .global _start 3 | _start: 4 | pop %rdi 5 | mov %rsp, %rsi 6 | and $-16, %rsp 7 | call main 8 | push %rax 9 | mov stdout, %rdi 10 | call fflush 11 | pop %rdi 12 | call _exit 13 | -------------------------------------------------------------------------------- /arch/x86_64/netbsd/note.s: -------------------------------------------------------------------------------- 1 | .section ".note.netbsd.ident", "", @note 2 | .long 2f-1f 3 | .long 4f-3f 4 | .long 1 5 | 1: .asciz "NetBSD" 6 | 2: .p2align 2 7 | 3: .long 400000000 8 | 4: .p2align 2 9 | -------------------------------------------------------------------------------- /arch/x86_64/netbsd/start.s: -------------------------------------------------------------------------------- 1 | .text 2 | .globl start 3 | start: 4 | pop %rdi 5 | mov %rsp, %rsi 6 | and $-16, %rsp 7 | call _main 8 | push %rax 9 | mov _stdout@GOTPCREL(%rip), %rdi 10 | call _fflush 11 | pop %rdi 12 | call __exit 13 | -------------------------------------------------------------------------------- /arch/x86_64/osx/start.s: -------------------------------------------------------------------------------- 1 | .text 2 | .globl start 3 | start: 4 | pop %rdi 5 | mov %rsp, %rsi 6 | and $-16, %rsp 7 | call _main 8 | push %rax 9 | mov _stdout@GOTPCREL(%rip), %rdi 10 | call _fflush 11 | pop %rdi 12 | call __exit 13 | -------------------------------------------------------------------------------- /example/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "stdlib.h" 4 | #include "num.h" 5 | 6 | int main(int argc, char **argv) { 7 | printf("hello with printf: %s\n", "world"); 8 | printf("args (%d):\n", argc); 9 | for (int i = 0; i < argc; i++) { 10 | printf(" %d @0x%p = \"%s\"\n", i, argv[i], argv[i]); 11 | } 12 | void *test = malloc(100); 13 | test = realloc(test, 10000); 14 | memset(test, '\0', 100); 15 | strcpy(test, "hi"); 16 | printf("test: %s\n", test); 17 | free(test); 18 | 19 | printf("memcmp(\"aa\", \"aa\") == %d\n", memcmp("aa", "aa", 2)); 20 | printf("memcmp(\"aa\", \"bb\") == %d\n", memcmp("aa", "bb", 2)); 21 | printf("memcmp(\"bb\", \"aa\") == %d\n", memcmp("bb", "aa", 2)); 22 | 23 | printf("itoa(-1): signed=%s", itoa_signed(-1, 10)); 24 | printf(" unsigned=%s\n", itoa(-1, 10)); 25 | 26 | printf("ceil(1.0) = %d, ceil(1.1) = %d\n", (int)ceil(1.0), (int)ceil(1.1)); 27 | printf("strcmp: %d, %d, %d\n", strcmp("a", "b"), strcmp("b", "a"), strcmp("a", "a")); 28 | 29 | char buf1[10] = {0}; 30 | char buf2[10] = {0}; 31 | FILE *f = fopen("test.file", "w+"); 32 | fwrite("success\n", 1, 8, f); 33 | fflush(f); 34 | fseek(f, 0, SEEK_SET); 35 | fread(buf1, 1, 8, f); 36 | printf("file test 1: %s\n", buf1); 37 | fclose(f); 38 | 39 | // freopen? 40 | f = fopen("test.file", "r"); 41 | fseek(f, 0, SEEK_SET); 42 | fread(buf2, 1, 8, f); 43 | printf("file test 2: %s\n", buf2); 44 | fclose(f); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /src/ctype.c: -------------------------------------------------------------------------------- 1 | #include "ctype.h" 2 | 3 | int digittoint(int c) { 4 | if (isxdigit(c)) { 5 | if (isdigit(c)) return c - 0x30; 6 | else return toupper(c) - 0x37; 7 | } else { 8 | return 0; 9 | } 10 | } 11 | 12 | int isalnum(int c) { 13 | return isalpha(c) || isdigit(c); 14 | } 15 | 16 | int isalpha(int c) { 17 | return islower(c) || isupper(c); 18 | } 19 | 20 | int isascii(int c) { 21 | return c >= 0 && c <= 127; 22 | } 23 | 24 | int iscntrl(int c) { 25 | return (c >= 0 && c <= 0x1f) || 26 | c == 0x7f; 27 | } 28 | 29 | int isdigit(int c) { 30 | return c >= '0' && c <= '9'; 31 | } 32 | 33 | int isgraph(int c) { 34 | return c >= 0x21 && c <= 0x7e; 35 | } 36 | 37 | int ishexnumber(int c) { 38 | return isxdigit(c); 39 | } 40 | 41 | int islower(int c) { 42 | return c >= 'a' && c <= 'z'; 43 | } 44 | 45 | int isnumber(int c) { 46 | return isdigit(c); 47 | } 48 | 49 | int isprint(int c) { 50 | return c >= 0x20 && c <= 0x7e; 51 | } 52 | 53 | int ispunct(int c) { 54 | return (c >= 0x21 && c <= 0x2f) || 55 | (c >= 0x3a && c <= 0x3f) || 56 | c == '@' || 57 | (c >= 0x5b && c <= 0x60) || 58 | (c >= 0x7b && c <= 0x7e); 59 | } 60 | 61 | int isrune(int c) { 62 | return isascii(c); 63 | } 64 | 65 | int isspace(int c) { 66 | return c == '\t' || 67 | c == '\n' || 68 | c == '\v' || 69 | c == '\f' || 70 | c == '\r' || 71 | c == ' '; 72 | } 73 | 74 | int isupper(int c) { 75 | return c >= 'A' && c <= 'Z'; 76 | } 77 | 78 | int isxdigit(int c) { 79 | return isdigit(c) || 80 | (c >= 'a' && c <= 'f') || 81 | (c >= 'A' && c <= 'F'); 82 | } 83 | 84 | int toascii(int c) { 85 | return c & 0x7f; 86 | } 87 | 88 | int tolower(int c) { 89 | return isupper(c) ? (c + 0x20) : c; 90 | } 91 | 92 | int toupper(int c) { 93 | return islower(c) ? (c - 0x20) : c; 94 | } 95 | -------------------------------------------------------------------------------- /src/ctype.h: -------------------------------------------------------------------------------- 1 | int digittoint(int c); 2 | int isalnum(int c); 3 | int isalpha(int c); 4 | int isascii(int c); 5 | int iscntrl(int c); 6 | int isdigit(int c); 7 | int isgraph(int c); 8 | int ishexnumber(int c); 9 | int islower(int c); 10 | int isnumber(int c); 11 | int isprint(int c); 12 | int ispunct(int c); 13 | int isrune(int c); 14 | int isspace(int c); 15 | int isupper(int c); 16 | int isxdigit(int c); 17 | int toascii(int c); 18 | int tolower(int c); 19 | int toupper(int c); -------------------------------------------------------------------------------- /src/errno.c: -------------------------------------------------------------------------------- 1 | int errno = 0; 2 | -------------------------------------------------------------------------------- /src/errno.h: -------------------------------------------------------------------------------- 1 | extern int errno; 2 | -------------------------------------------------------------------------------- /src/io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "stdlib.h" 7 | #include "syscalls.h" 8 | 9 | static FILE _stderr = {.fd = 2}; 10 | static FILE _stdin = {.fd = 0}; 11 | static FILE _stdout = {.fd = 1}; 12 | FILE *stderr = &_stderr; 13 | FILE *stdin = &_stdin; 14 | FILE *stdout = &_stdout; 15 | 16 | static void freset(FILE *f) { 17 | f->size = 0; 18 | f->pos = 0; 19 | memset(f->buf, 0, BUFSIZE); 20 | } 21 | 22 | static int buf_seek(FILE *f, long offset, int whence) { 23 | f->size = 0; 24 | off_t pos = _lseek(f->fd, offset, whence); 25 | if (pos >= 0) { 26 | f->pos = pos; 27 | } 28 | return pos; 29 | } 30 | 31 | static int buf_write(FILE *f, const char *s, int len) { 32 | if (len == 0) { 33 | return fflush(f) - f->size; 34 | } 35 | // TODO: EOF + error handling 36 | if ((len + f->size) > BUFSIZE) { 37 | fflush(f); 38 | return _write(f->fd, s, len); 39 | } 40 | memcpy(f->buf + f->size, s, len); 41 | f->size += len; 42 | if (memchr(f->buf, '\n', f->size) != 0) { 43 | return fflush(f) - f->size; 44 | } 45 | return len; 46 | } 47 | 48 | static int buf_read(FILE *f, char *d, int len) { 49 | // TODO: EOF + error handling 50 | int ret = 0; 51 | int size = f->size - f->pos; 52 | char *pos = f->buf + f->pos; 53 | int count; 54 | // TODO: handle len > bufsize 55 | if (size > 0) { 56 | if (len < size) count = len; 57 | else count = size; 58 | memcpy(d, pos, count); 59 | d += count; 60 | ret += count; 61 | len -= count; 62 | size -= count; 63 | if (count < size) { 64 | f->pos += count; 65 | pos += count; 66 | } else { 67 | freset(f); 68 | pos = f->buf; 69 | } 70 | } 71 | if (len > BUFSIZE) { 72 | // TODO: error precedence over length? 73 | return ret + _read(f->fd, d, len); 74 | } else if (len > 0) { 75 | count = _read(f->fd, pos, BUFSIZE); 76 | if (count < 0) { 77 | return count; 78 | } else if (count == 0) { 79 | if (ret > 0) return ret; 80 | else return -1; 81 | } else if (count > len) { 82 | memcpy(d, pos, len); 83 | memmove(f->buf, pos + len, count - len); 84 | f->size = count - len; 85 | return ret + len; 86 | } else { 87 | memcpy(d, pos, count); 88 | return ret + count; 89 | } 90 | } 91 | return ret; 92 | } 93 | 94 | FILE *fopen(const char *path, const char *mode) { 95 | int mask; 96 | switch (mode[0]) { 97 | case 'r': 98 | mask = O_RDONLY; 99 | if (mode[1] == '+') // r+ 100 | mask = O_RDWR; 101 | break; 102 | case 'w': 103 | mask = O_WRONLY; 104 | if (mode[1] == '+') // w+ 105 | mask = O_RDWR | O_CREAT | O_TRUNC; 106 | break; 107 | case 'a': 108 | mask = O_WRONLY | O_APPEND | O_CREAT; 109 | if (mode[1] == '+') // a+ 110 | mask = O_RDWR | O_APPEND | O_CREAT; 111 | break; 112 | default: 113 | return 0; 114 | } 115 | // TODO: source this from somewhere real 116 | mode_t perm = 0600; 117 | int fd = _open(path, mask, perm); 118 | if (fd < 0) { 119 | return 0; 120 | } 121 | FILE *f = malloc(sizeof(FILE)); 122 | f->fd = fd; 123 | f->pos = 0; 124 | f->size = 0; 125 | f->type = FD_FILE; 126 | return f; 127 | } 128 | 129 | int fclose(FILE *f) { 130 | fflush(f); 131 | _close(f->fd); 132 | free(f); 133 | // TODO: error checking 134 | return 0; 135 | } 136 | 137 | int fseek(FILE *f, long offset, int whence) { 138 | return buf_seek(f, offset, whence); 139 | } 140 | 141 | size_t fread(char *d, size_t size, size_t count, FILE *f) { 142 | return buf_read(f, d, size * count); 143 | } 144 | 145 | size_t fwrite(const char *s, size_t size, size_t count, FILE *f) { 146 | return buf_write(f, s, size * count); 147 | } 148 | 149 | int fflush(FILE *f) { 150 | if (f->size == 0) return 0; 151 | int ret = _write(f->fd, f->buf, f->size); 152 | freset(f); 153 | return ret; 154 | } 155 | 156 | int fgetc(FILE *f) { 157 | char c; 158 | int ret = buf_read(f, &c, 1); 159 | if (ret < 1) return -1; 160 | return (unsigned char)c; 161 | } 162 | 163 | int fputc(int c, FILE *f) { 164 | char _c = c; 165 | return buf_write(f, &_c, 1); 166 | } 167 | 168 | int putc(int c, FILE *f) { 169 | return fputc(c, f); 170 | } 171 | 172 | int puts(const char *s) { 173 | return buf_write(stdout, s, strlen(s)); 174 | } 175 | 176 | int printf(const char *fmt, ...) { 177 | #define arg(type) va_arg(params, type) 178 | const char *pos = fmt; 179 | char c; 180 | int control = false; 181 | 182 | va_list params; 183 | va_start(params, fmt); 184 | while ((c = *pos++) != 0) { 185 | if (control) { 186 | control = false; 187 | // TODO: implement padding controls 188 | if (c >= '0' && c <= '9') { 189 | control = true; 190 | continue; 191 | } 192 | switch (c) { 193 | case '%': 194 | putc(c, stdout); 195 | break; 196 | case 'c': 197 | putc(arg(int), stdout); 198 | break; 199 | case 's': { 200 | char *s = arg(char *); 201 | if (s == NULL) { 202 | puts("(null)"); 203 | } else { 204 | puts(s); 205 | } 206 | break; 207 | } 208 | case 'd': 209 | case 'i': { 210 | puts(itoa_signed(arg(int), 10)); 211 | break; 212 | } 213 | case 'u': 214 | puts(itoa(arg(int), 10)); 215 | break; 216 | case 'p': 217 | case 'x': 218 | puts(itoa(arg(int), 16)); 219 | break; 220 | case 'X': 221 | puts(strupr(itoa(arg(int), 16))); 222 | break; 223 | } 224 | } else if (c == '%') { 225 | control = true; 226 | } else { 227 | putc(c, stdout); 228 | } 229 | } 230 | va_end(params); 231 | #undef arg 232 | return 0; 233 | } 234 | -------------------------------------------------------------------------------- /src/io.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BUFSIZE 4096 4 | 5 | #define FD_FILE 1 6 | #define FD_STR 2 7 | 8 | typedef struct { 9 | int fd, pos, size, type; 10 | char buf[BUFSIZE]; 11 | } FILE; 12 | 13 | extern FILE *stderr; 14 | extern FILE *stdin; 15 | extern FILE *stdout; 16 | 17 | FILE *fopen(const char *path, const char *mode); 18 | int fclose(FILE *f); 19 | int fflush(FILE *f); 20 | int fgetc(FILE *f); 21 | int fputc(int c, FILE *f); 22 | int fseek(FILE *f, long offset, int whence); 23 | int printf(const char *fmt, ...); 24 | int putc(int c, FILE *f); 25 | int puts(const char *s); 26 | size_t fread(char *d, size_t size, size_t count, FILE *f); 27 | size_t fwrite(const char *s, size_t size, size_t count, FILE *f); 28 | -------------------------------------------------------------------------------- /src/math/ceil.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | double ceil(double d) { 4 | double frac = d - (int64_t)d; 5 | return frac ? d + (1 - frac) : d; 6 | } 7 | -------------------------------------------------------------------------------- /src/math/log.c: -------------------------------------------------------------------------------- 1 | double log(double d) { 2 | return 1; 3 | } 4 | -------------------------------------------------------------------------------- /src/mem.c: -------------------------------------------------------------------------------- 1 | #include "mem.h" 2 | 3 | int memcmp(const void *p1, const void *p2, size_t len) { 4 | unsigned char *c1 = (unsigned char *)p1, *c2 = (unsigned char *)p2; 5 | for (size_t i = 0; i < len; i++) { 6 | if (c1[i] < c2[i]) { 7 | return -1; 8 | } else if (c1[i] > c2[i]) { 9 | return 1; 10 | } 11 | } 12 | return 0; 13 | } 14 | 15 | void *memchr(const void *ptr, int c, size_t n) { 16 | unsigned const char *cptr = ptr; 17 | for (size_t i = 0; i < n; i++) { 18 | if (cptr[i] == c) { 19 | return (void *)cptr+i; 20 | } 21 | } 22 | return 0; 23 | } 24 | 25 | void *memcpy(void *dst, const void *src, size_t len) { 26 | char *cdst = dst; 27 | const char *csrc = src; 28 | for (size_t i = 0; i < len; i++) { 29 | *cdst++ = *csrc++; 30 | } 31 | return dst; 32 | } 33 | 34 | void *memmove(void *dst, const void *src, size_t len) { 35 | // TODO: handle overlapping memory 36 | return memcpy(dst, src, len); 37 | } 38 | 39 | void *memset(void *ptr, int val, size_t len) { 40 | unsigned char *cptr = ptr, c = val; 41 | for (size_t i = 0; i < len; i++) { 42 | *cptr++ = c; 43 | } 44 | return ptr; 45 | } 46 | -------------------------------------------------------------------------------- /src/mem.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int memcmp(const void *p1, const void *p2, size_t len); 4 | void *memchr(const void *ptr, int c, size_t n); 5 | void *memcpy(void *dst, const void *src, size_t len); 6 | void *memmove(void *dst, const void *src, size_t len); 7 | void *memset(void *ptr, int val, size_t len); 8 | -------------------------------------------------------------------------------- /src/mman.c: -------------------------------------------------------------------------------- 1 | #include "mem.h" 2 | #include "mman.h" 3 | #include "syscalls.h" 4 | 5 | #include 6 | 7 | #if !defined(MAP_ANONYMOUS) 8 | # ifdef __linux__ 9 | # define MAP_ANONYMOUS 0x20 10 | # elif defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 11 | # define MAP_ANONYMOUS 0x1000 12 | # endif 13 | #endif 14 | 15 | #define mmap_malloc(addr, size) _mmap(addr, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) 16 | 17 | void *calloc(size_t count, size_t size) { 18 | void *p = malloc(count * size); 19 | if (p == 0) return 0; 20 | memset(p, '\0', count * size); 21 | return p; 22 | } 23 | 24 | void *malloc(size_t size) { 25 | #ifdef __mips__ 26 | void *pos = _brk(0); 27 | _brk(pos + size + sizeof(size_t)); 28 | *(size_t *)pos = size; 29 | return (void *)pos + sizeof(size_t); 30 | #else 31 | size += sizeof(size_t); 32 | void *p = mmap_malloc(0, size); 33 | if (p == 0) return 0; 34 | *(size_t *)p = size; 35 | return p + sizeof(size_t); 36 | #endif 37 | } 38 | 39 | void *realloc(void *p, size_t new_size) { 40 | if (p == 0) { 41 | return malloc(new_size); 42 | } 43 | // really need to use a struct + helpers. this is so prone to human error. 44 | void *origin = p - sizeof(size_t); 45 | size_t old_size = *(size_t *)origin; 46 | #ifdef __mips__ 47 | void *new = malloc(new_size); 48 | memcpy(new, p, old_size); 49 | return new; 50 | #endif 51 | // if you realloc more than 31/63 bits more, you should *really* just do a malloc 52 | off_t diff = new_size - old_size; 53 | if (new_size > old_size) { 54 | // tried alignment + mmap, but it always made a split allocation 55 | // so I can't zero-cost realloc unless I preallocate+split like a real allocator 56 | void *new = malloc(new_size); 57 | memcpy(new, p, old_size - sizeof(size_t)); 58 | free(p); 59 | return new; 60 | } else if (new_size < old_size) { 61 | _munmap(origin + old_size, diff); 62 | *(size_t *)origin = new_size; 63 | } 64 | return p; 65 | } 66 | 67 | void free(void *p) { 68 | #ifndef __mips__ 69 | if (p != 0) { 70 | p -= sizeof(size_t); 71 | size_t size = *(size_t *)p; 72 | _munmap(p, size); 73 | } 74 | #endif 75 | } 76 | -------------------------------------------------------------------------------- /src/mman.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void *calloc(size_t count, size_t size); 4 | void *malloc(size_t size); 5 | void *realloc(void *p, size_t new_size); 6 | void free(void *p); 7 | -------------------------------------------------------------------------------- /src/num.c: -------------------------------------------------------------------------------- 1 | #include "num.h" 2 | 3 | static const char *digits = "0123456789abcdef"; 4 | 5 | char *itoa(unsigned int i, int base) { 6 | if (base < 0 || base > 16) { 7 | return "(invalid base)"; 8 | } 9 | static char buf[12] = {0}; 10 | char *pos = &buf[11]; 11 | do { 12 | *--pos = (char)digits[i % base]; 13 | i /= base; 14 | } while (i > 0); 15 | return pos; 16 | } 17 | 18 | char *itoa_signed(int i, int base) { 19 | if (i < 0) { 20 | char *tmp = itoa(-i, base); 21 | if (tmp[0] == '(' || tmp[0] == '\0') { 22 | return tmp; 23 | } 24 | *--tmp = '-'; 25 | return tmp; 26 | } else { 27 | return itoa(i, base); 28 | } 29 | } 30 | 31 | int atoi(char *str) { 32 | int i = 0; 33 | char c; 34 | while ((c = *str++) != '\0') { 35 | if (c >= '0' && c <= '9') { 36 | i *= 10; 37 | i += (c - 48); 38 | } 39 | } 40 | return i; 41 | } 42 | -------------------------------------------------------------------------------- /src/num.h: -------------------------------------------------------------------------------- 1 | char *itoa(unsigned int, int); 2 | char *itoa_signed(int, int); 3 | int atoi(char *); 4 | -------------------------------------------------------------------------------- /src/stdlib.h: -------------------------------------------------------------------------------- 1 | #include "io.h" 2 | #include "mem.h" 3 | #include "mman.h" 4 | #include "num.h" 5 | #include "string.h" 6 | #include "syscalls.h" 7 | -------------------------------------------------------------------------------- /src/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "string.h" 4 | #include "ctype.h" 5 | #include "mman.h" 6 | 7 | size_t strlen(const char *s1) { 8 | size_t i = 0; 9 | do i++; while (s1[i] != '\0'); 10 | return i; 11 | } 12 | 13 | char *strdup(const char *s1) { 14 | char *out = (char *)malloc(strlen(s1)); 15 | if (out) strcpy(out, s1); 16 | return out; 17 | } 18 | 19 | int strcmp(const char *s1, const char *s2) { 20 | return strncmp(s1, s2, SIZE_MAX); 21 | } 22 | 23 | int strncmp(const char *s1, const char *s2, size_t n) { 24 | for (size_t pos = 0; pos < n; pos++) { 25 | char c1 = *s1++; 26 | char c2 = *s2++; 27 | if (c1 < c2) return -1; 28 | if (c1 > c2) return 1; 29 | if (c1 == 0 || c2 == 0) break; 30 | } 31 | return 0; 32 | } 33 | 34 | char *strcpy(char *dst, const char *src) { 35 | return strncpy(dst, src, SIZE_MAX); 36 | } 37 | 38 | char *strncpy(char *dst, const char *src, size_t n) { 39 | size_t i = 0; 40 | do dst[i] = *src; while (*src++ != '\0' && i++ < n); 41 | return dst; 42 | } 43 | 44 | char *strcat(char *s1, const char *s2) { 45 | return strncat(s1, s2, SIZE_MAX); 46 | } 47 | 48 | char *strncat(char *s1, const char *s2, size_t n) { 49 | strncpy(s1 + strlen(s1), s2, n); 50 | return s1; 51 | } 52 | 53 | char *strupr(char *s1) { 54 | do *s1 = toupper(*s1); while (*s1++ != '\0'); 55 | return s1; 56 | } 57 | -------------------------------------------------------------------------------- /src/string.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char *strcat(char *s1, const char *s2); 4 | char *strcpy(char *dst, const char *src); 5 | char *strdup(const char *s1); 6 | char *strncat(char *s1, const char *s2, size_t n); 7 | char *strncpy(char *dst, const char *src, size_t n); 8 | char *strupr(char *s1); 9 | size_t strlen(const char *s1); 10 | int strcmp(const char *s1, const char *s2); 11 | int strncmp(const char *s1, const char *s2, size_t n); 12 | -------------------------------------------------------------------------------- /src/syscall.h: -------------------------------------------------------------------------------- 1 | #ifndef SYSCALL_H 2 | #define SYSCALL_H 3 | 4 | #include "errno.h" 5 | 6 | #include 7 | #include 8 | 9 | #if defined(__APPLE__) && defined(__LP64__) 10 | # define SYS(name) (SYS_##name + 0x2000000) 11 | #else 12 | # define SYS(name) SYS_##name 13 | #endif 14 | 15 | #if defined(__x86_64) 16 | typedef int64_t abi_long; 17 | #elif defined(__i386__) || defined(__mips__) || defined(__arm__) 18 | typedef int32_t abi_long; 19 | #endif 20 | 21 | extern abi_long syscall(int n, abi_long a1, abi_long a2, abi_long a3, abi_long a4, abi_long a5, abi_long a6); 22 | 23 | #define syscall6(n, a1, a2, a3, a4, a5, a6) \ 24 | ({ \ 25 | errno = 0; \ 26 | (abi_long)syscall((abi_long)a1, (abi_long)a2, (abi_long)a3, \ 27 | (abi_long)a4, (abi_long)a5, (abi_long)a6, n); \ 28 | }) 29 | #define syscall5(n, args...) syscall6(n, args, 0) 30 | #define syscall4(n, args...) syscall5(n, args, 0) 31 | #define syscall3(n, args...) syscall4(n, args, 0) 32 | #define syscall2(n, args...) syscall3(n, args, 0) 33 | #define syscall1(n, args...) syscall2(n, args, 0) 34 | #define syscall0(n) syscall1(n, 0) 35 | 36 | #endif // SYSCALL_H 37 | -------------------------------------------------------------------------------- /src/syscalls.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "syscall.h" 5 | #include "syscalls.h" 6 | 7 | size_t _write(int fd, const void *buf, size_t len) { 8 | return syscall3(SYS(write), fd, buf, len); 9 | } 10 | size_t _read(int fd, const void *buf, size_t len) { 11 | return syscall3(SYS(read), fd, buf, len); 12 | } 13 | void *_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off) { 14 | #ifdef SYS_mmap2 15 | int num = SYS(mmap2); 16 | #else 17 | int num = SYS(mmap); 18 | #endif 19 | abi_long ret = syscall6(num, addr, len, prot, flags, fd, off); 20 | if (ret == -1) { 21 | return (void *)0; 22 | } 23 | return (void *)ret; 24 | } 25 | int _munmap(void *addr, size_t len) { 26 | return syscall2(SYS(munmap), addr, len); 27 | } 28 | int _mprotect(void *addr, size_t len, int prot) { 29 | return syscall3(SYS(mprotect), addr, len, prot); 30 | } 31 | void _exit(int status) { 32 | (void)syscall1(SYS(exit), status); 33 | } 34 | off_t _lseek(int fd, off_t offset, int whence) { 35 | return syscall3(SYS(lseek), fd, offset, whence); 36 | } 37 | int _open(const char *path, int flags, ...) { 38 | va_list arg; 39 | va_start(arg, flags); 40 | mode_t mode = 0; 41 | if (flags & O_CREAT) { 42 | mode = va_arg(arg, int); 43 | } 44 | va_end(arg); 45 | return syscall3(SYS(open), path, flags, mode); 46 | } 47 | int _close(int fd) { 48 | return syscall1(SYS(close), fd); 49 | } 50 | #ifdef SYS_brk 51 | void *_brk(void *addr) { 52 | return (void *)syscall1(SYS(brk), addr); 53 | } 54 | #endif 55 | int _ptrace(int request, pid_t pid, uintptr_t addr, int data) { 56 | return syscall4(SYS(ptrace), request, pid, addr, data); 57 | } 58 | int _getpid() { 59 | return syscall0(SYS(getpid)); 60 | } 61 | -------------------------------------------------------------------------------- /src/syscalls.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | size_t _write(int fd, const void *buf, size_t len); 5 | size_t _read(int fd, const void *buf, size_t len); 6 | void *_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off); 7 | int _munmap(void *addr, size_t len); 8 | int mprotect(void *addr, size_t len, int prot); 9 | void _exit(int status); 10 | off_t _lseek(int fd, off_t offset, int whence); 11 | int _open(const char *path, int flags, ...); 12 | int _close(int fd); 13 | void *_brk(void *addr); 14 | int _ptrace(int request, pid_t pid, uintptr_t addr, int data); 15 | int _getpid(); 16 | --------------------------------------------------------------------------------