/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmELF.h" #include #include #include #include #include #include #include #include #include #include #include "cmsys/FStream.hxx" #include "cmelf/elf32.h" #include "cmelf/elf64.h" #include "cmelf/elf_common.h" // Low-level byte swapping implementation. template struct cmELFByteSwapSize { }; static void cmELFByteSwap(char* data, cmELFByteSwapSize<2> /*unused*/) { char one_byte; one_byte = data[0]; data[0] = data[1]; data[1] = one_byte; } static void cmELFByteSwap(char* data, cmELFByteSwapSize<4> /*unused*/) { char one_byte; one_byte = data[0]; data[0] = data[3]; data[3] = one_byte; one_byte = data[1]; data[1] = data[2]; data[2] = one_byte; } static void cmELFByteSwap(char* data, cmELFByteSwapSize<8> /*unused*/) { char one_byte; one_byte = data[0]; data[0] = data[7]; data[7] = one_byte; one_byte = data[1]; data[1] = data[6]; data[6] = one_byte; one_byte = data[2]; data[2] = data[5]; data[5] = one_byte; one_byte = data[3]; data[3] = data[4]; data[4] = one_byte; } // Low-level byte swapping interface. template void cmELFByteSwap(T& x) { cmELFByteSwap(reinterpret_cast(&x), cmELFByteSwapSize()); } class cmELFInternal { public: using StringEntry = cmELF::StringEntry; enum ByteOrderType { ByteOrderMSB, ByteOrderLSB }; // Construct and take ownership of the file stream object. cmELFInternal(cmELF* external, std::unique_ptr fin, ByteOrderType order) : External(external) , Stream(std::move(fin)) , ByteOrder(order) { // In most cases the processor-specific byte order will match that // of the target execution environment. If we choose wrong here // it is fixed when the header is read. #if KWIML_ABI_ENDIAN_ID == KWIML_ABI_ENDIAN_ID_LITTLE this->NeedSwap = (this->ByteOrder == ByteOrderMSB); #elif KWIML_ABI_ENDIAN_ID == KWIML_ABI_ENDIAN_ID_BIG this->NeedSwap = (this->ByteOrder == ByteOrderLSB); #else this->NeedSwap = false; // Final decision is at runtime anyway. #endif // We have not yet loaded the section info. this->DynamicSectionIndex = -1; } // Destruct and delete the file stream object. virtual ~cmELFInternal() = default; // Forward to the per-class implementation. virtual unsigned int GetNumberOfSections() const = 0; virtual unsigned long GetDynamicEntryPosition(int j) = 0; virtual cmELF::DynamicEntryList GetDynamicEntries() = 0; virtual std::vector EncodeDynamicEntries( const cmELF::DynamicEntryList&) = 0; virtual StringEntry const* GetDynamicSectionString(unsigned int tag) = 0; virtual bool IsMips() const = 0; virtual void PrintInfo(std::ostream& os) const = 0; // Lookup the SONAME in the DYNAMIC section. StringEntry const* GetSOName() { return this->GetDynamicSectionString(DT_SONAME); } // Lookup the RPATH in the DYNAMIC section. StringEntry const* GetRPath() { return this->GetDynamicSectionString(DT_RPATH); } // Lookup the RUNPATH in the DYNAMIC section. StringEntry const* GetRunPath() { return this->GetDynamicSectionString(DT_RUNPATH); } // Return the recorded ELF type. cmELF::FileType GetFileType() const { return this->ELFType; } // Return the recorded machine. std::uint16_t GetMachine() const { return this->Machine; } protected: // Data common to all ELF class implementations. // The external cmELF object. cmELF* External; // The stream from which to read. std::unique_ptr Stream; // The byte order of the ELF file. ByteOrderType ByteOrder; // The ELF file type. cmELF::FileType ELFType = cmELF::FileTypeInvalid; // The ELF architecture. std::uint16_t Machine; // Whether we need to byte-swap structures read from the stream. bool NeedSwap; // The section header index of the DYNAMIC section (-1 if none). int DynamicSectionIndex; // Helper methods for subclasses. void SetErrorMessage(const char* msg) { this->External->ErrorMessage = msg; this->ELFType = cmELF::FileTypeInvalid; } // Store string table entry states. std::map DynamicSectionStrings; }; // Configure the implementation template for 32-bit ELF files. struct cmELFTypes32 { using ELF_Ehdr = Elf32_Ehdr; using ELF_Shdr = Elf32_Shdr; using ELF_Dyn = Elf32_Dyn; using ELF_Half = Elf32_Half; using tagtype = ::uint32_t; static const char* GetName() { return "32-bit"; } }; // Configure the implementation template for 64-bit ELF files. struct cmELFTypes64 { using ELF_Ehdr = Elf64_Ehdr; using ELF_Shdr = Elf64_Shdr; using ELF_Dyn = Elf64_Dyn; using ELF_Half = Elf64_Half; using tagtype = ::uint64_t; static const char* GetName() { return "64-bit"; } }; // Parser implementation template. template class cmELFInternalImpl : public cmELFInternal { public: // Copy the ELF file format types from our configuration parameter. using ELF_Ehdr = typename Types::ELF_Ehdr; using ELF_Shdr = typename Types::ELF_Shdr; using ELF_Dyn = typename Types::ELF_Dyn; using ELF_Half = typename Types::ELF_Half; using tagtype = typename Types::tagtype; // Construct with a stream and byte swap indicator. cmELFInternalImpl(cmELF* external, std::unique_ptr fin, ByteOrderType order); // Return the number of sections as specified by the ELF header. unsigned int GetNumberOfSections() const override { return static_cast(this->ELFHeader.e_shnum + this->SectionHeaders[0].sh_size); } // Get the file position of a dynamic section entry. unsigned long GetDynamicEntryPosition(int j) override; cmELF::DynamicEntryList GetDynamicEntries() override; std::vector EncodeDynamicEntries( const cmELF::DynamicEntryList&) override; // Lookup a string from the dynamic section with the given tag. StringEntry const* GetDynamicSectionString(unsigned int tag) override; bool IsMips() const override { return this->ELFHeader.e_machine == EM_MIPS; } // Print information about the ELF file. void PrintInfo(std::ostream& os) const override { os << "ELF " << Types::GetName(); if (this->ByteOrder == ByteOrderMSB) { os << " MSB"; } else if (this->ByteOrder == ByteOrderLSB) { os << " LSB"; } switch (this->ELFType) { case cmELF::FileTypeInvalid: os << " invalid file"; break; case cmELF::FileTypeRelocatableObject: os << " relocatable object"; break; case cmELF::FileTypeExecutable: os << " executable"; break; case cmELF::FileTypeSharedLibrary: os << " shared library"; break; case cmELF::FileTypeCore: os << " core file"; break; case cmELF::FileTypeSpecificOS: os << " os-specific type"; break; case cmELF::FileTypeSpecificProc: os << " processor-specific type"; break; } os << "\n"; } private: static_assert(sizeof(ELF_Dyn().d_un.d_val) == sizeof(ELF_Dyn().d_un.d_ptr), "ByteSwap(ELF_Dyn) assumes d_val and d_ptr are the same size"); void ByteSwap(ELF_Ehdr& elf_header) { cmELFByteSwap(elf_header.e_type); cmELFByteSwap(elf_header.e_machine); cmELFByteSwap(elf_header.e_version); cmELFByteSwap(elf_header.e_entry); cmELFByteSwap(elf_header.e_phoff); cmELFByteSwap(elf_header.e_shoff); cmELFByteSwap(elf_header.e_flags); cmELFByteSwap(elf_header.e_ehsize); cmELFByteSwap(elf_header.e_phentsize); cmELFByteSwap(elf_header.e_phnum); cmELFByteSwap(elf_header.e_shentsize); cmELFByteSwap(elf_header.e_shnum); cmELFByteSwap(elf_header.e_shstrndx); } void ByteSwap(ELF_Shdr& sec_header) { cmELFByteSwap(sec_header.sh_name); cmELFByteSwap(sec_header.sh_type); cmELFByteSwap(sec_header.sh_flags); cmELFByteSwap(sec_header.sh_addr); cmELFByteSwap(sec_header.sh_offset); cmELFByteSwap(sec_header.sh_size); cmELFByteSwap(sec_header.sh_link); cmELFByteSwap(sec_header.sh_info); cmELFByteSwap(sec_header.sh_addralign); cmELFByteSwap(sec_header.sh_entsize); } void ByteSwap(ELF_Dyn& dyn) { cmELFByteSwap(dyn.d_tag); cmELFByteSwap(dyn.d_un.d_val); } bool FileTypeValid(ELF_Half et) { unsigned int eti = static_cast(et); if (eti == ET_NONE || eti == ET_REL || eti == ET_EXEC || eti == ET_DYN || eti == ET_CORE) { return true; } if (eti >= ET_LOOS && eti <= ET_HIOS) { return true; } if (eti >= ET_LOPROC && eti <= ET_HIPROC) { return true; } return false; } bool Read(ELF_Ehdr& x) { // Read the header from the file. if (!this->Stream->read(reinterpret_cast(&x), sizeof(x))) { return false; } // The byte order of ELF header fields may not match that of the // processor-specific data. The header fields are ordered to // match the target execution environment, so we may need to // memorize the order of all platforms based on the e_machine // value. As a heuristic, if the type is invalid but its // swapped value is okay then flip our swap mode. ELF_Half et = x.e_type; if (this->NeedSwap) { cmELFByteSwap(et); } if (!this->FileTypeValid(et)) { cmELFByteSwap(et); if (this->FileTypeValid(et)) { // The previous byte order guess was wrong. Flip it. this->NeedSwap = !this->NeedSwap; } } // Fix the byte order of the header. if (this->NeedSwap) { this->ByteSwap(x); } return true; } bool Read(ELF_Shdr& x) { if (this->Stream->read(reinterpret_cast(&x), sizeof(x)) && this->NeedSwap) { this->ByteSwap(x); } return !this->Stream->fail(); } bool Read(ELF_Dyn& x) { if (this->Stream->read(reinterpret_cast(&x), sizeof(x)) && this->NeedSwap) { this->ByteSwap(x); } return !this->Stream->fail(); } bool LoadSectionHeader(unsigned int i) { // Read the section header from the file. this->Stream->seekg(this->ELFHeader.e_shoff + this->ELFHeader.e_shentsize * i); if (!this->Read(this->SectionHeaders[i])) { return false; } // Identify some important sections. if (this->SectionHeaders[i].sh_type == SHT_DYNAMIC) { this->DynamicSectionIndex = static_cast(i); } return true; } bool LoadDynamicSection(); // Store the main ELF header. ELF_Ehdr ELFHeader; // Store all the section headers. std::vector SectionHeaders; // Store all entries of the DYNAMIC section. std::vector DynamicSectionEntries; }; template cmELFInternalImpl::cmELFInternalImpl(cmELF* external, std::unique_ptr fin, ByteOrderType order) : cmELFInternal(external, std::move(fin), order) { // Read the main header. if (!this->Read(this->ELFHeader)) { this->SetErrorMessage("Failed to read main ELF header."); return; } // Determine the ELF file type. switch (this->ELFHeader.e_type) { case ET_NONE: this->SetErrorMessage("ELF file type is NONE."); return; case ET_REL: this->ELFType = cmELF::FileTypeRelocatableObject; break; case ET_EXEC: this->ELFType = cmELF::FileTypeExecutable; break; case ET_DYN: this->ELFType = cmELF::FileTypeSharedLibrary; break; case ET_CORE: this->ELFType = cmELF::FileTypeCore; break; default: { unsigned int eti = static_cast(this->ELFHeader.e_type); if (eti >= ET_LOOS && eti <= ET_HIOS) { this->ELFType = cmELF::FileTypeSpecificOS; break; } if (eti >= ET_LOPROC && eti <= ET_HIPROC) { this->ELFType = cmELF::FileTypeSpecificProc; break; } std::ostringstream e; e << "Unknown ELF file type " << eti; this->SetErrorMessage(e.str().c_str()); return; } } this->Machine = this->ELFHeader.e_machine; // Load the section headers. this->SectionHeaders.resize( this->ELFHeader.e_shnum == 0 ? 1 : this->ELFHeader.e_shnum); this->LoadSectionHeader(0); this->SectionHeaders.resize(this->GetNumberOfSections()); for (unsigned int i = 1; i < this->GetNumberOfSections(); ++i) { if (!this->LoadSectionHeader(i)) { this->SetErrorMessage("Failed to load section headers."); return; } } } template bool cmELFInternalImpl::LoadDynamicSection() { // If there is no dynamic section we are done. if (this->DynamicSectionIndex < 0) { return false; } // If the section was already loaded we are done. if (!this->DynamicSectionEntries.empty()) { return true; } // If there are no entries we are done. ELF_Shdr const& sec = this->SectionHeaders[this->DynamicSectionIndex]; if (sec.sh_entsize == 0) { return false; } // Allocate the dynamic section entries. int n = static_cast(sec.sh_size / sec.sh_entsize); this->DynamicSectionEntries.resize(n); // Read each entry. for (int j = 0; j < n; ++j) { // Seek to the beginning of the section entry. this->Stream->seekg(sec.sh_offset + sec.sh_entsize * j); ELF_Dyn& dyn = this->DynamicSectionEntries[j]; // Try reading the entry. if (!this->Read(dyn)) { this->SetErrorMessage("Error reading entry from DYNAMIC section."); this->DynamicSectionIndex = -1; return false; } } return true; } template unsigned long cmELFInternalImpl::GetDynamicEntryPosition(int j) { if (!this->LoadDynamicSection()) { return 0; } if (j < 0 || j >= static_cast(this->DynamicSectionEntries.size())) { return 0; } ELF_Shdr const& sec = this->SectionHeaders[this->DynamicSectionIndex]; return static_cast(sec.sh_offset + sec.sh_entsize * j); } template cmELF::DynamicEntryList cmELFInternalImpl::GetDynamicEntries() { cmELF::DynamicEntryList result; // Ensure entries have been read from file if (!this->LoadDynamicSection()) { return result; } // Copy into public array result.reserve(this->DynamicSectionEntries.size()); for (ELF_Dyn& dyn : this->DynamicSectionEntries) { result.emplace_back(dyn.d_tag, dyn.d_un.d_val); } return result; } template std::vector cmELFInternalImpl::EncodeDynamicEntries( const cmELF::DynamicEntryList& entries) { std::vector result; result.reserve(sizeof(ELF_Dyn) * entries.size()); for (auto const& entry : entries) { // Store the entry in an ELF_Dyn, byteswap it, then serialize to chars ELF_Dyn dyn; dyn.d_tag = static_cast(entry.first); dyn.d_un.d_val = static_cast(entry.second); if (this->NeedSwap) { this->ByteSwap(dyn); } char* pdyn = reinterpret_cast(&dyn); cm::append(result, pdyn, pdyn + sizeof(ELF_Dyn)); } return result; } template cmELF::StringEntry const* cmELFInternalImpl::GetDynamicSectionString( unsigned int tag) { // Short-circuit if already checked. auto dssi = this->DynamicSectionStrings.find(tag); if (dssi != this->DynamicSectionStrings.end()) { if (dssi->second.Position > 0) { return &dssi->second; } return nullptr; } // Create an entry for this tag. Assume it is missing until found. StringEntry& se = this->DynamicSectionStrings[tag]; se.Position = 0; se.Size = 0; se.IndexInSection = -1; // Try reading the dynamic section. if (!this->LoadDynamicSection()) { return nullptr; } // Get the string table referenced by the DYNAMIC section. ELF_Shdr const& sec = this->SectionHeaders[this->DynamicSectionIndex]; if (sec.sh_link >= this->SectionHeaders.size()) { this->SetErrorMessage("Section DYNAMIC has invalid string table index."); return nullptr; } ELF_Shdr const& strtab = this->SectionHeaders[sec.sh_link]; // Look for the requested entry. for (auto di = this->DynamicSectionEntries.begin(); di != this->DynamicSectionEntries.end(); ++di) { ELF_Dyn& dyn = *di; if (static_cast(dyn.d_tag) == static_cast(tag)) { // We found the tag requested. // Make sure the position given is within the string section. if (dyn.d_un.d_val >= strtab.sh_size) { this->SetErrorMessage("Section DYNAMIC references string beyond " "the end of its string section."); return nullptr; } // Seek to the position reported by the entry. unsigned long first = static_cast(dyn.d_un.d_val); unsigned long last = first; unsigned long end = static_cast(strtab.sh_size); this->Stream->seekg(strtab.sh_offset + first); // Read the string. It may be followed by more than one NULL // terminator. Count the total size of the region allocated to // the string. This assumes that the next string in the table // is non-empty, but the "chrpath" tool makes the same // assumption. bool terminated = false; char c; while (last != end && this->Stream->get(c) && !(terminated && c)) { ++last; if (c) { se.Value += c; } else { terminated = true; } } // Make sure the whole value was read. if (!(*this->Stream)) { if (tag == cmELF::TagRPath) { this->SetErrorMessage( "Dynamic section specifies unreadable DT_RPATH"); } else if (tag == cmELF::TagRunPath) { this->SetErrorMessage( "Dynamic section specifies unreadable DT_RUNPATH"); } else if (tag == cmELF::TagMipsRldMapRel) { this->SetErrorMessage( "Dynamic section specifies unreadable DT_MIPS_RLD_MAP_REL"); } else { this->SetErrorMessage("Dynamic section specifies unreadable value" " for unexpected attribute"); } se.Value = ""; return nullptr; } // The value has been read successfully. Report it. se.Position = static_cast(strtab.sh_offset + first); se.Size = last - first; se.IndexInSection = static_cast(di - this->DynamicSectionEntries.begin()); return &se; } } return nullptr; } //============================================================================ // External class implementation. const long cmELF::TagRPath = DT_RPATH; const long cmELF::TagRunPath = DT_RUNPATH; const long cmELF::TagMipsRldMapRel = DT_MIPS_RLD_MAP_REL; cmELF::cmELF(const char* fname) { // Try to open the file. auto fin = cm::make_unique(fname, std::ios::binary); // Quit now if the file could not be opened. if (!fin || !*fin) { this->ErrorMessage = "Error opening input file."; return; } // Read the ELF identification block. char ident[EI_NIDENT]; if (!fin->read(ident, EI_NIDENT)) { this->ErrorMessage = "Error reading ELF identification."; return; } if (!fin->seekg(0)) { this->ErrorMessage = "Error seeking to beginning of file."; return; } // Verify the ELF identification. if (!(ident[EI_MAG0] == ELFMAG0 && ident[EI_MAG1] == ELFMAG1 && ident[EI_MAG2] == ELFMAG2 && ident[EI_MAG3] == ELFMAG3)) { this->ErrorMessage = "File does not have a valid ELF identification."; return; } // Check the byte order in which the rest of the file is encoded. cmELFInternal::ByteOrderType order; if (ident[EI_DATA] == ELFDATA2LSB) { // File is LSB. order = cmELFInternal::ByteOrderLSB; } else if (ident[EI_DATA] == ELFDATA2MSB) { // File is MSB. order = cmELFInternal::ByteOrderMSB; } else { this->ErrorMessage = "ELF file is not LSB or MSB encoded."; return; } // Check the class of the file and construct the corresponding // parser implementation. if (ident[EI_CLASS] == ELFCLASS32) { // 32-bit ELF this->Internal = cm::make_unique>( this, std::move(fin), order); } else if (ident[EI_CLASS] == ELFCLASS64) { // 64-bit ELF this->Internal = cm::make_unique>( this, std::move(fin), order); } else { this->ErrorMessage = "ELF file class is not 32-bit or 64-bit."; return; } } cmELF::~cmELF() = default; bool cmELF::Valid() const { return this->Internal && this->Internal->GetFileType() != FileTypeInvalid; } cmELF::FileType cmELF::GetFileType() const { if (this->Valid()) { return this->Internal->GetFileType(); } return FileTypeInvalid; } std::uint16_t cmELF::GetMachine() const { if (this->Valid()) { return this->Internal->GetMachine(); } return 0; } unsigned int cmELF::GetNumberOfSections() const { if (this->Valid()) { return this->Internal->GetNumberOfSections(); } return 0; } unsigned long cmELF::GetDynamicEntryPosition(int index) const { if (this->Valid()) { return this->Internal->GetDynamicEntryPosition(index); } return 0; } cmELF::DynamicEntryList cmELF::GetDynamicEntries() const { if (this->Valid()) { return this->Internal->GetDynamicEntries(); } return cmELF::DynamicEntryList(); } std::vector cmELF::EncodeDynamicEntries( const cmELF::DynamicEntryList& dentries) const { if (this->Valid()) { return this->Internal->EncodeDynamicEntries(dentries); } return std::vector(); } bool cmELF::GetSOName(std::string& soname) { if (StringEntry const* se = this->GetSOName()) { soname = se->Value; return true; } return false; } cmELF::StringEntry const* cmELF::GetSOName() { if (this->Valid() && this->Internal->GetFileType() == cmELF::FileTypeSharedLibrary) { return this->Internal->GetSOName(); } return nullptr; } cmELF::StringEntry const* cmELF::GetRPath() { if (this->Valid() && (this->Internal->GetFileType() == cmELF::FileTypeExecutable || this->Internal->GetFileType() == cmELF::FileTypeSharedLibrary)) { return this->Internal->GetRPath(); } return nullptr; } cmELF::StringEntry const* cmELF::GetRunPath() { if (this->Valid() && (this->Internal->GetFileType() == cmELF::FileTypeExecutable || this->Internal->GetFileType() == cmELF::FileTypeSharedLibrary)) { return this->Internal->GetRunPath(); } return nullptr; } bool cmELF::IsMIPS() const { if (this->Valid()) { return this->Internal->IsMips(); } return false; } void cmELF::PrintInfo(std::ostream& os) const { if (this->Valid()) { this->Internal->PrintInfo(os); } else { os << "Not a valid ELF file.\n"; } }