//===- ELFObjectWriter.h --------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_LD_ELFOBJECTWRITER_H_
#define MCLD_LD_ELFOBJECTWRITER_H_
#include "mcld/LD/ObjectWriter.h"
#include "mcld/Support/FileOutputBuffer.h"

#include <cassert>

namespace mcld {

class EhFrame;
class GNULDBackend;
class LDSection;
class LinkerConfig;
class Module;
class RelocData;
class SectionData;

/** \class ELFObjectWriter
 *  \brief ELFObjectWriter writes the target-independent parts of object files.
 *  ELFObjectWriter reads a MCLDFile and writes into raw_ostream
 *
 */
class ELFObjectWriter : public ObjectWriter {
 public:
  ELFObjectWriter(GNULDBackend& pBackend, const LinkerConfig& pConfig);

  ~ELFObjectWriter();

  std::error_code writeObject(Module& pModule, FileOutputBuffer& pOutput);

  size_t getOutputSize(const Module& pModule) const;

 private:
  void writeSection(Module& pModule,
                    FileOutputBuffer& pOutput,
                    LDSection* section);

  const GNULDBackend& target() const { return m_Backend; }
  GNULDBackend& target() { return m_Backend; }

  // writeELFHeader - emit ElfXX_Ehdr
  template <size_t SIZE>
  void writeELFHeader(const LinkerConfig& pConfig,
                      const Module& pModule,
                      FileOutputBuffer& pOutput) const;

  uint64_t getEntryPoint(const LinkerConfig& pConfig,
                         const Module& pModule) const;

  // emitSectionHeader - emit ElfXX_Shdr
  template <size_t SIZE>
  void emitSectionHeader(const Module& pModule,
                         const LinkerConfig& pConfig,
                         FileOutputBuffer& pOutput) const;

  // emitProgramHeader - emit ElfXX_Phdr
  template <size_t SIZE>
  void emitProgramHeader(FileOutputBuffer& pOutput) const;

  // emitShStrTab - emit .shstrtab
  void emitShStrTab(const LDSection& pShStrTab,
                    const Module& pModule,
                    FileOutputBuffer& pOutput);

  void emitSectionData(const LDSection& pSection, MemoryRegion& pRegion) const;

  void emitEhFrame(Module& pModule,
                   EhFrame& pFrame,
                   MemoryRegion& pRegion) const;

  void emitRelocation(const LinkerConfig& pConfig,
                      const LDSection& pSection,
                      MemoryRegion& pRegion) const;

  // emitRel - emit ElfXX_Rel
  template <size_t SIZE>
  void emitRel(const LinkerConfig& pConfig,
               const RelocData& pRelocData,
               MemoryRegion& pRegion) const;

  // emitRela - emit ElfXX_Rela
  template <size_t SIZE>
  void emitRela(const LinkerConfig& pConfig,
                const RelocData& pRelocData,
                MemoryRegion& pRegion) const;

  // getSectEntrySize - compute ElfXX_Shdr::sh_entsize
  template <size_t SIZE>
  uint64_t getSectEntrySize(const LDSection& pSection) const;

  // getSectLink - compute ElfXX_Shdr::sh_link
  uint64_t getSectLink(const LDSection& pSection,
                       const LinkerConfig& pConfig) const;

  // getSectInfo - compute ElfXX_Shdr::sh_info
  uint64_t getSectInfo(const LDSection& pSection) const;

  template <size_t SIZE>
  uint64_t getLastStartOffset(const Module& pModule) const {
    assert(0 && "Call invalid ELFObjectWriter::getLastStartOffset");
    return 0;
  }

  void emitSectionData(const SectionData& pSD, MemoryRegion& pRegion) const;

 private:
  GNULDBackend& m_Backend;

  const LinkerConfig& m_Config;
};

template <>
uint64_t ELFObjectWriter::getLastStartOffset<32>(const Module& pModule) const;

template <>
uint64_t ELFObjectWriter::getLastStartOffset<64>(const Module& pModule) const;

}  // namespace mcld

#endif  // MCLD_LD_ELFOBJECTWRITER_H_