diff --git a/Kernel/Makefile b/Kernel/Makefile index b75072a25ec..6c8544cbc1b 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -99,6 +99,8 @@ CXX_OBJS = \ init.o \ kprintf.o +MODULE_OBJS = TestModule.o + OBJS = $(CXX_OBJS) Arch/i386/Boot/boot.ao KERNEL = kernel @@ -109,7 +111,7 @@ CXXFLAGS += -I../Toolchain/Local/i686-pc-serenity/include/c++/8.3.0/i686-pc-sere DEFINES += -DKERNEL LDFLAGS += -Ttext 0x100000 -Wl,-T linker.ld -nostdlib -all: $(KERNEL) kernel.map +all: $(KERNEL) $(MODULE_OBJS) kernel.map kernel.map: kernel @echo "MKMAP $@"; sh mkmap.sh diff --git a/Kernel/Module.h b/Kernel/Module.h new file mode 100644 index 00000000000..8a27f41940f --- /dev/null +++ b/Kernel/Module.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include + +struct Module { + String name; + Vector sections; +}; + +typedef void* (*ModuleInitPtr)(); +typedef void* (*ModuleFiniPtr)(); diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 08ac19e0570..5822b695978 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -6,8 +6,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -55,9 +56,12 @@ static String* s_hostname; static Lock* s_hostname_lock; VirtualAddress g_return_to_ring3_from_signal_trampoline; VirtualAddress g_return_to_ring0_from_signal_trampoline; +HashMap>* g_modules; void Process::initialize() { + g_modules = new HashMap>; + next_pid = 0; g_processes = new InlineLinkedList; s_hostname = new String("courage"); @@ -3377,3 +3381,118 @@ int Process::sys$beep() Scheduler::beep(); return 0; } + +extern "C" void outside_func() +{ + kprintf("I'm the outside func!\n"); +} + +static u32 find_kernel_symbol(const StringView& name) +{ + if (name == "kprintf") + return (u32)kprintf; + if (name == "outside_func") + return (u32)outside_func; + ASSERT_NOT_REACHED(); +} + +int Process::sys$module_load(const char* path, size_t path_length) +{ +#if 0 + if (!is_superuser()) + return -EPERM; +#endif + if (!validate_read(path, path_length)) + return -EFAULT; + auto description_or_error = VFS::the().open(path, 0, 0, current_directory()); + if (description_or_error.is_error()) + return description_or_error.error(); + auto& description = description_or_error.value(); + auto payload = description->read_entire_file(); + auto storage = KBuffer::create_with_size(payload.size()); + memcpy(storage.data(), payload.data(), payload.size()); + payload.clear(); + + // FIXME: ELFImage should really be taking a size argument as well... + auto elf_image = make(storage.data()); + if (!elf_image->parse()) + return -ENOEXEC; + + ModuleInitPtr module_init = nullptr; + + HashMap section_storage_by_name; + + auto module = make(); + module->name = "FIXME"; + + elf_image->for_each_section_of_type(SHT_PROGBITS, [&](const ELFImage::Section& section) { + auto section_storage = KBuffer::copy(section.raw_data(), section.size()); + section_storage_by_name.set(section.name(), section_storage.data()); + module->sections.append(move(section_storage)); + return IterationDecision::Continue; + }); + + elf_image->for_each_section_of_type(SHT_PROGBITS, [&](const ELFImage::Section& section) { + auto* section_storage = section_storage_by_name.get(section.name()).value_or(nullptr); + ASSERT(section_storage); + section.relocations().for_each_relocation([&](const ELFImage::Relocation& relocation) { + auto& patch_ptr = *reinterpret_cast(section_storage + relocation.offset()); + switch (relocation.type()) { + case R_386_PC32: { + // PC-relative relocation + dbg() << "PC-relative relocation: " << relocation.symbol().name(); + u32 symbol_address = find_kernel_symbol(relocation.symbol().name()); + dbg() << " Symbol address: " << (void*)symbol_address; + ptrdiff_t relative_offset = (char*)symbol_address - ((char*)&patch_ptr + 4); + patch_ptr = relative_offset; + break; + } + case R_386_32: // Absolute relocation + dbg() << "Absolute relocation: '" << relocation.symbol().name() << "' value:" << relocation.symbol().value() << ", index:" << relocation.symbol_index(); + auto* section_storage_containing_symbol = section_storage_by_name.get(relocation.symbol().section().name()).value_or(nullptr); + ASSERT(section_storage_containing_symbol); + patch_ptr += (ptrdiff_t)(section_storage_containing_symbol + relocation.symbol().value()); + break; + } + return IterationDecision::Continue; + }); + + return IterationDecision::Continue; + }); + + auto* text_base = section_storage_by_name.get(".text").value_or(nullptr); + if (!text_base) { + dbg() << "No .text section found in module!"; + return -EINVAL; + } + + elf_image->for_each_symbol([&](const ELFImage::Symbol& symbol) { + dbg() << " - " << symbol.type() << " '" << symbol.name() << "' @ " << (void*)symbol.value() << ", size=" << symbol.size(); + if (!strcmp(symbol.name(), "module_init")) { + module_init = (ModuleInitPtr)(text_base + symbol.value()); + } + return IterationDecision::Continue; + }); + + if (!module_init) + return -EINVAL; + + module_init(); + + auto name = module->name; + g_modules->set(name, move(module)); + + return 0; +} + +int Process::sys$module_unload(const char* name, size_t name_length) +{ +#if 0 + if (!is_superuser()) + return -EPERM; +#endif + if (!validate_read(name, name_length)) + return -EFAULT; + // FIXME: Implement this syscall! + return 0; +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 41d35d8d982..192605afee3 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -220,6 +220,8 @@ public: int sys$realpath(const char* pathname, char*, size_t); ssize_t sys$getrandom(void*, size_t, unsigned int); int sys$setkeymap(char* map, char* shift_map, char* alt_map); + int sys$module_load(const char* path, size_t path_length); + int sys$module_unload(const char* name, size_t name_length); static void initialize(); diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 101490e2c2d..d72199f55ba 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -139,7 +139,9 @@ typedef u32 socklen_t; __ENUMERATE_SYSCALL(clock_gettime) \ __ENUMERATE_SYSCALL(clock_nanosleep) \ __ENUMERATE_SYSCALL(openat) \ - __ENUMERATE_SYSCALL(join_thread) + __ENUMERATE_SYSCALL(join_thread) \ + __ENUMERATE_SYSCALL(module_load) \ + __ENUMERATE_SYSCALL(module_unload) namespace Syscall { diff --git a/Kernel/TestModule.cpp b/Kernel/TestModule.cpp new file mode 100644 index 00000000000..a3a1296156f --- /dev/null +++ b/Kernel/TestModule.cpp @@ -0,0 +1,12 @@ +#include + +extern "C" void outside_func(); + +extern "C" void module_init() +{ + kprintf("TestModule has booted!\n"); + + for (int i = 0; i < 99; ++i) { + kprintf("i is now %d\n", i); + } +} diff --git a/Kernel/build-root-filesystem.sh b/Kernel/build-root-filesystem.sh index 0ac9cffc45a..de79043b054 100755 --- a/Kernel/build-root-filesystem.sh +++ b/Kernel/build-root-filesystem.sh @@ -134,11 +134,14 @@ ln -s SoundPlayer mnt/bin/sp ln -s Help mnt/bin/help ln -s Browser mnt/bin/br ln -s HackStudio mnt/bin/hs +ln -s modload mnt/bin/m echo "done" mkdir -p mnt/boot/ cp kernel mnt/boot/ +cp TestModule.o mnt/ + # Run local sync script, if it exists if [ -f sync-local.sh ]; then sh sync-local.sh diff --git a/Libraries/LibC/Makefile b/Libraries/LibC/Makefile index 6c301e96ece..391984ddca3 100644 --- a/Libraries/LibC/Makefile +++ b/Libraries/LibC/Makefile @@ -54,6 +54,7 @@ LIBC_OBJS = \ dlfcn.o \ libgen.o \ wchar.o \ + serenity.o \ syslog.o ASM_OBJS = setjmp.ao crti.ao crtn.ao diff --git a/Libraries/LibC/serenity.cpp b/Libraries/LibC/serenity.cpp new file mode 100644 index 00000000000..22756fe6a95 --- /dev/null +++ b/Libraries/LibC/serenity.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +extern "C" { + +int module_load(const char* path, size_t path_length) +{ + int rc = syscall(SC_module_load, path, path_length); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int module_unload(const char* name, size_t name_length) +{ + int rc = syscall(SC_module_unload, name, name_length); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +} diff --git a/Libraries/LibC/serenity.h b/Libraries/LibC/serenity.h index aa8aa26c742..26e0ebc5b66 100644 --- a/Libraries/LibC/serenity.h +++ b/Libraries/LibC/serenity.h @@ -35,3 +35,10 @@ private: }; #endif // __cplusplus + +__BEGIN_DECLS + +int module_load(const char* path, size_t path_length); +int module_unload(const char* name, size_t name_length); + +__END_DECLS diff --git a/Userland/modload.cpp b/Userland/modload.cpp new file mode 100644 index 00000000000..180f9a1a73d --- /dev/null +++ b/Userland/modload.cpp @@ -0,0 +1,15 @@ +#include +#include + +int main(int argc, char** argv) +{ + (void)argc; + (void)argv; + const char* path = "/TestModule.o"; + int rc = module_load(path, strlen(path)); + if (rc < 0) { + perror("module_load"); + return 1; + } + return 0; +}