/*
* File:        StELFFile.h
*
* Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
* See included license file for license details.
*/
#if !defined(_StELFFile_h_)
#define _StELFFile_h_

#include "stdafx.h"
#include <string>
#include <vector>
#include <map>
#include <iostream>
#include <stdexcept>
#include "ELF.h"

//! Variations of the ARM ELF format.
typedef enum {
       eARMVariant = 1,        //!< Standard ARM ELF specification.
       eGHSVariant,            //!< Green Hills Software variant.
       eGCCVariant             //!< GNU Compiler Collection variant.
} ELFVariant_t;

//! Possible ARM ELF symbol types.
typedef enum {
       eUnknownSymbol,
       eARMSymbol,
       eThumbSymbol,
       eDataSymbol
} ARMSymbolType_t;

/*!
* \brief Parser for Executable and Linking Format (ELF) files.
*
* The stream passed into the constructor needs to stay open for the life
* of the object. This is because calls to getSectionDataAtIndex() and
* getSegmentDataAtIndex() read the data directly from the input stream.
*/
class StELFFile
{
public:
       typedef std::vector<Elf32_Shdr>::const_iterator const_section_iterator;
       typedef std::vector<Elf32_Phdr>::const_iterator const_segment_iterator;

public:
       //! \brief Constructor.
       StELFFile(std::istream & inStream);

       //! \brief Destructor.
       virtual ~StELFFile();

       //! \name File format variant
       //@{
       //! \brief Return the ELF format variant to which this file is set.
       virtual ELFVariant_t ELFVariant() { return m_elfVariant; }

       //! \brief Set the ELF format variation to either #eARMVariant or #eGHSVariant.
       virtual void setELFVariant(ELFVariant_t variant) { m_elfVariant = variant; }
       //@}

       //! \name File name
       //@{
       virtual void setName(const std::string & inName) { m_name = inName; }
       virtual std::string getName() const { return m_name; }
       //@}

       //! \name ELF header
       //@{
       //! \brief Returns the ELF file header.
       inline const Elf32_Ehdr & getFileHeader() const { return m_header; }
       //@}

       //! \name Sections
       //! Methods pertaining to the object file's sections.
       //@{
       //! \brief Returns the number of sections in the file.
       inline unsigned getSectionCount() const { return static_cast<unsigned>(m_sectionHeaders.size()); }

       //! \brief Returns a reference to section number \a inIndex.
       const Elf32_Shdr & getSectionAtIndex(unsigned inIndex) const;

       inline const_section_iterator getSectionBegin() const { return m_sectionHeaders.begin(); }
       inline const_section_iterator getSectionEnd() const { return m_sectionHeaders.end(); }

       //! \brief Returns the index of the section with the name \a inName.
       unsigned getIndexOfSectionWithName(const std::string & inName);

       //! \brief Returns the data for the section.
       uint8_t * getSectionDataAtIndex(unsigned inIndex);

       //! \brief Returns the data for the section.
       uint8_t * getSectionData(const_section_iterator inSection);
       //@}

       //! \name Segments
       //! Methods for accessing the file's program headers for segments.
       //@{
       //! \brief Returns the number of segments, or program headers, in the file.
       inline unsigned getSegmentCount() const { return static_cast<unsigned>(m_programHeaders.size()); }

       //! \brief Returns a reference to the given segment.
       const Elf32_Phdr & getSegmentAtIndex(unsigned inIndex) const;

       inline const_segment_iterator getSegmentBegin() const { return m_programHeaders.begin(); }
       inline const_segment_iterator getSegmentEnd() const { return m_programHeaders.end(); }

       //! \brief Returns the data of the specified segment.
       uint8_t * getSegmentDataAtIndex(unsigned inIndex);

       //! \brief Returns the data of the specified segment.
       uint8_t * getSegmentData(const_segment_iterator inSegment);
       //@}

       //! \name String table
       //! Methods for accessing the string tables.
       //@{
       //! \brief Returns a string from the file's section name string table.
       std::string getSectionNameAtIndex(unsigned inIndex);

       //! \brief Returns a string from any string table in the object file.
       std::string getStringAtIndex(unsigned inStringTableSectionIndex, unsigned inStringIndex);
       //@}

       //! \name Symbol table
       //! Methods for accessing the object file's symbol table. Currently only
       //! a single symbol table with the section name ".symtab" is supported.
       //@{
       //! \brief Returns the number of symbols in the default ".symtab" symbol table.
       unsigned getSymbolCount();

       //! \brief Returns the symbol with index \a inIndex.
       const Elf32_Sym & getSymbolAtIndex(unsigned inIndex);

       //! \brief Returns the section index of the string table containing symbol names.
       unsigned getSymbolNameStringTableIndex() const;

       //! \brief Returns the name of the symbol described by \a inSymbol.
       std::string getSymbolName(const Elf32_Sym & inSymbol);

       unsigned getIndexOfSymbolAtAddress(uint32_t symbolAddress, bool strict=true);

       ARMSymbolType_t getTypeOfSymbolAtIndex(unsigned symbolIndex);
       //@}

       //! \name Debugging
       //@{
       void dumpSections();
       void dumpSymbolTable();
       //@}

protected:
       std::istream & m_stream;        //!< The source stream for the ELF file.
       ELFVariant_t m_elfVariant;      //!< Variant of the ARM ELF format specification.
       std::string m_name;                     //!< File name. (optional)
       Elf32_Ehdr m_header;    //!< The ELF file header.
       std::vector<Elf32_Shdr> m_sectionHeaders;       //!< All of the section headers.
       std::vector<Elf32_Phdr> m_programHeaders;       //!< All of the program headers.
       unsigned m_symbolTableIndex;    //!< Index of ".symtab" section, or #SHN_UNDEF if not present.

       /*!
        * Little structure containing information about cached section data.
        */
       struct SectionDataInfo
       {
               uint8_t * m_data;       //!< Pointer to section data.
               unsigned m_size;        //!< Section data size in bytes.
               bool m_swapped; //!< Has this section been byte swapped yet? Used for symbol table.
       };
       typedef std::map<unsigned, SectionDataInfo> SectionDataMap;
       SectionDataMap m_sectionDataCache;      //!< Cached data of sections.

       //! \brief Reads a section's data either from cache or from disk.
       SectionDataInfo & getCachedSectionData(unsigned inSectionIndex);

       //! \brief Reads the file, section, and program headers into memory.
       void readFileHeaders();

       uint8_t * readSectionData(const Elf32_Shdr & inHeader);
       uint8_t * readSegmentData(const Elf32_Phdr & inHeader);

       //! \brief Byte swaps the symbol table data into host endianness.
       void byteSwapSymbolTable(const Elf32_Shdr & header, SectionDataInfo & info);
};

/*!
* \brief Simple exception thrown to indicate an error in the input ELF file format.
*/
class StELFFileException : public std::runtime_error
{
public:
       //! \brief Default constructor.
       StELFFileException(const std::string & inMessage) : std::runtime_error(inMessage) {}
};

#endif // _StELFFile_h_