From c993531d0678de5e29c943fdbb912e1f20957765 Mon Sep 17 00:00:00 2001 From: Oxore Date: Sun, 3 Mar 2024 18:38:46 +0300 Subject: Impl ELF symbols extraction --- src/elf_image.cpp | 229 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 178 insertions(+), 51 deletions(-) (limited to 'src/elf_image.cpp') diff --git a/src/elf_image.cpp b/src/elf_image.cpp index 6db72f3..6572a85 100644 --- a/src/elf_image.cpp +++ b/src/elf_image.cpp @@ -4,9 +4,18 @@ #include "elf_image.h" #include +#include #include +#include -ELF::ProgramHeader32Table ELF::ProgramHeader32Table::FromBytes( +#ifdef __GNUC__ +#define _PRINTF(strindex, first) __attribute__((format(printf, strindex, first))) +#else +#define _PRINTF(strindex, first) +#endif + + +ELF::ProgramHeader32Table ELF::ProgramHeader32Table::FromView( const DataView &d, const DataEncoding e) { if (d.buffer == nullptr || d.size == 0) { @@ -22,119 +31,198 @@ ELF::ProgramHeader32Table ELF::ProgramHeader32Table::FromBytes( return ELF::ProgramHeader32Table{ headers, size, }; } +static _PRINTF(1, 2) char *Error(const char *fmt, ...) +{ + if (0 == strlen(fmt)) { + return nullptr; + } + char *error{}; + size_t size{}; + FILE *error_stream = open_memstream(&error, &size); + assert(error_stream); + va_list ap; + va_start(ap, fmt); + vfprintf(error_stream, fmt, ap); + va_end(ap); + fclose(error_stream); + assert(error != nullptr); + assert(*error != '\0'); + return error; +} + +static char *ValidateSectionHeader( + const DataView& d, + ELF::SectionHeader32 sh, + size_t shstrndx, + const char *shname) +{ + if (sh.offset >= d.size) { + return Error( + "Section header %zu (%s) offset is too big to fit into the file: " + "expected (<%zu), got (%zu)", + shstrndx, + shname, + d.size, + size_t(sh.offset)); + } + if (sh.size >= d.size - sh.offset) { + return Error( + "Section header %zu (%s) is too big to fit into the file: " + "expected (<%zu), got (%zu)", + shstrndx, + shname, + d.size - sh.offset, + size_t(sh.size)); + } + if (sh.entsize) { + if (sh.entsize > sh.size) { + return Error( + "Section header %zu (%s) entry size is too big to fit into " + "the table: expected (<%zu), got (%zu)", + shstrndx, + shname, + size_t(sh.size), + size_t(sh.entsize)); + } + size_t const remainder = sh.size % sh.entsize; + if (remainder) { + return Error( + "Section header %zu (%s) size is not multiple of entsize: " + "expected (%zu %% %zu == 0), got (%zu)", + shstrndx, + shname, + size_t(sh.size), + size_t(sh.entsize), + remainder); + } + } + return nullptr; +} + static char *ValidateELF(const DataView& d) { - char *error; - size_t size; - FILE *s = open_memstream(&error, &size); - assert(s); using namespace ELF; if (d.size < kHeaderSize) { - fprintf( - s, + return Error( "data size (%zu) is lower than minimum ELF header size (%zu): " "ELF header could not fit", d.size, kHeaderSize); - fclose(s); - return error; } const auto header_raw = Header32Raw::FromBytes(d.buffer); const auto header = Header32::FromBytes(d.buffer); if (!MagicIsValid(header.ident.magic)) { const uint8_t *m = header.ident.magic; - fprintf( - s, + return Error( "ELF Magic is invalid: expected [%02x %02x %02x %02x], got [%02x %02x %02x %02x]", 0x7f, 'E', 'L', 'F', m[0], m[1], m[2], m[3]); - fclose(s); - return error; } if (header.ident.version != Version::kCurrent) { - fprintf( - s, + return Error( "version (0x%02x) of ELF header.ident.version is not supported, " "only \"Current\" version (0x%02x) is supported", header_raw.ident.version, static_cast(Version::kCurrent)); - fclose(s); - return error; } if (header.version != Version::kCurrent) { - fprintf( - s, + return Error( "version (0x%02x) of ELF header.version is not supported, " "only \"Current\" version (0x%02x) is supported", header_raw.version, static_cast(Version::kCurrent)); - fclose(s); - return error; } if (header.type != ObjectType::kExec) { - fprintf( - s, + return Error( "object type (0x%02x) is not supported, " "only Exec (0x%02x) object type is supported", header_raw.type, static_cast(ObjectType::kExec)); - fclose(s); - return error; } if (header.machine != Machine::k68k) { - fprintf( - s, + return Error( "machine (0x%02x) is not supported, " "only Motorola 68k (0x%02x) machine is supported", header_raw.machine, static_cast(Machine::k68k)); - fclose(s); - return error; + } + if (header.ehsize > d.size) { + return Error( + "ELF header ehsize is too big to fit into the file: expected (<=%zu), got (%zu)", + size_t(d.size), + size_t(header.ehsize)); } if (header.phentsize != kProgramHeaderSize) { - fprintf( - s, + return Error( "phentsize is invalid: expected (%zu), got (%zu)", kProgramHeaderSize, size_t(header.phentsize)); - fclose(s); - return error; + } + if (header.shentsize != kSectionHeaderSize) { + return Error( + "shentsize is invalid: expected (%zu), got (%zu)", + kSectionHeaderSize, + size_t(header.shentsize)); + } + if (header.shoff < header.ehsize) { + return Error( + "shoff intersects with an ELF header: expected (>%zu), got (%zu)", + size_t(header.ehsize), + size_t(header.shoff)); + } + if (header.shoff >= d.size) { + return Error( + "shoff is too big for a file size: expected (<%zu), got (%zu)", + d.size, + size_t(header.shoff)); + } + if (header.shnum > (d.size - header.shoff) / header.shentsize) { + return Error( + "shnum is too big to fit shared headers table into the file: expected (<=%zu), got (%zu)", + (d.size - header.shoff) / header.shentsize, + size_t(header.shnum)); + } + if (header.shstrndx > header.shnum) { + return Error( + "shstrndx exceeds shared headers table entries count: expected (<%zu), got (%zu)", + size_t(header.shnum), + size_t(header.shstrndx)); + } + if (header.shstrndx) { + const auto shstrtab = ELF::SectionHeader32::FromBytes( + d.buffer + header.shoff + header.shstrndx * kSectionHeaderSize, + header.ident.data_encoding); + char *error = ValidateSectionHeader(d, shstrtab, header.shstrndx, ".shstrtab"); + if (error != nullptr) { + return error; + } } if (d.size < header.phoff + header.phentsize * header.phnum) { - fprintf( - s, + return Error( "data size (%zu) is lower than program header table end offset (%zu): " "program header table could not fit", d.size, size_t(header.phoff + header.phentsize * header.phnum)); - fclose(s); - return error; } bool has_segment_with_entry = false; for (size_t i = 0; i < header.phnum; i++) { const auto ph = ProgramHeader32::FromBytes( d.buffer + header.phoff + header.phentsize * i, header.ident.data_encoding); if (d.size < ph.offset + ph.filesz) { - fprintf( - s, + return Error( "data size (%zu) is lower than pht[%zu] segment end offset (%zu): " "segment could not fit", d.size, i, size_t(ph.offset + ph.filesz)); - fclose(s); - return error; } const bool is_code = (ph.flags & (kPHFlagX | kPHFlagW | kPHFlagR)) == (kPHFlagX | kPHFlagR); if (ParsePHType(ph.type) == PHType::kLoad && is_code && ph.vaddr != 0) { - fprintf( - s, + return Error( "pht[%zu] segment is a code, but it's vaddr (0x%08x) is not zero: " "non-zero base address is not supported", i, ph.vaddr); - fclose(s); - return error; } const bool contains_entry = header.entry >= ph.vaddr && header.entry < ph.vaddr + ph.memsz; if (ParsePHType(ph.type) == PHType::kLoad && is_code && contains_entry) { @@ -142,12 +230,8 @@ static char *ValidateELF(const DataView& d) } } if (!has_segment_with_entry) { - fprintf(s, "no code segments containing entry point (0x%08x) found", header.entry); - fclose(s); - return error; + return Error("no code segments containing entry point (0x%08x) found", header.entry); } - fclose(s); - free(error); return nullptr; } @@ -157,10 +241,53 @@ ELF::Image::Image(DataBuffer&& data) , _h(_error ? ELF::Header32{} : ELF::Header32::FromBytes(_data.View().buffer)) , _pht(_error ? ELF::ProgramHeader32Table{} - : ELF::ProgramHeader32Table::FromBytes( + : ELF::ProgramHeader32Table::FromView( _data.View(_h.phoff, _h.phnum * kProgramHeaderSize), _h.ident.data_encoding)) + , _shstrtab(_error + ? ELF::SectionHeader32{} + : ELF::SectionHeader32::FromBytes( + _data.buffer + _h.shoff + _h.shstrndx * kSectionHeaderSize, _h.ident.data_encoding)) + , _symtab(GetSectionHeaderByName(".symtab")) + , _strtab(GetSectionHeader(_symtab.link)) {} +ELF::SectionHeader32 ELF::Image::GetSectionHeaderByName(const char *name) const +{ + const uint32_t index = GetSectionHeaderIndexByName(name); + if (index == 0) { + return SectionHeader32{}; + } + const size_t offset = _h.shoff + kSectionHeaderSize * index; + return SectionHeader32::FromBytes(_data.buffer + offset, _h.ident.data_encoding); +} + +uint32_t ELF::Image::GetSectionHeaderIndexByName(const char *name) const +{ + if (!IsValid()) { + return 0; + } + if (name == nullptr) { + return 0; + } + if (!_shstrtab.IsValid()) { + return 0; + } + for (uint32_t index = 0; index < _h.shnum; index++) { + const size_t offset = _h.shoff + kSectionHeaderSize * index; + if (offset + kSectionHeaderSize > _data.buffer_size) { + return 0; + } + const auto header = SectionHeader32::FromBytes( + _data.buffer + offset, _h.ident.data_encoding); + const char *name_in_elf = reinterpret_cast( + _data.buffer + _shstrtab.offset + header.name); + if (0 == strcmp(name, name_in_elf)) { + return index; + } + } + return 0; +} + ELF::Image::~Image() { if (_error) { -- cgit v1.2.3