فهرست منبع

Kernel: Implement very simple kernel module loading

It's now possible to load a .o file into the kernel via a syscall.
The kernel will perform all the necessary ELF relocations, and then
call the "module_init" symbol in the loaded module.
Andreas Kling 5 سال پیش
والد
کامیت
6b150c794a
11فایلهای تغییر یافته به همراه198 افزوده شده و 3 حذف شده
  1. 3 1
      Kernel/Makefile
  2. 13 0
      Kernel/Module.h
  3. 120 1
      Kernel/Process.cpp
  4. 2 0
      Kernel/Process.h
  5. 3 1
      Kernel/Syscall.h
  6. 12 0
      Kernel/TestModule.cpp
  7. 3 0
      Kernel/build-root-filesystem.sh
  8. 1 0
      Libraries/LibC/Makefile
  9. 19 0
      Libraries/LibC/serenity.cpp
  10. 7 0
      Libraries/LibC/serenity.h
  11. 15 0
      Userland/modload.cpp

+ 3 - 1
Kernel/Makefile

@@ -99,6 +99,8 @@ CXX_OBJS = \
     init.o \
     init.o \
     kprintf.o
     kprintf.o
 
 
+MODULE_OBJS = TestModule.o
+
 OBJS = $(CXX_OBJS) Arch/i386/Boot/boot.ao
 OBJS = $(CXX_OBJS) Arch/i386/Boot/boot.ao
 
 
 KERNEL = kernel
 KERNEL = kernel
@@ -109,7 +111,7 @@ CXXFLAGS += -I../Toolchain/Local/i686-pc-serenity/include/c++/8.3.0/i686-pc-sere
 DEFINES += -DKERNEL
 DEFINES += -DKERNEL
 LDFLAGS += -Ttext 0x100000 -Wl,-T linker.ld -nostdlib
 LDFLAGS += -Ttext 0x100000 -Wl,-T linker.ld -nostdlib
 
 
-all: $(KERNEL) kernel.map
+all: $(KERNEL) $(MODULE_OBJS) kernel.map
 
 
 kernel.map: kernel
 kernel.map: kernel
 	@echo "MKMAP $@"; sh mkmap.sh
 	@echo "MKMAP $@"; sh mkmap.sh

+ 13 - 0
Kernel/Module.h

@@ -0,0 +1,13 @@
+#pragma once
+
+#include <AK/String.h>
+#include <AK/Vector.h>
+#include <Kernel/KBuffer.h>
+
+struct Module {
+    String name;
+    Vector<KBuffer> sections;
+};
+
+typedef void* (*ModuleInitPtr)();
+typedef void* (*ModuleFiniPtr)();

+ 120 - 1
Kernel/Process.cpp

@@ -6,8 +6,8 @@
 #include <Kernel/Arch/i386/CPU.h>
 #include <Kernel/Arch/i386/CPU.h>
 #include <Kernel/Arch/i386/PIT.h>
 #include <Kernel/Arch/i386/PIT.h>
 #include <Kernel/Console.h>
 #include <Kernel/Console.h>
-#include <Kernel/Devices/NullDevice.h>
 #include <Kernel/Devices/KeyboardDevice.h>
 #include <Kernel/Devices/KeyboardDevice.h>
+#include <Kernel/Devices/NullDevice.h>
 #include <Kernel/Devices/RandomDevice.h>
 #include <Kernel/Devices/RandomDevice.h>
 #include <Kernel/FileSystem/Custody.h>
 #include <Kernel/FileSystem/Custody.h>
 #include <Kernel/FileSystem/DevPtsFS.h>
 #include <Kernel/FileSystem/DevPtsFS.h>
@@ -23,6 +23,7 @@
 #include <Kernel/IO.h>
 #include <Kernel/IO.h>
 #include <Kernel/KBufferBuilder.h>
 #include <Kernel/KBufferBuilder.h>
 #include <Kernel/KSyms.h>
 #include <Kernel/KSyms.h>
+#include <Kernel/Module.h>
 #include <Kernel/Multiboot.h>
 #include <Kernel/Multiboot.h>
 #include <Kernel/Net/Socket.h>
 #include <Kernel/Net/Socket.h>
 #include <Kernel/Process.h>
 #include <Kernel/Process.h>
@@ -55,9 +56,12 @@ static String* s_hostname;
 static Lock* s_hostname_lock;
 static Lock* s_hostname_lock;
 VirtualAddress g_return_to_ring3_from_signal_trampoline;
 VirtualAddress g_return_to_ring3_from_signal_trampoline;
 VirtualAddress g_return_to_ring0_from_signal_trampoline;
 VirtualAddress g_return_to_ring0_from_signal_trampoline;
