From 163d2e4ba86cc4dfd63fe632760493601c32c95c Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sat, 22 Apr 2023 18:57:24 +0300 Subject: [PATCH] LibELF: Add 32 bit support --- LibELF/LibELF/ELF.cpp | 294 ++++++++++++++++++++++++++-------- LibELF/include/LibELF/ELF.h | 35 +++- LibELF/include/LibELF/Types.h | 74 +++++++++ 3 files changed, 332 insertions(+), 71 deletions(-) diff --git a/LibELF/LibELF/ELF.cpp b/LibELF/LibELF/ELF.cpp index a87f826a..928885dd 100644 --- a/LibELF/LibELF/ELF.cpp +++ b/LibELF/LibELF/ELF.cpp @@ -40,12 +40,106 @@ namespace LibELF return elf; } - const char* ELF::lookup_section_name(uint32_t offset) const + BAN::ErrorOr ELF::load() { - return lookup_string(file_header64().e_shstrndx, offset); + 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 {}; } - const char* ELF::lookup_string(size_t table_index, uint32_t offset) const + 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; @@ -90,7 +184,7 @@ namespace LibELF bool ELF::parse_elf64_section_header(const Elf64SectionHeader& header) { #if ELF_PRINT_HEADERS - if (auto* name = lookup_section_name(header.sh_name)) + if (auto* name = lookup_section_name64(header.sh_name)) dprintln("{}", name); switch (header.sh_type) @@ -105,7 +199,7 @@ namespace LibELF 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)) + if (auto* name = lookup_string64(header.sh_link, symbol.st_name)) dprintln(" {}", name); } break; @@ -135,76 +229,15 @@ namespace LibELF 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 = 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); - } - - return {}; - } - - 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); @@ -212,10 +245,135 @@ namespace LibELF 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); + } + + } diff --git a/LibELF/include/LibELF/ELF.h b/LibELF/include/LibELF/ELF.h index f6d1638d..340ae2f2 100644 --- a/LibELF/include/LibELF/ELF.h +++ b/LibELF/include/LibELF/ELF.h @@ -2,6 +2,7 @@ #include #include +#include #include "Types.h" namespace LibELF @@ -11,16 +12,40 @@ namespace LibELF { 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_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 char* lookup_section_name(uint32_t) const; - const char* lookup_string(size_t, uint32_t) const; + 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(i386) + 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: ELF(BAN::Vector&& data) : m_data(BAN::move(data)) @@ -31,6 +56,10 @@ namespace LibELF 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: const BAN::Vector m_data; }; diff --git a/LibELF/include/LibELF/Types.h b/LibELF/include/LibELF/Types.h index bc72c4aa..2f9257c8 100644 --- a/LibELF/include/LibELF/Types.h +++ b/LibELF/include/LibELF/Types.h @@ -5,6 +5,80 @@ namespace LibELF { + using Elf32Addr = uint32_t; + using Elf32Off = uint32_t; + using Elf32Half = uint16_t; + using Elf32Word = uint32_t; + using Elf32Sword = int32_t; + + struct Elf32FileHeader + { + unsigned char e_ident[16]; + Elf32Half e_type; + Elf32Half e_machine; + Elf32Word e_version; + Elf32Addr e_entry; + Elf32Off e_phoff; + Elf32Off e_shoff; + Elf32Word e_flags; + Elf32Half e_ehsize; + Elf32Half e_phentsize; + Elf32Half e_phnum; + Elf32Half e_shentsize; + Elf32Half e_shnum; + Elf32Half e_shstrndx; + }; + + struct Elf32SectionHeader + { + Elf32Word sh_name; + Elf32Word sh_type; + Elf32Word sh_flags; + Elf32Addr sh_addr; + Elf32Off sh_offset; + Elf32Word sh_size; + Elf32Word sh_link; + Elf32Word sh_info; + Elf32Word sh_addralign; + Elf32Word sh_entsize; + }; + + struct Elf32Symbol + { + Elf32Word st_name; + Elf32Addr st_value; + Elf32Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32Half st_shndx; + }; + + struct Elf32Relocation + { + Elf32Addr r_offset; + Elf32Word r_info; + }; + + struct Elf32RelocationA + { + Elf32Addr r_offset; + Elf32Word r_info; + Elf32Sword r_addend; + }; + + struct Elf32ProgramHeader + { + Elf32Word p_type; + Elf32Off p_offset; + Elf32Addr p_vaddr; + Elf32Addr p_paddr; + Elf32Word p_filesz; + Elf32Word p_memsz; + Elf32Word p_flags; + Elf32Word p_align; + }; + + using Elf64Addr = uint64_t; using Elf64Off = uint64_t; using Elf64Half = uint16_t;