Kernel: Invoke ELF interpreter instead if it is specified
This commit is contained in:
parent
d20752c318
commit
2bf65ef512
|
@ -55,14 +55,15 @@ start_userspace_thread:
|
||||||
movw %bx, %gs
|
movw %bx, %gs
|
||||||
xorw %bx, %bx
|
xorw %bx, %bx
|
||||||
|
|
||||||
popl %edx
|
|
||||||
popl %esi
|
|
||||||
popl %edi
|
popl %edi
|
||||||
|
popl %esi
|
||||||
|
popl %edx
|
||||||
popl %ecx
|
popl %ecx
|
||||||
|
popl %ebx
|
||||||
|
|
||||||
pushl $(0x20 | 3)
|
pushl $(0x20 | 3)
|
||||||
pushl %eax
|
pushl %eax
|
||||||
pushl $0x202
|
pushl $0x202
|
||||||
pushl $(0x18 | 3)
|
pushl $(0x18 | 3)
|
||||||
pushl %ecx
|
pushl %ebx
|
||||||
iret
|
iret
|
||||||
|
|
|
@ -39,14 +39,15 @@ start_userspace_thread:
|
||||||
|
|
||||||
call get_userspace_thread_stack_top
|
call get_userspace_thread_stack_top
|
||||||
|
|
||||||
popq %rdx
|
|
||||||
popq %rsi
|
|
||||||
popq %rdi
|
popq %rdi
|
||||||
|
popq %rsi
|
||||||
|
popq %rdx
|
||||||
popq %rcx
|
popq %rcx
|
||||||
|
popq %rbx
|
||||||
|
|
||||||
pushq $(0x20 | 3)
|
pushq $(0x20 | 3)
|
||||||
pushq %rax
|
pushq %rax
|
||||||
pushq $0x202
|
pushq $0x202
|
||||||
pushq $(0x18 | 3)
|
pushq $(0x18 | 3)
|
||||||
pushq %rcx
|
pushq %rbx
|
||||||
iretq
|
iretq
|
||||||
|
|
|
@ -41,6 +41,7 @@ namespace Kernel
|
||||||
int argc { 0 };
|
int argc { 0 };
|
||||||
char** argv { nullptr };
|
char** argv { nullptr };
|
||||||
char** envp { nullptr };
|
char** envp { nullptr };
|
||||||
|
int file_fd { -1 };
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -411,7 +411,7 @@ namespace Kernel
|
||||||
}
|
}
|
||||||
|
|
||||||
auto file = TRY(VirtualFileSystem::get().file_from_absolute_path(credentials, absolute_path, O_EXEC));
|
auto file = TRY(VirtualFileSystem::get().file_from_absolute_path(credentials, absolute_path, O_EXEC));
|
||||||
return TRY(LibELF::LoadableELF::load_from_inode(page_table, file.inode));
|
return TRY(LibELF::LoadableELF::load_from_inode(page_table, credentials, file.inode));
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<long> Process::sys_fork(uintptr_t sp, uintptr_t ip)
|
BAN::ErrorOr<long> Process::sys_fork(uintptr_t sp, uintptr_t ip)
|
||||||
|
@ -515,6 +515,13 @@ namespace Kernel
|
||||||
m_loadable_elf->reserve_address_space();
|
m_loadable_elf->reserve_address_space();
|
||||||
m_loadable_elf->update_suid_sgid(m_credentials);
|
m_loadable_elf->update_suid_sgid(m_credentials);
|
||||||
m_userspace_info.entry = m_loadable_elf->entry_point();
|
m_userspace_info.entry = m_loadable_elf->entry_point();
|
||||||
|
if (m_loadable_elf->has_interpreter())
|
||||||
|
{
|
||||||
|
VirtualFileSystem::File file;
|
||||||
|
TRY(file.canonical_path.append("<self>"));
|
||||||
|
file.inode = m_loadable_elf->inode();
|
||||||
|
m_userspace_info.file_fd = TRY(m_open_file_descriptors.open(BAN::move(file), O_EXEC));
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(m_signal_handlers) / sizeof(*m_signal_handlers); i++)
|
for (size_t i = 0; i < sizeof(m_signal_handlers) / sizeof(*m_signal_handlers); i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -190,22 +190,23 @@ namespace Kernel
|
||||||
|
|
||||||
// Signal mask is inherited
|
// Signal mask is inherited
|
||||||
|
|
||||||
auto& userspace_info = process().userspace_info();
|
const auto& userspace_info = process().userspace_info();
|
||||||
ASSERT(userspace_info.entry);
|
ASSERT(userspace_info.entry);
|
||||||
|
|
||||||
// Initialize stack for returning
|
// Initialize stack for returning
|
||||||
PageTable::with_fast_page(process().page_table().physical_address_of(kernel_stack_top() - PAGE_SIZE), [&] {
|
PageTable::with_fast_page(process().page_table().physical_address_of(kernel_stack_top() - PAGE_SIZE), [&] {
|
||||||
uintptr_t sp = PageTable::fast_page() + PAGE_SIZE;
|
uintptr_t sp = PageTable::fast_page() + PAGE_SIZE;
|
||||||
write_to_stack(sp, userspace_info.entry);
|
write_to_stack(sp, userspace_info.entry);
|
||||||
write_to_stack(sp, userspace_info.argc);
|
write_to_stack(sp, userspace_info.file_fd);
|
||||||
write_to_stack(sp, userspace_info.argv);
|
|
||||||
write_to_stack(sp, userspace_info.envp);
|
write_to_stack(sp, userspace_info.envp);
|
||||||
|
write_to_stack(sp, userspace_info.argv);
|
||||||
|
write_to_stack(sp, userspace_info.argc);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_interrupt_stack.ip = reinterpret_cast<vaddr_t>(start_userspace_thread);;
|
m_interrupt_stack.ip = reinterpret_cast<vaddr_t>(start_userspace_thread);
|
||||||
m_interrupt_stack.cs = 0x08;
|
m_interrupt_stack.cs = 0x08;
|
||||||
m_interrupt_stack.flags = 0x002;
|
m_interrupt_stack.flags = 0x002;
|
||||||
m_interrupt_stack.sp = kernel_stack_top() - 4 * sizeof(uintptr_t);
|
m_interrupt_stack.sp = kernel_stack_top() - 5 * sizeof(uintptr_t);
|
||||||
m_interrupt_stack.ss = 0x10;
|
m_interrupt_stack.ss = 0x10;
|
||||||
|
|
||||||
memset(&m_interrupt_registers, 0, sizeof(InterruptRegisters));
|
memset(&m_interrupt_registers, 0, sizeof(InterruptRegisters));
|
||||||
|
|
|
@ -1,145 +1,253 @@
|
||||||
#include <BAN/ScopeGuard.h>
|
#include <BAN/ScopeGuard.h>
|
||||||
|
#include <kernel/FS/VirtualFileSystem.h>
|
||||||
#include <kernel/Lock/LockGuard.h>
|
#include <kernel/Lock/LockGuard.h>
|
||||||
#include <kernel/Memory/Heap.h>
|
#include <kernel/Memory/Heap.h>
|
||||||
#include <kernel/Random.h>
|
#include <kernel/Random.h>
|
||||||
#include <LibELF/LoadableELF.h>
|
#include <LibELF/LoadableELF.h>
|
||||||
#include <LibELF/Values.h>
|
#include <LibELF/Values.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
namespace LibELF
|
namespace LibELF
|
||||||
{
|
{
|
||||||
|
|
||||||
using namespace Kernel;
|
using namespace Kernel;
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::load_from_inode(PageTable& page_table, BAN::RefPtr<Inode> inode)
|
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::load_from_inode(PageTable& page_table, const Credentials& credentials, BAN::RefPtr<Inode> inode)
|
||||||
{
|
{
|
||||||
auto* elf_ptr = new LoadableELF(page_table, inode);
|
auto elf = TRY(BAN::UniqPtr<LoadableELF>::create(page_table));
|
||||||
if (elf_ptr == nullptr)
|
TRY(elf->initialize(credentials, inode));
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
return elf;
|
||||||
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
|
|
||||||
TRY(elf->initialize());
|
|
||||||
return BAN::move(elf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadableELF::LoadableELF(PageTable& page_table, BAN::RefPtr<Inode> inode)
|
LoadableELF::LoadableELF(PageTable& page_table)
|
||||||
: m_inode(inode)
|
: m_page_table(page_table)
|
||||||
, m_page_table(page_table)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadableELF::~LoadableELF()
|
LoadableELF::~LoadableELF()
|
||||||
{
|
{
|
||||||
if (!m_loaded)
|
const auto cleanup_program_headers =
|
||||||
return;
|
[&](BAN::Span<const ElfNativeProgramHeader> headers)
|
||||||
for (const auto& program_header : m_program_headers)
|
|
||||||
{
|
{
|
||||||
switch (program_header.p_type)
|
for (const auto& header : headers)
|
||||||
{
|
{
|
||||||
case PT_NULL:
|
ASSERT(header.p_type == PT_LOAD);
|
||||||
continue;
|
|
||||||
case 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);
|
||||||
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++)
|
for (size_t i = 0; i < pages; i++)
|
||||||
{
|
if (paddr_t paddr = m_page_table.physical_address_of(vaddr + i * PAGE_SIZE))
|
||||||
paddr_t paddr = m_page_table.physical_address_of(start + i * PAGE_SIZE);
|
|
||||||
if (paddr != 0)
|
|
||||||
Heap::get().release_page(paddr);
|
Heap::get().release_page(paddr);
|
||||||
|
m_page_table.unmap_range(vaddr, pages * PAGE_SIZE);
|
||||||
}
|
}
|
||||||
m_page_table.unmap_range(start, pages * PAGE_SIZE);
|
};
|
||||||
break;
|
|
||||||
}
|
if (!m_is_loaded)
|
||||||
default:
|
return;
|
||||||
ASSERT_NOT_REACHED();
|
cleanup_program_headers(m_executable.program_headers.span());
|
||||||
}
|
cleanup_program_headers(m_interpreter.program_headers.span());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> LoadableELF::initialize()
|
static BAN::ErrorOr<ElfNativeFileHeader> read_and_validate_file_header(BAN::RefPtr<Inode> inode)
|
||||||
{
|
{
|
||||||
if ((size_t)m_inode->size() < sizeof(ElfNativeFileHeader))
|
if ((size_t)inode->size() < sizeof(ElfNativeFileHeader))
|
||||||
{
|
{
|
||||||
dprintln("Too small file");
|
dprintln("File is too small to be ELF");
|
||||||
return BAN::Error::from_errno(ENOEXEC);
|
return BAN::Error::from_errno(ENOEXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nread = TRY(m_inode->read(0, BAN::ByteSpan::from(m_file_header)));
|
ElfNativeFileHeader file_header;
|
||||||
ASSERT(nread == sizeof(m_file_header));
|
|
||||||
|
|
||||||
if (m_file_header.e_ident[EI_MAG0] != ELFMAG0 ||
|
size_t nread = TRY(inode->read(0, BAN::ByteSpan::from(file_header)));
|
||||||
m_file_header.e_ident[EI_MAG1] != ELFMAG1 ||
|
ASSERT(nread == sizeof(file_header));
|
||||||
m_file_header.e_ident[EI_MAG2] != ELFMAG2 ||
|
|
||||||
m_file_header.e_ident[EI_MAG3] != ELFMAG3)
|
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("Invalid magic in header");
|
dprintln("Not an ELF file");
|
||||||
return BAN::Error::from_errno(ENOEXEC);
|
return BAN::Error::from_errno(ENOEXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_file_header.e_ident[EI_DATA] != ELFDATA2LSB)
|
if (file_header.e_ident[EI_DATA] != ELFDATA2LSB)
|
||||||
{
|
{
|
||||||
dprintln("Only little-endian is supported");
|
dprintln("Not in little-endian");
|
||||||
return BAN::Error::from_errno(ENOEXEC);
|
return BAN::Error::from_errno(ENOEXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_file_header.e_ident[EI_VERSION] != EV_CURRENT)
|
if (file_header.e_ident[EI_VERSION] != EV_CURRENT)
|
||||||
{
|
{
|
||||||
dprintln("Invalid version");
|
dprintln("Unsupported version {}", file_header.e_ident[EI_VERSION]);
|
||||||
return BAN::Error::from_errno(ENOEXEC);
|
return BAN::Error::from_errno(ENOEXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ARCH(i686)
|
#if ARCH(i686)
|
||||||
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS32)
|
if (file_header.e_ident[EI_CLASS] != ELFCLASS32)
|
||||||
#elif ARCH(x86_64)
|
#elif ARCH(x86_64)
|
||||||
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS64)
|
if (file_header.e_ident[EI_CLASS] != ELFCLASS64)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
dprintln("Not in native format");
|
dprintln("Not in native format");
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_file_header.e_type != ET_EXEC && m_file_header.e_type != ET_DYN)
|
if (file_header.e_type != ET_EXEC && file_header.e_type != ET_DYN)
|
||||||
{
|
{
|
||||||
dprintln("Unsupported file header type {}", m_file_header.e_type);
|
dprintln("Unsupported file header type {}", file_header.e_type);
|
||||||
return BAN::Error::from_errno(ENOTSUP);
|
return BAN::Error::from_errno(ENOTSUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_file_header.e_version != EV_CURRENT)
|
if (file_header.e_version != EV_CURRENT)
|
||||||
{
|
{
|
||||||
dprintln("Unsupported version");
|
dprintln("Unsupported version {}", file_header.e_version);
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(m_file_header.e_phentsize <= sizeof(ElfNativeProgramHeader));
|
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;
|
BAN::Vector<uint8_t> pheader_buffer;
|
||||||
TRY(pheader_buffer.resize(m_file_header.e_phnum * m_file_header.e_phentsize));
|
TRY(pheader_buffer.resize(file_header.e_phnum * file_header.e_phentsize));
|
||||||
TRY(m_inode->read(m_file_header.e_phoff, BAN::ByteSpan(pheader_buffer.span())));
|
TRY(inode->read(file_header.e_phoff, BAN::ByteSpan(pheader_buffer.span())));
|
||||||
|
|
||||||
const vaddr_t dyn_base = (m_file_header.e_type == ET_DYN) ? 0x40000000 + (Random::get_u32() & 0x3FFFF000) : 0;
|
BAN::Vector<ElfNativeProgramHeader> program_headers;
|
||||||
m_file_header.e_entry += dyn_base;
|
BAN::RefPtr<Inode> interp;
|
||||||
|
|
||||||
for (size_t i = 0; i < m_file_header.e_phnum; i++)
|
for (size_t i = 0; i < file_header.e_phnum; i++)
|
||||||
{
|
{
|
||||||
const auto& pheader = *reinterpret_cast<ElfNativeProgramHeader*>(pheader_buffer.data() + i * m_file_header.e_phentsize);
|
const auto& pheader = *reinterpret_cast<ElfNativeProgramHeader*>(pheader_buffer.data() + i * file_header.e_phentsize);
|
||||||
if (pheader.p_memsz < pheader.p_filesz)
|
if (pheader.p_memsz < pheader.p_filesz)
|
||||||
{
|
{
|
||||||
dprintln("Invalid program header");
|
dprintln("Invalid program header, memsz less than filesz");
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (pheader.p_type)
|
switch (pheader.p_type)
|
||||||
{
|
{
|
||||||
case PT_NULL:
|
|
||||||
case PT_DYNAMIC:
|
|
||||||
break;
|
|
||||||
case PT_LOAD:
|
case PT_LOAD:
|
||||||
TRY(m_program_headers.push_back(pheader));
|
for (const auto& program_header : program_headers)
|
||||||
m_program_headers.back().p_vaddr += dyn_base;
|
{
|
||||||
m_virtual_page_count += BAN::Math::div_round_up<size_t>((pheader.p_vaddr % PAGE_SIZE) + pheader.p_memsz, PAGE_SIZE);
|
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;
|
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:
|
default:
|
||||||
dprintln("Unsupported program header type {}", pheader.p_type);
|
break;
|
||||||
return BAN::Error::from_errno(ENOTSUP);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadResult {
|
||||||
|
.elf_file = {
|
||||||
|
.inode = inode,
|
||||||
|
.file_header = file_header,
|
||||||
|
.program_headers = BAN::move(program_headers),
|
||||||
|
.dynamic_base = 0
|
||||||
|
},
|
||||||
|
.interp = interp
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoadableELF::does_executable_and_interpreter_overlap() const
|
||||||
|
{
|
||||||
|
ASSERT(m_executable.inode);
|
||||||
|
ASSERT(m_interpreter.inode);
|
||||||
|
|
||||||
|
for (const auto& epheader : m_executable.program_headers)
|
||||||
|
{
|
||||||
|
for (const auto& ipheader : m_interpreter.program_headers)
|
||||||
|
{
|
||||||
|
const vaddr_t e1 = epheader.p_vaddr & PAGE_ADDR_MASK;
|
||||||
|
const vaddr_t i1 = ipheader.p_vaddr & PAGE_ADDR_MASK;
|
||||||
|
const vaddr_t e2 = (epheader.p_vaddr + epheader.p_memsz + PAGE_SIZE - 1) & PAGE_ADDR_MASK;
|
||||||
|
const vaddr_t i2 = (ipheader.p_vaddr + ipheader.p_memsz + PAGE_SIZE - 1) & PAGE_ADDR_MASK;
|
||||||
|
if (e1 < i2 && i1 < e2)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> LoadableELF::initialize(const Credentials& credentials, BAN::RefPtr<Inode> inode)
|
||||||
|
{
|
||||||
|
auto executable_load_result = TRY(load_elf_file(credentials, inode));
|
||||||
|
m_executable = executable_load_result.elf_file;
|
||||||
|
|
||||||
|
if (m_executable.file_header.e_type == ET_DYN)
|
||||||
|
{
|
||||||
|
m_executable.dynamic_base = (Random::get_u32() & 0x7FFFF000) + 0x100000;
|
||||||
|
m_executable.file_header.e_entry += m_executable.dynamic_base;
|
||||||
|
for (auto& program_header : m_executable.program_headers)
|
||||||
|
program_header.p_vaddr += m_executable.dynamic_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (executable_load_result.interp)
|
||||||
|
{
|
||||||
|
auto interp_load_result = TRY(load_elf_file(credentials, executable_load_result.interp));
|
||||||
|
m_interpreter = interp_load_result.elf_file;
|
||||||
|
|
||||||
|
if (interp_load_result.interp)
|
||||||
|
{
|
||||||
|
dwarnln("Executable has specified interpreter for its interpreter");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_interpreter.file_header.e_type == ET_DYN)
|
||||||
|
{
|
||||||
|
for (int attempt = 0; attempt < 100; attempt++)
|
||||||
|
{
|
||||||
|
const vaddr_t dynamic_base = (Random::get_u32() & 0x3FFFF000) + 0x40000000;
|
||||||
|
for (auto& program_header : m_interpreter.program_headers)
|
||||||
|
program_header.p_vaddr += dynamic_base;
|
||||||
|
if (does_executable_and_interpreter_overlap())
|
||||||
|
{
|
||||||
|
for (auto& program_header : m_interpreter.program_headers)
|
||||||
|
program_header.p_vaddr -= dynamic_base;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
m_interpreter.dynamic_base = dynamic_base;
|
||||||
|
m_interpreter.file_header.e_entry += dynamic_base;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool can_load_interpreter = (m_interpreter.file_header.e_type == ET_DYN)
|
||||||
|
? (m_interpreter.dynamic_base != 0)
|
||||||
|
: !does_executable_and_interpreter_overlap();
|
||||||
|
|
||||||
|
if (!can_load_interpreter)
|
||||||
|
{
|
||||||
|
dwarnln("Could not find space to load interpreter");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,23 +256,28 @@ namespace LibELF
|
||||||
|
|
||||||
vaddr_t LoadableELF::entry_point() const
|
vaddr_t LoadableELF::entry_point() const
|
||||||
{
|
{
|
||||||
return m_file_header.e_entry;
|
if (m_interpreter.inode)
|
||||||
|
return m_interpreter.file_header.e_entry;
|
||||||
|
return m_executable.file_header.e_entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LoadableELF::contains(vaddr_t address) const
|
bool LoadableELF::contains(vaddr_t address) const
|
||||||
{
|
{
|
||||||
for (const auto& program_header : m_program_headers)
|
for (const auto& program_header : m_executable.program_headers)
|
||||||
{
|
if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)
|
||||||
ASSERT(program_header.p_type == PT_LOAD);
|
return true;
|
||||||
|
for (const auto& program_header : m_interpreter.program_headers)
|
||||||
if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)
|
if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LoadableELF::is_address_space_free() const
|
bool LoadableELF::is_address_space_free() const
|
||||||
{
|
{
|
||||||
for (const auto& program_header : m_program_headers)
|
const auto are_program_headers_free =
|
||||||
|
[&](BAN::Span<const ElfNativeProgramHeader> program_headers) -> bool
|
||||||
|
{
|
||||||
|
for (const auto& program_header : program_headers)
|
||||||
{
|
{
|
||||||
ASSERT(program_header.p_type == PT_LOAD);
|
ASSERT(program_header.p_type == PT_LOAD);
|
||||||
const vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
|
const vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||||
|
@ -173,35 +286,53 @@ namespace LibELF
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
};
|
||||||
|
if (!are_program_headers_free(m_executable.program_headers.span()))
|
||||||
|
return false;
|
||||||
|
if (!are_program_headers_free(m_interpreter.program_headers.span()))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadableELF::reserve_address_space()
|
void LoadableELF::reserve_address_space()
|
||||||
{
|
{
|
||||||
for (const auto& program_header : m_program_headers)
|
const auto reserve_program_headers =
|
||||||
|
[&](BAN::Span<const ElfNativeProgramHeader> program_headers)
|
||||||
|
{
|
||||||
|
for (const auto& program_header : program_headers)
|
||||||
{
|
{
|
||||||
ASSERT(program_header.p_type == PT_LOAD);
|
ASSERT(program_header.p_type == PT_LOAD);
|
||||||
const vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
|
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);
|
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))
|
if (!m_page_table.reserve_range(page_vaddr, pages * PAGE_SIZE))
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
|
m_virtual_page_count += pages;
|
||||||
}
|
}
|
||||||
m_loaded = true;
|
};
|
||||||
|
reserve_program_headers(m_executable.program_headers.span());
|
||||||
|
reserve_program_headers(m_interpreter.program_headers.span());
|
||||||
|
m_is_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadableELF::update_suid_sgid(Kernel::Credentials& credentials)
|
void LoadableELF::update_suid_sgid(Kernel::Credentials& credentials)
|
||||||
{
|
{
|
||||||
if (m_inode->mode().mode & +Inode::Mode::ISUID)
|
auto inode = m_executable.inode;
|
||||||
credentials.set_euid(m_inode->uid());
|
ASSERT(inode);
|
||||||
if (m_inode->mode().mode & +Inode::Mode::ISGID)
|
|
||||||
credentials.set_egid(m_inode->gid());
|
if (inode->mode().mode & +Inode::Mode::ISUID)
|
||||||
|
credentials.set_euid(inode->uid());
|
||||||
|
if (inode->mode().mode & +Inode::Mode::ISGID)
|
||||||
|
credentials.set_egid(inode->gid());
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> LoadableELF::load_page_to_memory(vaddr_t address)
|
BAN::ErrorOr<void> LoadableELF::load_page_to_memory(vaddr_t address)
|
||||||
{
|
{
|
||||||
for (const auto& program_header : m_program_headers)
|
const auto load_page_from_program_header =
|
||||||
|
[&](BAN::RefPtr<Inode> inode, BAN::Span<const ElfNativeProgramHeader> program_headers) -> BAN::ErrorOr<bool>
|
||||||
|
{
|
||||||
|
for (const auto& program_header : program_headers)
|
||||||
{
|
{
|
||||||
ASSERT(program_header.p_type == PT_LOAD);
|
ASSERT(program_header.p_type == PT_LOAD);
|
||||||
|
|
||||||
if (!(program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz))
|
if (!(program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -233,35 +364,52 @@ namespace LibELF
|
||||||
file_offset = 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);
|
size_t bytes = BAN::Math::min<size_t>(PAGE_SIZE - vaddr_offset, program_header.p_filesz - file_offset);
|
||||||
TRY(m_inode->read(program_header.p_offset + file_offset, { (uint8_t*)vaddr + vaddr_offset, bytes }));
|
TRY(inode->read(program_header.p_offset + file_offset, { (uint8_t*)vaddr + vaddr_offset, bytes }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map page with the correct flags
|
// Map page with the correct flags
|
||||||
m_page_table.map_page_at(paddr, vaddr, flags);
|
m_page_table.map_page_at(paddr, vaddr, flags);
|
||||||
|
|
||||||
return {};
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (TRY(load_page_from_program_header(m_executable.inode, m_executable.program_headers.span())))
|
||||||
|
return {};
|
||||||
|
if (TRY(load_page_from_program_header(m_interpreter.inode, m_interpreter.program_headers.span())))
|
||||||
|
return {};
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::clone(Kernel::PageTable& new_page_table)
|
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::clone(Kernel::PageTable& new_page_table)
|
||||||
{
|
{
|
||||||
auto* elf_ptr = new LoadableELF(new_page_table, m_inode);
|
auto elf = TRY(BAN::UniqPtr<LoadableELF>::create(new_page_table));
|
||||||
if (elf_ptr == nullptr)
|
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
|
||||||
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
|
|
||||||
|
|
||||||
memcpy(&elf->m_file_header, &m_file_header, sizeof(ElfNativeFileHeader));
|
const auto clone_loadable_file =
|
||||||
|
[](const LoadableElfFile& source, LoadableElfFile& destination) -> BAN::ErrorOr<void>
|
||||||
|
{
|
||||||
|
if (!source.inode)
|
||||||
|
return {};
|
||||||
|
|
||||||
TRY(elf->m_program_headers.resize(m_program_headers.size()));
|
destination.inode = source.inode;
|
||||||
memcpy(elf->m_program_headers.data(), m_program_headers.data(), m_program_headers.size() * sizeof(ElfNativeProgramHeader));
|
destination.file_header = source.file_header;
|
||||||
|
destination.dynamic_base = source.dynamic_base;
|
||||||
|
|
||||||
elf->reserve_address_space();
|
TRY(destination.program_headers.reserve(source.program_headers.size()));
|
||||||
|
for (const auto& program_header : source.program_headers)
|
||||||
|
MUST(destination.program_headers.emplace_back(program_header));
|
||||||
|
|
||||||
for (const auto& program_header : m_program_headers)
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto map_loadable_file =
|
||||||
|
[&](BAN::Span<const ElfNativeProgramHeader> program_headers) -> BAN::ErrorOr<void>
|
||||||
|
{
|
||||||
|
for (const auto& program_header : program_headers)
|
||||||
{
|
{
|
||||||
ASSERT(program_header.p_type == PT_LOAD);
|
ASSERT(program_header.p_type == PT_LOAD);
|
||||||
|
|
||||||
if (!(program_header.p_flags & LibELF::PF_W))
|
if (!(program_header.p_flags & LibELF::PF_W))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -291,6 +439,16 @@ namespace LibELF
|
||||||
elf->m_physical_page_count++;
|
elf->m_physical_page_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
TRY(clone_loadable_file(m_executable, elf->m_executable));
|
||||||
|
TRY(clone_loadable_file(m_interpreter, elf->m_interpreter));
|
||||||
|
|
||||||
|
elf->reserve_address_space();
|
||||||
|
|
||||||
|
TRY(map_loadable_file(elf->m_executable.program_headers.span()));
|
||||||
|
TRY(map_loadable_file(elf->m_interpreter.program_headers.span()));
|
||||||
|
|
||||||
return elf;
|
return elf;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,14 @@ namespace LibELF
|
||||||
BAN_NON_MOVABLE(LoadableELF);
|
BAN_NON_MOVABLE(LoadableELF);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> load_from_inode(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
|
static BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> load_from_inode(Kernel::PageTable&, const Kernel::Credentials&, BAN::RefPtr<Kernel::Inode>);
|
||||||
~LoadableELF();
|
~LoadableELF();
|
||||||
|
|
||||||
Kernel::vaddr_t entry_point() const;
|
Kernel::vaddr_t entry_point() const;
|
||||||
|
|
||||||
|
bool has_interpreter() const { return !!m_interpreter.inode; }
|
||||||
|
BAN::RefPtr<Kernel::Inode> inode() { return m_executable.inode; }
|
||||||
|
|
||||||
bool contains(Kernel::vaddr_t address) const;
|
bool contains(Kernel::vaddr_t address) const;
|
||||||
bool is_address_space_free() const;
|
bool is_address_space_free() const;
|
||||||
void reserve_address_space();
|
void reserve_address_space();
|
||||||
|
@ -41,17 +44,36 @@ namespace LibELF
|
||||||
size_t physical_page_count() const { return m_physical_page_count; }
|
size_t physical_page_count() const { return m_physical_page_count; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LoadableELF(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
|
struct LoadableElfFile
|
||||||
BAN::ErrorOr<void> initialize();
|
{
|
||||||
|
BAN::RefPtr<Kernel::Inode> inode;
|
||||||
|
ElfNativeFileHeader file_header;
|
||||||
|
BAN::Vector<ElfNativeProgramHeader> program_headers;
|
||||||
|
Kernel::vaddr_t dynamic_base;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LoadResult
|
||||||
|
{
|
||||||
|
LoadableElfFile elf_file;
|
||||||
|
BAN::RefPtr<Kernel::Inode> interp;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BAN::RefPtr<Kernel::Inode> m_inode;
|
LoadableELF(Kernel::PageTable&);
|
||||||
|
BAN::ErrorOr<void> initialize(const Kernel::Credentials&, BAN::RefPtr<Kernel::Inode>);
|
||||||
|
|
||||||
|
bool does_executable_and_interpreter_overlap() const;
|
||||||
|
BAN::ErrorOr<LoadResult> load_elf_file(const Kernel::Credentials&, BAN::RefPtr<Kernel::Inode>) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
LoadableElfFile m_executable;
|
||||||
|
LoadableElfFile m_interpreter;
|
||||||
Kernel::PageTable& m_page_table;
|
Kernel::PageTable& m_page_table;
|
||||||
ElfNativeFileHeader m_file_header;
|
size_t m_virtual_page_count { 0 };
|
||||||
BAN::Vector<ElfNativeProgramHeader> m_program_headers;
|
size_t m_physical_page_count { 0 };
|
||||||
size_t m_virtual_page_count = 0;
|
bool m_is_loaded { false };
|
||||||
size_t m_physical_page_count = 0;
|
|
||||||
bool m_loaded { false };
|
friend class BAN::UniqPtr<LoadableELF>;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue