diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-11-28 20:59:11 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-11-28 20:59:11 +0100 |
commit | 6b150c794a4c71e36bd1e5e545be171d0e3b45d0 (patch) | |
tree | fb2ba6deacf57defc3c567b502f3c3be66872301 | |
parent | c10a5ac4ade2c70eee46bc401fd875e848393213 (diff) | |
download | serenity-6b150c794a4c71e36bd1e5e545be171d0e3b45d0.zip |
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.
-rw-r--r-- | Kernel/Makefile | 4 | ||||
-rw-r--r-- | Kernel/Module.h | 13 | ||||
-rw-r--r-- | Kernel/Process.cpp | 121 | ||||
-rw-r--r-- | Kernel/Process.h | 2 | ||||
-rw-r--r-- | Kernel/Syscall.h | 4 | ||||
-rw-r--r-- | Kernel/TestModule.cpp | 12 | ||||
-rwxr-xr-x | Kernel/build-root-filesystem.sh | 3 | ||||
-rw-r--r-- | Libraries/LibC/Makefile | 1 | ||||
-rw-r--r-- | Libraries/LibC/serenity.cpp | 19 | ||||
-rw-r--r-- | Libraries/LibC/serenity.h | 7 | ||||
-rw-r--r-- | Userland/modload.cpp | 15 |
11 files changed, 198 insertions, 3 deletions
diff --git a/Kernel/Makefile b/Kernel/Makefile index b75072a25e..6c8544cbc1 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 0000000000..8a27f41940 --- /dev/null +++ b/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)(); diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 08ac19e057..5822b69597 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -6,8 +6,8 @@ #include <Kernel/Arch/i386/CPU.h> #include <Kernel/Arch/i386/PIT.h> #include <Kernel/Console.h> -#include <Kernel/Devices/NullDevice.h> #include <Kernel/Devices/KeyboardDevice.h> +#include <Kernel/Devices/NullDevice.h> #include <Kernel/Devices/RandomDevice.h> #include <Kernel/FileSystem/Custody.h> #include <Kernel/FileSystem/DevPtsFS.h> @@ -23,6 +23,7 @@ #include <Kernel/IO.h> #include <Kernel/KBufferBuilder.h> #include <Kernel/KSyms.h> +#include <Kernel/Module.h> #include <Kernel/Multiboot.h> #include <Kernel/Net/Socket.h> #include <Kernel/Process.h> @@ -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<String, OwnPtr<Module>>* g_modules; void Process::initialize() { + g_modules = new HashMap<String, OwnPtr<Module>>; + next_pid = 0; g_processes = new InlineLinkedList<Process>; 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<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; +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 41d35d8d98..192605afee 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 101490e2c2..d72199f55b 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 0000000000..a3a1296156 --- /dev/null +++ b/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); + } +} diff --git a/Kernel/build-root-filesystem.sh b/Kernel/build-root-filesystem.sh index 0ac9cffc45..de79043b05 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 6c301e96ec..391984ddca 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 0000000000..22756fe6a9 --- /dev/null +++ b/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); +} + +} diff --git a/Libraries/LibC/serenity.h b/Libraries/LibC/serenity.h index aa8aa26c74..26e0ebc5b6 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 0000000000..180f9a1a73 --- /dev/null +++ b/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; +} |