Compare commits

...

6 Commits

Author SHA1 Message Date
Bananymous 3bdcd8f1fb 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.
2024-09-15 23:20:32 +03:00
Bananymous 54732edff4 Kernel: Fix bugs in memory regions
MemoryRegion::initialize() required size to be page aligned, this is not
necessary.

MemoryBackedRegion::copy_data_to_region() required user-write
permissions for underlying data. This did not matter as memory backed
regions dont support COW memory, but it could lead to bugs later on.
2024-09-15 23:18:05 +03:00
Bananymous 1a24d1839f LibInput: KeyEvent to ansi string now handles delete key
This allows Terminal emulator to also handle delete key.

The value of `\0x7F` may be wrong as qemu's serial console seems to be
sending `\e[3~`
2024-09-15 03:13:21 +03:00
Bananymous c3040a04a3 Terminal: Optimize printing a lot
Terminal used to run `yes` at around 400 lines per second

This patch pumps that to over 100'000 lines per second!

There are 2 optimizations done:
  1. only invalidate window once after rendering is done
  2. if printing more than `rows()` newlines skip prior data
2024-09-15 03:12:26 +03:00
Bananymous 7feb4c4ebd Kernel: VFS::file_from_absolute_path now used root_file() API 2024-09-15 02:38:25 +03:00
Bananymous 2911d1f018 Kernel: Cleanup and fix pseudo terminals 2024-09-15 02:38:07 +03:00
15 changed files with 153 additions and 1099 deletions

View File

@ -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}
)

View File

@ -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:

View File

@ -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;

View File

@ -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 };

View File

@ -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);
});

View File

@ -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 {};
}

View File

@ -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();

View File

@ -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()

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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
};
}

View File

@ -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>;
};
}

View File

@ -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", "@", "£", "$", "",

View File

@ -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)

View File

@ -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();