diff --git a/.gitignore b/.gitignore index bf6dcb5..e448b1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ kernel-hdr build progs/rsh +etc/rc.d/99-imtesting diff --git a/progs/dyn/Makefile b/progs/dyn/Makefile index 7be6a81..e2f18d2 100644 --- a/progs/dyn/Makefile +++ b/progs/dyn/Makefile @@ -1,7 +1,13 @@ CC?=$(CROSS_COMPILE)gcc O=build -all: mkdirs $(O)/dyn $(O)/ld +CFLAGS=-Wall \ + -Werror \ + -Wextra \ + -z max-page-size=0x1000 \ + -O0 + +all: mkdirs $(O)/dyn $(O)/test $(O)/ld mkdirs: mkdir -p $(O) @@ -9,20 +15,24 @@ mkdirs: clean: rm -rf $(O) -install: mkdirs $(O)/dyn $(O)/ld +install: mkdirs $(O)/dyn $(O)/test $(O)/ld mkdir -p $(DESTDIR)/lib $(DESTDIR)/bin install -D -m 0755 $(O)/dyn $(DESTDIR)/bin/ install -D -m 0755 $(O)/libfunc.so $(DESTDIR)/lib/ install -D -m 0755 $(O)/ld $(DESTDIR)/lib/ + install -D -m 0755 $(O)/test $(DESTDIR)/bin/dyntest -$(O)/ld: ld.c - $(CC) -static -o $@ $+ +$(O)/ld: ld.c ld-link.ld + $(CC) $(CFLAGS) -Tld-link.ld -static -o $@ ld.c $(O)/libfunc.o: lib.c - $(CC) -fPIC -c -o $@ $< + $(CC) $(CFLAGS) -fPIC -c -o $@ $< $(O)/libfunc.so: $(O)/libfunc.o $(CROSS_COMPILE)ld -shared -o $@ $+ +$(O)/test: test.c + $(CC) $(CFLAGS) -nostdlib -o $@ test.c + $(O)/dyn: dyn.c $(O)/libfunc.so - $(CC) -L$(O) -lfunc -o $@ dyn.c + $(CC) $(CFLAGS) -L$(O) -lfunc -o $@ dyn.c diff --git a/progs/dyn/ld-link.ld b/progs/dyn/ld-link.ld new file mode 100644 index 0000000..5c32711 --- /dev/null +++ b/progs/dyn/ld-link.ld @@ -0,0 +1,23 @@ +ENTRY(_start); + +SECTIONS { + . = 0x200000; + + _ld_start = .; + .text : ALIGN(4K) { + *(.init .fini) + *(.text .text.*) + *(.rodata .rodata.*) + } + + .data : ALIGN(4K) { + *(.ctors .dtors) + *(.data .data.*) + } + + .bss : ALIGN(4K) { + *(.bss) + *(COMMON) + } + _ld_end = .; +} diff --git a/progs/dyn/ld.c b/progs/dyn/ld.c index c99e99f..86da85f 100644 --- a/progs/dyn/ld.c +++ b/progs/dyn/ld.c @@ -1,9 +1,139 @@ +#include +#include #include +#include +#include #include +#include #include "elf.h" -int main(int argc, char **argv) { - fprintf(stderr, "Dynamic linking not yet implemented\n"); - exit(-1); +extern char _ld_start, _ld_end; + +struct object { + //FILE *fp; + int fd; + Elf64_Ehdr ehdr; +}; + +static int elf_read(struct object *obj, void *dst, size_t count, off_t pos) { + ssize_t bread; + if (lseek(obj->fd, pos, SEEK_SET) != pos) { + return -1; + } + if ((bread = read(obj->fd, dst, count)) < 0) { + return -1; + } + if ((size_t) bread != count) { + errno = ENOEXEC; + return -1; + } + return 0; +} + +static struct object *object_open(const char *pathname) { + struct object *obj = calloc(1, sizeof(struct object)); + + obj->fd = open(pathname, O_RDONLY, 0); + if (obj->fd < 0) { + return NULL; + } + + if (elf_read(obj, &obj->ehdr, sizeof(Elf64_Ehdr), 0) != 0) { + close(obj->fd); + free(obj); + return NULL; + } + + return obj; +} + +static void object_close(struct object *obj) { + close(obj->fd); + free(obj); +} + +static int object_load(struct object *obj) { + Elf64_Phdr phdr; + + printf("%u program headers\n", obj->ehdr.e_phnum); + for (size_t i = 0; i < obj->ehdr.e_phnum; ++i) { + off_t phoff = obj->ehdr.e_phoff + i * obj->ehdr.e_phentsize; + + if (elf_read(obj, &phdr, obj->ehdr.e_phentsize, phoff) != 0) { + return -1; + } + + switch (phdr.p_type) { + case PT_LOAD: + { + uintptr_t base = phdr.p_vaddr & ~0xFFF; + size_t size = (((phdr.p_vaddr + phdr.p_memsz + 0xFFF) & ~0xFFF) - base) / 0x1000; + + if (base < (uintptr_t) &_ld_end) { + fprintf(stderr, "Segment will overwrite the loader code\n"); + errno = ENOEXEC; + return -1; + } + + void *addr = mmap((void *) base, + size * 0x1000, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, + -1, 0); + if (addr == MAP_FAILED) { + perror("mmap()"); + return -1; + } + + // Load filesz bytes + if (elf_read(obj, (void *) phdr.p_vaddr, phdr.p_filesz, phdr.p_offset) != 0) { + return -1; + } + + // Zero (memsz - filesz) bytes + if (phdr.p_memsz > phdr.p_filesz) { + memset((void *) phdr.p_vaddr + phdr.p_filesz, 0, phdr.p_memsz - phdr.p_filesz); + } + } + break; + case PT_DYNAMIC: + fprintf(stderr, "TODO: handle dynamic binaries\n"); + return -1; + } + } + + return 0; +} + +int main(int argc, char **argv) { + const char *prog; + struct object *program; + + printf("_ld_start = %p, _ld_end = %p\n", &_ld_start, &_ld_end); + + if (argc < 2) { + fprintf(stderr, "usage: %s PROGRAM [ARGS...]\n", argv[0]); + return -1; + } + + prog = argv[1]; + program = object_open(prog); + + if (!program) { + fprintf(stderr, "Failed to load program: %s\n", prog); + return -1; + } + + if (object_load(program) != 0) { + fprintf(stderr, "Failed to load object\n"); + } + + void (*entry) (void *p) = (void (*) (void *)) program->ehdr.e_entry; + + object_close(program); + + // TODO: form arg pointer like kernel does + entry(NULL); + return 0; } diff --git a/progs/dyn/test.c b/progs/dyn/test.c new file mode 100644 index 0000000..b18f160 --- /dev/null +++ b/progs/dyn/test.c @@ -0,0 +1,35 @@ + + +static inline long __syscall3(long n, long a1, long a2, long a3) { + unsigned long ret; + asm volatile ("syscall" + :"=a"(ret) + :"a"(n),"D"(a1),"S"(a2),"d"(a3) + :"rcx","r11","memory"); + return ret; +} + +static inline long __syscall1(long n, long a1) { + unsigned long ret; + asm volatile ("syscall" + :"=a"(ret) + :"a"(n),"D"(a1) + :"rcx","r11","memory"); + return ret; +} + +char data[128] = {0}; + +void _start(void *arg) { + (void) arg; + const char *src = "abcde!\n"; + for (long i = 0; i < 128; ++i) { + data[i] = src[i]; + if (!src[i]) { + break; + } + } + __syscall3(1, 1, (long) data, 7); + __syscall1(60, 0); + while (1); +}