//===- ELFDynamic.h -------------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_TARGET_ELFDYNAMIC_H
#define MCLD_TARGET_ELFDYNAMIC_H

#include <mcld/LD/LDSection.h>
#include <mcld/Support/FileOutputBuffer.h>
#include <llvm/Support/ELF.h>
#include <vector>
#include <cstring>

namespace mcld {

class ELFFileFormat;
class GNULDBackend;
class LinkerConfig;

namespace elf_dynamic {

/** \class EntryIF
*  \brief EntryIF provides a common interface for one entry in the dynamic
*  section
*/
class EntryIF
{
protected:
  EntryIF();

public:
  virtual ~EntryIF();

  virtual EntryIF* clone() const = 0;
  virtual size_t size() const = 0;
  virtual size_t symbolSize() const = 0;
  virtual size_t relSize() const = 0;
  virtual size_t relaSize() const = 0;
  virtual size_t emit(uint8_t* pAddress) const = 0;
  virtual void setValue(uint64_t pTag, uint64_t pValue) = 0;
};

template<size_t BITNUMBER, bool LITTLEENDIAN>
class Entry
{ };

template<>
class Entry<32, true> : public EntryIF
{
public:
  typedef llvm::ELF::Elf32_Dyn  Pair;
  typedef llvm::ELF::Elf32_Sym  Symbol;
  typedef llvm::ELF::Elf32_Rel  Rel;
  typedef llvm::ELF::Elf32_Rela Rela;

public:
  inline Entry();

  inline ~Entry();

  Entry* clone() const
  { return new Entry(); }

  size_t size() const
  { return sizeof(Pair); }

  size_t symbolSize() const
  { return sizeof(Symbol); }

  size_t relSize() const
  { return sizeof(Rel); }

  size_t relaSize() const
  { return sizeof(Rela); }

  inline void setValue(uint64_t pTag, uint64_t pValue);

  inline size_t emit(uint8_t* pAddress) const;

private:
  Pair m_Pair;
};

template<>
class Entry<64, true> : public EntryIF
{
public:
  typedef llvm::ELF::Elf64_Dyn  Pair;
  typedef llvm::ELF::Elf64_Sym  Symbol;
  typedef llvm::ELF::Elf64_Rel  Rel;
  typedef llvm::ELF::Elf64_Rela Rela;

public:
  inline Entry();

  inline ~Entry();

  Entry* clone() const
  { return new Entry(); }

  size_t size() const
  { return sizeof(Pair); }

  size_t symbolSize() const
  { return sizeof(Symbol); }

  size_t relSize() const
  { return sizeof(Rel); }

  size_t relaSize() const
  { return sizeof(Rela); }

  inline void setValue(uint64_t pTag, uint64_t pValue);

  inline size_t emit(uint8_t* pAddress) const;

private:
  Pair m_Pair;
};

#include "ELFDynamic.tcc"

} // namespace of elf_dynamic

/** \class ELFDynamic
 *  \brief ELFDynamic is the .dynamic section in ELF shared and executable
 *  files.
 */
class ELFDynamic
{
public:
  typedef std::vector<elf_dynamic::EntryIF*> EntryListType;
  typedef EntryListType::iterator iterator;
  typedef EntryListType::const_iterator const_iterator;

public:
  ELFDynamic(const GNULDBackend& pBackend, const LinkerConfig& pConfig);

  virtual ~ELFDynamic();

  size_t size() const;

  size_t entrySize() const;

  size_t numOfBytes() const;

  /// reserveEntries - reserve entries
  void reserveEntries(const ELFFileFormat& pFormat);

  /// reserveNeedEntry - reserve on DT_NEED entry.
  void reserveNeedEntry();

  /// applyEntries - apply entries
  void applyEntries(const ELFFileFormat& pFormat);

  void applySoname(uint64_t pStrTabIdx);

  const_iterator needBegin() const { return m_NeedList.begin(); }
  iterator       needBegin()       { return m_NeedList.begin(); }

  const_iterator needEnd() const { return m_NeedList.end(); }
  iterator       needEnd()       { return m_NeedList.end(); }

  /// emit
  void emit(const LDSection& pSection, MemoryRegion& pRegion) const;

protected:
  /// reserveTargetEntries - reserve target dependent entries
  virtual void reserveTargetEntries(const ELFFileFormat& pFormat) = 0;

  /// applyTargetEntries - apply target-dependant
  virtual void applyTargetEntries(const ELFFileFormat& pFormat) = 0;

protected:
  void reserveOne(uint64_t pTag);

  void applyOne(uint64_t pTag, uint64_t pValue);

  size_t symbolSize() const;

  const LinkerConfig& config() const { return m_Config; }

private:
  EntryListType m_EntryList;
  EntryListType m_NeedList;
  elf_dynamic::EntryIF* m_pEntryFactory;
  const GNULDBackend& m_Backend;
  const LinkerConfig& m_Config;

  // The entry reserved and the entry being applied are not must matched.
  // For better performance, we use a simple counter and apply entry one-by-one
  // by the counter. m_Idx is the counter indicating to the entry being applied.
  size_t m_Idx;
};

} // namespace of mcld

#endif