From 3bdcd8f1fb9164bd99bf4c0e3dd6359572dd919b Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sun, 15 Sep 2024 23:20:32 +0300 Subject: [PATCH] Kernel: Rework kernel-side ELF loading ELFs are now loaded as MemoryRegions so they don't need special handling anywhere. This also allows file backed COW optimizations to work. This was not the case before. This patch removes now obsolete LoadableELF and unused ELF files from LibElf. --- kernel/CMakeLists.txt | 6 +- kernel/include/kernel/Process.h | 3 - kernel/kernel/Process.cpp | 73 ++- userspace/libraries/LibELF/LibELF/ELF.cpp | 405 ----------------- .../libraries/LibELF/LibELF/LoadableELF.cpp | 422 ------------------ .../libraries/LibELF/include/LibELF/ELF.h | 89 ---- .../LibELF/include/LibELF/LoadableELF.h | 74 --- 7 files changed, 29 insertions(+), 1043 deletions(-) delete mode 100644 userspace/libraries/LibELF/LibELF/ELF.cpp delete mode 100644 userspace/libraries/LibELF/LibELF/LoadableELF.cpp delete mode 100644 userspace/libraries/LibELF/include/LibELF/ELF.h delete mode 100644 userspace/libraries/LibELF/include/LibELF/LoadableELF.h diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 8d5d0938..d25bdd89 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -22,6 +22,7 @@ set(KERNEL_SOURCES kernel/Device/NullDevice.cpp kernel/Device/RandomDevice.cpp kernel/Device/ZeroDevice.cpp + kernel/ELF.cpp kernel/Errors.cpp kernel/FS/DevFS/FileSystem.cpp kernel/FS/Ext2/FileSystem.cpp @@ -151,10 +152,6 @@ set(KLIBC_SOURCES klibc/string.cpp ) -set(LIBELF_SOURCES - ../userspace/libraries/LibELF/LibELF/LoadableELF.cpp -) - set(LIBFONT_SOURCES ../userspace/libraries/LibFont/Font.cpp ../userspace/libraries/LibFont/PSF.cpp @@ -169,7 +166,6 @@ set(KERNEL_SOURCES ${KERNEL_SOURCES} ${BAN_SOURCES} ${KLIBC_SOURCES} - ${LIBELF_SOURCES} ${LIBFONT_SOURCES} ${LIBINPUT_SOURCE} ) diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 512aed9a..c1567aa7 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -22,8 +22,6 @@ #include #include -namespace LibELF { class LoadableELF; } - namespace Kernel { @@ -269,7 +267,6 @@ namespace Kernel OpenFileDescriptorSet m_open_file_descriptors; - BAN::UniqPtr m_loadable_elf; BAN::Vector> m_mapped_regions; pid_t m_sid; diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index a4170ae2..0db597ba 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -121,13 +122,8 @@ namespace Kernel auto absolute_path = TRY(process->absolute_path_of(path)); auto executable_inode = TRY(VirtualFileSystem::get().file_from_absolute_path(process->m_credentials, absolute_path, O_EXEC)).inode; - process->m_loadable_elf = TRY(LibELF::LoadableELF::load_from_inode(process->page_table(), process->m_credentials, executable_inode)); - if (!process->m_loadable_elf->is_address_space_free()) - { - dprintln("Could not load ELF address space"); - return BAN::Error::from_errno(ENOEXEC); - } - process->m_loadable_elf->reserve_address_space(); + auto executable = TRY(ELF::load_from_inode(executable_inode, process->m_credentials, process->page_table())); + process->m_mapped_regions = BAN::move(executable.regions); char** argv = nullptr; { @@ -154,8 +150,21 @@ namespace Kernel MUST(process->m_mapped_regions.push_back(BAN::move(argv_region))); } + if (executable_inode->mode().mode & +Inode::Mode::ISUID) + process->m_credentials.set_euid(executable_inode->uid()); + if (executable_inode->mode().mode & +Inode::Mode::ISGID) + process->m_credentials.set_egid(executable_inode->gid()); + + if (executable.has_interpreter) + { + VirtualFileSystem::File file; + TRY(file.canonical_path.append("")); + file.inode = executable_inode; + process->m_userspace_info.file_fd = TRY(process->m_open_file_descriptors.open(BAN::move(file), O_RDONLY)); + } + process->m_is_userspace = true; - process->m_userspace_info.entry = process->m_loadable_elf->entry_point(); + process->m_userspace_info.entry = executable.entry_point; process->m_userspace_info.argc = 1; process->m_userspace_info.argv = argv; process->m_userspace_info.envp = nullptr; @@ -185,7 +194,6 @@ namespace Kernel { ASSERT(m_threads.empty()); ASSERT(m_mapped_regions.empty()); - ASSERT(!m_loadable_elf); ASSERT(&PageTable::current() != m_page_table.ptr()); } @@ -216,7 +224,6 @@ namespace Kernel // NOTE: We must unmap ranges while the page table is still alive m_mapped_regions.clear(); - m_loadable_elf.clear(); } bool Process::on_thread_exit(Thread& thread) @@ -302,11 +309,6 @@ namespace Kernel meminfo.virt_pages += region->virtual_page_count(); meminfo.phys_pages += region->physical_page_count(); } - if (m_loadable_elf) - { - meminfo.virt_pages += m_loadable_elf->virtual_page_count(); - meminfo.phys_pages += m_loadable_elf->physical_page_count(); - } } size_t bytes = BAN::Math::min(sizeof(proc_meminfo_t) - offset, buffer.size()); @@ -424,15 +426,12 @@ namespace Kernel for (auto& mapped_region : m_mapped_regions) MUST(mapped_regions.push_back(TRY(mapped_region->clone(*page_table)))); - auto loadable_elf = TRY(m_loadable_elf->clone(*page_table)); - Process* forked = create_process(m_credentials, m_pid, m_sid, m_pgrp); forked->m_controlling_terminal = m_controlling_terminal; forked->m_working_directory = BAN::move(working_directory); forked->m_page_table = BAN::move(page_table); forked->m_open_file_descriptors = BAN::move(open_file_descriptors); forked->m_mapped_regions = BAN::move(mapped_regions); - forked->m_loadable_elf = BAN::move(loadable_elf); forked->m_is_userspace = m_is_userspace; forked->m_userspace_info = m_userspace_info; forked->m_has_called_exec = false; @@ -461,7 +460,6 @@ namespace Kernel auto absolute_path = TRY(absolute_path_of(path)); auto executable_inode = TRY(VirtualFileSystem::get().file_from_absolute_path(m_credentials, absolute_path, O_EXEC)).inode; - auto loadable_elf = TRY(LibELF::LoadableELF::load_from_inode(page_table(), m_credentials, executable_inode)); BAN::Vector str_argv; for (int i = 0; argv && argv[i]; i++) @@ -479,29 +477,24 @@ namespace Kernel TRY(str_envp.emplace_back(envp[i])); } - BAN::String executable_path; - TRY(executable_path.append(path)); - m_open_file_descriptors.close_cloexec(); m_mapped_regions.clear(); - m_loadable_elf = BAN::move(loadable_elf); - if (!m_loadable_elf->is_address_space_free()) - { - dprintln("ELF has unloadable address space"); - MUST(sys_kill(pid(), SIGKILL)); - // NOTE: signal will only execute after return from syscall - return BAN::Error::from_errno(EINTR); - } - m_loadable_elf->reserve_address_space(); - m_loadable_elf->update_suid_sgid(m_credentials); - m_userspace_info.entry = m_loadable_elf->entry_point(); - if (m_loadable_elf->has_interpreter()) + auto executable = TRY(ELF::load_from_inode(executable_inode, m_credentials, page_table())); + m_mapped_regions = BAN::move(executable.regions); + + if (executable_inode->mode().mode & +Inode::Mode::ISUID) + m_credentials.set_euid(executable_inode->uid()); + if (executable_inode->mode().mode & +Inode::Mode::ISGID) + m_credentials.set_egid(executable_inode->gid()); + + m_userspace_info.entry = executable.entry_point; + if (executable.has_interpreter) { VirtualFileSystem::File file; TRY(file.canonical_path.append("")); - file.inode = m_loadable_elf->executable(); + file.inode = executable_inode; m_userspace_info.file_fd = TRY(m_open_file_descriptors.open(BAN::move(file), O_RDONLY)); } @@ -845,12 +838,6 @@ namespace Kernel return true; } - if (m_loadable_elf && m_loadable_elf->contains(address)) - { - TRY(m_loadable_elf->load_page_to_memory(address)); - return true; - } - return false; } @@ -2387,10 +2374,6 @@ namespace Kernel return {}; } - // FIXME: elf should use MemoryRegions instead of mapping executables itself - if (m_loadable_elf->contains(vaddr)) - return {}; - unauthorized_access: dwarnln("process {}, thread {} attempted to make an invalid pointer access to 0x{H}->0x{H}", pid(), Thread::current().tid(), vaddr, vaddr + size); Debug::dump_stack_trace(); diff --git a/userspace/libraries/LibELF/LibELF/ELF.cpp b/userspace/libraries/LibELF/LibELF/ELF.cpp deleted file mode 100644 index 566826cd..00000000 --- a/userspace/libraries/LibELF/LibELF/ELF.cpp +++ /dev/null @@ -1,405 +0,0 @@ -#include -#include -#include - -#ifdef __is_kernel -#include -#include -#endif - -#include - -#define ELF_PRINT_HEADERS 0 - -#ifdef __is_kernel -extern uint8_t g_kernel_end[]; -using namespace Kernel; -#endif - -namespace LibELF -{ - -#ifdef __is_kernel - BAN::ErrorOr> ELF::load_from_file(BAN::RefPtr inode) - { - BAN::Vector buffer; - TRY(buffer.resize(inode->size())); - - TRY(inode->read(0, buffer.data(), inode->size())); - - ELF* elf_ptr = new ELF(BAN::move(buffer)); - if (elf_ptr == nullptr) - return BAN::Error::from_errno(ENOMEM); - - auto elf = BAN::UniqPtr::adopt(elf_ptr); - TRY(elf->load()); - - return BAN::move(elf); - } -#else - BAN::ErrorOr ELF::load_from_file(BAN::StringView file_path) - { - ELF* elf = nullptr; - - { - BAN::Vector data; - - int fd = TRY(Kernel::Process::current().open(file_path, O_RDONLY)); - BAN::ScopeGuard _([fd] { MUST(Kernel::Process::current().close(fd)); }); - - struct stat st; - TRY(Kernel::Process::current().fstat(fd, &st)); - - TRY(data.resize(st.st_size)); - - TRY(Kernel::Process::current().read(fd, data.data(), data.size())); - - elf = new ELF(BAN::move(data)); - ASSERT(elf); - } - - if (auto res = elf->load(); res.is_error()) - { - delete elf; - return res.error(); - } - - return elf; - } -#endif - - BAN::ErrorOr ELF::load() - { - if (m_data.size() < EI_NIDENT) - { - dprintln("Too small ELF file"); - return BAN::Error::from_errno(EINVAL); - } - - if (m_data[EI_MAG0] != ELFMAG0 || - m_data[EI_MAG1] != ELFMAG1 || - m_data[EI_MAG2] != ELFMAG2 || - m_data[EI_MAG3] != ELFMAG3) - { - dprintln("Invalid ELF header"); - return BAN::Error::from_errno(EINVAL); - } - - if (m_data[EI_DATA] != ELFDATA2LSB) - { - dprintln("Only little-endian is supported"); - return BAN::Error::from_errno(EINVAL); - } - - if (m_data[EI_VERSION] != EV_CURRENT) - { - dprintln("Invalid ELF version"); - return BAN::Error::from_errno(EINVAL); - } - - if (m_data[EI_CLASS] == ELFCLASS64) - { - if (m_data.size() <= sizeof(Elf64FileHeader)) - { - dprintln("Too small ELF file"); - return BAN::Error::from_errno(EINVAL); - } - - auto& header = file_header64(); - if (!parse_elf64_file_header(header)) - return BAN::Error::from_errno(EINVAL); - - for (size_t i = 0; i < header.e_phnum; i++) - { - auto& program_header = program_header64(i); - if (!parse_elf64_program_header(program_header)) - return BAN::Error::from_errno(EINVAL); - } - - for (size_t i = 1; i < header.e_shnum; i++) - { - auto& section_header = section_header64(i); - if (!parse_elf64_section_header(section_header)) - return BAN::Error::from_errno(EINVAL); - } - } - else if (m_data[EI_CLASS] == ELFCLASS32) - { - if (m_data.size() <= sizeof(Elf32FileHeader)) - { - dprintln("Too small ELF file"); - return BAN::Error::from_errno(EINVAL); - } - - auto& header = file_header32(); - if (!parse_elf32_file_header(header)) - return BAN::Error::from_errno(EINVAL); - - for (size_t i = 0; i < header.e_phnum; i++) - { - auto& program_header = program_header32(i); - if (!parse_elf32_program_header(program_header)) - return BAN::Error::from_errno(EINVAL); - } - - for (size_t i = 1; i < header.e_shnum; i++) - { - auto& section_header = section_header32(i); - if (!parse_elf32_section_header(section_header)) - return BAN::Error::from_errno(EINVAL); - } - } - - return {}; - } - - bool ELF::is_x86_32() const { return m_data[EI_CLASS] == ELFCLASS32; } - bool ELF::is_x86_64() const { return m_data[EI_CLASS] == ELFCLASS64; } - - /* - - 64 bit ELF - - */ - - const char* ELF::lookup_section_name64(uint32_t offset) const - { - return lookup_string64(file_header64().e_shstrndx, offset); - } - - const char* ELF::lookup_string64(size_t table_index, uint32_t offset) const - { - if (table_index == SHN_UNDEF) - return nullptr; - auto& section_header = section_header64(table_index); - return (const char*)m_data.data() + section_header.sh_offset + offset; - } - - bool ELF::parse_elf64_file_header(const Elf64FileHeader& header) - { - if (header.e_type != ET_EXEC) - { - dprintln("Only executable files are supported"); - return false; - } - - if (header.e_version != EV_CURRENT) - { - dprintln("Invalid ELF version"); - return false; - } - - return true; - } - - bool ELF::parse_elf64_program_header(const Elf64ProgramHeader& header) - { -#if ELF_PRINT_HEADERS - dprintln("program header"); - dprintln(" type {H}", header.p_type); - dprintln(" flags {H}", header.p_flags); - dprintln(" offset {H}", header.p_offset); - dprintln(" vaddr {H}", header.p_vaddr); - dprintln(" paddr {H}", header.p_paddr); - dprintln(" filesz {}", header.p_filesz); - dprintln(" memsz {}", header.p_memsz); - dprintln(" align {}", header.p_align); -#endif - (void)header; - return true; - } - - bool ELF::parse_elf64_section_header(const Elf64SectionHeader& header) - { -#if ELF_PRINT_HEADERS - if (auto* name = lookup_section_name64(header.sh_name)) - dprintln("{}", name); - - switch (header.sh_type) - { - case SHT_NULL: - dprintln(" SHT_NULL"); - break; - case SHT_PROGBITS: - dprintln(" SHT_PROGBITS"); - break; - case SHT_SYMTAB: - for (size_t i = 1; i < header.sh_size / header.sh_entsize; i++) - { - auto& symbol = ((const Elf64Symbol*)(m_data.data() + header.sh_offset))[i]; - if (auto* name = lookup_string64(header.sh_link, symbol.st_name)) - dprintln(" {}", name); - } - break; - case SHT_STRTAB: - dprintln(" SHT_STRTAB"); - break; - case SHT_RELA: - dprintln(" SHT_RELA"); - break; - case SHT_NOBITS: - dprintln(" SHT_NOBITS"); - break; - case SHT_REL: - dprintln(" SHT_REL"); - break; - case SHT_SHLIB: - dprintln(" SHT_SHLIB"); - break; - case SHT_DYNSYM: - dprintln(" SHT_DYNSYM"); - break; - default: - ASSERT(false); - } -#endif - (void)header; - return true; - } - - const Elf64FileHeader& ELF::file_header64() const - { - ASSERT(is_x86_64()); - return *(const Elf64FileHeader*)m_data.data(); - } - - const Elf64ProgramHeader& ELF::program_header64(size_t index) const - { - ASSERT(is_x86_64()); - const auto& file_header = file_header64(); - ASSERT(index < file_header.e_phnum); - return *(const Elf64ProgramHeader*)(m_data.data() + file_header.e_phoff + file_header.e_phentsize * index); - } - - const Elf64SectionHeader& ELF::section_header64(size_t index) const - { - ASSERT(is_x86_64()); - const auto& file_header = file_header64(); - ASSERT(index < file_header.e_shnum); - return *(const Elf64SectionHeader*)(m_data.data() + file_header.e_shoff + file_header.e_shentsize * index); - } - - /* - - 32 bit ELF - - */ - - const char* ELF::lookup_section_name32(uint32_t offset) const - { - return lookup_string32(file_header32().e_shstrndx, offset); - } - - const char* ELF::lookup_string32(size_t table_index, uint32_t offset) const - { - if (table_index == SHN_UNDEF) - return nullptr; - auto& section_header = section_header32(table_index); - return (const char*)m_data.data() + section_header.sh_offset + offset; - } - - bool ELF::parse_elf32_file_header(const Elf32FileHeader& header) - { - if (header.e_type != ET_EXEC) - { - dprintln("Only executable files are supported"); - return false; - } - - if (header.e_version != EV_CURRENT) - { - dprintln("Invalid ELF version"); - return false; - } - - return true; - } - - bool ELF::parse_elf32_program_header(const Elf32ProgramHeader& header) - { -#if ELF_PRINT_HEADERS - dprintln("program header"); - dprintln(" type {H}", header.p_type); - dprintln(" flags {H}", header.p_flags); - dprintln(" offset {H}", header.p_offset); - dprintln(" vaddr {H}", header.p_vaddr); - dprintln(" paddr {H}", header.p_paddr); - dprintln(" filesz {}", header.p_filesz); - dprintln(" memsz {}", header.p_memsz); - dprintln(" align {}", header.p_align); -#endif - (void)header; - return true; - } - - bool ELF::parse_elf32_section_header(const Elf32SectionHeader& header) - { -#if ELF_PRINT_HEADERS - if (auto* name = lookup_section_name32(header.sh_name)) - dprintln("{}", name); - - switch (header.sh_type) - { - case SHT_NULL: - dprintln(" SHT_NULL"); - break; - case SHT_PROGBITS: - dprintln(" SHT_PROGBITS"); - break; - case SHT_SYMTAB: - for (size_t i = 1; i < header.sh_size / header.sh_entsize; i++) - { - auto& symbol = ((const Elf32Symbol*)(m_data.data() + header.sh_offset))[i]; - if (auto* name = lookup_string32(header.sh_link, symbol.st_name)) - dprintln(" {}", name); - } - break; - case SHT_STRTAB: - dprintln(" SHT_STRTAB"); - break; - case SHT_RELA: - dprintln(" SHT_RELA"); - break; - case SHT_NOBITS: - dprintln(" SHT_NOBITS"); - break; - case SHT_REL: - dprintln(" SHT_REL"); - break; - case SHT_SHLIB: - dprintln(" SHT_SHLIB"); - break; - case SHT_DYNSYM: - dprintln(" SHT_DYNSYM"); - break; - default: - ASSERT(false); - } -#endif - (void)header; - return true; - } - - const Elf32FileHeader& ELF::file_header32() const - { - ASSERT(is_x86_32()); - return *(const Elf32FileHeader*)m_data.data(); - } - - const Elf32ProgramHeader& ELF::program_header32(size_t index) const - { - ASSERT(is_x86_32()); - const auto& file_header = file_header32(); - ASSERT(index < file_header.e_phnum); - return *(const Elf32ProgramHeader*)(m_data.data() + file_header.e_phoff + file_header.e_phentsize * index); - } - - const Elf32SectionHeader& ELF::section_header32(size_t index) const - { - ASSERT(is_x86_32()); - const auto& file_header = file_header32(); - ASSERT(index < file_header.e_shnum); - return *(const Elf32SectionHeader*)(m_data.data() + file_header.e_shoff + file_header.e_shentsize * index); - } - -} diff --git a/userspace/libraries/LibELF/LibELF/LoadableELF.cpp b/userspace/libraries/LibELF/LibELF/LoadableELF.cpp deleted file mode 100644 index fdc3d4c4..00000000 --- a/userspace/libraries/LibELF/LibELF/LoadableELF.cpp +++ /dev/null @@ -1,422 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -namespace LibELF -{ - - using namespace Kernel; - - BAN::ErrorOr> LoadableELF::load_from_inode(PageTable& page_table, const Credentials& credentials, BAN::RefPtr inode) - { - auto elf = TRY(BAN::UniqPtr::create(page_table)); - TRY(elf->initialize(credentials, inode)); - return elf; - } - - LoadableELF::LoadableELF(PageTable& page_table) - : m_page_table(page_table) - { - } - - LoadableELF::~LoadableELF() - { - if (!m_is_loaded) - return; - - for (const auto& header : m_program_headers) - { - ASSERT(header.p_type == PT_LOAD); - - const vaddr_t vaddr = header.p_vaddr & PAGE_ADDR_MASK; - const size_t pages = range_page_count(header.p_vaddr, header.p_memsz); - for (size_t i = 0; i < pages; i++) - if (paddr_t paddr = m_page_table.physical_address_of(vaddr + i * PAGE_SIZE)) - Heap::get().release_page(paddr); - m_page_table.unmap_range(vaddr, pages * PAGE_SIZE); - } - } - - static BAN::ErrorOr read_and_validate_file_header(BAN::RefPtr inode) - { - if ((size_t)inode->size() < sizeof(ElfNativeFileHeader)) - { - dprintln("File is too small to be ELF"); - return BAN::Error::from_errno(ENOEXEC); - } - - ElfNativeFileHeader file_header; - - size_t nread = TRY(inode->read(0, BAN::ByteSpan::from(file_header))); - ASSERT(nread == sizeof(file_header)); - - if (file_header.e_ident[EI_MAG0] != ELFMAG0 || - file_header.e_ident[EI_MAG1] != ELFMAG1 || - file_header.e_ident[EI_MAG2] != ELFMAG2 || - file_header.e_ident[EI_MAG3] != ELFMAG3) - { - dprintln("Not an ELF file"); - return BAN::Error::from_errno(ENOEXEC); - } - - if (file_header.e_ident[EI_DATA] != ELFDATA2LSB) - { - dprintln("Not in little-endian"); - return BAN::Error::from_errno(ENOEXEC); - } - - if (file_header.e_ident[EI_VERSION] != EV_CURRENT) - { - dprintln("Unsupported version {}", file_header.e_ident[EI_VERSION]); - return BAN::Error::from_errno(ENOEXEC); - } - -#if ARCH(i686) - if (file_header.e_ident[EI_CLASS] != ELFCLASS32) -#elif ARCH(x86_64) - if (file_header.e_ident[EI_CLASS] != ELFCLASS64) -#endif - { - dprintln("Not in native format"); - return BAN::Error::from_errno(EINVAL); - } - - if (file_header.e_type != ET_EXEC && file_header.e_type != ET_DYN) - { - dprintln("Unsupported file header type {}", file_header.e_type); - return BAN::Error::from_errno(ENOTSUP); - } - - if (file_header.e_version != EV_CURRENT) - { - dprintln("Unsupported version {}", file_header.e_version); - return BAN::Error::from_errno(EINVAL); - } - - if (file_header.e_phentsize < sizeof(ElfNativeProgramHeader)) - { - dprintln("Too small program header size ({} bytes)", file_header.e_phentsize); - return BAN::Error::from_errno(EINVAL); - } - - return file_header; - } - - BAN::ErrorOr LoadableELF::load_elf_file(const Credentials& credentials, BAN::RefPtr inode) const - { - auto file_header = TRY(read_and_validate_file_header(inode)); - - BAN::Vector pheader_buffer; - TRY(pheader_buffer.resize(file_header.e_phnum * file_header.e_phentsize)); - TRY(inode->read(file_header.e_phoff, BAN::ByteSpan(pheader_buffer.span()))); - - BAN::Vector program_headers; - BAN::RefPtr interp; - - for (size_t i = 0; i < file_header.e_phnum; i++) - { - const auto& pheader = *reinterpret_cast(pheader_buffer.data() + i * file_header.e_phentsize); - if (pheader.p_memsz < pheader.p_filesz) - { - dprintln("Invalid program header, memsz less than filesz"); - return BAN::Error::from_errno(EINVAL); - } - - switch (pheader.p_type) - { - case PT_LOAD: - for (const auto& program_header : program_headers) - { - const vaddr_t a1 = program_header.p_vaddr & PAGE_ADDR_MASK; - const vaddr_t b1 = pheader.p_vaddr & PAGE_ADDR_MASK; - const vaddr_t a2 = (program_header.p_vaddr + program_header.p_memsz + PAGE_SIZE - 1) & PAGE_ADDR_MASK; - const vaddr_t b2 = (pheader.p_vaddr + pheader.p_memsz + PAGE_SIZE - 1) & PAGE_ADDR_MASK; - if (a1 < b2 && b1 < a2) - { - dwarnln("Overlapping LOAD segments"); - return BAN::Error::from_errno(EINVAL); - } - } - TRY(program_headers.push_back(pheader)); - break; - case PT_INTERP: - { - BAN::Vector buffer; - TRY(buffer.resize(pheader.p_memsz, 0)); - TRY(inode->read(pheader.p_offset, BAN::ByteSpan(buffer.data(), pheader.p_filesz))); - - BAN::StringView path(reinterpret_cast(buffer.data())); - interp = TRY(VirtualFileSystem::get().file_from_absolute_path(credentials, path, O_EXEC)).inode; - break; - } - default: - break; - } - } - - return LoadResult { - .inode = inode, - .interp = interp, - .file_header = file_header, - .program_headers = BAN::move(program_headers) - }; - } - - static bool do_program_headers_overlap(BAN::Span pheaders1, BAN::Span pheaders2, vaddr_t base2) - { - for (const auto& pheader1 : pheaders1) - { - for (const auto& pheader2 : pheaders2) - { - const vaddr_t s1 = pheader1.p_vaddr & PAGE_ADDR_MASK; - const vaddr_t e1 = (pheader1.p_vaddr + pheader1.p_memsz + PAGE_SIZE - 1) & PAGE_ADDR_MASK; - - const vaddr_t s2 = pheader2.p_vaddr & PAGE_ADDR_MASK; - const vaddr_t e2 = (pheader2.p_vaddr + pheader2.p_memsz + PAGE_SIZE - 1) & PAGE_ADDR_MASK; - - if (s1 < e2 + base2 && s2 + base2 < e1) - return true; - } - } - - return false; - } - - BAN::ErrorOr LoadableELF::initialize(const Credentials& credentials, BAN::RefPtr inode) - { - const auto generate_random_dynamic_base = - []() -> vaddr_t - { - // 1 MiB -> 2 GiB + 1 MiB - return (Random::get_u32() & 0x7FFFF000) + 0x100000; - }; - - - auto executable_load_result = TRY(load_elf_file(credentials, inode)); - - m_executable = executable_load_result.inode; - m_interpreter = executable_load_result.interp; - - vaddr_t dynamic_base = 0; - - if (m_interpreter) - { - auto interp_load_result = TRY(load_elf_file(credentials, m_interpreter)); - if (interp_load_result.interp) - { - dwarnln("ELF interpreter has an interpreter"); - return BAN::Error::from_errno(EINVAL); - } - - if (executable_load_result.file_header.e_type == ET_EXEC) - { - if (interp_load_result.file_header.e_type == ET_EXEC) - { - const bool has_overlap = do_program_headers_overlap( - executable_load_result.program_headers.span(), - interp_load_result.program_headers.span(), - 0 - ); - - if (has_overlap) - { - dwarnln("Executable and interpreter LOAD segments overlap"); - return BAN::Error::from_errno(EINVAL); - } - } - else - { - for (int attempt = 0; attempt < 100; attempt++) - { - const vaddr_t test_dynamic_base = generate_random_dynamic_base(); - const bool has_overlap = do_program_headers_overlap( - executable_load_result.program_headers.span(), - interp_load_result.program_headers.span(), - test_dynamic_base - ); - if (has_overlap) - continue; - dynamic_base = test_dynamic_base; - break; - } - - if (dynamic_base == 0) - { - dwarnln("Could not find space to load interpreter"); - return BAN::Error::from_errno(EINVAL); - } - } - } - - m_file_header = interp_load_result.file_header; - m_program_headers = BAN::move(interp_load_result.program_headers); - } - else - { - m_file_header = executable_load_result.file_header; - m_program_headers = BAN::move(executable_load_result.program_headers); - } - - if (m_file_header.e_type == ET_DYN && dynamic_base == 0) - dynamic_base = generate_random_dynamic_base(); - - if (dynamic_base) - { - m_file_header.e_entry += dynamic_base; - for (auto& program_header : m_program_headers) - program_header.p_vaddr += dynamic_base; - } - - return {}; - } - - bool LoadableELF::contains(vaddr_t address) const - { - for (const auto& program_header : m_program_headers) - if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz) - return true; - return false; - } - - bool LoadableELF::is_address_space_free() const - { - for (const auto& program_header : m_program_headers) - { - ASSERT(program_header.p_type == PT_LOAD); - const vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK; - const size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz); - if (!m_page_table.is_range_free(page_vaddr, pages * PAGE_SIZE)) - return false; - } - return true; - } - - void LoadableELF::reserve_address_space() - { - for (const auto& program_header : m_program_headers) - { - ASSERT(program_header.p_type == PT_LOAD); - const vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK; - const size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz); - if (!m_page_table.reserve_range(page_vaddr, pages * PAGE_SIZE)) - ASSERT_NOT_REACHED(); - m_virtual_page_count += pages; - } - m_is_loaded = true; - } - - void LoadableELF::update_suid_sgid(Kernel::Credentials& credentials) - { - if (m_executable->mode().mode & +Inode::Mode::ISUID) - credentials.set_euid(m_executable->uid()); - if (m_executable->mode().mode & +Inode::Mode::ISGID) - credentials.set_egid(m_executable->gid()); - } - - BAN::ErrorOr LoadableELF::load_page_to_memory(vaddr_t address) - { - auto inode = has_interpreter() ? m_interpreter : m_executable; - - // FIXME: use MemoryBackedRegion/FileBackedRegion instead of manually mapping and allocating pages - - for (const auto& program_header : m_program_headers) - { - ASSERT(program_header.p_type == PT_LOAD); - if (!(program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)) - continue; - - PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present; - if (program_header.p_flags & LibELF::PF_W) - flags |= PageTable::Flags::ReadWrite; - if (program_header.p_flags & LibELF::PF_X) - flags |= PageTable::Flags::Execute; - - const vaddr_t vaddr = address & PAGE_ADDR_MASK; - const paddr_t paddr = Heap::get().take_free_page(); - if (paddr == 0) - return BAN::Error::from_errno(ENOMEM); - - // Temporarily map page as RW so kernel can write to it - m_page_table.map_page_at(paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present); - m_physical_page_count++; - - memset((void*)vaddr, 0x00, PAGE_SIZE); - - if (vaddr / PAGE_SIZE < BAN::Math::div_round_up(program_header.p_vaddr + program_header.p_filesz, PAGE_SIZE)) - { - size_t vaddr_offset = 0; - if (vaddr < program_header.p_vaddr) - vaddr_offset = program_header.p_vaddr - vaddr; - - size_t file_offset = 0; - if (vaddr > program_header.p_vaddr) - file_offset = vaddr - program_header.p_vaddr; - - size_t bytes = BAN::Math::min(PAGE_SIZE - vaddr_offset, program_header.p_filesz - file_offset); - TRY(inode->read(program_header.p_offset + file_offset, { (uint8_t*)vaddr + vaddr_offset, bytes })); - } - - // Map page with the correct flags - m_page_table.map_page_at(paddr, vaddr, flags); - - return {}; - } - - ASSERT_NOT_REACHED(); - } - - BAN::ErrorOr> LoadableELF::clone(Kernel::PageTable& new_page_table) - { - auto elf = TRY(BAN::UniqPtr::create(new_page_table)); - - elf->m_executable = m_executable; - elf->m_interpreter = m_interpreter; - elf->m_file_header = m_file_header; - TRY(elf->m_program_headers.reserve(m_program_headers.size())); - for (const auto& program_header : m_program_headers) - MUST(elf->m_program_headers.emplace_back(program_header)); - - elf->reserve_address_space(); - - for (const auto& program_header : m_program_headers) - { - ASSERT(program_header.p_type == PT_LOAD); - if (!(program_header.p_flags & LibELF::PF_W)) - continue; - - PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present; - if (program_header.p_flags & LibELF::PF_W) - flags |= PageTable::Flags::ReadWrite; - if (program_header.p_flags & LibELF::PF_X) - flags |= PageTable::Flags::Execute; - - vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK; - size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz); - - for (size_t i = 0; i < pages; i++) - { - if (m_page_table.physical_address_of(start + i * PAGE_SIZE) == 0) - continue; - - paddr_t paddr = Heap::get().take_free_page(); - if (paddr == 0) - return BAN::Error::from_errno(ENOMEM); - - PageTable::with_fast_page(paddr, [&] { - memcpy(PageTable::fast_page_as_ptr(), (void*)(start + i * PAGE_SIZE), PAGE_SIZE); - }); - - new_page_table.map_page_at(paddr, start + i * PAGE_SIZE, flags); - elf->m_physical_page_count++; - } - } - - return elf; - } - -} diff --git a/userspace/libraries/LibELF/include/LibELF/ELF.h b/userspace/libraries/LibELF/include/LibELF/ELF.h deleted file mode 100644 index a3eea008..00000000 --- a/userspace/libraries/LibELF/include/LibELF/ELF.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#ifdef __is_kernel -#include -#include -#endif - -#include -#include -#include -#include -#include "Types.h" - -namespace LibELF -{ - - class ELF - { - public: -#ifdef __is_kernel - static BAN::ErrorOr> load_from_file(BAN::RefPtr); -#else - static BAN::ErrorOr> load_from_file(BAN::StringView); -#endif - - const Elf64FileHeader& file_header64() const; - const Elf64ProgramHeader& program_header64(size_t) const; - const Elf64SectionHeader& section_header64(size_t) const; - const char* lookup_section_name64(uint32_t) const; - const char* lookup_string64(size_t, uint32_t) const; -#if ARCH(x86_64) - const Elf64FileHeader& file_header_native() const { return file_header64(); } - const Elf64ProgramHeader& program_header_native(size_t index) const { return program_header64(index); } - const Elf64SectionHeader& section_header_native(size_t index) const { return section_header64(index); } - const char* lookup_section_name_native(uint32_t offset) const { return lookup_section_name64(offset); } - const char* lookup_string_native(size_t table_index, uint32_t offset) const { return lookup_string64(table_index, offset); } - bool is_native() const { return is_x86_64(); } -#endif - - const Elf32FileHeader& file_header32() const; - const Elf32ProgramHeader& program_header32(size_t) const; - const Elf32SectionHeader& section_header32(size_t) const; - const char* lookup_section_name32(uint32_t) const; - const char* lookup_string32(size_t, uint32_t) const; -#if ARCH(i686) - const Elf32FileHeader& file_header_native() const { return file_header32(); } - const Elf32ProgramHeader& program_header_native(size_t index) const { return program_header32(index); } - const Elf32SectionHeader& section_header_native(size_t index) const { return section_header32(index); } - const char* lookup_section_name_native(uint32_t offset) const { return lookup_section_name32(offset); } - const char* lookup_string_native(size_t table_index, uint32_t offset) const { return lookup_string32(table_index, offset); } - bool is_native() const { return is_x86_32(); } -#endif - - const uint8_t* data() const { return m_data.data(); } - - bool is_x86_32() const; - bool is_x86_64() const; - - private: -//#ifdef __is_kernel -// ELF(BAN::UniqPtr&& storage, size_t size) -// : m_storage(BAN::move(storage)) -// , m_data((const uint8_t*)m_storage->vaddr(), size) -// {} -//#else - ELF(BAN::Vector&& data) - : m_data(BAN::move(data)) - {} -//#endif - BAN::ErrorOr load(); - - bool parse_elf64_file_header(const Elf64FileHeader&); - bool parse_elf64_program_header(const Elf64ProgramHeader&); - bool parse_elf64_section_header(const Elf64SectionHeader&); - - bool parse_elf32_file_header(const Elf32FileHeader&); - bool parse_elf32_program_header(const Elf32ProgramHeader&); - bool parse_elf32_section_header(const Elf32SectionHeader&); - - private: -//#ifdef __is_kernel -// BAN::UniqPtr m_storage; -// BAN::Span m_data; -//#else - const BAN::Vector m_data; -//#endif - }; - -} diff --git a/userspace/libraries/LibELF/include/LibELF/LoadableELF.h b/userspace/libraries/LibELF/include/LibELF/LoadableELF.h deleted file mode 100644 index 527289fe..00000000 --- a/userspace/libraries/LibELF/include/LibELF/LoadableELF.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#ifndef __is_kernel -#error "This is kernel only header" -#endif - -#include -#include - -#include -#include -#include - -#include - -namespace LibELF -{ - - class LoadableELF - { - BAN_NON_COPYABLE(LoadableELF); - BAN_NON_MOVABLE(LoadableELF); - - public: - static BAN::ErrorOr> load_from_inode(Kernel::PageTable&, const Kernel::Credentials&, BAN::RefPtr); - ~LoadableELF(); - - Kernel::vaddr_t entry_point() const { return m_file_header.e_entry; } - - bool has_interpreter() const { return !!m_interpreter; } - BAN::RefPtr executable() { return m_executable; } - - bool contains(Kernel::vaddr_t address) const; - bool is_address_space_free() const; - void reserve_address_space(); - - void update_suid_sgid(Kernel::Credentials&); - - BAN::ErrorOr load_page_to_memory(Kernel::vaddr_t address); - - BAN::ErrorOr> clone(Kernel::PageTable&); - - size_t virtual_page_count() const { return m_virtual_page_count; } - size_t physical_page_count() const { return m_physical_page_count; } - - private: - struct LoadResult - { - BAN::RefPtr inode; - BAN::RefPtr interp; - ElfNativeFileHeader file_header; - BAN::Vector program_headers; - }; - - private: - LoadableELF(Kernel::PageTable&); - BAN::ErrorOr initialize(const Kernel::Credentials&, BAN::RefPtr); - BAN::ErrorOr load_elf_file(const Kernel::Credentials&, BAN::RefPtr) const; - - private: - BAN::RefPtr m_executable; - BAN::RefPtr m_interpreter; - ElfNativeFileHeader m_file_header; - BAN::Vector m_program_headers; - - Kernel::PageTable& m_page_table; - size_t m_virtual_page_count { 0 }; - size_t m_physical_page_count { 0 }; - bool m_is_loaded { false }; - - friend class BAN::UniqPtr; - }; - -}