//===- ELFReader.h --------------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_ELF_READER_INTERFACE_H
#define MCLD_ELF_READER_INTERFACE_H
#ifdef ENABLE_UNITTEST
#include <gtest.h>
#endif

#include <llvm/ADT/StringRef.h>
#include <llvm/Support/ELF.h>
#include <llvm/Support/Host.h>

#include <mcld/MC/MCLDInfo.h>
#include <mcld/MC/MCLDInput.h>
#include <mcld/MC/MCLinker.h>
#include <mcld/LD/Fragment.h>
#include <mcld/LD/FillFragment.h>
#include <mcld/LD/AlignFragment.h>
#include <mcld/LD/RegionFragment.h>
#include <mcld/LD/ResolveInfo.h>
#include <mcld/LD/LDContext.h>
#include <mcld/Target/GNULDBackend.h>
#include <mcld/Support/MemoryRegion.h>
#include <mcld/Support/MsgHandling.h>

namespace mcld
{

/** \class ELFReaderIF
 *  \brief ELFReaderIF provides common interface for all kind of ELF readers.
 */
class ELFReaderIF
{
public:
  ELFReaderIF(GNULDBackend& pBackend)
    : m_Backend(pBackend)
  { }

  virtual ~ELFReaderIF() { }

  /// ELFHeaderSize - return the size of the ELFHeader
  virtual size_t getELFHeaderSize() const = 0;

  /// isELF - is this a ELF file
  virtual bool isELF(void* pELFHeader) const = 0;

  /// isMyEndian - is this ELF file in the same endian to me?
  virtual bool isMyEndian(void* pELFHeader) const = 0;

  /// isMyMachine - is this ELF file generated for the same machine.
  virtual bool isMyMachine(void* pELFHeader) const = 0;

  /// fileType - the file type of this file
  virtual MCLDFile::Type fileType(void* pELFHeader) const = 0;

  /// target - the target backend
  GNULDBackend& target()
  { return m_Backend; }

  /// target - the target backend
  const GNULDBackend& target() const
  { return m_Backend; }

  /// readSectionHeaders - read ELF section header table and create LDSections
  virtual bool readSectionHeaders(Input& pInput,
                                  MCLinker& pLinker,
                                  void* pELFHeader) const = 0;

  /// readRegularSection - read a regular section and create fragments.
  virtual bool readRegularSection(Input& pInput,
                                  MCLinker& pLinker,
                                  LDSection& pSectHdr) const = 0;

  /// readRegularSection - read a target section and create fragments.
  virtual bool readTargetSection(Input& pInput,
                                 MCLinker& pLinker,
                                 LDSection& pSectHdr) = 0;

  /// readSymbols - read ELF symbols and create LDSymbol
  virtual bool readSymbols(Input& pInput,
                           MCLinker& pLinker,
                           const MemoryRegion& pRegion,
                           const char* StrTab) const = 0;

  /// readSymbol - read a symbol from the given Input and index in symtab
  /// This is used to get the signature of a group section.
  virtual ResolveInfo* readSymbol(Input& pInput,
                                  LDSection& pSymTab,
                                  MCLDInfo& pLDInfo,
                                  uint32_t pSymIdx) const = 0;

  /// readRela - read ELF rela and create Relocation
  virtual bool readRela(Input& pInput,
                        MCLinker& pLinker,
                        LDSection& pSection,
                        const MemoryRegion& pRegion) const = 0;

  /// readRel - read ELF rel and create Relocation
  virtual bool readRel(Input& pInput,
                       MCLinker& pLinker,
                       LDSection& pSection,
                       const MemoryRegion& pRegion) const = 0;

  bool readEhFrame(Input& pInput,
                   MCLinker& pLinker,
                   LDSection& pSection) const;

  /// readDynamic - read ELF .dynamic in input dynobj
  virtual bool readDynamic(Input& pInput) const = 0;

protected:
  /// LinkInfo - some section needs sh_link and sh_info, remember them.
  struct LinkInfo {
    LDSection* section;
    uint32_t sh_link;
    uint32_t sh_info;
  };

