From 079df39ca85ec7ddcc6667d2cbc1271e1632682c Mon Sep 17 00:00:00 2001 From: Bananymous Date: Wed, 12 Apr 2023 22:20:18 +0300 Subject: [PATCH] LibELF: Start implementing elf library --- CMakeLists.txt | 3 + LibELF/CMakeLists.txt | 12 ++ LibELF/LibELF/ELF.cpp | 189 ++++++++++++++++++++++++++++++++ LibELF/include/LibELF/ELF.h | 35 ++++++ LibELF/include/LibELF/Structs.h | 0 LibELF/include/LibELF/Types.h | 83 ++++++++++++++ LibELF/include/LibELF/Values.h | 140 +++++++++++++++++++++++ kernel/CMakeLists.txt | 5 + kernel/kernel/kernel.cpp | 14 ++- userspace/test.c | 5 + 10 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 LibELF/CMakeLists.txt create mode 100644 LibELF/LibELF/ELF.cpp create mode 100644 LibELF/include/LibELF/ELF.h create mode 100644 LibELF/include/LibELF/Structs.h create mode 100644 LibELF/include/LibELF/Types.h create mode 100644 LibELF/include/LibELF/Values.h create mode 100644 userspace/test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 830dec19..cce014cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ set(DISK_IMAGE_PATH ${CMAKE_BINARY_DIR}/banan-os.img) add_subdirectory(kernel) add_subdirectory(BAN) add_subdirectory(libc) +add_subdirectory(LibELF) add_custom_target(sysroot COMMAND mkdir -p ${BANAN_SYSROOT} @@ -41,6 +42,7 @@ add_custom_target(headers DEPENDS kernel-headers DEPENDS ban-headers DEPENDS libc-headers + DEPENDS libelf-headers ) add_custom_target(toolchain @@ -50,6 +52,7 @@ add_custom_target(toolchain ) add_custom_target(image + COMMAND ${CMAKE_CXX_COMPILER} -x c ${CMAKE_SOURCE_DIR}/userspace/test.c -o ${BANAN_SYSROOT}/bin/test COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/image.sh DEPENDS kernel-install DEPENDS ban-install diff --git a/LibELF/CMakeLists.txt b/LibELF/CMakeLists.txt new file mode 100644 index 00000000..01904ba1 --- /dev/null +++ b/LibELF/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.26) + +project(LibELF CXX) + +add_custom_target(libelf-headers + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include ${BANAN_INCLUDE} + DEPENDS sysroot +) + +add_custom_target(libelf-install + DEPENDS libelf-headers +) diff --git a/LibELF/LibELF/ELF.cpp b/LibELF/LibELF/ELF.cpp new file mode 100644 index 00000000..390e8bbd --- /dev/null +++ b/LibELF/LibELF/ELF.cpp @@ -0,0 +1,189 @@ +#include +#include +#include +#include + +#include + +namespace LibELF +{ + + BAN::ErrorOr ELF::load_from_file(BAN::StringView file_path) + { + ELF* elf = nullptr; + + { + BAN::Vector data; + + int fd = TRY(Kernel::Process::current()->open(file_path, O_RDONLY)); + BAN::ScopeGuard _([fd] { MUST(Kernel::Process::current()->close(fd)); }); + + 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; + } + + const char* ELF::lookup_section_name(uint32_t offset) const + { + return lookup_string(file_header64().e_shstrndx, offset); + } + + const char* ELF::lookup_string(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_section_header(const Elf64SectionHeader& header) + { + if (auto* name = lookup_section_name(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_string(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); + } + + return true; + } + + BAN::ErrorOr ELF::load() + { + if (m_data.size() < EI_NIDENT) + { + dprintln("Too small ELF file"); + return BAN::Error::from_errno(EINVAL); + } + + if (m_data[EI_MAG0] != ELFMAG0 || + m_data[EI_MAG1] != ELFMAG1 || + m_data[EI_MAG2] != ELFMAG2 || + m_data[EI_MAG3] != ELFMAG3) + { + dprintln("Invalid ELF header"); + return BAN::Error::from_errno(EINVAL); + } + + if (m_data[EI_DATA] != ELFDATA2LSB) + { + dprintln("Only little-endian is supported"); + return BAN::Error::from_errno(EINVAL); + } + + if (m_data[EI_VERSION] != EV_CURRENT) + { + dprintln("Invalid ELF version"); + return BAN::Error::from_errno(EINVAL); + } + + if (m_data[EI_CLASS] != ELFCLASS64) + { + dprintln("Only 64 bit is supported"); + return BAN::Error::from_errno(EINVAL); + } + + 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 = 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); + } + + return {}; + } + + + const Elf64FileHeader& ELF::file_header64() const + { + return *(const Elf64FileHeader*)m_data.data(); + } + + const Elf64ProgramHeader& ELF::program_header64(size_t index) const + { + return ((const Elf64ProgramHeader*)(m_data.data() + file_header64().e_phoff))[index]; + } + + const Elf64SectionHeader& ELF::section_header64(size_t index) const + { + return ((const Elf64SectionHeader*)(m_data.data() + file_header64().e_shoff))[index]; + } + + +} diff --git a/LibELF/include/LibELF/ELF.h b/LibELF/include/LibELF/ELF.h new file mode 100644 index 00000000..7aa335c8 --- /dev/null +++ b/LibELF/include/LibELF/ELF.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include "Types.h" + +namespace LibELF +{ + + class ELF + { + public: + static BAN::ErrorOr load_from_file(BAN::StringView); + + const Elf64FileHeader& file_header64() const; + const Elf64ProgramHeader& program_header64(size_t) const; + const Elf64SectionHeader& section_header64(size_t) const; + + const char* lookup_section_name(uint32_t) const; + const char* lookup_string(size_t, uint32_t) const; + + private: + ELF(BAN::Vector&& data) + : m_data(BAN::move(data)) + {} + BAN::ErrorOr load(); + + bool parse_elf64_file_header(const Elf64FileHeader&); + bool parse_elf64_section_header(const Elf64SectionHeader&); + + private: + const BAN::Vector m_data; + }; + +} \ No newline at end of file diff --git a/LibELF/include/LibELF/Structs.h b/LibELF/include/LibELF/Structs.h new file mode 100644 index 00000000..e69de29b diff --git a/LibELF/include/LibELF/Types.h b/LibELF/include/LibELF/Types.h new file mode 100644 index 00000000..bc72c4aa --- /dev/null +++ b/LibELF/include/LibELF/Types.h @@ -0,0 +1,83 @@ +#pragma once + +#include + +namespace LibELF +{ + + using Elf64Addr = uint64_t; + using Elf64Off = uint64_t; + using Elf64Half = uint16_t; + using Elf64Word = uint32_t; + using Elf64Sword = int32_t; + using Elf64Xword = uint64_t; + using Elf64Sxword = int64_t; + + struct Elf64FileHeader + { + unsigned char e_ident[16]; + Elf64Half e_type; + Elf64Half e_machine; + Elf64Word e_version; + Elf64Addr e_entry; + Elf64Off e_phoff; + Elf64Off e_shoff; + Elf64Word e_flags; + Elf64Half e_ehsize; + Elf64Half e_phentsize; + Elf64Half e_phnum; + Elf64Half e_shentsize; + Elf64Half e_shnum; + Elf64Half e_shstrndx; + }; + + struct Elf64SectionHeader + { + Elf64Word sh_name; + Elf64Word sh_type; + Elf64Xword sh_flags; + Elf64Addr sh_addr; + Elf64Off sh_offset; + Elf64Xword sh_size; + Elf64Word sh_link; + Elf64Word sh_info; + Elf64Xword sh_addralign; + Elf64Xword sh_entsize; + }; + + struct Elf64Symbol + { + Elf64Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf64Half st_shndx; + Elf64Addr st_value; + Elf64Xword st_size; + }; + + struct Elf64Relocation + { + Elf64Addr r_offset; + Elf64Xword r_info; + }; + + struct Elf64RelocationA + { + Elf64Addr r_offset; + Elf64Xword r_info; + Elf64Sxword r_addend; + }; + + struct Elf64ProgramHeader + { + Elf64Word p_type; + Elf64Word p_flags; + Elf64Off p_offset; + Elf64Addr p_vaddr; + Elf64Addr p_paddr; + Elf64Xword p_filesz; + Elf64Xword p_memsz; + Elf64Xword p_align; + }; + +} \ No newline at end of file diff --git a/LibELF/include/LibELF/Values.h b/LibELF/include/LibELF/Values.h new file mode 100644 index 00000000..e2ec5836 --- /dev/null +++ b/LibELF/include/LibELF/Values.h @@ -0,0 +1,140 @@ +#pragma once + +namespace LibELF +{ + + enum ELF_Ident + { + ELFMAG0 = 0x7F, + ELFMAG1 = 'E', + ELFMAG2 = 'L', + ELFMAG3 = 'F', + + ELFCLASSNONE = 0, + ELFCLASS32 = 1, + ELFCLASS64 = 2, + + ELFDATANONE = 0, + ELFDATA2LSB = 1, + ELFDATA2MSB = 2, + }; + + enum ELF_EI + { + EI_MAG0 = 0, + EI_MAG1 = 1, + EI_MAG2 = 2, + EI_MAG3 = 3, + EI_CLASS = 4, + EI_DATA = 5, + EI_VERSION = 6, + EI_OSABI = 7, + EI_ABIVERSION = 8, + EI_NIDENT = 16, + }; + + enum ELF_ET + { + ET_NONE = 0, + ET_REL = 1, + ET_EXEC = 2, + ET_DYN = 3, + ET_CORE = 4, + ET_LOOS = 0xfe00, + ET_HIOS = 0xfeff, + ET_LOPROC = 0xff00, + ET_HIPROC = 0xffff, + }; + + enum ELF_EV + { + EV_NONE = 0, + EV_CURRENT = 1, + }; + + enum ELF_SHT + { + SHT_NULL = 0, + SHT_PROGBITS = 1, + SHT_SYMTAB = 2, + SHT_STRTAB = 3, + SHT_RELA = 4, + SHT_NOBITS = 8, + SHT_REL = 9, + SHT_SHLIB = 10, + SHT_DYNSYM = 11, + SHT_LOOS = 0x60000000, + SHT_HIOS = 0x6FFFFFFF, + SHT_LOPROC = 0x70000000, + SHT_HIPROC = 0x7FFFFFFF, + }; + + enum ELF_SHF + { + SHF_WRITE = 0x1, + SHF_ALLOC = 0x2, + SHF_EXECINSTR = 0x4, + SHF_MASKOS = 0x0F000000, + SHF_MASKPROC = 0xF0000000, + }; + + enum ELF_SHN + { + SHN_UNDEF = 0, + SHN_LOPROC = 0xFF00, + SHN_HIPROC = 0xFF1F, + SHN_LOOS = 0xFF20, + SHN_HIOS = 0xFF3F, + SHN_ABS = 0xFFF1, + SHN_COMMON = 0xFFF2, + }; + + enum ELF_STB + { + STB_LOCAL = 0, + STB_GLOBAL = 1, + STB_WEAK = 2, + STB_LOOS = 10, + STB_HIOS = 12, + STB_LOPROC = 13, + STB_HIPROC = 15, + }; + + enum ELF_STT + { + STT_NOTYPE = 0, + STT_OBJECT = 1, + STT_FUNC = 2, + STT_SECTION = 3, + STT_FILE = 4, + STT_LOOS = 10, + STT_HIOS = 12, + STT_LOPROC = 13, + STT_HIPROC = 15, + }; + + enum ELF_PT + { + PT_NULL = 0, + PT_LOAD = 1, + PT_DYNAMIC = 2, + PT_INTERP = 3, + PT_NOTE = 4, + PT_SHLIB = 5, + PT_PHDR = 6, + PT_LOOS = 0x60000000, + PT_HIOS = 0x6FFFFFFF, + PT_LOPROC = 0x70000000, + PT_HIPROC = 0x7FFFFFFF, + }; + + enum ELF_PF + { + PF_X = 0x1, + PF_W = 0x2, + PF_R = 0x4, + PF_MASKOS = 0x00FF0000, + PF_MASKPROC = 0xFF000000, + }; + +} \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index cea44e17..60e964a9 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -93,10 +93,15 @@ set(LIBC_SOURCES ../libc/string.cpp ) +set(LIBELF_SOURCES + ../LibELF/LibELF/ELF.cpp +) + set(KERNEL_SOURCES ${KERNEL_SOURCES} ${BAN_SOURCES} ${LIBC_SOURCES} + ${LIBELF_SOURCES} ) add_executable(kernel ${KERNEL_SOURCES}) diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index 6c2ffb2c..12f460a2 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -22,6 +22,8 @@ #include #include +#include + extern "C" const char g_kernel_cmdline[]; struct ParsedCommandLine @@ -177,7 +179,17 @@ static void init2(void* terminal_driver) ASSERT(tty1); DeviceManager::get().add_device(tty1); - return jump_userspace(); + MUST(Process::create_kernel( + [](void*) + { + MUST(LibELF::ELF::load_from_file("/bin/test"sv)); + Process::current()->exit(); + }, nullptr + )); + return; + + jump_userspace(); + return; MUST(Process::create_kernel( [](void*) diff --git a/userspace/test.c b/userspace/test.c new file mode 100644 index 00000000..ffacf6e6 --- /dev/null +++ b/userspace/test.c @@ -0,0 +1,5 @@ +int main() +{ + return 0; +} +