/*========================================================================= Program: CMake - Cross-Platform Makefile Generator Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "cmStandardIncludes.h" // to get CMAKE_USE_ELF_PARSER first #include "cmELF.h" #include // Need the native byte order of the running CPU. #include // Include the ELF format information system header. #include //---------------------------------------------------------------------------- // Low-level byte swapping implementation. template struct cmELFByteSwapSize {}; void cmELFByteSwap(char*, cmELFByteSwapSize<1> const&) { } void cmELFByteSwap(char* data, cmELFByteSwapSize<2> const&) { char one_byte; one_byte = data[0]; data[0] = data[1]; data[1] = one_byte; } void cmELFByteSwap(char* data, cmELFByteSwapSize<4> const&) { 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; } void cmELFByteSwap(char* data, cmELFByteSwapSize<8> const&) { 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: enum ByteOrderType { ByteOrderMSB, ByteOrderLSB }; // Construct and take ownership of the file stream object. cmELFInternal(cmELF* external, cmsys::auto_ptr& fin, ByteOrderType order): External(external), Stream(*fin.release()), ByteOrder(order), ELFType(cmELF::FileTypeInvalid) { // Decide whether we need to byte swap fields. #if cmsys_CPU_ENDIAN_ID == cmsys_CPU_ENDIAN_ID_LITTLE this->NeedSwap = (this->ByteOrder == ByteOrderMSB); #elif cmsys_CPU_ENDIAN_ID == cmsys_CPU_ENDIAN_ID_BIG this->NeedSwap = (this->ByteOrder == ByteOrderLSB); #endif // We have not yet loaded the section info. this->DynamicSectionIndex = -1; } // Destruct and delete the file stream object. ~cmELFInternal() { delete &this->Stream; } // Forward to the per-class implementation. virtual unsigned int GetNumberOfSections() const = 0; virtual bool GetSOName(std::string& soname) = 0; virtual void PrintInfo(std::ostream& os) const = 0; // Return the recorded ELF type. cmELF::FileType GetFileType() const { return this->ELFType; } protected: // Data common to all ELF class implementations. // The external cmELF object. cmELF* External; // The stream from which to read. std::istream& Stream; // The byte order of the ELF file. ByteOrderType ByteOrder; // The ELF file type. cmELF::FileType ELFType; // 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; } }; //---------------------------------------------------------------------------- // Configure the implementation template for 32-bit ELF files. struct cmELFTypes32 { typedef Elf32_Ehdr ELF_Ehdr; typedef Elf32_Shdr ELF_Shdr; typedef Elf32_Dyn ELF_Dyn; typedef Elf32_Half ELF_Half; static const char* GetName() { return "32-bit"; } }; // Configure the implementation template for 32-bit ELF files. struct cmELFTypes64 { typedef Elf64_Ehdr ELF_Ehdr; typedef Elf64_Shdr ELF_Shdr; typedef Elf64_Dyn ELF_Dyn; typedef Elf64_Half ELF_Half; 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. typedef typename Types::ELF_Ehdr ELF_Ehdr; typedef typename Types::ELF_Shdr ELF_Shdr; typedef typename Types::ELF_Dyn ELF_Dyn; typedef typename Types::ELF_Half ELF_Half; // Construct with a stream and byte swap indicator. cmELFInternalImpl(cmELF* external, cmsys::auto_ptr& fin, ByteOrderType order); // Return the number of sections as specified by the ELF header. virtual unsigned int GetNumberOfSections() const { return static_cast(this->ELFHeader.e_shnum); } // Lookup the SONAME in the DYNAMIC section. virtual bool GetSOName(std::string& soname); // Print information about the ELF file. virtual void PrintInfo(std::ostream& os) const { os << "ELF " << Types::GetName(); if(this->ByteOrder == ByteOrderMSB) { os << " MSB"; } else if(this->ByteOrder == ByteOrderLSB) { os << " LSB"; } switch(this->ELFType) { 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; } os << "\n"; } private: 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); switch (dyn.d_tag) { case DT_NULL: /* dyn.d_un ignored */ break; case DT_NEEDED: cmELFByteSwap(dyn.d_un.d_val); break; case DT_PLTRELSZ: cmELFByteSwap(dyn.d_un.d_val); break; case DT_PLTGOT: cmELFByteSwap(dyn.d_un.d_ptr); break; case DT_HASH: cmELFByteSwap(dyn.d_un.d_ptr); break; case DT_STRTAB: cmELFByteSwap(dyn.d_un.d_ptr); break; case DT_SYMTAB: cmELFByteSwap(dyn.d_un.d_ptr); break; case DT_RELA: cmELFByteSwap(dyn.d_un.d_ptr); break; case DT_RELASZ: cmELFByteSwap(dyn.d_un.d_val); break; case DT_RELAENT: cmELFByteSwap(dyn.d_un.d_val); break; case DT_STRSZ: cmELFByteSwap(dyn.d_un.d_val); break; case DT_SYMENT: cmELFByteSwap(dyn.d_un.d_val); break; case DT_INIT: cmELFByteSwap(dyn.d_un.d_ptr); break; case DT_FINI: cmELFByteSwap(dyn.d_un.d_ptr); break; case DT_SONAME: cmELFByteSwap(dyn.d_un.d_val); break; case DT_RPATH: cmELFByteSwap(dyn.d_un.d_val); break; case DT_SYMBOLIC: /* dyn.d_un ignored */ break; case DT_REL: cmELFByteSwap(dyn.d_un.d_ptr); break; case DT_RELSZ: cmELFByteSwap(dyn.d_un.d_val); break; case DT_RELENT: cmELFByteSwap(dyn.d_un.d_val); break; case DT_PLTREL: cmELFByteSwap(dyn.d_un.d_val); break; case DT_DEBUG: cmELFByteSwap(dyn.d_un.d_ptr); break; case DT_TEXTREL: /* dyn.d_un ignored */ break; case DT_JMPREL: cmELFByteSwap(dyn.d_un.d_ptr); break; } } bool Read(ELF_Ehdr& x) { if(this->Stream.read(reinterpret_cast(&x), sizeof(x)) && this->NeedSwap) { ByteSwap(x); } return this->Stream? true:false; } bool Read(ELF_Shdr& x) { if(this->Stream.read(reinterpret_cast(&x), sizeof(x)) && this->NeedSwap) { ByteSwap(x); } return this->Stream? true:false; } bool Read(ELF_Dyn& x) { if(this->Stream.read(reinterpret_cast(&x), sizeof(x)) && this->NeedSwap) { ByteSwap(x); } return this->Stream? true:false; } bool LoadSectionHeader(ELF_Half 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 = 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; // Store the SOName if it has been loaded. std::string SOName; bool SONameChecked; }; //---------------------------------------------------------------------------- template cmELFInternalImpl ::cmELFInternalImpl(cmELF* external, cmsys::auto_ptr& fin, ByteOrderType order): cmELFInternal(external, fin, order) { // Initialize state. this->SONameChecked = false; // Read the main header. if(!this->Read(this->ELFHeader)) { return; } // Determine the ELF file type. switch(this->ELFHeader.e_type) { case 1: this->ELFType = cmELF::FileTypeRelocatableObject; break; case 2: this->ELFType = cmELF::FileTypeExecutable; break; case 3: this->ELFType = cmELF::FileTypeSharedLibrary; break; case 4: this->ELFType = cmELF::FileTypeCore; break; default: return; } // Load the section headers. this->SectionHeaders.resize(this->ELFHeader.e_shnum); for(ELF_Half i=0; i < this->ELFHeader.e_shnum; ++i) { if(!this->LoadSectionHeader(i)) { this->ELFType = cmELF::FileTypeInvalid; 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; } // Allocate the dynamic section entries. ELF_Shdr const& sec = this->SectionHeaders[this->DynamicSectionIndex]; int n = 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 bool cmELFInternalImpl::GetSOName(std::string& soname) { // Short-circuit if already checked. if(this->SONameChecked) { soname = this->SOName; return !soname.empty(); } this->SONameChecked = true; // Try reading the dynamic section. if(!this->LoadDynamicSection()) { return false; } // 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 false; } ELF_Shdr const& strtab = this->SectionHeaders[sec.sh_link]; // Look for the soname entry. for(typename std::vector::iterator di = this->DynamicSectionEntries.begin(); di != this->DynamicSectionEntries.end(); ++di) { ELF_Dyn& dyn = *di; if(dyn.d_tag == DT_SONAME) { this->Stream.seekg(strtab.sh_offset + dyn.d_un.d_val); char c; while(this->Stream.get(c) && c != 0) { this->SOName += c; } if(!this->Stream) { this->SetErrorMessage("Dynamic section specifies unreadable SONAME."); this->SOName = ""; return false; } soname = this->SOName; return true; } } return false; } //============================================================================ // External class implementation. //---------------------------------------------------------------------------- cmELF::cmELF(const char* fname): Internal(0) { // Try to open the file. cmsys::auto_ptr fin(new std::ifstream(fname)); // Quit now if the file could not be opened. if(!fin.get() || !*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 = new cmELFInternalImpl(this, fin, order); } else if(ident[EI_CLASS] == ELFCLASS64) { // 64-bit ELF this->Internal = new cmELFInternalImpl(this, fin, order); } else { this->ErrorMessage = "ELF file class is not 32-bit or 64-bit."; return; } } //---------------------------------------------------------------------------- cmELF::~cmELF() { delete this->Internal; } //---------------------------------------------------------------------------- bool cmELF::Valid() const { return this->Internal && this->Internal->GetFileType() != FileTypeInvalid; } //---------------------------------------------------------------------------- cmELF::FileType cmELF::GetFileType() const { if(this->Valid()) { return this->Internal->GetFileType(); } else { return FileTypeInvalid; } } //---------------------------------------------------------------------------- unsigned int cmELF::GetNumberOfSections() const { if(this->Valid()) { return this->Internal->GetNumberOfSections(); } else { return 0; } } //---------------------------------------------------------------------------- bool cmELF::GetSOName(std::string& soname) { if(this->Valid() && this->Internal->GetFileType() == cmELF::FileTypeSharedLibrary) { return this->Internal->GetSOName(soname); } else { return false; } } //---------------------------------------------------------------------------- void cmELF::PrintInfo(std::ostream& os) const { if(this->Valid()) { this->Internal->PrintInfo(os); } else { os << "Not a valid ELF file.\n"; } }