Compare commits
6 Commits
d68ad893f0
...
3bdcd8f1fb
Author | SHA1 | Date |
---|---|---|
Bananymous | 3bdcd8f1fb | |
Bananymous | 54732edff4 | |
Bananymous | 1a24d1839f | |
Bananymous | c3040a04a3 | |
Bananymous | 7feb4c4ebd | |
Bananymous | 2911d1f018 |
|
@ -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}
|
||||
)
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace Kernel
|
|||
BAN::ErrorOr<File> file_from_relative_path(const File& parent, const Credentials&, BAN::StringView, int);
|
||||
BAN::ErrorOr<File> file_from_absolute_path(const Credentials& credentials, BAN::StringView path, int flags)
|
||||
{
|
||||
return file_from_relative_path(File(root_inode()), credentials, path, flags);
|
||||
return file_from_relative_path(root_file(), credentials, path, flags);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -51,8 +51,6 @@ namespace Kernel
|
|||
class PseudoTerminalSlave final : public TTY, public BAN::Weakable<PseudoTerminalSlave>
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<PseudoTerminalSlave>> create(BAN::String&& name, mode_t, uid_t, gid_t);
|
||||
|
||||
BAN::StringView name() const override { return m_name; }
|
||||
|
||||
uint32_t height() const override { return m_height; }
|
||||
|
@ -64,16 +62,12 @@ namespace Kernel
|
|||
void putchar_impl(uint8_t ch) override;
|
||||
|
||||
private:
|
||||
PseudoTerminalSlave(BAN::UniqPtr<VirtualRange>, BAN::String&& name, mode_t, uid_t, gid_t);
|
||||
PseudoTerminalSlave(BAN::String&& name, mode_t, uid_t, gid_t);
|
||||
|
||||
private:
|
||||
BAN::String m_name;
|
||||
|
||||
BAN::WeakPtr<PseudoTerminalMaster> m_master;
|
||||
BAN::UniqPtr<VirtualRange> m_buffer;
|
||||
size_t m_buffer_tail { 0 };
|
||||
size_t m_buffer_size { 0 };
|
||||
|
||||
uint32_t m_width { 0 };
|
||||
uint32_t m_height { 0 };
|
||||
|
||||
|
|
|
@ -94,9 +94,19 @@ namespace Kernel
|
|||
vaddr_t page_offset = write_vaddr % PAGE_SIZE;
|
||||
size_t bytes = BAN::Math::min<size_t>(buffer_size - written, PAGE_SIZE - page_offset);
|
||||
|
||||
TRY(allocate_page_containing(write_vaddr, true));
|
||||
paddr_t paddr = m_page_table.physical_address_of(write_vaddr & PAGE_ADDR_MASK);
|
||||
if (paddr == 0)
|
||||
{
|
||||
if (!TRY(allocate_page_containing(write_vaddr, false)))
|
||||
{
|
||||
dwarnln("Could not allocate page for data copying");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
paddr = m_page_table.physical_address_of(write_vaddr & PAGE_ADDR_MASK);
|
||||
ASSERT(paddr);
|
||||
}
|
||||
|
||||
PageTable::with_fast_page(m_page_table.physical_address_of(write_vaddr & PAGE_ADDR_MASK), [&] {
|
||||
PageTable::with_fast_page(paddr, [&] {
|
||||
memcpy(PageTable::fast_page_as_ptr(page_offset), (void*)(buffer + written), bytes);
|
||||
});
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace Kernel
|
|||
m_vaddr = m_page_table.reserve_free_contiguous_pages(needed_pages, address_range.start);
|
||||
if (m_vaddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
if (m_vaddr + needed_pages * PAGE_SIZE > address_range.end)
|
||||
if (m_vaddr + m_size > address_range.end)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -20,14 +20,8 @@ namespace Kernel
|
|||
));
|
||||
auto pts_master = TRY(BAN::RefPtr<PseudoTerminalMaster>::create(BAN::move(pts_master_buffer), mode, uid, gid));
|
||||
|
||||
auto pts_slave_buffer = TRY(VirtualRange::create_to_vaddr_range(
|
||||
PageTable::kernel(),
|
||||
KERNEL_OFFSET, static_cast<vaddr_t>(-1),
|
||||
16 * PAGE_SIZE,
|
||||
PageTable::Flags::ReadWrite | PageTable::Flags::Present, true
|
||||
));
|
||||
auto pts_slave_name = TRY(BAN::String::formatted("pts{}", s_pts_slave_number++));
|
||||
auto pts_slave = TRY(BAN::RefPtr<PseudoTerminalSlave>::create(BAN::move(pts_slave_buffer), BAN::move(pts_slave_name), 0610, uid, gid));
|
||||
auto pts_slave = TRY(BAN::RefPtr<PseudoTerminalSlave>::create(BAN::move(pts_slave_name), 0610, uid, gid));
|
||||
|
||||
pts_master->m_slave = TRY(pts_slave->get_weak_ptr());
|
||||
pts_slave->m_master = TRY(pts_master->get_weak_ptr());
|
||||
|
@ -67,15 +61,12 @@ namespace Kernel
|
|||
{
|
||||
SpinLockGuard _(m_buffer_lock);
|
||||
|
||||
if (m_buffer_size == m_buffer->size())
|
||||
{
|
||||
dwarnln("PseudoTerminalMaster buffer full");
|
||||
m_buffer_tail = (m_buffer_tail + 1) % m_buffer->size();
|
||||
m_buffer_size--;
|
||||
}
|
||||
reinterpret_cast<uint8_t*>(m_buffer->vaddr())[(m_buffer_tail + m_buffer_size) % m_buffer->size()] = ch;
|
||||
|
||||
*reinterpret_cast<uint8_t*>(m_buffer->vaddr() + (m_buffer_tail + m_buffer_size) % m_buffer->size()) = ch;
|
||||
m_buffer_size++;
|
||||
if (m_buffer_size < m_buffer->size())
|
||||
m_buffer_size++;
|
||||
else
|
||||
m_buffer_tail = (m_buffer_tail + 1) % m_buffer->size();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> PseudoTerminalMaster::read_impl(off_t, BAN::ByteSpan buffer)
|
||||
|
@ -91,11 +82,11 @@ namespace Kernel
|
|||
|
||||
const size_t to_copy = BAN::Math::min(buffer.size(), m_buffer_size);
|
||||
|
||||
if (m_buffer_tail + to_copy < m_buffer->size())
|
||||
if (m_buffer_tail + to_copy <= m_buffer->size())
|
||||
memcpy(buffer.data(), reinterpret_cast<void*>(m_buffer->vaddr() + m_buffer_tail), to_copy);
|
||||
else
|
||||
{
|
||||
const size_t before_wrap = m_buffer_size - m_buffer_tail;
|
||||
const size_t before_wrap = m_buffer->size() - m_buffer_tail;
|
||||
const size_t after_wrap = to_copy - before_wrap;
|
||||
|
||||
memcpy(buffer.data(), reinterpret_cast<void*>(m_buffer->vaddr() + m_buffer_tail), before_wrap);
|
||||
|
@ -121,10 +112,9 @@ namespace Kernel
|
|||
return buffer.size();
|
||||
}
|
||||
|
||||
PseudoTerminalSlave::PseudoTerminalSlave(BAN::UniqPtr<VirtualRange> buffer, BAN::String&& name, mode_t mode, uid_t uid, gid_t gid)
|
||||
PseudoTerminalSlave::PseudoTerminalSlave(BAN::String&& name, mode_t mode, uid_t uid, gid_t gid)
|
||||
: TTY(mode, uid, gid)
|
||||
, m_name(BAN::move(name))
|
||||
, m_buffer(BAN::move(buffer))
|
||||
{}
|
||||
|
||||
void PseudoTerminalSlave::clear()
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
#include <BAN/Array.h>
|
||||
#include <LibInput/KeyEvent.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace LibInput
|
||||
|
@ -59,7 +58,7 @@ namespace LibInput
|
|||
"å", "ä", "ö",
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
/*"Insert", "PrintScreen", "Delete", "Home", "End", "PageUp", "PageDown",*/ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "\n", " ",
|
||||
/*"Insert", "PrintScreen", "Delete", "Home", "End", "PageUp", "PageDown",*/ nullptr, nullptr, "\x7F", nullptr, nullptr, nullptr, nullptr, "\n", " ",
|
||||
"!", "\"", "#", "¤", "%", "&", "/", "§", "½",
|
||||
"(", ")", "[", "]", "{", "}",
|
||||
"=", "?", "+", "\\", "´", "`", "¨", "¸", "\b \b", "@", "£", "$", "€",
|
||||
|
@ -78,7 +77,7 @@ namespace LibInput
|
|||
"Å", "Ä", "Ö",
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
/*"Insert", "PrintScreen", "Delete", "Home", "End", "PageUp", "PageDown",*/ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "\n", " ",
|
||||
/*"Insert", "PrintScreen", "Delete", "Home", "End", "PageUp", "PageDown",*/ nullptr, nullptr, "\x7F", nullptr, nullptr, nullptr, nullptr, "\n", " ",
|
||||
"!", "\"", "#", "¤", "%", "&", "/", "§", "½",
|
||||
"(", ")", "[", "]", "{", "}",
|
||||
"=", "?", "+", "\\", "´", "`", "¨", "¸", "\b \b", "@", "£", "$", "€",
|
||||
|
@ -97,7 +96,7 @@ namespace LibInput
|
|||
"Å", "Ä", "Ö",
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
/*"Insert", "PrintScreen", "Delete", "Home", "End", "PageUp", "PageDown",*/ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "\n", " ",
|
||||
/*"Insert", "PrintScreen", "Delete", "Home", "End", "PageUp", "PageDown",*/ nullptr, nullptr, "\x7F", nullptr, nullptr, nullptr, nullptr, "\n", " ",
|
||||
"!", "\"", "#", "¤", "%", "&", "/", "§", "½",
|
||||
"(", ")", "[", "]", "{", "}",
|
||||
"=", "?", "+", "\\", "´", "`", "¨", "¸", "\b \b", "@", "£", "$", "€",
|
||||
|
|
|
@ -196,14 +196,69 @@ void Terminal::show_cursor()
|
|||
|
||||
bool Terminal::read_shell()
|
||||
{
|
||||
char buffer[128];
|
||||
ssize_t nread = read(m_shell_info.pts_master, buffer, sizeof(buffer) - 1);
|
||||
char buffer[512];
|
||||
ssize_t nread = read(m_shell_info.pts_master, buffer, sizeof(buffer));
|
||||
if (nread < 0)
|
||||
dwarnln("read: {}", strerror(errno));
|
||||
if (nread <= 0)
|
||||
return false;
|
||||
for (ssize_t i = 0; i < nread; i++)
|
||||
putchar(buffer[i]);
|
||||
|
||||
Rectangle should_invalidate;
|
||||
|
||||
ssize_t i = 0;
|
||||
while (i < nread)
|
||||
{
|
||||
// all ansi escape codes must be handled
|
||||
if (buffer[i] == '\e')
|
||||
{
|
||||
while (i < nread)
|
||||
{
|
||||
char ch = buffer[i++];
|
||||
should_invalidate = should_invalidate.get_bounding_box(putchar(ch));
|
||||
if (isalpha(ch))
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// find the next ansi escape code or end of buffer
|
||||
size_t non_ansi_end = i;
|
||||
while (non_ansi_end < nread && buffer[non_ansi_end] != '\e')
|
||||
non_ansi_end++;
|
||||
|
||||
// we only need to process maximum of `rows()` newlines.
|
||||
// anything before that would get overwritten anyway
|
||||
size_t start = non_ansi_end;
|
||||
size_t newline_count = 0;
|
||||
while (start > i && newline_count < rows())
|
||||
newline_count += (buffer[--start] == '\n');
|
||||
|
||||
// do possible scrolling already in here, so `putchar()` doesnt
|
||||
// have to scroll up to `rows()` times
|
||||
if (m_cursor.y + newline_count >= rows())
|
||||
{
|
||||
const uint32_t scroll = m_cursor.y + newline_count - rows() + 1;
|
||||
m_cursor.y -= scroll;
|
||||
m_window->shift_vertical(-scroll * (int32_t)m_font.height());
|
||||
m_window->fill_rect(0, m_window->height() - scroll * m_font.height(), m_window->width(), scroll * m_font.height(), m_bg_color);
|
||||
should_invalidate = { 0, 0, m_window->width(), m_window->height() };
|
||||
}
|
||||
|
||||
i = start;
|
||||
for (i = start; i < non_ansi_end; i++)
|
||||
should_invalidate = should_invalidate.get_bounding_box(putchar(buffer[i]));
|
||||
}
|
||||
|
||||
if (should_invalidate.height && should_invalidate.width)
|
||||
{
|
||||
m_window->invalidate(
|
||||
should_invalidate.x,
|
||||
should_invalidate.y,
|
||||
should_invalidate.width,
|
||||
should_invalidate.height
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -361,7 +416,7 @@ void Terminal::handle_csi(char ch)
|
|||
m_state = State::Normal;
|
||||
}
|
||||
|
||||
void Terminal::putchar(uint8_t ch)
|
||||
Rectangle Terminal::putchar(uint8_t ch)
|
||||
{
|
||||
if (m_state == State::ESC)
|
||||
{
|
||||
|
@ -369,14 +424,14 @@ void Terminal::putchar(uint8_t ch)
|
|||
{
|
||||
dprintln("unknown escape character 0x{2H}", ch);
|
||||
m_state = State::Normal;
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
m_state = State::CSI;
|
||||
m_csi_info.index = 0;
|
||||
m_csi_info.fields[0] = -1;
|
||||
m_csi_info.fields[1] = -1;
|
||||
m_csi_info.question = false;
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (m_state == State::CSI)
|
||||
|
@ -385,10 +440,10 @@ void Terminal::putchar(uint8_t ch)
|
|||
{
|
||||
dprintln("invalid CSI 0x{2H}", ch);
|
||||
m_state = State::Normal;
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
handle_csi(ch);
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
m_utf8_bytes[m_utf8_index++] = ch;
|
||||
|
@ -398,10 +453,10 @@ void Terminal::putchar(uint8_t ch)
|
|||
{
|
||||
dwarnln("invalid utf8 leading byte 0x{2H}", ch);
|
||||
m_utf8_index = 0;
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
if (m_utf8_index < utf8_len)
|
||||
return;
|
||||
return {};
|
||||
|
||||
const uint32_t codepoint = BAN::UTF8::to_codepoint(m_utf8_bytes);
|
||||
m_utf8_index = 0;
|
||||
|
@ -421,9 +476,11 @@ void Terminal::putchar(uint8_t ch)
|
|||
*--ptr = '\0';
|
||||
|
||||
dwarnln("invalid utf8 {}", utf8_hex);
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
Rectangle should_invalidate;
|
||||
|
||||
switch (codepoint)
|
||||
{
|
||||
case '\e':
|
||||
|
@ -449,7 +506,7 @@ void Terminal::putchar(uint8_t ch)
|
|||
|
||||
m_window->fill_rect(cell_x, cell_y, cell_w, cell_h, m_bg_color);
|
||||
m_window->draw_character(codepoint, m_font, cell_x, cell_y, m_fg_color);
|
||||
m_window->invalidate(cell_x, cell_y, cell_w, cell_h);
|
||||
should_invalidate = { cell_x, cell_y, cell_w, cell_h };
|
||||
m_cursor.x++;
|
||||
break;
|
||||
}
|
||||
|
@ -461,14 +518,10 @@ void Terminal::putchar(uint8_t ch)
|
|||
m_cursor.y++;
|
||||
}
|
||||
|
||||
if (m_cursor.y >= rows())
|
||||
{
|
||||
uint32_t scroll = m_cursor.y - rows() + 1;
|
||||
m_cursor.y -= scroll;
|
||||
m_window->shift_vertical(-scroll * (int32_t)m_font.height());
|
||||
m_window->fill_rect(0, m_window->height() - scroll * m_font.height(), m_window->width(), scroll * m_font.height(), m_bg_color);
|
||||
m_window->invalidate();
|
||||
}
|
||||
// scrolling is already handled in `read_shell()`
|
||||
ASSERT(m_cursor.y < rows());
|
||||
|
||||
return should_invalidate;
|
||||
}
|
||||
|
||||
void Terminal::on_key_event(LibGUI::EventPacket::KeyEvent event)
|
||||
|
|
|
@ -4,6 +4,28 @@
|
|||
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
struct Rectangle
|
||||
{
|
||||
uint32_t x { 0 };
|
||||
uint32_t y { 0 };
|
||||
uint32_t width { 0 };
|
||||
uint32_t height { 0 };
|
||||
|
||||
Rectangle get_bounding_box(Rectangle other) const
|
||||
{
|
||||
const auto min_x = BAN::Math::min(x, other.x);
|
||||
const auto min_y = BAN::Math::min(y, other.y);
|
||||
const auto max_x = BAN::Math::max(x + width, other.x + other.width);
|
||||
const auto max_y = BAN::Math::max(y + height, other.y + other.height);
|
||||
return Rectangle {
|
||||
.x = min_x,
|
||||
.y = min_y,
|
||||
.width = max_x - min_x,
|
||||
.height = max_y - min_y,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class Terminal
|
||||
{
|
||||
public:
|
||||
|
@ -15,7 +37,7 @@ public:
|
|||
private:
|
||||
void handle_csi(char ch);
|
||||
void handle_sgr();
|
||||
void putchar(uint8_t ch);
|
||||
Rectangle putchar(uint8_t ch);
|
||||
bool read_shell();
|
||||
|
||||
void hide_cursor();
|
||||
|
|
Loading…
Reference in New Issue