+HashMap<String, OwnPtr<Module>>* g_modules;
 
 
 void Process::initialize()
 void Process::initialize()
 {
 {
+    g_modules = new HashMap<String, OwnPtr<Module>>;
+
     next_pid = 0;
     next_pid = 0;
     g_processes = new InlineLinkedList<Process>;
     g_processes = new InlineLinkedList<Process>;
     s_hostname = new String("courage");
     s_hostname = new String("courage");
@@ -3377,3 +3381,118 @@ int Process::sys$beep()
     Scheduler::beep();
     Scheduler::beep();
     return 0;
     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<ELFImage>(storage.data());
+    if (!elf_image->parse())
+        return -ENOEXEC;
+
+    ModuleInitPtr module_init = nullptr;
+
+    HashMap<String, u8*> section_storage_by_name;
+
+    auto module = make<Module>();
+    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<ptrdiff_t*>(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;
+}

+ 2 - 0
Kernel/Process.h

@@ -220,6 +220,8 @@ public:
     int sys$realpath(const char* pathname, char*, size_t);
     int sys$realpath(const char* pathname, char*, size_t);
     ssize_t sys$getrandom(void*, size_t, unsigned int);
     ssize_t sys$getrandom(void*, size_t, unsigned int);
     int sys$setkeymap(char* map, char* shift_map, char* alt_map);
     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();
     static void initialize();
 
 

+ 3 - 1
Kernel/Syscall.h

@@ -139,7 +139,9 @@ typedef u32 socklen_t;
     __ENUMERATE_SYSCALL(clock_gettime)          \
     __ENUMERATE_SYSCALL(clock_gettime)          \
     __ENUMERATE_SYSCALL(clock_nanosleep)        \
     __ENUMERATE_SYSCALL(clock_nanosleep)        \
     __ENUMERATE_SYSCALL(openat)                 \
     __ENUMERATE_SYSCALL(openat)                 \
-    __ENUMERATE_SYSCALL(join_thread)
+    __ENUMERATE_SYSCALL(join_thread)            \
+    __ENUMERATE_SYSCALL(module_load)            \
+    __ENUMERATE_SYSCALL(module_unload)
 
 
 namespace Syscall {
 namespace Syscall {
 
 

+ 12 - 0
Kernel/TestModule.cpp

@@ -0,0 +1,12 @@
+#include <Kernel/kstdio.h>
+
+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);
+    }
+}

+ 3 - 0
Kernel/build-root-filesystem.sh

@@ -134,11 +134,14 @@ ln -s SoundPlayer mnt/bin/sp
 ln -s Help mnt/bin/help
 ln -s Help mnt/bin/help
 ln -s Browser mnt/bin/br
 ln -s Browser mnt/bin/br
 ln -s HackStudio mnt/bin/hs
 ln -s HackStudio mnt/bin/hs
+ln -s modload mnt/bin/m
 echo "done"
 echo "done"
 
 
 mkdir -p mnt/boot/
 mkdir -p mnt/boot/
 cp kernel mnt/boot/
 cp kernel mnt/boot/
 
 
+cp TestModule.o mnt/
+
 # Run local sync script, if it exists
 # Run local sync script, if it exists
 if [ -f sync-local.sh ]; then
 if [ -f sync-local.sh ]; then
     sh sync-local.sh
     sh sync-local.sh

+ 1 - 0
Libraries/LibC/Makefile

@@ -54,6 +54,7 @@ LIBC_OBJS = \
        dlfcn.o \
        dlfcn.o \
        libgen.o \
        libgen.o \
        wchar.o \
        wchar.o \
+       serenity.o \
        syslog.o
        syslog.o
 
 
 ASM_OBJS = setjmp.ao crti.ao crtn.ao
 ASM_OBJS = setjmp.ao crti.ao crtn.ao

+ 19 - 0
Libraries/LibC/serenity.cpp

@@ -0,0 +1,19 @@
+#include <Kernel/Syscall.h>
+#include <errno.h>
+#include <serenity.h>
+
+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);
+}
+
+}

+ 7 - 0
Libraries/LibC/serenity.h

@@ -35,3 +35,10 @@ private:
 };
 };
 
 
 #endif // __cplusplus
 #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

+ 15 - 0
Userland/modload.cpp

@@ -0,0 +1,15 @@
+#include <serenity.h>
+#include <string.h>
+
+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;
+}