  typedef std::vector<LinkInfo> LinkInfoList;

protected:
  LDFileFormat::Kind getLDSectionKind(uint32_t pType, const char* pName) const;

  ResolveInfo::Type getSymType(uint8_t pInfo, uint16_t pShndx) const;

  ResolveInfo::Desc getSymDesc(uint16_t pShndx, const Input& pInput) const;

  ResolveInfo::Binding getSymBinding(uint8_t pBinding,
                                     uint16_t pShndx,
                                     uint8_t pVisibility) const;

  uint64_t getSymValue(uint64_t pValue,
                       uint16_t pShndx,
                       const Input& pInput) const;

  FragmentRef* getSymFragmentRef(Input& pInput,
                                 MCLinker& pLinker,
                                 uint16_t pShndx,
                                 uint32_t pOffset) const;

  ResolveInfo::Visibility getSymVisibility(uint8_t pVis) const;

private:
  GNULDBackend& m_Backend;
};

/** \class ELFReader
 *  \brief ELFReader is a template scaffolding for partial specification.
 */
template<size_t BIT, bool LITTLEENDIAN>
class ELFReader
{ };

/** \class ELFReader<32, true>
 *  \brief ELFReader<32, true> is a 32-bit, little endian ELFReader.
 */
template<>
class ELFReader<32, true> : public ELFReaderIF
{
public:
  typedef llvm::ELF::Elf32_Ehdr ELFHeader;
  typedef llvm::ELF::Elf32_Shdr SectionHeader;
  typedef llvm::ELF::Elf32_Sym  Symbol;
  typedef llvm::ELF::Elf32_Rel  Rel;
  typedef llvm::ELF::Elf32_Rela Rela;

public:
  inline ELFReader(GNULDBackend& pBackend);

  inline ~ELFReader();

  /// ELFHeaderSize - return the size of the ELFHeader
  inline size_t getELFHeaderSize() const
  { return sizeof(ELFHeader); }

  /// isELF - is this a ELF file
  inline bool isELF(void* pELFHeader) const;

  /// isMyEndian - is this ELF file in the same endian to me?
  inline bool isMyEndian(void* pELFHeader) const;

  /// isMyMachine - is this ELF file generated for the same machine.
  inline bool isMyMachine(void* pELFHeader) const;

  /// fileType - the file type of this file
  inline MCLDFile::Type fileType(void* pELFHeader) const;

  /// readSectionHeaders - read ELF section header table and create LDSections
  inline bool readSectionHeaders(Input& pInput,
                          MCLinker& pLinker,
                          void* pELFHeader) const;

  /// readRegularSection - read a regular section and create fragments.
  inline bool readRegularSection(Input& pInput,
                                 MCLinker& pLinker,
                                 LDSection& pInputSectHdr) const;

  /// readRegularSection - read a target section and create fragments.
  inline bool readTargetSection(Input& pInput,
                                MCLinker& pLinker,
                                LDSection& pInputSectHdr);

  /// readSymbols - read ELF symbols and create LDSymbol
  inline bool readSymbols(Input& pInput,
                          MCLinker& pLinker,
                          const MemoryRegion& pRegion,
                          const char* StrTab) const;

  /// readSymbol - read a symbol from the given Input and index in symtab
  /// This is used to get the signature of a group section.
  inline ResolveInfo* readSymbol(Input& pInput,
                                 LDSection& pSymTab,
                                 MCLDInfo& pLDInfo,
                                 uint32_t pSymIdx) const;

  /// readRela - read ELF rela and create Relocation
  inline bool readRela(Input& pInput,
                       MCLinker& pLinker,
                       LDSection& pSection,
                       const MemoryRegion& pRegion) const;

  /// readRel - read ELF rel and create Relocation
  inline bool readRel(Input& pInput,
                      MCLinker& pLinker,
                      LDSection& pSection,
                      const MemoryRegion& pRegion) const;

  /// readDynamic - read ELF .dynamic in input dynobj
  inline bool readDynamic(Input& pInput) const;
};

#include "ELFReader.tcc"

} // namespace of mcld

#endif