#define START "_start" #include #include #include #include #include "common.h" enum { flush_on_newline = 1 }; int errno = 0; char *progname; static long sysret(unsigned long r) { if (r > -4096UL) { errno = -r; return -1; } return r; } void exit(int code) { __syscall1(__NR_exit, code); } static void *brk(void *addr) { return (void *)__syscall1(__NR_brk, (long)addr); } int write(int fd, char *buf, int len) { return sysret(__syscall3(__NR_write, fd, (long)buf, len)); } int read(int fd, char *buf, int len) { return sysret(__syscall3(__NR_read, fd, (long)buf, len)); } int open(char *fname, int flags, int mode) { int o_largefile = 0100000; return sysret(__syscall3(__NR_open, (long)fname, flags|o_largefile, mode)); } int chdir(char *path) { return sysret(__syscall1(__NR_chdir, (long)path)); } int getdents64(int fd, char *buf, int size) { return sysret(__syscall3(__NR_getdents64, fd, (long)buf, size)); } int stat(char *fname, struct stat *st) { return sysret(__syscall2(__NR_stat, (long)fname, (long)st)); } int fstat(int fd, struct stat *st) { return sysret(__syscall2(__NR_fstat, (long)fd, (long)st)); } int access(char *path, int mode) { return sysret(__syscall2(__NR_access, (long)path, mode)); } int dup2(int oldfd, int newfd) { return sysret(__syscall2(__NR_dup2, oldfd, newfd)); } int pipe(int *fds) { return sysret(__syscall1(__NR_pipe, (long)fds)); } int close(int fd) { return sysret(__syscall1(__NR_close, fd)); } void *mmap(char *addr, unsigned long length, int prot, int flags, int fd, unsigned long offset) { return (void*)sysret(__syscall6(__NR_mmap, (long)addr, length, prot, flags, fd, offset)); } int munmap(char *addr, int length) { return sysret(__syscall2(__NR_munmap, (long)addr, length)); } #define MREMAP_MAYMOVE 1 void *mremap(void *old_addr, unsigned long old_size, unsigned long new_size, int flags, void *new_addr) { return (void *)sysret(__syscall5(__NR_mremap, (long)old_addr, old_size, new_size, flags, (long)new_addr)); } int fork() { return sysret(__syscall0(__NR_fork)); } int wait4(int pid, int *wstatus, int options, void *rusage) { return sysret(__syscall6(__NR_wait4, pid, (long)wstatus, options, (long)rusage, 0, 0)); } int execve(char *path, char **argv, char **envp) { return sysret(__syscall3(__NR_execve, (long)path, (long)argv, (long)envp)); } int getrusage(int who, struct rusage *rusage) { return sysret(__syscall2(__NR_getrusage, who, (long)rusage)); } enum { dirbuf_size = 16 * 1024 }; int opendir(struct dir *dir, char *path) { dir->fd = open(path, O_RDONLY|O_DIRECTORY, 0); if (dir->fd == -1) return 0; dir->buf = malloc(dirbuf_size); dir->cur = dir->buf; dir->end = dir->cur; dir->path = path; return 1; } void closedir(struct dir *dir) { close(dir->fd); } struct dirent *readdir(struct dir *dir) { struct dirent *tmp; if (dir->cur == dir->end) { int r = getdents64(dir->fd, dir->buf, dirbuf_size); if (r == -1) syscall_error(dir->path); if (r == 0) return NULL; dir->cur = dir->buf; dir->end = dir->buf + r; } tmp = (struct dirent *)dir->cur; dir->cur += tmp->dsize; return tmp; } int strzlen(char *s) { char *sc = s; for (; *s; ++s) ; return s - sc; } struct str *debug2str(char *s) { static struct str str; str.start = s; str.end = str.start + strzlen(s); return &str; } int strzeq(char *s1, char *s2) { for (; *s1 && *s1 == *s2; ++s1, ++s2) ; return *s1 == 0 && *s2 == 0; } int streq(struct str *s1, struct str *s2) { int i; char * restrict tmp1, * restrict tmp2; if (s1->end - s1->start != s2->end - s2->start) return 0; tmp1 = s1->start; tmp2 = s2->start; for (i = 0; s1->start + i < s1->end; ++i) { if (tmp1[i] != tmp2[i]) return 0; } return 1; } int strge(struct str *s1, struct str *s2) { char *tmp1 =s1->start, *tmp2 = s2->start; for (;; ++tmp1, ++tmp2) { if (tmp1 == s1->end) return tmp2 == s2->end; if (tmp2 == s2->end) return 1; if (*tmp1 != *tmp2) return *tmp1 > *tmp2; } } void memmove(char *dest, char *src, int len) { int i; if (dest < src) { for (i = 0; i < len; ++i) dest[i] = src[i]; } else { for (i = len - 1; i >= 0; --i) dest[i] = src[i]; } } void memcpy(void * restrict dest, void * restrict src, int len) { char *d = dest; char *s = src; int i; for (i = 0; i < len; ++i) d[i] = s[i]; } void memset(void *dest, char c, int len) { char *tmp = dest; for (; tmp < (char*)dest + len; ++tmp) *tmp = c; } int memeq(void *m1, void *m2, int len) { char *s1 = m1; char *s2 = m2; int i; for (i = 0; i < len; ++i) { if (s1[i] != s2[i]) return 0; } return 1; } #define E(e, s) [e] = s, char *estrings[256] = { #include "../src/errno/__strerror.h" }; #undef E void perror(char *s) { int idx = errno; if (errno > 256 || estrings[errno] == NULL) idx = 0; eprintf("%s: %s\n", s, estrings[idx]); } enum { buflen = 16 * 1024 }; struct iobuf { int fd; char buf[buflen]; int len; }; struct iobuf stdout = { 1 }; struct iobuf stderr = { 2 }; void flush(struct iobuf *buf) { int ret; if (buf->len == 0) return; ret = write(buf->fd, buf->buf, buf->len); if (ret != buf->len) { write(2, _S("some stdout/stderr error\n")); exit(3); } buf->len = 0; } void syscall_error(char *fname) { perror(fname); hexit(1); } static void putchar(struct iobuf *buf, char c) { if (buf->len == buflen) flush(buf); buf->buf[buf->len] = c; ++buf->len; if (flush_on_newline && c == '\n') flush(buf); } void hexit(int code) { flush(&stdout); flush(&stderr); exit(code); } static void print_int(struct iobuf *buf, int nsigned, int byte4, unsigned long n, int size) { char tmpbuf[64]; char *p = tmpbuf; unsigned long k; if (nsigned) { if (byte4) { int tmp = n; if (tmp < 0) { putchar(buf, '-'); tmp = -tmp; } k = tmp; } else { if ((signed long)n < 0) { putchar(buf, '-'); n = -n; } k = n; } } else { k = n; } if (k == 0) { putchar(buf, '0'); return; } for (; k > 0; k /= 10) { *p = k % 10 + '0'; ++p; } if (p - tmpbuf < size) { for (; p < tmpbuf + size; ++p) *p = '0'; } for (--p;; --p) { putchar(buf, *p); if (p == tmpbuf) break; } } void print_strz(struct iobuf *buf, char *s) { for (; *s; ++s) putchar(buf, *s); } void print_str(struct iobuf *buf, struct str *s) { char *tmp = s->start; for (; tmp != s->end; ++tmp) putchar(buf, *tmp); } static void format(struct iobuf *buf, char c, int size, va_list vl) { switch(c) { unsigned long arg; case '%': if (size != -1) goto size_err; putchar(buf, '%'); break; case 'd': case 'u': arg = va_arg(vl, unsigned int); print_int(buf, c == 'd', 1, arg, size); break; case 'D': case 'U': arg = va_arg(vl, unsigned long); print_int(buf, c == 'D', 0, arg, size); break; case 's': if (size != -1) goto size_err; print_strz(buf, va_arg(vl, char*)); break; case 'S': if (size != -1) goto size_err; print_str(buf, va_arg(vl, struct str *)); break; case 'c': if (size != -1) goto size_err; arg = va_arg(vl, int); putchar(buf, arg); break; default: write(2, _S("printf: unexpected format option \n")); hexit(2); } return; size_err: write(2, _S("printf: %c: size not applicable\n")); hexit(2); } static void __printf(struct iobuf *buf, char *s, va_list vl) { for (; *s; ++s) { if (*s == '%') { int size = -1; if (s[1] == 0) goto zero_err; if (s[1] >= '0' && s[1] <= '9') { size = s[1] - '0'; ++s; } if (s[1] == 0) goto zero_err; format(buf, s[1], size, vl); ++s; } else { putchar(buf, *s); } } return; zero_err: write(2, _S("printf: nothing specifier after '%'")); hexit(2); } void printf(char *fmt, ...) { va_list vl; va_start(vl, fmt); __printf(&stdout, fmt, vl); va_end(vl); } void evprintf(char *fmt, va_list vl) { __printf(&stderr, fmt, vl); } void eprintf(char *fmt, ...) { va_list vl; va_start(vl, fmt); evprintf(fmt, vl); va_end(vl); } void error(char *fmt, ...) { va_list vl; va_start(vl, fmt); print_strz(&stderr, progname); print_strz(&stderr, ": "); evprintf(fmt, vl); va_end(vl); print_strz(&stderr, "\n"); hexit(1); } /* Memory management */ struct heap heap; void heap_offset_save() { if (heap.snum == heap_saves_max) error("heap: offset stack is full"); heap.saved[heap.snum] = heap.a.cur; ++heap.snum; } void heap_offset_remove() { if (heap.snum == 0) { error("heap: offset stack is empty"); } --heap.snum; } void heap_offset_restore() { if (heap.snum == 0) error("mem_buffer: no offset was saved\n"); heap.a.cur = heap.saved[heap.snum - 1]; --heap.snum; heap.last_alloc = NULL; } void *mem_alloc(int size) { void *tmp = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (tmp == (void*)-1) syscall_error("mmap"); return tmp; } static int get_alignment(int n) { if (n >= 16) return 16; if (n >= 8) return 8; if (n >= 4) return 4; if (n >= 2) return 2; return 1; } static void heap_grow(struct arena *a, int size) { char *tmp; if (size < 128*1024) { size = 128*1024; } else { size *= 2; size &= -4096; } /*printf("Growing (%d)\n", size);*/ tmp = brk(a->end + size); if (tmp == a->end) error("heap: unable to grow"); a->end = tmp; } static void heap_init() { heap.a.start = heap.a.cur = heap.a.end = brk(NULL); heap.a.grow = heap_grow; heap.snum = 0; heap.last_alloc = NULL; } #define ALIGN(v, align) \ (void*)(((unsigned long)(v) + (align) - 1ul) & -(align)) void *amalloc(struct arena *a, int size) { int align = get_alignment(size); char *tmp = ALIGN(a->cur, align); int diff = tmp + size - a->end; if (diff > 0) a->grow(a, diff); a->cur = tmp + size; return tmp; } void *malloc(int size) { void *tmp = amalloc(&heap.a, size); heap.last_alloc = tmp; return tmp; } void realloc(void *p, int newsize) { int diff; if (heap.last_alloc != p) error("realloc: not a previously allocated region"); diff = heap.last_alloc + newsize - heap.a.end; if (diff > 0) heap.a.grow(&heap.a, diff); heap.a.cur = heap.last_alloc + newsize; } extern int main(int argc, char **argv, char **envp); void _start_c(unsigned long *p) { int argc = p[0]; char **argv = (void *)(p + 1); char **envp = argv + argc + 1; progname = argv[0]; heap_init(); hexit(main(argc, argv, envp)); }