//===- lib/ReaderWriter/ELF/OutputELFWriter.h ----------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef LLD_READER_WRITER_ELF_OUTPUT_WRITER_H
#define LLD_READER_WRITER_ELF_OUTPUT_WRITER_H

#include "ELFFile.h"
#include "TargetLayout.h"
#include "lld/Core/Writer.h"
#include "llvm/ADT/StringSet.h"

namespace lld {
class ELFLinkingContext;

namespace elf {
using namespace llvm;
using namespace llvm::object;

//  OutputELFWriter Class
//
/// \brief This acts as the base class for all the ELF writers that are output
/// for emitting an ELF output file. This class also acts as a common class for
/// creating static and dynamic executables. All the function in this class
/// can be overridden and an appropriate writer be created
template<class ELFT>
class OutputELFWriter : public ELFWriter {
public:
  typedef Elf_Shdr_Impl<ELFT> Elf_Shdr;
  typedef Elf_Sym_Impl<ELFT> Elf_Sym;
  typedef Elf_Dyn_Impl<ELFT> Elf_Dyn;

  OutputELFWriter(ELFLinkingContext &ctx, TargetLayout<ELFT> &layout);

protected:
  // build the sections that need to be created
  virtual void createDefaultSections();

  // Build all the output sections
  void buildChunks(const File &file) override;

  // Build the output file
  virtual std::error_code buildOutput(const File &file);

  // Setup the ELF header.
  virtual std::error_code setELFHeader();

  // Write the file to the path specified
  std::error_code writeFile(const File &File, StringRef path) override;

  // Write to the output file.
  virtual std::error_code writeOutput(const File &file, StringRef path);

  // Get the size of the output file that the linker would emit.
  virtual uint64_t outputFileSize() const;

  // Build the atom to address map, this has to be called
  // before applying relocations
  virtual void buildAtomToAddressMap(const File &file);

  // Build the symbol table for static linking
  virtual void buildStaticSymbolTable(const File &file);

  // Build the dynamic symbol table for dynamic linking
  virtual void buildDynamicSymbolTable(const File &file);

  // Build the section header table
  virtual void buildSectionHeaderTable();

  // Assign sections that have no segments such as the symbol table,
  // section header table, string table etc
  virtual void assignSectionsWithNoSegments();

  // Add any runtime files and their atoms to the output
  void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override;

  // Finalize the default atom values
  virtual void finalizeDefaultAtomValues();

  // This is called by the write section to apply relocations
  uint64_t addressOfAtom(const Atom *atom) override {
    auto addr = _atomToAddressMap.find(atom);
    return addr == _atomToAddressMap.end() ? 0 : addr->second;
  }

  // This is a hook for creating default dynamic entries
  virtual void createDefaultDynamicEntries() {}

  /// \brief Create symbol table.
  virtual unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable();

  /// \brief create dynamic table.
  virtual unique_bump_ptr<DynamicTable<ELFT>> createDynamicTable();

  /// \brief create dynamic symbol table.
  virtual unique_bump_ptr<DynamicSymbolTable<ELFT>>
      createDynamicSymbolTable();

  /// \brief Create entry in the dynamic symbols table for this atom.
  virtual bool isDynSymEntryRequired(const SharedLibraryAtom *sla) const {
    return _layout.isReferencedByDefinedAtom(sla);
  }

  /// \brief Create DT_NEEDED dynamic tage for the shared library.
  virtual bool isNeededTagRequired(const SharedLibraryAtom *sla) const {
    return false;
  }

  /// \brief Process undefined symbols that left after resolution step.
  virtual void processUndefinedSymbol(StringRef symName,
                                      RuntimeFile<ELFT> &file) const;

  /// \brief Assign addresses to atoms marking section's start and end.
  void updateScopeAtomValues(StringRef sym, StringRef sec);

  llvm::BumpPtrAllocator _alloc;

  ELFLinkingContext &_ctx;
  TargetHandler &_targetHandler;

  typedef llvm::DenseMap<const Atom *, uint64_t> AtomToAddress;
  AtomToAddress _atomToAddressMap;
  TargetLayout<ELFT> &_layout;
  unique_bump_ptr<ELFHeader<ELFT>> _elfHeader;
  unique_bump_ptr<ProgramHeader<ELFT>> _programHeader;
  unique_bump_ptr<SymbolTable<ELFT>> _symtab;
  unique_bump_ptr<StringTable<ELFT>> _strtab;
  unique_bump_ptr<StringTable<ELFT>> _shstrtab;
  unique_bump_ptr<SectionHeader<ELFT>> _shdrtab;
  unique_bump_ptr<EHFrameHeader<ELFT>> _ehFrameHeader;
  /// \name Dynamic sections.
  /// @{
  unique_bump_ptr<DynamicTable<ELFT>> _dynamicTable;
  unique_bump_ptr<DynamicSymbolTable<ELFT>> _dynamicSymbolTable;
  unique_bump_ptr<StringTable<ELFT>> _dynamicStringTable;
  unique_bump_ptr<HashSection<ELFT>> _hashTable;
  llvm::StringSet<> _soNeeded;
  /// @}

private:
  static StringRef maybeGetSOName(Node *node);
  void updateScopeAtomValues(StringRef start, StringRef end, StringRef sec);
};

} // namespace elf
} // namespace lld

#endif // LLD_READER_WRITER_ELF_OUTPUT_WRITER_H
