#pragma once #include #include #include #include #include "Types.h" #include "ElfDefs.h" #include "PtrStream.h" #include "EndianUtils.h" #include "maybe_unused.h" struct ELFTRAITS32 { typedef ELF::ELFHEADER32 ELFHEADER; typedef ELF::ELFPROGRAMHEADER32 ELFPROGRAMHEADER; typedef ELF::ELFSECTIONHEADER32 ELFSECTIONHEADER; typedef ELF::ELFSYMBOL32 ELFSYMBOL; enum { HEADER_ID_CLASS = ELF::ELFCLASS32 }; }; struct ELFTRAITS64 { typedef ELF::ELFHEADER64 ELFHEADER; typedef ELF::ELFPROGRAMHEADER64 ELFPROGRAMHEADER; typedef ELF::ELFSECTIONHEADER64 ELFSECTIONHEADER; typedef ELF::ELFSYMBOL64 ELFSYMBOL; enum { HEADER_ID_CLASS = ELF::ELFCLASS64 }; }; template class CELF { public: typedef typename ElfTraits::ELFHEADER HEADER; typedef typename ElfTraits::ELFPROGRAMHEADER PROGRAMHEADER; typedef typename ElfTraits::ELFSECTIONHEADER SECTIONHEADER; typedef typename ElfTraits::ELFSYMBOL SYMBOL; CELF(uint8* content, uint64 contentSize = ~0ULL) : m_content(content) { Framework::CPtrStream stream(m_content, contentSize); stream.Read(&m_header, sizeof(m_header)); if(m_header.nId[0] != 0x7F || m_header.nId[1] != 'E' || m_header.nId[2] != 'L' || m_header.nId[3] != 'F') { throw std::runtime_error("This file isn't a valid ELF file."); } if(m_header.nId[ELF::EI_CLASS] != ElfTraits::HEADER_ID_CLASS) { throw std::runtime_error("Failed to load ELF file: wrong bitness."); } if(m_header.nId[ELF::EI_DATA] == ELF::ELFDATA2MSB) { Framework::CEndian::FromMSBF(m_header.nType); Framework::CEndian::FromMSBF(m_header.nCPU); Framework::CEndian::FromMSBF(m_header.nVersion); Framework::CEndian::FromMSBF(m_header.nEntryPoint); Framework::CEndian::FromMSBF(m_header.nProgHeaderStart); Framework::CEndian::FromMSBF(m_header.nSectHeaderStart); Framework::CEndian::FromMSBF(m_header.nFlags); Framework::CEndian::FromMSBF(m_header.nSize); Framework::CEndian::FromMSBF(m_header.nProgHeaderEntrySize); Framework::CEndian::FromMSBF(m_header.nProgHeaderCount); Framework::CEndian::FromMSBF(m_header.nSectHeaderEntrySize); Framework::CEndian::FromMSBF(m_header.nSectHeaderCount); Framework::CEndian::FromMSBF(m_header.nSectHeaderStringTableIndex); } ReadProgramHeaders(stream); ReadSectionHeaders(stream); } CELF(const CELF&) = delete; virtual ~CELF() = default; CELF& operator=(const CELF&) = delete; uint8* GetContent() const { return m_content; } const HEADER& GetHeader() const { return m_header; } uint64 GetSectionHeaderCount() const { return m_sections.size(); } const SECTIONHEADER* GetSection(unsigned int index) const { if(index >= m_sections.size()) { return nullptr; } return &m_sections[index]; } const void* GetSectionData(unsigned int index) const { auto section = GetSection(index); if(section == nullptr) return nullptr; return m_content + section->nOffset; } const char* GetSectionName(unsigned int sectionIndex) { auto stringTableData = reinterpret_cast(GetSectionData(m_header.nSectHeaderStringTableIndex)); if(stringTableData == nullptr) return nullptr; auto sectionHeader = GetSection(sectionIndex); if(sectionHeader == nullptr) return nullptr; return stringTableData + sectionHeader->nStringTableIndex; } const SECTIONHEADER* FindSection(const char* requestedSectionName) const { auto sectionIndex = FindSectionIndex(requestedSectionName); if(sectionIndex == 0) return nullptr; return GetSection(sectionIndex); } unsigned int FindSectionIndex(const char* requestedSectionName) const { auto stringTableData = reinterpret_cast(GetSectionData(m_header.nSectHeaderStringTableIndex)); if(stringTableData == nullptr) return 0; for(unsigned int i = 0; i < GetSectionHeaderCount(); i++) { auto sectionHeader = GetSection(i); auto sectionName = stringTableData + sectionHeader->nStringTableIndex; if(!strcmp(sectionName, requestedSectionName)) { return i; } } return 0; } const void* FindSectionData(const char* requestedSectionName) const { auto section = FindSection(requestedSectionName); if(section == nullptr) return nullptr; return m_content + section->nOffset; } PROGRAMHEADER* GetProgram(unsigned int index) { if(index >= m_header.nProgHeaderCount) { return nullptr; } return &m_programs[index]; } uint64 GetSymbolCount() const { auto symbolTable = FindSection(".symtab"); if(!symbolTable) return 0; return symbolTable->nSize / sizeof(SYMBOL); } typedef std::function SymbolEnumerationCallback; void EnumerateSymbols(const SymbolEnumerationCallback& callback) const { auto symbolTable = FindSection(".symtab"); if(!symbolTable) return; auto stringTable = reinterpret_cast(GetSectionData(symbolTable->nIndex)); if(!stringTable) return; auto symbols = reinterpret_cast(FindSectionData(".symtab")); unsigned int symbolCount = symbolTable->nSize / sizeof(SYMBOL); for(unsigned int i = 0; i < symbolCount; i++) { auto symbol = symbols[i]; if(m_header.nId[ELF::EI_DATA] == ELF::ELFDATA2MSB) { Framework::CEndian::FromMSBF(symbol.nName); Framework::CEndian::FromMSBF(symbol.nValue); Framework::CEndian::FromMSBF(symbol.nSize); Framework::CEndian::FromMSBF(symbol.nSectionIndex); } uint8 symbolType = symbol.nInfo & 0x0F; uint8 symbolBinding = (symbol.nInfo >> 4) & 0x0F; auto symbolName = stringTable + symbol.nName; callback(symbol, symbolType, symbolBinding, symbolName); } } private: void ReadSectionHeaders(Framework::CStream& stream) { //Fetch extended section header count if needed decltype(SECTIONHEADER::nSize) sectionHeaderCount = 0; if((m_header.nSectHeaderCount == 0) && (m_header.nSectHeaderStart != 0)) { SECTIONHEADER zeroSectionHeader = {}; stream.Seek(m_header.nSectHeaderStart, Framework::STREAM_SEEK_SET); //Some ELF files have their section headers truncated (ex.: Psychonauts PS2) //Make sure that we were able to read the zeroth section header //properly before proceeding auto amountRead = stream.Read(&zeroSectionHeader, sizeof(zeroSectionHeader)); if(amountRead == sizeof(zeroSectionHeader)) { if(m_header.nId[ELF::EI_DATA] == ELF::ELFDATA2MSB) { Framework::CEndian::FromMSBF(zeroSectionHeader.nSize); } assert(zeroSectionHeader.nSize != 0); sectionHeaderCount = zeroSectionHeader.nSize; } } else { sectionHeaderCount = m_header.nSectHeaderCount; } m_sections.resize(sectionHeaderCount); stream.Seek(m_header.nSectHeaderStart, Framework::STREAM_SEEK_SET); for(auto& section : m_sections) { FRAMEWORK_MAYBE_UNUSED auto readAmount = stream.Read(§ion, sizeof(section)); assert(readAmount == sizeof(section)); if(m_header.nId[ELF::EI_DATA] == ELF::ELFDATA2MSB) { Framework::CEndian::FromMSBF(section.nStringTableIndex); Framework::CEndian::FromMSBF(section.nType); Framework::CEndian::FromMSBF(section.nFlags); Framework::CEndian::FromMSBF(section.nStart); Framework::CEndian::FromMSBF(section.nOffset); Framework::CEndian::FromMSBF(section.nSize); Framework::CEndian::FromMSBF(section.nIndex); Framework::CEndian::FromMSBF(section.nInfo); Framework::CEndian::FromMSBF(section.nAlignment); Framework::CEndian::FromMSBF(section.nOther); } } } void ReadProgramHeaders(Framework::CStream& stream) { m_programs.resize(m_header.nProgHeaderCount); stream.Seek(m_header.nProgHeaderStart, Framework::STREAM_SEEK_SET); for(auto& program : m_programs) { stream.Read(&program, sizeof(program)); if(m_header.nId[ELF::EI_DATA] == ELF::ELFDATA2MSB) { Framework::CEndian::FromMSBF(program.nType); Framework::CEndian::FromMSBF(program.nFlags); Framework::CEndian::FromMSBF(program.nOffset); Framework::CEndian::FromMSBF(program.nVAddress); Framework::CEndian::FromMSBF(program.nPAddress); Framework::CEndian::FromMSBF(program.nFileSize); Framework::CEndian::FromMSBF(program.nMemorySize); Framework::CEndian::FromMSBF(program.nAlignment); } } } HEADER m_header; uint8* m_content = nullptr; std::vector m_sections; std::vector m_programs; }; typedef CELF CELF32; typedef CELF CELF64;