From eebeef2a143d607fb9f6fe6ed7ba9deb197fb152 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 10 Apr 2020 11:02:10 +0300 Subject: [PATCH] PoC module loading + external relocation resolution --- arch/amd64/syscall.c | 97 ++++----- etc/make/conf.mk | 3 +- fs/vfs_ops.c | 19 ++ include/fs/vfs.h | 1 + include/sys/debug.h | 1 + include/sys/mod.h | 4 + include/user/module.h | 14 ++ include/user/syscall.h | 98 ++++----- install-headers.sh | 1 + sys/debug.c | 19 ++ sys/mod.c | 438 +++++++++++++++++++++++++++++++++++++++++ sys/ubsan.c | 8 +- 12 files changed, 601 insertions(+), 102 deletions(-) create mode 100644 include/sys/mod.h create mode 100644 include/user/module.h create mode 100644 sys/mod.c diff --git a/arch/amd64/syscall.c b/arch/amd64/syscall.c index df0dbe9..5385acd 100644 --- a/arch/amd64/syscall.c +++ b/arch/amd64/syscall.c @@ -10,6 +10,7 @@ #include "sys/sys_sys.h" #include "sys/sys_proc.h" #include "sys/sys_net.h" +#include "sys/mod.h" #define MSR_IA32_STAR 0xC0000081 #define MSR_IA32_LSTAR 0xC0000082 @@ -24,62 +25,64 @@ static void sys_debug_trace(const char *msg, uintptr_t v0, uintptr_t v1) { void *syscall_table[256] = { // I/O - [SYSCALL_NR_READ] = sys_read, - [SYSCALL_NR_WRITE] = sys_write, - [SYSCALL_NR_OPEN] = sys_open, - [SYSCALL_NR_CLOSE] = sys_close, - [SYSCALL_NR_STAT] = sys_stat, - [SYSCALL_NR_LSEEK] = sys_lseek, - [SYSCALL_NR_MMAP] = sys_mmap, - [SYSCALL_NR_MUNMAP] = sys_munmap, - [SYSCALL_NR_IOCTL] = sys_ioctl, - [SYSCALL_NR_ACCESS] = sys_access, - [SYSCALL_NR_SELECT] = sys_select, - [SYSCALL_NR_GETCWD] = sys_getcwd, - [SYSCALL_NR_CHDIR] = sys_chdir, - [SYSCALL_NR_MKDIR] = sys_mkdir, - [SYSCALL_NR_RMDIR] = sys_rmdir, - [SYSCALL_NR_CREAT] = sys_creat, - [SYSCALL_NR_UNLINK] = sys_unlink, - [SYSCALL_NR_READDIR] = sys_readdir, - [SYSCALL_NR_CHMOD] = sys_chmod, - [SYSCALL_NR_CHOWN] = sys_chown, + [SYSCALL_NR_READ] = sys_read, + [SYSCALL_NR_WRITE] = sys_write, + [SYSCALL_NR_OPEN] = sys_open, + [SYSCALL_NR_CLOSE] = sys_close, + [SYSCALL_NR_STAT] = sys_stat, + [SYSCALL_NR_LSEEK] = sys_lseek, + [SYSCALL_NR_MMAP] = sys_mmap, + [SYSCALL_NR_MUNMAP] = sys_munmap, + [SYSCALL_NR_IOCTL] = sys_ioctl, + [SYSCALL_NR_ACCESS] = sys_access, + [SYSCALL_NR_SELECT] = sys_select, + [SYSCALL_NR_GETCWD] = sys_getcwd, + [SYSCALL_NR_CHDIR] = sys_chdir, + [SYSCALL_NR_MKDIR] = sys_mkdir, + [SYSCALL_NR_RMDIR] = sys_rmdir, + [SYSCALL_NR_CREAT] = sys_creat, + [SYSCALL_NR_UNLINK] = sys_unlink, + [SYSCALL_NR_READDIR] = sys_readdir, + [SYSCALL_NR_CHMOD] = sys_chmod, + [SYSCALL_NR_CHOWN] = sys_chown, // User control - [SYSCALL_NR_GETUID] = sys_getuid, - [SYSCALL_NR_GETGID] = sys_getgid, - [SYSCALL_NR_SETUID] = sys_setuid, - [SYSCALL_NR_SETGID] = sys_setgid, + [SYSCALL_NR_GETUID] = sys_getuid, + [SYSCALL_NR_GETGID] = sys_getgid, + [SYSCALL_NR_SETUID] = sys_setuid, + [SYSCALL_NR_SETGID] = sys_setgid, // Process control - [SYSCALL_NRX_SIGENTRY] = sys_sigentry, - [SYSCALL_NR_EXECVE] = sys_execve, - [SYSCALL_NR_GETPID] = sys_getpid, - [SYSCALL_NR_KILL] = sys_kill, - [SYSCALL_NR_EXIT] = sys_exit, - [SYSCALL_NR_SIGRETURN] = sys_sigreturn, - [SYSCALL_NRX_WAITPID] = sys_waitpid, - [SYSCALL_NR_GETPGID] = sys_getpgid, - [SYSCALL_NR_SETPGID] = sys_setpgid, + [SYSCALL_NRX_SIGENTRY] = sys_sigentry, + [SYSCALL_NR_EXECVE] = sys_execve, + [SYSCALL_NR_GETPID] = sys_getpid, + [SYSCALL_NR_KILL] = sys_kill, + [SYSCALL_NR_EXIT] = sys_exit, + [SYSCALL_NR_SIGRETURN] = sys_sigreturn, + [SYSCALL_NRX_WAITPID] = sys_waitpid, + [SYSCALL_NR_GETPGID] = sys_getpgid, + [SYSCALL_NR_SETPGID] = sys_setpgid, // System - [SYSCALL_NR_SYNC] = sys_sync, - [SYSCALL_NR_MOUNT] = sys_mount, - [SYSCALL_NRX_UMOUNT] = sys_umount, - [SYSCALL_NR_UNAME] = sys_uname, - [SYSCALL_NR_NANOSLEEP] = sys_nanosleep, - [SYSCALL_NR_GETTIMEOFDAY] = sys_gettimeofday, - [SYSCALL_NR_REBOOT] = sys_reboot, + [SYSCALL_NR_SYNC] = sys_sync, + [SYSCALL_NR_MOUNT] = sys_mount, + [SYSCALL_NRX_UMOUNT] = sys_umount, + [SYSCALL_NR_UNAME] = sys_uname, + [SYSCALL_NR_NANOSLEEP] = sys_nanosleep, + [SYSCALL_NR_GETTIMEOFDAY] = sys_gettimeofday, + [SYSCALL_NR_REBOOT] = sys_reboot, + [SYSCALL_NRX_MODULE_LOAD] = sys_module_load, + [SYSCALL_NRX_MODULE_UNLOAD] = sys_module_unload, // Network - [SYSCALL_NR_SOCKET] = sys_socket, - [SYSCALL_NR_SENDTO] = sys_sendto, - [SYSCALL_NR_RECVFROM] = sys_recvfrom, - [SYSCALL_NR_BIND] = sys_bind, - [SYSCALL_NR_SETSOCKOPT] = sys_setsockopt, - [SYSCALL_NRX_NETCTL] = sys_netctl, + [SYSCALL_NR_SOCKET] = sys_socket, + [SYSCALL_NR_SENDTO] = sys_sendto, + [SYSCALL_NR_RECVFROM] = sys_recvfrom, + [SYSCALL_NR_BIND] = sys_bind, + [SYSCALL_NR_SETSOCKOPT] = sys_setsockopt, + [SYSCALL_NRX_NETCTL] = sys_netctl, // Extension - [SYSCALL_NRX_TRACE] = sys_debug_trace, + [SYSCALL_NRX_TRACE] = sys_debug_trace, }; int syscall_undefined(uint64_t rax) { diff --git a/etc/make/conf.mk b/etc/make/conf.mk index 605ebc6..db47f30 100644 --- a/etc/make/conf.mk +++ b/etc/make/conf.mk @@ -63,7 +63,8 @@ OBJS+=$(O)/sys/ubsan.o \ $(O)/sys/font/default8x16.o \ $(O)/sys/mem/shmem.o \ $(O)/sys/mem/slab.o \ - $(O)/sys/wait.o + $(O)/sys/wait.o \ + $(O)/sys/mod.o DIRS+=$(O)/sys \ $(O)/sys/char \ $(O)/sys/block \ diff --git a/fs/vfs_ops.c b/fs/vfs_ops.c index 7e07121..4d9209b 100644 --- a/fs/vfs_ops.c +++ b/fs/vfs_ops.c @@ -456,6 +456,25 @@ int vfs_access(struct vfs_ioctx *ctx, const char *path, int accmode) { return vfs_access_node(ctx, node, accmode); } +int vfs_fstat(struct vfs_ioctx *ctx, struct ofile *fd, struct stat *st) { + int res; + + _assert(ctx); + _assert(fd); + _assert(!(fd->flags & OF_SOCKET)); + _assert(st); + + struct vnode *node = fd->file.vnode; + + _assert(node); + + if (!node->op || !node->op->stat) { + return -EINVAL; + } + + return node->op->stat(node, st); +} + int vfs_stat(struct vfs_ioctx *ctx, const char *path, struct stat *st) { struct vnode *node; int res; diff --git a/include/fs/vfs.h b/include/fs/vfs.h index 7a08164..f29ac9d 100644 --- a/include/fs/vfs.h +++ b/include/fs/vfs.h @@ -52,6 +52,7 @@ int vfs_chown(struct vfs_ioctx *ctx, const char *path, uid_t uid, gid_t gid); int vfs_ioctl(struct vfs_ioctx *ctx, struct ofile *fd, unsigned int cmd, void *arg); int vfs_access(struct vfs_ioctx *ctx, const char *path, int accmode); +int vfs_fstat(struct vfs_ioctx *ctx, struct ofile *fd, struct stat *st); int vfs_stat(struct vfs_ioctx *ctx, const char *path, struct stat *st); int vfs_access_check(struct vfs_ioctx *ctx, int desm, mode_t mode, uid_t uid, gid_t gid); int vfs_access_node(struct vfs_ioctx *ctx, struct vnode *vn, int mode); diff --git a/include/sys/debug.h b/include/sys/debug.h index d0fa0bc..0ea96c5 100644 --- a/include/sys/debug.h +++ b/include/sys/debug.h @@ -40,6 +40,7 @@ void debug_symbol_table_set(uintptr_t symtab, uintptr_t strtab, size_t symtab_size, size_t strtab_size); int debug_symbol_find(uintptr_t addr, const char **name, uintptr_t *base); +int debug_symbol_find_by_name(const char *name, uintptr_t *value); void debug_backtrace(uintptr_t rbp, int depth, int limit); void fmtsiz(char *buf, size_t sz); diff --git a/include/sys/mod.h b/include/sys/mod.h new file mode 100644 index 0000000..5527f5b --- /dev/null +++ b/include/sys/mod.h @@ -0,0 +1,4 @@ +#pragma once + +int sys_module_load(const char *path, const char *params); +int sys_module_unload(const char *name); diff --git a/include/user/module.h b/include/user/module.h new file mode 100644 index 0000000..8b3b574 --- /dev/null +++ b/include/user/module.h @@ -0,0 +1,14 @@ +#pragma once + +struct module_desc { + char name[64]; + int version; +}; + +#define MODULE_ENTRY _mod_entry +#define MODULE_EXIT _mod_exit +#define MODULE_DESC(_name, _version) \ + struct module_desc _mod_desc = { \ + .name = _name, \ + .version = _version, \ + } diff --git a/include/user/syscall.h b/include/user/syscall.h index 7f34577..ad2249d 100644 --- a/include/user/syscall.h +++ b/include/user/syscall.h @@ -1,54 +1,56 @@ #pragma once -#define SYSCALL_NR_READ 0 -#define SYSCALL_NR_WRITE 1 -#define SYSCALL_NR_OPEN 2 -#define SYSCALL_NR_CLOSE 3 -#define SYSCALL_NR_STAT 4 -#define SYSCALL_NR_LSEEK 8 -#define SYSCALL_NR_MMAP 9 -#define SYSCALL_NR_MUNMAP 11 -#define SYSCALL_NR_IOCTL 16 -#define SYSCALL_NR_ACCESS 21 -#define SYSCALL_NR_SELECT 23 -#define SYSCALL_NR_GETCWD 79 -#define SYSCALL_NR_CHDIR 80 -#define SYSCALL_NR_MKDIR 83 -#define SYSCALL_NR_RMDIR 84 -#define SYSCALL_NR_CREAT 85 -#define SYSCALL_NR_UNLINK 87 -#define SYSCALL_NR_READDIR 89 -#define SYSCALL_NR_CHMOD 90 -#define SYSCALL_NR_CHOWN 92 +#define SYSCALL_NR_READ 0 +#define SYSCALL_NR_WRITE 1 +#define SYSCALL_NR_OPEN 2 +#define SYSCALL_NR_CLOSE 3 +#define SYSCALL_NR_STAT 4 +#define SYSCALL_NR_LSEEK 8 +#define SYSCALL_NR_MMAP 9 +#define SYSCALL_NR_MUNMAP 11 +#define SYSCALL_NR_IOCTL 16 +#define SYSCALL_NR_ACCESS 21 +#define SYSCALL_NR_SELECT 23 +#define SYSCALL_NR_GETCWD 79 +#define SYSCALL_NR_CHDIR 80 +#define SYSCALL_NR_MKDIR 83 +#define SYSCALL_NR_RMDIR 84 +#define SYSCALL_NR_CREAT 85 +#define SYSCALL_NR_UNLINK 87 +#define SYSCALL_NR_READDIR 89 +#define SYSCALL_NR_CHMOD 90 +#define SYSCALL_NR_CHOWN 92 -#define SYSCALL_NR_NANOSLEEP 35 -#define SYSCALL_NR_UNAME 63 -#define SYSCALL_NR_GETTIMEOFDAY 96 -#define SYSCALL_NR_SYNC 162 -#define SYSCALL_NR_MOUNT 165 -#define SYSCALL_NRX_UMOUNT 166 -#define SYSCALL_NR_REBOOT 169 +#define SYSCALL_NR_NANOSLEEP 35 +#define SYSCALL_NR_UNAME 63 +#define SYSCALL_NR_GETTIMEOFDAY 96 +#define SYSCALL_NR_SYNC 162 +#define SYSCALL_NR_MOUNT 165 +#define SYSCALL_NRX_UMOUNT 166 +#define SYSCALL_NR_REBOOT 169 +#define SYSCALL_NRX_MODULE_LOAD 175 +#define SYSCALL_NRX_MODULE_UNLOAD 176 -#define SYSCALL_NRX_SIGENTRY 13 -#define SYSCALL_NR_SIGRETURN 15 -#define SYSCALL_NR_GETPID 39 -#define SYSCALL_NR_FORK 57 -#define SYSCALL_NR_EXECVE 59 -#define SYSCALL_NR_EXIT 60 -#define SYSCALL_NR_KILL 62 -#define SYSCALL_NR_GETUID 102 -#define SYSCALL_NR_GETGID 104 -#define SYSCALL_NR_SETUID 105 -#define SYSCALL_NR_SETGID 106 -#define SYSCALL_NR_SETPGID 109 -#define SYSCALL_NR_GETPGID 121 -#define SYSCALL_NRX_WAITPID 247 +#define SYSCALL_NRX_SIGENTRY 13 +#define SYSCALL_NR_SIGRETURN 15 +#define SYSCALL_NR_GETPID 39 +#define SYSCALL_NR_FORK 57 +#define SYSCALL_NR_EXECVE 59 +#define SYSCALL_NR_EXIT 60 +#define SYSCALL_NR_KILL 62 +#define SYSCALL_NR_GETUID 102 +#define SYSCALL_NR_GETGID 104 +#define SYSCALL_NR_SETUID 105 +#define SYSCALL_NR_SETGID 106 +#define SYSCALL_NR_SETPGID 109 +#define SYSCALL_NR_GETPGID 121 +#define SYSCALL_NRX_WAITPID 247 -#define SYSCALL_NR_SOCKET 41 -#define SYSCALL_NR_SENDTO 44 -#define SYSCALL_NR_RECVFROM 45 -#define SYSCALL_NR_BIND 49 -#define SYSCALL_NR_SETSOCKOPT 54 -#define SYSCALL_NRX_NETCTL 248 +#define SYSCALL_NR_SOCKET 41 +#define SYSCALL_NR_SENDTO 44 +#define SYSCALL_NR_RECVFROM 45 +#define SYSCALL_NR_BIND 49 +#define SYSCALL_NR_SETSOCKOPT 54 +#define SYSCALL_NRX_NETCTL 248 -#define SYSCALL_NRX_TRACE 249 +#define SYSCALL_NRX_TRACE 249 diff --git a/install-headers.sh b/install-headers.sh index d5e5e31..91b1602 100755 --- a/install-headers.sh +++ b/install-headers.sh @@ -24,6 +24,7 @@ KERNEL_HEADERS="include/user/fcntl.h \ include/user/mman.h \ include/user/video.h \ include/user/select.h \ + include/user/module.h \ include/user/reboot.h" for src_file in $KERNEL_HEADERS; do diff --git a/sys/debug.c b/sys/debug.c index 90f5599..6dcb4bf 100644 --- a/sys/debug.c +++ b/sys/debug.c @@ -5,6 +5,7 @@ #include "sys/spin.h" #include "sys/config.h" #include "sys/elf.h" +#include "sys/assert.h" #include #if defined(ARCH_AMD64) @@ -26,6 +27,24 @@ void debug_symbol_table_set(uintptr_t s0, uintptr_t s1, size_t z0, size_t z1) { g_strtab_size = z1; } +int debug_symbol_find_by_name(const char *name, uintptr_t *value) { + _assert(g_symtab_ptr && g_strtab_ptr); + Elf64_Sym *symtab = (Elf64_Sym *) g_symtab_ptr; + for (size_t i = 0; i < g_symtab_size / sizeof(Elf64_Sym); ++i) { + Elf64_Sym *sym = &symtab[i]; + if (sym->st_name < g_strtab_size) { + const char *r_name = (const char *) g_strtab_ptr + sym->st_name; + + if (!strcmp(r_name, name)) { + *value = sym->st_value; + return 0; + } + } + } + + return -1; +} + int debug_symbol_find(uintptr_t addr, const char **name, uintptr_t *base) { if (g_symtab_ptr && g_strtab_ptr) { size_t offset = 0; diff --git a/sys/mod.c b/sys/mod.c new file mode 100644 index 0000000..26a5282 --- /dev/null +++ b/sys/mod.c @@ -0,0 +1,438 @@ +#include "sys/mem/vmalloc.h" +#include "arch/amd64/cpu.h" +#include "sys/mem/phys.h" +#include "sys/mem/slab.h" +#include "user/module.h" +#include "user/errno.h" +#include "sys/string.h" +#include "user/fcntl.h" +#include "sys/thread.h" +#include "sys/assert.h" +#include "sys/debug.h" +#include "sys/panic.h" +#include "sys/heap.h" +#include "fs/ofile.h" +#include "sys/elf.h" +#include "sys/mm.h" +#include "fs/vfs.h" + +// Beyond 4GiB identity mapping +#define MOD_LINK_BASE 0x100000000 +#define MOD_VIRT_BASE (KERNEL_VIRT_BASE + 4 * (1UL << 30)) +#define MOD_ENTRY_SYM "_mod_entry" +#define MOD_EXIT_SYM "_mod_exit" +#define MOD_DESC_SYM "_mod_desc" + +struct module { + struct list_head list; + + int (*entry_func) (void); + void (*exit_func) (void); + // TODO: maybe clone this? Only valid as long as the module + // is mapped + struct module_desc *desc; + + uintptr_t base; + size_t page_count; +}; + +static LIST_HEAD(module_list); +static struct slab_cache *module_cache = NULL; + +static int elf_map_section(mm_space_t space, uintptr_t base, uintptr_t addr, size_t size) { + // Convert size to pages, align addr down to page boundary + size = (size + MM_PAGE_SIZE - 1) / MM_PAGE_SIZE; + addr &= MM_PAGE_MASK; + // Apply module offset + addr += base; + + for (size_t i = 0; i < size; ++i) { + uintptr_t virt = addr + i * MM_PAGE_SIZE; + + if (mm_map_get(space, virt, NULL) == MM_NADDR) { + uintptr_t phys = mm_phys_alloc_page(); + _assert(phys != MM_NADDR); + kdebug("%p: mapping to %p\n", virt, phys); + _assert(mm_map_single(space, virt, phys, MM_PAGE_WRITE | MM_PAGE_GLOBAL) == 0); + } else { + kdebug("%p: already mapped\n", virt); + } + } + + return 0; +} + +static int elf_sym_lookup(const char *name, int64_t *result) { + return debug_symbol_find_by_name(name, (uintptr_t *) result); +} + +static int elf_sym_get(void *image, uintptr_t base, Elf64_Shdr *shdrs, size_t table, size_t index, int64_t *value) { + if (table == SHN_UNDEF || index == SHN_UNDEF) { + return -1; + } + + Elf64_Shdr *shdr_symtab = &shdrs[table]; + size_t count = shdr_symtab->sh_size / sizeof(Elf64_Sym); + + if (index >= count) { + kerror("Symbol index is outside of the table\n"); + return -1; + } + + Elf64_Sym *sym = image + shdr_symtab->sh_offset + index * sizeof(Elf64_Sym); + if (sym->st_shndx == SHN_UNDEF) { + // Lookup external reference + Elf64_Shdr *shdr_strtab = &shdrs[shdr_symtab->sh_link]; + const char *name = image + shdr_strtab->sh_offset + sym->st_name; + + if (elf_sym_lookup(name, value) != 0) { + kerror("Undefined reference to %s\n", name); + return -1; + } else { + return 0; + } + } else if (sym->st_shndx == SHN_ABS) { + // TODO + panic("1234\n"); + } else { + // Internally defined symbol + Elf64_Shdr *target = &shdrs[sym->st_shndx]; + *value = sym->st_value + target->sh_addr + base - MOD_LINK_BASE; + return 0; + } +} + +#define R_X86_64_64 1 +#define R_X86_64_PC32 2 +#define R_X86_64_PLT32 4 + +static int elf_rela_apply(void *image, uintptr_t base, Elf64_Shdr *shdrs, Elf64_Shdr *shdr_relatab) { + Elf64_Rela *relatab = image + shdr_relatab->sh_offset; + Elf64_Shdr *target = &shdrs[shdr_relatab->sh_info]; + Elf64_Ehdr *ehdr = image; + + (void) elf_sym_get; + + if (target->sh_flags & SHF_ALLOC) { + for (size_t i = 0; i < shdr_relatab->sh_size / sizeof(Elf64_Rela); ++i) { + Elf64_Rela *rela = &relatab[i]; + + void *value_ptr = image + target->sh_offset + rela->r_offset; + intptr_t value = 0; + const char *target_name = image + shdrs[ehdr->e_shstrndx].sh_offset + target->sh_name; + + if (ELF64_R_SYM(rela->r_info) != SHN_UNDEF) { + if (elf_sym_get(image, + base, + shdrs, + shdr_relatab->sh_link, + ELF64_R_SYM(rela->r_info), + &value) != 0) { + return -1; + } + } + + switch (ELF64_R_TYPE(rela->r_info)) { + case R_X86_64_64: + *((uint64_t *) value_ptr) = (uint64_t) (value + (intptr_t) rela->r_addend); + break; + case R_X86_64_PLT32: // Treat this the same as PC32 + case R_X86_64_PC32: + // Symbol + Offset - Section Offset + { + intptr_t off = value + + (intptr_t) rela->r_offset - + (base + target->sh_addr - MOD_LINK_BASE); + if (!!(off & 0x8000000000000000) != !!(off & 0x80000000)) { + kfatal("From: %p <%04x + %s>\n", + rela->r_offset + base + target->sh_addr - MOD_LINK_BASE, + rela->r_offset, + target_name); + kfatal("Against: %p\n", value); + panic("Cannot fit relocation: %ld (%p)\n", off, off); + } + *(int32_t *) value_ptr = (int32_t) off; + } + break; + default: + panic("Unsupported relocation type: %02x\n", ELF64_R_TYPE(rela->r_info)); + } + } + } + + return 0; +} + +static int elf_mod_load(struct module *mod, struct vfs_ioctx *ctx, struct ofile *fd) { + Elf64_Ehdr *ehdr; + Elf64_Shdr *shdrs; + Elf64_Shdr *shdr_symtab = NULL, + *shdr_strtab = NULL, + *shdr_shstrtab = NULL; + char *strtab, *shstrtab; + Elf64_Sym *symtab; + uintptr_t base; + size_t virt_size = 0; + struct stat st; + void *image; + int res = 0; + + // Load the whole module image into memory + if ((res = vfs_fstat(ctx, fd, &st)) != 0) { + return res; + } + + kdebug("Image size: %S\n", st.st_size); + + image = kmalloc(st.st_size); + if (!image) { + kerror("Failed to allocate memory for module image\n"); + return -ENOMEM; + } + + size_t total = 0; + size_t rem = st.st_size; + ssize_t bread; + + // Chunked read (1024 bytes per read) + while ((bread = vfs_read(ctx, fd, image + total, MIN(rem, 1024))) > 0) { + total += bread; + rem -= bread; + } + + if (rem != 0) { + kerror("Failed to load module image\n"); + res = -EINVAL; + goto end; + } + + ehdr = image; + // TODO: check magic and whatever + shdrs = ehdr->e_shoff + image; + shdr_shstrtab = &shdrs[ehdr->e_shstrndx]; + shstrtab = image + shdr_shstrtab->sh_offset; + + // Find relevant sections + for (size_t i = 0; i < ehdr->e_shnum; ++i) { + Elf64_Shdr *shdr = &shdrs[i]; + const char *name = &shstrtab[shdr->sh_name]; + + if (shdr->sh_type == SHT_STRTAB && !strcmp(name, ".strtab")) { + _assert(!shdr_strtab); + shdr_strtab = shdr; + } + if (shdr->sh_type == SHT_SYMTAB) { + _assert(!shdr_symtab); + shdr_symtab = shdr; + } + } + + _assert(shdr_symtab); + _assert(shdr_strtab); + + strtab = shdr_strtab->sh_offset + image; + symtab = shdr_symtab->sh_offset + image; + + // Find out virtual memory length for module + for (size_t i = 0; i < ehdr->e_shnum; ++i) { + Elf64_Shdr *shdr = &shdrs[i]; + if (shdr->sh_flags & SHF_ALLOC) { + if (shdr->sh_addr >= MOD_LINK_BASE && shdr->sh_size + shdr->sh_addr - MOD_LINK_BASE > virt_size) { + virt_size = shdr->sh_size + shdr->sh_addr - MOD_LINK_BASE; + } + } + } + + // Convert to page count + virt_size = (virt_size + MM_PAGE_SIZE - 1) / MM_PAGE_SIZE; + + // Allocate a virtual memory region for the module + base = vmfind(mm_kernel, MOD_VIRT_BASE, MOD_VIRT_BASE + 4 * (1UL << 30), virt_size); + if (base == MM_NADDR) { + panic("Failed to allocate memory for module\n"); + } + // Module is now based + mod->base = base; + mod->page_count = virt_size; + + // Perform symbol relocations + for (size_t i = 0; i < ehdr->e_shnum; ++i) { + Elf64_Shdr *shdr = &shdrs[i]; + + if (shdr->sh_type == SHT_RELA) { + if (elf_rela_apply(image, base, shdrs, shdr) != 0) { + res = -EINVAL; + goto end; + } + } + } + + // Load BITS sections + for (size_t i = 0; i < ehdr->e_shnum; ++i) { + Elf64_Shdr *shdr = &shdrs[i]; + + if (shdr->sh_flags & SHF_ALLOC) { + const char *name = &shstrtab[shdr->sh_name]; + _assert(shdr->sh_addr >= MOD_LINK_BASE); + _assert(elf_map_section(mm_kernel, base, shdr->sh_addr - MOD_LINK_BASE, shdr->sh_size) == 0); + + switch (shdr->sh_type) { + case SHT_PROGBITS: + memcpy((void *) (shdr->sh_addr + base - MOD_LINK_BASE), image + shdr->sh_offset, shdr->sh_size); + break; + case SHT_NOBITS: + memset((void *) (shdr->sh_addr + base - MOD_LINK_BASE), 0, shdr->sh_size); + break; + } + } + } + + // Find relevant symbols + for (size_t i = 0; i < shdr_symtab->sh_size / sizeof(Elf64_Sym); ++i) { + Elf64_Sym *sym = &symtab[i]; + if (sym->st_name > shdr_strtab->sh_size) { + continue; + } + const char *name = &strtab[sym->st_name]; + Elf64_Shdr *shdr = &shdrs[sym->st_shndx]; + uintptr_t addr = sym->st_value + base - MOD_LINK_BASE + shdr->sh_addr; + + if (!strcmp(name, MOD_ENTRY_SYM)) { + if (ELF_ST_TYPE(sym->st_info) != STT_FUNC) { + panic("%s: not a function symbol\n", name); + } + mod->entry_func = (void *) addr; + } else if (!strcmp(name, MOD_EXIT_SYM)) { + if (ELF_ST_TYPE(sym->st_info) != STT_FUNC) { + panic("%s: not a function symbol\n", name); + } + mod->exit_func = (void *) addr; + } else if (!strcmp(name, MOD_DESC_SYM)) { + mod->desc = (void *) addr; + } + } +end: + kfree(image); + + return res; +} + +static int module_enter(struct module *mod) { + return mod->entry_func(); +} + +static void module_destroy(struct module *mod) { + if (mod->base) { + for (size_t i = 0; i < mod->page_count; ++i) { + uintptr_t mapping = mm_map_get(mm_kernel, mod->base + i * MM_PAGE_SIZE, NULL); + + if (mapping != MM_NADDR) { + kdebug("Releasing module page: %p\n", mapping); + + _assert(mm_umap_single(mm_kernel, mod->base + i * MM_PAGE_SIZE, 1) == mapping); + } + } + } + slab_free(module_cache, mod); +} + +int sys_module_unload(const char *name) { + struct thread *thr = thread_self; + + if (thr->ioctx.uid != 0) { + return -EACCES; + } + + struct module *mod = NULL; + struct module *it; + list_for_each_entry(it, &module_list, list) { + if (!strcmp(it->desc->name, name)) { + mod = it; + break; + } + } + + if (!mod) { + return -ENOENT; + } + + kinfo("Unloading %s\n", mod->desc->name); + + if (mod->exit_func) { + mod->exit_func(); + } + + list_del(&mod->list); + module_destroy(mod); + + return 0; +} + +int sys_module_load(const char *path, const char *params) { + struct vfs_ioctx kernel_ioctx = {0, 0, 0}; + int res; + struct ofile fd; + struct thread *thr = thread_self; + + if (thr->ioctx.uid != 0) { + return -EACCES; + } + + // Create a new module + if (!module_cache) { + _assert(module_cache = slab_cache_get(sizeof(struct module))); + } + struct module *mod = slab_calloc(module_cache); + if (!mod) { + kerror("Failed to allocate module struct\n"); + return -ENOMEM; + } + list_head_init(&mod->list); + + userptr_check(path); + if (params) { + userptr_check(params); + } else { + params = ""; + } + + kinfo("Load module %s\n", path); + + if ((res = vfs_open(&kernel_ioctx, &fd, path, O_RDONLY, 0)) != 0) { + kerror("%s: %s\n", path, kstrerror(res)); + slab_free(module_cache, mod); + return res; + } + + if ((res = elf_mod_load(mod, &kernel_ioctx, &fd)) != 0) { + vfs_close(&kernel_ioctx, &fd); + kerror("elf load failed: %s\n", kstrerror(res)); + slab_free(module_cache, mod); + return res; + } + + vfs_close(&kernel_ioctx, &fd); + + if (!mod->desc) { + kerror("%s: module doesn't have description structure\n", path); + module_destroy(mod); + return -EINVAL; + } + + if (!mod->entry_func) { + kerror("%s: module doesn't have an entry function\n", path); + module_destroy(mod); + return -EINVAL; + } + + if ((res = module_enter(mod)) != 0) { + module_destroy(mod); + return res; + } + + list_add(&mod->list, &module_list); + + kdebug("Loaded %s\n", mod->desc->name); + + return 0; +} diff --git a/sys/ubsan.c b/sys/ubsan.c index 31cf160..4c058fd 100644 --- a/sys/ubsan.c +++ b/sys/ubsan.c @@ -2,8 +2,8 @@ #include "sys/panic.h" #include "sys/debug.h" -#define UBSAN_ABORT 1 -//#undef UBSAN_ABORT +//#define UBSAN_ABORT 1 +#undef UBSAN_ABORT struct source_location { const char *file; @@ -143,10 +143,6 @@ void __ubsan_handle_type_mismatch_v1(struct type_mismatch_info *type_mismatch, Type_Check_Kinds[type_mismatch->type_check_kind], (void *) pointer, type_mismatch->type->name); } - //log("Insufficient size"); - //logf("%s address %p with insufficient space for object of type %s\n", - // Type_Check_Kinds[type_mismatch->type_check_kind], (void *)pointer, - // type_mismatch->type->name); } ubsan_abort(location, "type mismatch");