From 363d30eadc76e580cbe6050de0977fdf98b1e2c4 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sat, 31 Jul 2021 15:10:03 -0700 Subject: [PATCH] [elf] Ressurect elf library Resurrect the existing but unused ELF library in libraries/elf, and use it instead of boot/elf.h for parsing ELF files in the bootloader. Also adds a const version of offset_iterator called const_offset_iterator. --- src/boot/elf.h | 83 ---------------------------- src/boot/loader.cpp | 60 ++++++-------------- src/boot/module.toml | 2 +- src/include/pointer_manipulation.h | 57 ++++++++++++++----- src/libraries/elf/elf.cpp | 50 ----------------- src/libraries/elf/file.cpp | 45 +++++++++++++++ src/libraries/elf/include/elf/elf.h | 61 -------------------- src/libraries/elf/include/elf/file.h | 75 +++++++++++++++++++++++++ src/libraries/elf/module.toml | 6 ++ 9 files changed, 189 insertions(+), 250 deletions(-) delete mode 100644 src/boot/elf.h delete mode 100644 src/libraries/elf/elf.cpp create mode 100644 src/libraries/elf/file.cpp delete mode 100644 src/libraries/elf/include/elf/elf.h create mode 100644 src/libraries/elf/include/elf/file.h create mode 100644 src/libraries/elf/module.toml diff --git a/src/boot/elf.h b/src/boot/elf.h deleted file mode 100644 index 2ca071a..0000000 --- a/src/boot/elf.h +++ /dev/null @@ -1,83 +0,0 @@ -/// \file elf.h -/// Definitions and related constants for ELF64 structures -#pragma once -#include - -namespace boot { -namespace elf { - -constexpr uint8_t version = 1; -constexpr uint8_t word_size = 2; -constexpr uint8_t endianness = 1; -constexpr uint8_t os_abi = 0; -constexpr uint16_t machine = 0x3e; - -const unsigned PT_LOAD = 1; -const unsigned ST_PROGBITS = 1; -const unsigned ST_NOBITS = 8; -const unsigned long SHF_ALLOC = 0x2; - -struct header -{ - char magic[4]; - - uint8_t word_size; - uint8_t endianness; - uint8_t header_version; - uint8_t os_abi; - - uint64_t reserved; - - uint16_t type; - uint16_t machine; - - uint32_t version; - - uint64_t entrypoint; - uint64_t ph_offset; - uint64_t sh_offset; - - uint32_t flags; - - uint16_t eh_size; - - uint16_t ph_entsize; - uint16_t ph_num; - - uint16_t sh_entsize; - uint16_t sh_num; - - uint16_t sh_str_idx; -} __attribute__ ((packed)); - -struct program_header -{ - uint32_t type; - uint32_t flags; - uint64_t offset; - - uint64_t vaddr; - uint64_t paddr; - - uint64_t file_size; - uint64_t mem_size; - - uint64_t align; -} __attribute__ ((packed)); - -struct section_header -{ - uint32_t name; - uint32_t type; - uint64_t flags; - uint64_t addr; - uint64_t offset; - uint64_t size; - uint32_t link; - uint32_t info; - uint64_t align; - uint64_t entry_size; -} __attribute__ ((packed)); - -} // namespace elf -} // namespace boot diff --git a/src/boot/loader.cpp b/src/boot/loader.cpp index c1c9286..aaec5f5 100644 --- a/src/boot/loader.cpp +++ b/src/boot/loader.cpp @@ -3,7 +3,8 @@ #include "allocator.h" #include "console.h" -#include "elf.h" +#include "elf/file.h" +#include "elf/headers.h" #include "error.h" #include "fs.h" #include "init_args.h" @@ -35,21 +36,6 @@ load_file( } -static bool -is_elfheader_valid(const elf::header *header) -{ - return - header->magic[0] == 0x7f && - header->magic[1] == 'E' && - header->magic[2] == 'L' && - header->magic[3] == 'F' && - header->word_size == elf::word_size && - header->endianness == elf::endianness && - header->os_abi == elf::os_abi && - header->machine == elf::machine && - header->header_version == elf::version; -} - static void create_module(buffer data, const program_desc &desc, bool loaded) { @@ -82,55 +68,45 @@ load_program( if (add_module) create_module(data, desc, true); - const elf::header *header = reinterpret_cast(data.pointer); - uintptr_t program_base = reinterpret_cast(data.pointer); - - if (data.count < sizeof(elf::header) || !is_elfheader_valid(header)) + elf::file program(data.pointer, data.count); + if (!program.valid()) error::raise(uefi::status::load_error, L"ELF file not valid"); size_t num_sections = 0; - for (int i = 0; i < header->ph_num; ++i) { - ptrdiff_t offset = header->ph_offset + i * header->ph_entsize; - const elf::program_header *pheader = - offset_ptr(data.pointer, offset); - - if (pheader->type == elf::PT_LOAD) + for (auto &seg : program.programs()) { + if (seg.type == elf::segment_type::load) ++num_sections; } init::program_section *sections = new init::program_section [num_sections]; size_t next_section = 0; - for (int i = 0; i < header->ph_num; ++i) { - ptrdiff_t offset = header->ph_offset + i * header->ph_entsize; - const elf::program_header *pheader = - offset_ptr(data.pointer, offset); - - if (pheader->type != elf::PT_LOAD) + for (auto &seg : program.programs()) { + if (seg.type != elf::segment_type::load) continue; init::program_section §ion = sections[next_section++]; - size_t page_count = memory::bytes_to_pages(pheader->mem_size); + size_t page_count = memory::bytes_to_pages(seg.mem_size); - if (pheader->mem_size > pheader->file_size) { + if (seg.mem_size > seg.file_size) { void *pages = g_alloc.allocate_pages(page_count, alloc_type::program, true); - void *source = offset_ptr(data.pointer, pheader->offset); - g_alloc.copy(pages, source, pheader->file_size); + void *source = offset_ptr(data.pointer, seg.offset); + g_alloc.copy(pages, source, seg.file_size); section.phys_addr = reinterpret_cast(pages); } else { - section.phys_addr = program_base + pheader->offset; + section.phys_addr = program.base() + seg.offset; } - section.virt_addr = pheader->vaddr; - section.size = pheader->mem_size; - section.type = static_cast(pheader->flags); + section.virt_addr = seg.vaddr; + section.size = seg.mem_size; + section.type = static_cast(seg.flags); } init::program *prog = new init::program; prog->sections = { .pointer = sections, .count = num_sections }; - prog->phys_base = program_base; - prog->entrypoint = header->entrypoint; + prog->phys_base = program.base(); + prog->entrypoint = program.entrypoint(); return prog; } diff --git a/src/boot/module.toml b/src/boot/module.toml index 183623f..0b0ff7a 100644 --- a/src/boot/module.toml +++ b/src/boot/module.toml @@ -2,7 +2,7 @@ name = "boot" kind = "exe" output = "boot.efi" targets = ["boot"] -deps = ["cpu", "kutil"] +deps = ["cpu", "elf", "kutil"] sources = [ "allocator.cpp", "console.cpp", diff --git a/src/include/pointer_manipulation.h b/src/include/pointer_manipulation.h index 1dd3711..ae1b1bf 100644 --- a/src/include/pointer_manipulation.h +++ b/src/include/pointer_manipulation.h @@ -10,32 +10,63 @@ inline T* offset_ptr(S* input, ptrdiff_t offset) { return reinterpret_cast(reinterpret_cast(input) + offset); } -/// Iterator for an array of `T` whose size is known at runtime +/// Iterator for an array of `const T` whose size is known at runtime /// \tparam T Type of the objects in the array, whose size might not be /// what is returned by sizeof(T). template -class offset_iterator +class const_offset_iterator { public: /// Constructor. /// \arg t Pointer to the first item in the array /// \arg off Offset applied to reach successive items. Default is 0, /// which creates an effectively constant iterator. - offset_iterator(T* t, size_t off=0) : m_t(t), m_off(off) {} + const_offset_iterator(T const *t, size_t off=0) : m_t(t), m_off(off) {} - T* operator++() { m_t = offset_ptr(m_t, m_off); return m_t; } - T* operator++(int) { T* tmp = m_t; operator++(); return tmp; } + const T * operator++() { m_t = offset_ptr(m_t, m_off); return m_t; } + const T * operator++(int) { T* tmp = m_t; operator++(); return tmp; } - bool operator==(T* p) { return p == m_t; } - bool operator!=(T* p) { return p != m_t; } - bool operator==(offset_iterator &i) { return i.m_t == m_t; } - bool operator!=(offset_iterator &i) { return i.m_t != m_t; } + bool operator==(T* p) const { return p == m_t; } + bool operator!=(T* p) const { return p != m_t; } + bool operator==(const_offset_iterator &i) const { return i.m_t == m_t; } + bool operator!=(const_offset_iterator &i) const { return i.m_t != m_t; } - T& operator*() const { return *m_t; } - operator T& () const { return *m_t; } - T* operator->() const { return m_t; } + const T& operator*() const { return *m_t; } + operator const T& () const { return *m_t; } + const T* operator->() const { return m_t; } private: - T* m_t; + T const *m_t; size_t m_off; }; + +/// iterator for an array of `const T` whose size is known at runtime +/// \tparam T type of the objects in the array, whose size might not be +/// what is returned by sizeof(T). +template +class offset_iterator +{ +public: + /// constructor. + /// \arg t pointer to the first item in the array + /// \arg off offset applied to reach successive items. default is 0, + /// which creates an effectively constant iterator. + offset_iterator(T *t, size_t off=0) : m_t(t), m_off(off) {} + + T * operator++() { m_t = offset_ptr(m_t, m_off); return m_t; } + T * operator++(int) { T* tmp = m_t; operator++(); return tmp; } + + bool operator==(T *p) const { return p == m_t; } + bool operator!=(T *p) const { return p != m_t; } + bool operator==(offset_iterator &i) const { return i.m_t == m_t; } + bool operator!=(offset_iterator &i) const { return i.m_t != m_t; } + + T & operator*() const { return *m_t; } + operator T & () const { return *m_t; } + T * operator->() const { return m_t; } + +private: + T *m_t; + size_t m_off; +}; + diff --git a/src/libraries/elf/elf.cpp b/src/libraries/elf/elf.cpp deleted file mode 100644 index 729be67..0000000 --- a/src/libraries/elf/elf.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "elf/elf.h" -#include "elf/headers.h" -#include "kutil/memory.h" - -static const uint32_t expected_magic = 0x464c457f; // "\x7f" "ELF" - -namespace elf { - -elf::elf(const void *data, size_t size) : - m_data(data), - m_size(size) -{ -} - -bool -elf::valid() const -{ - const file_header *fheader = header(); - - return - fheader->magic == expected_magic && - fheader->word_size == wordsize::bits64 && - fheader->endianness == encoding::lsb && - fheader->os_abi == osabi::sysV && - fheader->file_type == filetype::executable && - fheader->machine_type == machine::x64 && - fheader->ident_version == 1 && - fheader->version == 1; -} - -const program_header * -elf::program(unsigned index) const -{ - const file_header *fheader = header(); - uint64_t off = fheader->ph_offset + (index * fheader->ph_entsize); - const void *pheader = kutil::offset_pointer(m_data, off); - return reinterpret_cast(pheader); -} - -const section_header * -elf::section(unsigned index) const -{ - const file_header *fheader = header(); - uint64_t off = fheader->sh_offset + (index * fheader->sh_entsize); - const void *sheader = kutil::offset_pointer(m_data, off); - return reinterpret_cast(sheader); -} - - -} // namespace elf diff --git a/src/libraries/elf/file.cpp b/src/libraries/elf/file.cpp new file mode 100644 index 0000000..e35e151 --- /dev/null +++ b/src/libraries/elf/file.cpp @@ -0,0 +1,45 @@ +#include "elf/file.h" +#include "elf/headers.h" +#include "pointer_manipulation.h" + +static const uint32_t expected_magic = 0x464c457f; // "\x7f" "ELF" + +namespace elf { + +inline const file_header * fh(const void *data) { return reinterpret_cast(data); } + +file::file(const void *data, size_t size) : + m_programs(offset_ptr(data, fh(data)->ph_offset), fh(data)->ph_entsize, fh(data)->ph_num), + m_sections(offset_ptr(data, fh(data)->sh_offset), fh(data)->sh_entsize, fh(data)->sh_num), + m_data(data), + m_size(size) +{ +} + +bool +file::valid() const +{ + if (m_size < sizeof(file_header)) + return false; + + const file_header *fheader = header(); + + return + fheader->magic == expected_magic && + fheader->word_size == wordsize::bits64 && + fheader->endianness == encoding::lsb && + fheader->os_abi == osabi::sysV && + fheader->file_type == filetype::executable && + fheader->machine_type == machine::x64 && + fheader->ident_version == 1 && + fheader->version == 1; +} + +uintptr_t +file::entrypoint() const +{ + return static_cast(header()->entrypoint); +} + + +} // namespace elf diff --git a/src/libraries/elf/include/elf/elf.h b/src/libraries/elf/include/elf/elf.h deleted file mode 100644 index 64730da..0000000 --- a/src/libraries/elf/include/elf/elf.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once -#include -#include -#include "elf/headers.h" - -namespace elf { - -class elf -{ -public: - /// Constructor: Create an elf object out of ELF data in memory - /// \arg data The ELF data to read - /// \arg size Size of the ELF data, in bytes - elf(const void *data, size_t size); - - /// Check the validity of the ELF data - /// \returns true for valid ELF data - bool valid() const; - - /// Get the entrypoint address of the program image - /// \returns A pointer to the entrypoint of the program - inline uintptr_t entrypoint() const - { - return static_cast(header()->entrypoint); - } - - /// Get the number of program sections in the image - /// \returns The number of program section entries - inline unsigned program_count() const - { - return header()->ph_num; - } - - /// Get a program header - /// \arg index The index number of the program header - /// \returns A pointer to the program header data - const program_header * program(unsigned index) const; - - /// Get the number of data sections in the image - /// \returns The number of section entries - inline unsigned section_count() const - { - return header()->sh_num; - } - - /// Get a section header - /// \arg index The index number of the section header - /// \returns A pointer to the section header data - const section_header * section(unsigned index) const; - -private: - inline const file_header *header() const - { - return reinterpret_cast(m_data); - } - - const void *m_data; - size_t m_size; -}; - -} diff --git a/src/libraries/elf/include/elf/file.h b/src/libraries/elf/include/elf/file.h new file mode 100644 index 0000000..025f054 --- /dev/null +++ b/src/libraries/elf/include/elf/file.h @@ -0,0 +1,75 @@ +#pragma once +#include +#include + +#include "pointer_manipulation.h" + +namespace elf { + +struct file_header; +struct program_header; +struct section_header; + +template +class subheaders +{ +public: + using iterator = const_offset_iterator; + + subheaders(const T *start, size_t size, unsigned count) : + m_start(start), m_size(size), m_count(count) {} + + inline size_t size() const { return m_size; } + inline unsigned count() const { return m_count; } + + inline const T & operator [] (int i) const { return *offset_ptr(m_start, m_size*i); } + inline const iterator begin() const { return iterator(m_start, m_size); } + inline const iterator end() const { return offset_ptr(m_start, m_size*m_count); } + +private: + const T *m_start; + size_t m_size; + unsigned m_count; +}; + +/// Represents a full ELF file's data +class file +{ +public: + /// Constructor: Create an elf object out of ELF data in memory + /// \arg data The ELF data to read + /// \arg size Size of the ELF data, in bytes + file(const void *data, size_t size); + + /// Check the validity of the ELF data + /// \returns true for valid ELF data + bool valid() const; + + /// Get the entrypoint address of the program image + /// \returns A pointer to the entrypoint of the program + uintptr_t entrypoint() const; + + /// Get the base address of the program in memory + inline uintptr_t base() const { + return reinterpret_cast(m_data); + } + + /// Get the ELF program headers + inline const subheaders & programs() const { return m_programs; } + + /// Get the ELF section headers + inline const subheaders & sections() const { return m_sections; } + + inline const file_header * header() const { + return reinterpret_cast(m_data); + } + +private: + subheaders m_programs; + subheaders m_sections; + + const void *m_data; + size_t m_size; +}; + +} diff --git a/src/libraries/elf/module.toml b/src/libraries/elf/module.toml new file mode 100644 index 0000000..4085c32 --- /dev/null +++ b/src/libraries/elf/module.toml @@ -0,0 +1,6 @@ +name = "elf" +kind = "lib" +includes = [ "src/libraries/elf/include" ] +sources = [ + "file.cpp", +]