From 559ae4cbccec564e6597bdaa628bf6a2c4fc4fb4 Mon Sep 17 00:00:00 2001 From: Theodore Dubois Date: Thu, 15 Jun 2017 14:18:24 -0700 Subject: [PATCH] Make Hello World with libc and ld work IF you want to understand how amazing this is, watch https://www.twitch.tv/videos/152115137 --- emu/cpu.c | 10 +++++++++- emu/instructions.h | 5 +++++ emu/memory.c | 18 +++++++++++++++++- emu/memory.h | 6 ++++-- fs/real.c | 4 ++-- meson.build | 2 +- misc.h | 2 +- sys/calls.c | 6 +++++- sys/calls.h | 9 +++++++++ sys/fs.c | 14 ++++++++++++++ sys/mmap.c | 42 +++++++++++++++++++++++++++++++++++------- tests/meson.build | 8 ++++---- tools/ptraceomatic.c | 3 ++- vdso/note.S | 2 +- 14 files changed, 109 insertions(+), 22 deletions(-) diff --git a/emu/cpu.c b/emu/cpu.c index f8616e5a4c..d3ac3e0890 100644 --- a/emu/cpu.c +++ b/emu/cpu.c @@ -183,6 +183,9 @@ int cpu_step(struct cpu_state *cpu) { do_cpuid(&cpu->eax, &cpu->ebx, &cpu->ecx, &cpu->edx); break; + case 0xa3: TRACEI("bt reg, modrm\t"); + READMODRM; BT(modrm_reg, modrm_val); break; + case 0xa5: TRACEI("shld cl, reg, modrm"); READMODRM; SHLD(cpu->cl, modrm_reg, modrm_val); break; @@ -259,8 +262,13 @@ int cpu_step(struct cpu_state *cpu) { } break; + case 0x11: TRACEI("adc reg, modrm"); + READMODRM; ADC(modrm_reg, modrm_val_w); break; + case 0x19: TRACEI("sbb reg, modrm"); READMODRM; SBB(modrm_reg, modrm_val); break; + case 0x1b: TRACEI("sbb modrm, reg"); + READMODRM; SBB(modrm_val, modrm_reg); break; case 0x21: TRACEI("and reg, modrm"); READMODRM; AND(modrm_reg, modrm_val_w); break; @@ -379,7 +387,7 @@ int cpu_step(struct cpu_state *cpu) { case 0x6a: TRACEI("push imm8\t"); READIMM8; PUSH((int8_t) imm8); break; case 0x6b: TRACEI("imul imm8\t"); - READMODRM; READIMM8; MUL3((int8_t) imm8, (int8_t) modrm_val, modrm_reg); break; + READMODRM; READIMM8; MUL3((int8_t) imm8, (int32_t) modrm_val, modrm_reg); break; case 0x70: TRACEI("jo rel8\t"); READIMM8; J_REL(O, (int8_t) imm8); break; diff --git a/emu/instructions.h b/emu/instructions.h index 466316bdd2..3a136d15de 100644 --- a/emu/instructions.h +++ b/emu/instructions.h @@ -115,6 +115,8 @@ OR(src, dst##_w); break; \ case 2: TRACE("adc"); \ ADC(src, dst##_w); break; \ + case 3: TRACE("sbb"); \ + SBB(src, dst##_w); break; \ case 4: TRACE("and"); \ AND(src, dst##_w); break; \ case 5: TRACE("sub"); \ @@ -203,6 +205,9 @@ case 7: TRACE("undefined"); return INT_UNDEFINED; \ } +#define BT(bit, val) \ + cpu->cf = (val) & (1 << bit) + #define BUMP_SI(size) \ if (!cpu->df) \ cpu->esi += size; \ diff --git a/emu/memory.c b/emu/memory.c index d76680f85f..ba8434af17 100644 --- a/emu/memory.c +++ b/emu/memory.c @@ -44,6 +44,7 @@ int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, unsigned pt_unmap(mem, page, 1); } struct pt_entry *entry = malloc(sizeof(struct pt_entry)); + // FIXME this could allocate some of the memory and then abort if (entry == NULL) return _ENOMEM; entry->data = memory; entry->refcount = 1; @@ -57,7 +58,10 @@ int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, unsigned return 0; } -void pt_unmap(struct mem *mem, page_t start, pages_t pages) { +int pt_unmap(struct mem *mem, page_t start, pages_t pages) { + for (page_t page = start; page < start + pages; page++) + if (mem->pt[page] == NULL) + return -1; for (page_t page = start; page < start + pages; page++) { struct pt_entry *entry = mem->pt[page]; mem->pt[page] = NULL; @@ -67,6 +71,7 @@ void pt_unmap(struct mem *mem, page_t start, pages_t pages) { free(entry); } } + return 0; } int pt_map_nothing(struct mem *mem, page_t start, pages_t pages, unsigned flags) { @@ -84,6 +89,17 @@ int pt_map_file(struct mem *mem, page_t start, pages_t pages, int fd, off_t off, return pt_map(mem, start, pages, memory, flags); } +// FIXME this can overwrite P_GROWSDOWN or P_GUARD +int pt_set_flags(struct mem *mem, page_t start, pages_t pages, int flags) { + for (page_t page = start; page < start + pages; page++) + if (mem->pt[page] == NULL) + return _ENOMEM; + for (page_t page = start; page < start + pages; page++) { + mem->pt[page]->flags = flags; + } + return 0; +} + void pt_dump(struct mem *mem) { for (unsigned i = 0; i < PT_SIZE; i++) { if (mem->pt[i] != NULL) { diff --git a/emu/memory.h b/emu/memory.h index f618d5b0e2..71cd5bc570 100644 --- a/emu/memory.h +++ b/emu/memory.h @@ -50,8 +50,10 @@ int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, unsigned int pt_map_file(struct mem *mem, page_t start, pages_t pages, int fd, off_t off, unsigned flags); // Map empty space into fake memory int pt_map_nothing(struct mem *mem, page_t page, pages_t pages, unsigned flags); -// Unmap fake memory -void pt_unmap(struct mem *mem, page_t page, pages_t pages); +// Unmap fake memory, return -1 if any part of the range isn't mapped and 0 otherwise +int pt_unmap(struct mem *mem, page_t start, pages_t pages); +// Set the flags on memory +int pt_set_flags(struct mem *mem, page_t start, pages_t pages, int flags); void pt_dump(struct mem *mem); diff --git a/fs/real.c b/fs/real.c index 4ca6db06b2..cd0a71d7ae 100644 --- a/fs/real.c +++ b/fs/real.c @@ -32,7 +32,7 @@ static void copy_stat(struct statbuf *fake_stat, struct stat *real_stat) { fake_stat->nlink = real_stat->st_nlink; fake_stat->uid = real_stat->st_uid; fake_stat->gid = real_stat->st_gid; - fake_stat->rdev = real_stat->st_dev; + fake_stat->rdev = real_stat->st_rdev; fake_stat->size = real_stat->st_size; fake_stat->blksize = real_stat->st_blksize; fake_stat->blocks = real_stat->st_blocks; @@ -91,7 +91,7 @@ static int realfs_mmap(struct fd *fd, off_t offset, size_t len, int prot, int fl int mmap_flags = 0; if (flags & MMAP_PRIVATE) mmap_flags |= MAP_PRIVATE; // TODO more flags are probably needed - void *mem = mmap(NULL, len, prot, flags, fd->real_fd, offset); + void *mem = mmap(NULL, len, prot, mmap_flags, fd->real_fd, offset); if (mem == MAP_FAILED) return err_map(errno); *mem_out = mem; diff --git a/meson.build b/meson.build index e9f11095d8..d4addc52a7 100644 --- a/meson.build +++ b/meson.build @@ -45,4 +45,4 @@ subdir('tests') # ptraceomatic et al subdir('tools') -executable('thingy', ['main.c'], dependencies: [ish]) +executable('ish', ['main.c'], dependencies: [ish]) diff --git a/misc.h b/misc.h index 2d46ea7a05..da661e9a4d 100644 --- a/misc.h +++ b/misc.h @@ -9,7 +9,7 @@ #include // debug output utilities -#if 1 +#if 0 #define TRACE(msg, ...) printf(msg, ##__VA_ARGS__) #else #define TRACE(msg, ...) (void)NULL diff --git a/sys/calls.c b/sys/calls.c index f1d292515e..225b01226e 100644 --- a/sys/calls.c +++ b/sys/calls.c @@ -22,8 +22,12 @@ syscall_t syscall_table[] = { [41] = (syscall_t) sys_dup, [45] = (syscall_t) sys_brk, [85] = (syscall_t) sys_readlink, + [90] = (syscall_t) sys_mmap, + [91] = (syscall_t) sys_munmap, [122] = (syscall_t) _sys_uname, - [192] = (syscall_t) sys_mmap, + [125] = (syscall_t) sys_mprotect, + [146] = (syscall_t) sys_writev, + [192] = (syscall_t) sys_mmap2, [197] = (syscall_t) sys_fstat64, [243] = (syscall_t) sys_set_thread_area, [252] = (syscall_t) sys_exit_group, diff --git a/sys/calls.h b/sys/calls.h index 5e0ee40367..1e805d7016 100644 --- a/sys/calls.h +++ b/sys/calls.h @@ -27,8 +27,13 @@ dword_t sys_fstat64(fd_t fd_no, addr_t statbuf_addr); dword_t sys_access(addr_t pathname_addr, dword_t mode); dword_t sys_readlink(addr_t pathname, addr_t buf, dword_t bufsize); +struct io_vec { + addr_t base; + uint_t len; +}; dword_t sys_read(fd_t fd_no, addr_t buf_addr, dword_t size); dword_t sys_write(fd_t fd_no, addr_t buf_addr, dword_t size); +dword_t sys_writev(fd_t fd_no, addr_t iovec_addr, dword_t iovec_count); dword_t sys_dup(fd_t fd); @@ -37,8 +42,12 @@ int handle_pagefault(addr_t addr); #define MMAP_SHARED 0x1 #define MMAP_PRIVATE 0x2 +#define MMAP_FIXED 0x10 #define MMAP_ANONYMOUS 0x20 addr_t sys_mmap(addr_t addr, dword_t len, dword_t prot, dword_t flags, fd_t fd_no, dword_t offset); +addr_t sys_mmap2(addr_t addr, dword_t len, dword_t prot, dword_t flags, fd_t fd_no, dword_t offset); +int_t sys_munmap(addr_t addr, uint_t len); +int_t sys_mprotect(addr_t addr, uint_t len, int_t prot); #define UNAME_LENGTH 65 struct uname { diff --git a/sys/fs.c b/sys/fs.c index b9070aa44a..72aa32cdfb 100644 --- a/sys/fs.c +++ b/sys/fs.c @@ -69,6 +69,20 @@ dword_t sys_write(fd_t fd_no, addr_t buf_addr, dword_t size) { return fd->ops->write(fd, buf, size); } +dword_t sys_writev(fd_t fd_no, addr_t iovec_addr, dword_t iovec_count) { + struct io_vec iovecs[iovec_count]; + user_get_count(iovec_addr, iovecs, sizeof(iovecs)); + int res; + dword_t count = 0; + for (unsigned i = 0; i < iovec_count; i++) { + res = sys_write(fd_no, iovecs[i].base, iovecs[i].len); + if (res < 0) + return res; + count += res; + } + return count; +} + dword_t sys_fstat64(fd_t fd_no, addr_t statbuf_addr) { struct fd *fd = current->files[fd_no]; struct statbuf stat; diff --git a/sys/mmap.c b/sys/mmap.c index 89f8302016..eb5a663639 100644 --- a/sys/mmap.c +++ b/sys/mmap.c @@ -3,6 +3,10 @@ #include "emu/process.h" #include "emu/memory.h" +addr_t sys_mmap2(addr_t addr, dword_t len, dword_t prot, dword_t flags, fd_t fd_no, dword_t offset) { + return sys_mmap(addr, len, prot, flags, fd_no, offset << PAGE_BITS); +} + addr_t sys_mmap(addr_t addr, dword_t len, dword_t prot, dword_t flags, fd_t fd_no, dword_t offset) { int err; @@ -10,16 +14,22 @@ addr_t sys_mmap(addr_t addr, dword_t len, dword_t prot, dword_t flags, fd_t fd_n return _EINVAL; if (prot & ~(P_READ | P_WRITE | P_EXEC)) return _EINVAL; - if (!(flags & MMAP_PRIVATE)) - // TODO MMAP_SHARED - return _EINVAL; - if (addr != 0) + if (!(flags & MMAP_PRIVATE)) { + TODO("MMAP_SHARED"); return _EINVAL; + } pages_t pages = PAGE_ROUND_UP(len); - page_t page = pt_find_hole(&curmem, pages); - if (page == BAD_PAGE) - return _ENOMEM; + page_t page; + if (addr == 0) { + page = pt_find_hole(&curmem, pages); + if (page == BAD_PAGE) + return _ENOMEM; + } else { + if (OFFSET(addr) != 0) + return _EINVAL; + page = PAGE(addr); + } if (flags & MMAP_ANONYMOUS) { if ((err = pt_map_nothing(&curmem, page, pages, prot)) < 0) return err; @@ -39,3 +49,21 @@ addr_t sys_mmap(addr_t addr, dword_t len, dword_t prot, dword_t flags, fd_t fd_n return page << PAGE_BITS; } +int_t sys_munmap(addr_t addr, uint_t len) { + if (OFFSET(addr) != 0) + return _EINVAL; + if (len == 0) + return _EINVAL; + if (pt_unmap(&curmem, PAGE(addr), PAGE_ROUND_UP(len)) < 0) + return _EINVAL; + return 0; +} + +int_t sys_mprotect(addr_t addr, uint_t len, int_t prot) { + if (OFFSET(addr) != 0) + return _EINVAL; + if (prot & ~(P_READ | P_WRITE | P_EXEC)) + return _EINVAL; + pages_t pages = PAGE_ROUND_UP(len); + return pt_set_flags(&curmem, PAGE(addr), pages, prot); +} diff --git a/tests/meson.build b/tests/meson.build index eb97aa51bf..ee60f74e99 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -4,9 +4,9 @@ executable('hello-libc-static', ['hello-clib.c'], c_args: ['-m32'], link_args: [ executable('hello-libc', ['hello-clib.c'], c_args: ['-m32'], link_args: ['-m32']) # simple benchmark -executable('looper', ['looper.c', 'nothing.c'], c_args: ['-m32'], link_args: ['-m32', '-static']) -executable('fibbonaci', ['fibbonaci.c'], c_args: ['-m32'], link_args: ['-m32', '-static']) +executable('looper', ['looper.c', 'nothing.c'], c_args: ['-m32'], link_args: ['-m32']) +executable('fibbonaci', ['fibbonaci.c'], c_args: ['-m32'], link_args: ['-m32']) # filesystem motivation -executable('cat', ['cat.c'], c_args: ['-m32'], link_args: ['-m32', '-static']) -executable('stat', ['stat.c'], c_args: ['-m32', '-D_FILE_OFFSET_BITS=64'], link_args: ['-m32', '-static']) +executable('cat', ['cat.c'], c_args: ['-m32'], link_args: ['-m32']) +executable('stat', ['stat.c'], c_args: ['-m32', '-D_FILE_OFFSET_BITS=64'], link_args: ['-m32']) diff --git a/tools/ptraceomatic.c b/tools/ptraceomatic.c index 0b102d9d06..793a335a22 100644 --- a/tools/ptraceomatic.c +++ b/tools/ptraceomatic.c @@ -210,6 +210,7 @@ void step_tracing(struct cpu_state *cpu, int pid, int sender, int receiver) { pt_copy(pid, regs.rcx, sizeof(struct newstat64)); break; + case 90: // mmap case 192: // mmap2 if (cpu->eax < 0xfffff000 && cpu->edi != (dword_t) -1) { // fake mmap didn't fail, change fd @@ -220,7 +221,7 @@ void step_tracing(struct cpu_state *cpu, int pid, int sender, int receiver) { // some syscalls need to just happen case 45: // brk - case 90: // mmap + case 125: // mprotect case 243: // set_thread_area goto do_step; } diff --git a/vdso/note.S b/vdso/note.S index 44652b41ab..5f0450a0f6 100644 --- a/vdso/note.S +++ b/vdso/note.S @@ -8,7 +8,7 @@ name: after_name: .balign 4 note: - .long 0x010000 // let's pretend we're linux 2.0.0 + .long 0x020620 // let's pretend we're linux 2.6.32 after_note: .balign 4 .popsection