summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-11-28 20:59:11 +0100
committerAndreas Kling <awesomekling@gmail.com>2019-11-28 20:59:11 +0100
commit6b150c794a4c71e36bd1e5e545be171d0e3b45d0 (patch)
treefb2ba6deacf57defc3c567b502f3c3be66872301
parentc10a5ac4ade2c70eee46bc401fd875e848393213 (diff)
downloadserenity-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/Makefile4
-rw-r--r--Kernel/Module.h13
-rw-r--r--Kernel/Process.cpp121
-rw-r--r--Kernel/Process.h2
-rw-r--r--Kernel/Syscall.h4
-rw-r--r--Kernel/TestModule.cpp12
-rwxr-xr-xKernel/build-root-filesystem.sh3
-rw-r--r--Libraries/LibC/Makefile1
-rw-r--r--Libraries/LibC/serenity.cpp19
-rw-r--r--Libraries/LibC/serenity.h7
-rw-r--r--Userland/modload.cpp15
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;
+}