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.
This commit is contained in:
parent
54732edff4
commit
edc69ab2cd
|
@ -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}
|
||||
)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/FS/Inode.h>
|
||||
#include <kernel/Memory/MemoryRegion.h>
|
||||
|
||||
namespace Kernel::ELF
|
||||
{
|
||||
|
||||
struct LoadResult
|
||||
{
|
||||
bool has_interpreter;
|
||||
vaddr_t entry_point;
|
||||
BAN::Vector<BAN::UniqPtr<MemoryRegion>> regions;
|
||||
};
|
||||
|
||||
BAN::ErrorOr<LoadResult> load_from_inode(BAN::RefPtr<Inode>, const Credentials&, PageTable&);
|
||||
|
||||
}
|
|
@ -22,8 +22,6 @@
|
|||
#include <sys/time.h>
|
||||
#include <termios.h>
|
||||
|
||||
namespace LibELF { class LoadableELF; }
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
|
@ -269,7 +267,6 @@ namespace Kernel
|
|||
|
||||
OpenFileDescriptorSet m_open_file_descriptors;
|
||||
|
||||
BAN::UniqPtr<LibELF::LoadableELF> m_loadable_elf;
|
||||
BAN::Vector<BAN::UniqPtr<MemoryRegion>> m_mapped_regions;
|
||||
|
||||
pid_t m_sid;
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
#include <kernel/ELF.h>
|
||||
#include <kernel/FS/VirtualFileSystem.h>
|
||||
#include <kernel/Memory/FileBackedRegion.h>
|
||||
#include <kernel/Memory/MemoryBackedRegion.h>
|
||||
|
||||
#include <LibELF/Types.h>
|
||||
#include <LibELF/Values.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace Kernel::ELF
|
||||
{
|
||||
|
||||
using namespace LibELF;
|
||||
|
||||
static BAN::ErrorOr<ElfNativeFileHeader> read_and_validate_file_header(BAN::RefPtr<Inode> 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;
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<BAN::Vector<ElfNativeProgramHeader>> read_program_headers(BAN::RefPtr<Inode> inode, const ElfNativeFileHeader& file_header)
|
||||
{
|
||||
BAN::Vector<uint8_t> program_header_buffer;
|
||||
TRY(program_header_buffer.resize(file_header.e_phnum * file_header.e_phentsize));
|
||||
TRY(inode->read(file_header.e_phoff, BAN::ByteSpan(program_header_buffer.span())));
|
||||
|
||||
BAN::Vector<ElfNativeProgramHeader> program_headers;
|
||||
TRY(program_headers.reserve(file_header.e_phnum));
|
||||
|
||||
for (size_t i = 0; i < file_header.e_phnum; i++)
|
||||
{
|
||||
const auto& pheader = *reinterpret_cast<ElfNativeProgramHeader*>(program_header_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);
|
||||
}
|
||||
|
||||
MUST(program_headers.emplace_back(pheader));
|
||||
}
|
||||
|
||||
return BAN::move(program_headers);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<LoadResult> load_from_inode(BAN::RefPtr<Inode> inode, const Credentials& credentials, PageTable& page_table)
|
||||
{
|
||||
auto file_header = TRY(read_and_validate_file_header(inode));
|
||||
auto program_headers = TRY(read_program_headers(inode, file_header));
|
||||
|
||||
vaddr_t executable_end = 0;
|
||||
BAN::String interpreter;
|
||||
|
||||
for (const auto& program_header : program_headers)
|
||||
{
|
||||
if (program_header.p_type == PT_LOAD)
|
||||
executable_end = BAN::Math::max<vaddr_t>(executable_end, program_header.p_vaddr + program_header.p_memsz);
|
||||
else if (program_header.p_type == PT_INTERP)
|
||||
{
|
||||
BAN::Vector<uint8_t> interp_buffer;
|
||||
TRY(interp_buffer.resize(program_header.p_memsz, 0));
|
||||
TRY(inode->read(program_header.p_offset, BAN::ByteSpan(interp_buffer.data(), program_header.p_filesz)));
|
||||
if (interp_buffer.empty() || interp_buffer.front() != '/' || interp_buffer.back() != '\0')
|
||||
{
|
||||
dprintln("ELF interpreter is not an valid absolute path");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
auto interpreter_sv = BAN::StringView(reinterpret_cast<const char*>(interp_buffer.data()), interp_buffer.size() - 1);
|
||||
for (char ch : interpreter_sv)
|
||||
{
|
||||
if (isprint(ch))
|
||||
continue;
|
||||
dprintln("ELF interpreter name contains non-printable characters");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
interpreter.clear();
|
||||
TRY(interpreter.append(interpreter_sv));
|
||||
}
|
||||
}
|
||||
|
||||
if (file_header.e_type == ET_DYN)
|
||||
executable_end = 0x400000;
|
||||
|
||||
if (!interpreter.empty())
|
||||
{
|
||||
auto interpreter_inode = TRY(VirtualFileSystem::get().file_from_absolute_path(credentials, interpreter, O_EXEC)).inode;
|
||||
auto interpreter_file_header = TRY(read_and_validate_file_header(interpreter_inode));
|
||||
auto interpreter_program_headers = TRY(read_program_headers(interpreter_inode, interpreter_file_header));
|
||||
|
||||
for (const auto& program_header : interpreter_program_headers)
|
||||
{
|
||||
if (program_header.p_type == PT_INTERP)
|
||||
{
|
||||
dprintln("ELF interpreter has an interpreter specified");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
inode = interpreter_inode;
|
||||
file_header = interpreter_file_header;
|
||||
program_headers = BAN::move(interpreter_program_headers);
|
||||
}
|
||||
|
||||
const vaddr_t load_base_vaddr =
|
||||
[&file_header, executable_end]() -> vaddr_t
|
||||
{
|
||||
if (file_header.e_type == ET_EXEC)
|
||||
return 0;
|
||||
if (file_header.e_type == ET_DYN)
|
||||
return (executable_end + PAGE_SIZE - 1) & PAGE_ADDR_MASK;
|
||||
ASSERT_NOT_REACHED();
|
||||
}();
|
||||
|
||||
BAN::Vector<BAN::UniqPtr<MemoryRegion>> memory_regions;
|
||||
for (const auto& program_header : program_headers)
|
||||
{
|
||||
if (program_header.p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
const PageTable::flags_t flags =
|
||||
[&program_header]() -> int
|
||||
{
|
||||
PageTable::flags_t result = PageTable::Flags::UserSupervisor;
|
||||
if (program_header.p_flags & PF_R)
|
||||
result |= PageTable::Flags::Present;
|
||||
if (program_header.p_flags & PF_W)
|
||||
result |= PageTable::Flags::ReadWrite;
|
||||
if (program_header.p_flags & PF_X)
|
||||
result |= PageTable::Flags::Execute;
|
||||
return result;
|
||||
}();
|
||||
|
||||
const size_t file_backed_size =
|
||||
[&program_header]() -> size_t
|
||||
{
|
||||
if ((program_header.p_vaddr & 0xFFF) || (program_header.p_offset & 0xFFF))
|
||||
return 0;
|
||||
if (program_header.p_filesz == program_header.p_memsz)
|
||||
return program_header.p_filesz;
|
||||
return program_header.p_filesz & ~(uintptr_t)0xFFF;
|
||||
}();
|
||||
|
||||
const vaddr_t pheader_base = load_base_vaddr + program_header.p_vaddr;
|
||||
|
||||
if (file_backed_size)
|
||||
{
|
||||
auto region = TRY(FileBackedRegion::create(
|
||||
inode,
|
||||
page_table,
|
||||
program_header.p_offset,
|
||||
file_backed_size,
|
||||
{ .start = pheader_base, .end = pheader_base + file_backed_size },
|
||||
MemoryRegion::Type::PRIVATE,
|
||||
flags
|
||||
));
|
||||
TRY(memory_regions.emplace_back(BAN::move(region)));
|
||||
}
|
||||
|
||||
if (file_backed_size < program_header.p_memsz)
|
||||
{
|
||||
const vaddr_t aligned_vaddr = pheader_base & PAGE_ADDR_MASK;
|
||||
|
||||
auto region = TRY(MemoryBackedRegion::create(
|
||||
page_table,
|
||||
(pheader_base + program_header.p_memsz) - (aligned_vaddr + file_backed_size),
|
||||
{ .start = aligned_vaddr + file_backed_size, .end = pheader_base + program_header.p_memsz },
|
||||
MemoryRegion::Type::PRIVATE,
|
||||
flags
|
||||
));
|
||||
|
||||
if (file_backed_size < program_header.p_filesz)
|
||||
{
|
||||
BAN::Vector<uint8_t> file_data_buffer;
|
||||
TRY(file_data_buffer.resize(program_header.p_filesz - file_backed_size));
|
||||
if (TRY(inode->read(program_header.p_offset + file_backed_size, file_data_buffer.span())) != file_data_buffer.size())
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
TRY(region->copy_data_to_region(pheader_base - aligned_vaddr, file_data_buffer.data(), file_data_buffer.size()));
|
||||
}
|
||||
|
||||
TRY(memory_regions.emplace_back(BAN::move(region)));
|
||||
}
|
||||
}
|
||||
|
||||
LoadResult result;
|
||||
result.has_interpreter = !interpreter.empty();
|
||||
result.entry_point = load_base_vaddr + file_header.e_entry;
|
||||
result.regions = BAN::move(memory_regions);
|
||||
return BAN::move(result);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#include <BAN/ScopeGuard.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <kernel/ACPI/ACPI.h>
|
||||
#include <kernel/ELF.h>
|
||||
#include <kernel/FS/DevFS/FileSystem.h>
|
||||
#include <kernel/FS/ProcFS/FileSystem.h>
|
||||
#include <kernel/FS/VirtualFileSystem.h>
|
||||
|
@ -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("<self>"));
|
||||
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<size_t>(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<BAN::String> 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("<self>"));
|
||||
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();
|
||||
|
|
|
@ -1,405 +0,0 @@
|
|||
#include <BAN/ScopeGuard.h>
|
||||
#include <LibELF/ELF.h>
|
||||
#include <LibELF/Values.h>
|
||||
|
||||
#ifdef __is_kernel
|
||||
#include <kernel/FS/VirtualFileSystem.h>
|
||||
#include <kernel/Process.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#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<BAN::UniqPtr<ELF>> ELF::load_from_file(BAN::RefPtr<Inode> inode)
|
||||
{
|
||||
BAN::Vector<uint8_t> 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<ELF>::adopt(elf_ptr);
|
||||
TRY(elf->load());
|
||||
|
||||
return BAN::move(elf);
|
||||
}
|
||||
#else
|
||||
BAN::ErrorOr<ELF*> ELF::load_from_file(BAN::StringView file_path)
|
||||
{
|
||||
ELF* elf = nullptr;
|
||||
|
||||
{
|
||||
BAN::Vector<uint8_t> 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<void> 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,422 +0,0 @@
|
|||
#include <BAN/ScopeGuard.h>
|
||||
#include <kernel/FS/VirtualFileSystem.h>
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Memory/Heap.h>
|
||||
#include <kernel/Random.h>
|
||||
#include <LibELF/LoadableELF.h>
|
||||
#include <LibELF/Values.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
using namespace Kernel;
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::load_from_inode(PageTable& page_table, const Credentials& credentials, BAN::RefPtr<Inode> inode)
|
||||
{
|
||||
auto elf = TRY(BAN::UniqPtr<LoadableELF>::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<ElfNativeFileHeader> read_and_validate_file_header(BAN::RefPtr<Inode> 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::LoadResult> LoadableELF::load_elf_file(const Credentials& credentials, BAN::RefPtr<Inode> inode) const
|
||||
{
|
||||
auto file_header = TRY(read_and_validate_file_header(inode));
|
||||
|
||||
BAN::Vector<uint8_t> 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<ElfNativeProgramHeader> program_headers;
|
||||
BAN::RefPtr<Inode> interp;
|
||||
|
||||
for (size_t i = 0; i < file_header.e_phnum; i++)
|
||||
{
|
||||
const auto& pheader = *reinterpret_cast<ElfNativeProgramHeader*>(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<uint8_t> 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<const char*>(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<const ElfNativeProgramHeader> pheaders1, BAN::Span<const ElfNativeProgramHeader> 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<void> LoadableELF::initialize(const Credentials& credentials, BAN::RefPtr<Inode> 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<void> 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<size_t>(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<size_t>(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<BAN::UniqPtr<LoadableELF>> LoadableELF::clone(Kernel::PageTable& new_page_table)
|
||||
{
|
||||
auto elf = TRY(BAN::UniqPtr<LoadableELF>::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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __is_kernel
|
||||
#include <kernel/FS/Inode.h>
|
||||
#include <kernel/Memory/VirtualRange.h>
|
||||
#endif
|
||||
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/UniqPtr.h>
|
||||
#include <BAN/Vector.h>
|
||||
#include <kernel/Arch.h>
|
||||
#include "Types.h"
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
class ELF
|
||||
{
|
||||
public:
|
||||
#ifdef __is_kernel
|
||||
static BAN::ErrorOr<BAN::UniqPtr<ELF>> load_from_file(BAN::RefPtr<Kernel::Inode>);
|
||||
#else
|
||||
static BAN::ErrorOr<BAN::UniqPtr<ELF>> 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<Kernel::VirtualRange>&& storage, size_t size)
|
||||
// : m_storage(BAN::move(storage))
|
||||
// , m_data((const uint8_t*)m_storage->vaddr(), size)
|
||||
// {}
|
||||
//#else
|
||||
ELF(BAN::Vector<uint8_t>&& data)
|
||||
: m_data(BAN::move(data))
|
||||
{}
|
||||
//#endif
|
||||
BAN::ErrorOr<void> 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<Kernel::VirtualRange> m_storage;
|
||||
// BAN::Span<const uint8_t> m_data;
|
||||
//#else
|
||||
const BAN::Vector<uint8_t> m_data;
|
||||
//#endif
|
||||
};
|
||||
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef __is_kernel
|
||||
#error "This is kernel only header"
|
||||
#endif
|
||||
|
||||
#include <BAN/UniqPtr.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <kernel/Credentials.h>
|
||||
#include <kernel/FS/Inode.h>
|
||||
#include <kernel/Memory/PageTable.h>
|
||||
|
||||
#include <LibELF/Types.h>
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
class LoadableELF
|
||||
{
|
||||
BAN_NON_COPYABLE(LoadableELF);
|
||||
BAN_NON_MOVABLE(LoadableELF);
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> load_from_inode(Kernel::PageTable&, const Kernel::Credentials&, BAN::RefPtr<Kernel::Inode>);
|
||||
~LoadableELF();
|
||||
|
||||
Kernel::vaddr_t entry_point() const { return m_file_header.e_entry; }
|
||||
|
||||
bool has_interpreter() const { return !!m_interpreter; }
|
||||
BAN::RefPtr<Kernel::Inode> 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<void> load_page_to_memory(Kernel::vaddr_t address);
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> 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<Kernel::Inode> inode;
|
||||
BAN::RefPtr<Kernel::Inode> interp;
|
||||
ElfNativeFileHeader file_header;
|
||||
BAN::Vector<ElfNativeProgramHeader> program_headers;
|
||||
};
|
||||
|
||||
private:
|
||||
LoadableELF(Kernel::PageTable&);
|
||||
BAN::ErrorOr<void> initialize(const Kernel::Credentials&, BAN::RefPtr<Kernel::Inode>);
|
||||
BAN::ErrorOr<LoadResult> load_elf_file(const Kernel::Credentials&, BAN::RefPtr<Kernel::Inode>) const;
|
||||
|
||||
private:
|
||||
BAN::RefPtr<Kernel::Inode> m_executable;
|
||||
BAN::RefPtr<Kernel::Inode> m_interpreter;
|
||||
ElfNativeFileHeader m_file_header;
|
||||
BAN::Vector<ElfNativeProgramHeader> 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<LoadableELF>;
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in New Issue