mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-06 19:19:44 +03:00
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.
This commit is contained in:
parent
c10a5ac4ad
commit
6b150c794a
Notes:
sideshowbarker
2024-07-19 11:02:28 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/6b150c794a4
@ -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
|
||||
|
13
Kernel/Module.h
Normal file
13
Kernel/Module.h
Normal file
@ -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)();
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
12
Kernel/TestModule.cpp
Normal file
12
Kernel/TestModule.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -54,6 +54,7 @@ LIBC_OBJS = \
|
||||
dlfcn.o \
|
||||
libgen.o \
|
||||
wchar.o \
|
||||
serenity.o \
|
||||
syslog.o
|
||||
|
||||
ASM_OBJS = setjmp.ao crti.ao crtn.ao
|
||||
|
19
Libraries/LibC/serenity.cpp
Normal file
19
Libraries/LibC/serenity.cpp
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
15
Userland/modload.cpp
Normal file
15
Userland/modload.cpp
Normal file
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user