/*
 * Copyright (c) 2008-2015 Freescale Semiconductor, Inc.
 * Copyright 2016-2018 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#if !defined(_ELFSourceFile_h_)
#define _ELFSourceFile_h_

#include "SourceFile.h"
#include "StELFFile.h"
#include "smart_ptr.h"
#include "DataSource.h"
#include "DataTarget.h"
#include "ELF.h"

namespace elftosb
{
//! Set of supported compiler toolsets.
enum elf_toolset_t
{
    kUnknownToolset, //!< Unknown.
    kGHSToolset,     //!< Green Hills Software MULTI
    kGCCToolset,     //!< GNU GCC
    kADSToolset,     //!< ARM UK RealView
    kIARToolset,     //!< IAR
    kKDSToolset,     //!< KDS
    kKeilToolset     //!< Keil
};

//! Options for handling the .secinfo section in GHS-produced ELF files.
enum secinfo_clear_t
{
    // Default value for the .secinfo action.
    kSecinfoDefault,

    //! Ignore the .secinfo section if present. The standard ELF loading
    //! rules are followed.
    kSecinfoIgnore,

    //! The boot ROM clears only those SHT_NOBITS sections present in .secinfo.
    kSecinfoROMClear,

    //! The C startup is responsible for clearing sections. No fill commands
    //! are generated for any SHT_NOBITS sections.
    kSecinfoCStartupClear
};

/*!
 * \brief Executable and Loading Format (ELF) source file.
 */
class ELFSourceFile : public SourceFile
{
public:
    //! \brief Default constructor.
    ELFSourceFile(const std::string &path);

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

    //! \brief Identifies whether the stream contains an ELF file.
    static bool isELFFile(std::istream &stream);

    //! \name Opening and closing
    //@{
    //! \brief Opens the file.
    virtual void open();

    //! \brief Closes the file.
    virtual void close();
    //@}

    //! \name Format capabilities
    //@{
    virtual bool supportsNamedSections() const { return true; }
    virtual bool supportsNamedSymbols() const { return true; }
    //@}

    //! \name Data source
    //@{
    //! \brief Creates a data source from the entire file.
    virtual DataSource *createDataSource();

    //! \brief Creates a data source from one or more sections of the file.
    virtual DataSource *createDataSource(StringMatcher &matcher);
    //@}

    //! \name Entry point
    //@{
    //! \brief Returns true if an entry point was set in the file.
    virtual bool hasEntryPoint();

    //! \brief Returns the entry point address.
    virtual uint32_t getEntryPointAddress();
    //@}

    //! \name Data target
    //@{
    virtual DataTarget *createDataTargetForSection(const std::string &section);
    virtual DataTarget *createDataTargetForSymbol(const std::string &symbol);
    //@}

    //! \name Symbols
    //@{
    //! \brief Returns whether a symbol exists in the source file.
    virtual bool hasSymbol(const std::string &name);

    //! \brief Returns the value of a symbol.
    virtual uint32_t getSymbolValue(const std::string &name);

    //! \brief Returns the size of a symbol.
    virtual unsigned getSymbolSize(const std::string &name);
    //@}

    //! \name Direct ELF format access
    //@{
    //! \brief Returns the underlying StELFFile object.
    StELFFile *getELFFile() { return m_file; }
    //! \brief Gets information about a symbol in the ELF file.
    bool lookupSymbol(const std::string &name, Elf32_Sym &info);
    //@}

protected:
    smart_ptr<StELFFile> m_file;     //!< Parser for the ELF file.
    elf_toolset_t m_toolset;         //!< Toolset that produced the ELF file.
    secinfo_clear_t m_secinfoOption; //!< How to deal with the .secinfo section. Ignored if the toolset is not GHS.

protected:
    //! \brief Parses the toolset option value.
    elf_toolset_t readToolsetOption();

    //! \brief Reads the secinfoClear option.
    secinfo_clear_t readSecinfoClearOption();

public:
    /*!
     * \brief A data source with ELF file sections as the contents.
     *
     * Each segment of this data source corresponds directly with a named section
     * of the ELF file it represents. When the data source is created, it contains
     * no segments. Segments are created with the addSection() method, which takes
     * the index of an ELF section and creates a corresponding segment.
     *
     * Two segment subclasses are used with this data source. The first, ProgBitsSegment,
     * is used to represent sections whose type is #SHT_PROGBITS. These sections have
     * binary data stored in the ELF file. The second segment type is NoBitsSegment.
     * It is used to represent sections whose type is #SHT_NOBITS. These sections have
     * no data, but simply allocate a region of memory to be filled with zeroes.
     * As such, the NoBitsSegment class is a subclass of DataSource::PatternSegment.
     */
    class ELFDataSource : public DataSource
    {
    public:
        /*!
         * \brief Represents one named #SHT_PROGBITS section within the ELF file.
         */
        class ProgBitsSegment : public DataSource::Segment
        {
        public:
            ProgBitsSegment(ELFDataSource &source, StELFFile *elf, unsigned index);

            virtual unsigned getData(unsigned offset, unsigned maxBytes, uint8_t *buffer);
            virtual unsigned getLength();

            virtual bool hasNaturalLocation() { return true; }
            virtual uint32_t getBaseAddress();

        protected:
            StELFFile *m_elf;        //!< The format parser instance for this ELF file.
            unsigned m_sectionIndex; //!< The index of the section this segment represents.
        };

        /*!
         * \brief Represents one named #SHT_NOBITS section within the ELF file.
         *
         * This segment class is a subclass of DataSource::PatternSegment since it
         * represents a region of memory to be filled with zeroes.
         */
        class NoBitsSegment : public DataSource::PatternSegment
        {
        public:
            NoBitsSegment(ELFDataSource &source, StELFFile *elf, unsigned index);

            virtual unsigned getLength();

            virtual bool hasNaturalLocation() { return true; }
            virtual uint32_t getBaseAddress();

        protected:
            StELFFile *m_elf;        //!< The format parser instance for this ELF file.
            unsigned m_sectionIndex; //!< The index of the section this segment represents.
        };

    public:
        //! \brief Default constructor.
        ELFDataSource(StELFFile *elf)
            : DataSource()
            , m_elf(elf)
        {
        }

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

        //! Set the option to control .secinfo usage.
        inline void setSecinfoOption(secinfo_clear_t option) { m_secinfoOption = option; }
        //! \brief Adds the ELF section at position \a sectionIndex to the data source.
        void addSection(unsigned sectionIndex);

        //! \brief Returns the number of segments in the source.
        virtual unsigned getSegmentCount() { return (unsigned)m_segments.size(); }
        //! \brief Returns the segment at position \a index.
        virtual DataSource::Segment *getSegmentAt(unsigned index) { return m_segments[index]; }

    protected:
        StELFFile *m_elf;                //!< The ELF file parser.
        secinfo_clear_t m_secinfoOption; //!< How to deal with the .secinfo section. Ignored if the toolset is not GHS.

        typedef std::vector<DataSource::Segment *> segment_vector_t; //!< A list of segment instances.
        segment_vector_t m_segments;                                 //!< The segments of this data source.
    };
};

}; // namespace elftosb

#endif // _ELFSourceFile_h_
