//===- lib/ReaderWriter/ELF/SectionChunks.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_SECTION_CHUNKS_H
#define LLD_READER_WRITER_ELF_SECTION_CHUNKS_H

#include "Chunk.h"
#include "TargetHandler.h"
#include "Writer.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/range.h"
#include "lld/ReaderWriter/AtomLayout.h"
#include "lld/ReaderWriter/ELFLinkingContext.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileOutputBuffer.h"
#include <memory>
#include <mutex>

namespace lld {
namespace elf {
template <class> class OutputSection;
using namespace llvm::ELF;
template <class ELFT> class Segment;
template <class ELFT> class TargetLayout;

/// \brief An ELF section.
template <class ELFT> class Section : public Chunk<ELFT> {
public:
  Section(const ELFLinkingContext &ctx, StringRef sectionName,
          StringRef chunkName,
          typename Chunk<ELFT>::Kind k = Chunk<ELFT>::Kind::ELFSection);

  /// \brief Modify the section contents before assigning virtual addresses
  //  or assigning file offsets

  /// \brief Finalize the section contents before writing

  /// \brief Does this section have an output segment.
  virtual bool hasOutputSegment() const { return false; }

  /// Return if the section is a loadable section that occupies memory
  virtual bool isLoadableSection() const { return false; }

  /// \brief Assign file offsets starting at offset.
  virtual void assignFileOffsets(uint64_t offset) {}

  /// \brief Assign virtual addresses starting at addr.
  virtual void assignVirtualAddress(uint64_t addr) {}

  uint64_t getFlags() const { return _flags; }
  uint64_t getEntSize() const { return _entSize; }
  uint32_t getType() const { return _type; }
  uint32_t getLink() const { return _link; }
  uint32_t getInfo() const { return _info; }

  typename TargetLayout<ELFT>::SegmentType getSegmentType() const {
    return _segmentType;
  }

  /// \brief Return the type of content that the section contains
  int getContentType() const override;

  /// \brief convert the segment type to a String for diagnostics and printing
  /// purposes
  virtual StringRef segmentKindToStr() const;

  /// \brief Records the segmentType, that this section belongs to
  void
  setSegmentType(const typename TargetLayout<ELFT>::SegmentType segmentType) {
    this->_segmentType = segmentType;
  }

  virtual const AtomLayout *findAtomLayoutByName(StringRef) const {
    return nullptr;
  }

  void setOutputSection(OutputSection<ELFT> *os, bool isFirst = false) {
    _outputSection = os;
    _isFirstSectionInOutputSection = isFirst;
  }

  static bool classof(const Chunk<ELFT> *c) {
    return c->kind() == Chunk<ELFT>::Kind::ELFSection ||
           c->kind() == Chunk<ELFT>::Kind::AtomSection;
  }

  uint64_t alignment() const override {
    return _isFirstSectionInOutputSection ? _outputSection->alignment()
                                          : this->_alignment;
  }

  virtual StringRef inputSectionName() const { return _inputSectionName; }

  virtual StringRef outputSectionName() const { return _outputSectionName; }

  virtual void setOutputSectionName(StringRef outputSectionName) {
    _outputSectionName = outputSectionName;
  }

  void setArchiveNameOrPath(StringRef name) { _archivePath = name; }

  void setMemberNameOrPath(StringRef name) { _memberPath = name; }

  StringRef archivePath() { return _archivePath; }

  StringRef memberPath() { return _memberPath; }

protected:
  /// \brief OutputSection this Section is a member of, or nullptr.
  OutputSection<ELFT> *_outputSection = nullptr;
  /// \brief ELF SHF_* flags.
  uint64_t _flags = 0;
  /// \brief The size of each entity.
  uint64_t _entSize = 0;
  /// \brief ELF SHT_* type.
  uint32_t _type = 0;
  /// \brief sh_link field.
  uint32_t _link = 0;
  /// \brief the sh_info field.
  uint32_t _info = 0;
  /// \brief Is this the first section in the output section.
  bool _isFirstSectionInOutputSection = false;
  /// \brief the output ELF segment type of this section.
  typename TargetLayout<ELFT>::SegmentType _segmentType = SHT_NULL;
  /// \brief Input section name.
  StringRef _inputSectionName;
  /// \brief Output section name.
  StringRef _outputSectionName;
  StringRef _archivePath;
  StringRef _memberPath;
};

/// \brief A section containing atoms.
template <class ELFT> class AtomSection : public Section<ELFT> {
public:
  AtomSection(const ELFLinkingContext &ctx, StringRef sectionName,
              int32_t contentType, int32_t permissions, int32_t order);

  /// Align the offset to the required modulus defined by the atom alignment
  uint64_t alignOffset(uint64_t offset, DefinedAtom::Alignment &atomAlign);

  /// Return if the section is a loadable section that occupies memory
  bool isLoadableSection() const override { return _isLoadedInMemory; }

  // \brief Append an atom to a Section. The atom gets pushed into a vector
  // contains the atom, the atom file offset, the atom virtual address
  // the atom file offset is aligned appropriately as set by the Reader
  virtual const AtomLayout *appendAtom(const Atom *atom);

  /// \brief Set the virtual address of each Atom in the Section. This
  /// routine gets called after the linker fixes up the virtual address
  /// of the section
  virtual void assignVirtualAddress(uint64_t addr) override;

  /// \brief Set the file offset of each Atom in the section. This routine
  /// gets called after the linker fixes up the section offset
  void assignFileOffsets(uint64_t offset) override;

  /// \brief Find the Atom address given a name, this is needed to properly
  ///  apply relocation. The section class calls this to find the atom address
  ///  to fix the relocation
  const AtomLayout *findAtomLayoutByName(StringRef name) const override;

  /// \brief Return the raw flags, we need this to sort segments
  int64_t atomflags() const { return _contentPermissions; }

  /// Atom Iterators
  typedef typename std::vector<AtomLayout *>::iterator atom_iter;

  range<atom_iter> atoms() { return _atoms; }

  void write(ELFWriter *writer, TargetLayout<ELFT> &layout,
             llvm::FileOutputBuffer &buffer) override;

  static bool classof(const Chunk<ELFT> *c) {
    return c->kind() == Chunk<ELFT>::Kind::AtomSection;
  }

protected:
  llvm::BumpPtrAllocator _alloc;
  int32_t _contentType;
  int32_t _contentPermissions;
  bool _isLoadedInMemory = true;
  std::vector<AtomLayout *> _atoms;
  mutable std::mutex _outputMutex;

  std::string formatError(const std::string &errorStr, const AtomLayout &atom,
                          const Reference &ref) const;
};

/// \brief A OutputSection represents a set of sections grouped by the same
/// name. The output file that gets written by the linker has sections grouped
/// by similar names
template <class ELFT> class OutputSection {
public:
  // Iterators
  typedef typename std::vector<Section<ELFT> *>::iterator SectionIter;

  OutputSection(StringRef name) : _name(name) {}

  // Appends a section into the list of sections that are part of this Output
  // Section
  void appendSection(Section<ELFT> *c);

  // Set the OutputSection is associated with a segment
  void setHasSegment() { _hasSegment = true; }

  /// Sets the ordinal
  void setOrdinal(uint64_t ordinal) { _ordinal = ordinal; }

  /// Sets the Memory size
  void setMemSize(uint64_t memsz) { _memSize = memsz; }

  /// Sets the size fo the output Section.
  void setSize(uint64_t fsiz) { _size = fsiz; }

  // The offset of the first section contained in the output section is
  // contained here.
  void setFileOffset(uint64_t foffset) { _fileOffset = foffset; }

  // Sets the starting address of the section
  void setAddr(uint64_t addr) { _virtualAddr = addr; }

  // Is the section loadable?
  bool isLoadableSection() const { return _isLoadableSection; }

  // Set section Loadable
  void setLoadableSection(bool isLoadable) {
    _isLoadableSection = isLoadable;
  }

  void setLink(uint64_t link) { _link = link; }
  void setInfo(uint64_t info) { _shInfo = info; }
  void setFlag(uint64_t flags) { _flags = flags; }
  void setType(int64_t type) { _type = type; }
  range<SectionIter> sections() { return _sections; }

  // The below functions returns the properties of the OutputSection.
  bool hasSegment() const { return _hasSegment; }
  StringRef name() const { return _name; }
  int64_t shinfo() const { return _shInfo; }
  uint64_t alignment() const { return _alignment; }
  int64_t link() const { return _link; }
  int64_t type() const { return _type; }
  uint64_t virtualAddr() const { return _virtualAddr; }
  int64_t ordinal() const { return _ordinal; }
  int64_t kind() const { return _kind; }
  uint64_t fileSize() const { return _size; }
  int64_t entsize() const { return _entSize; }
  uint64_t fileOffset() const { return _fileOffset; }
  uint64_t flags() const { return _flags; }
  uint64_t memSize() const { return _memSize; }

private:
  StringRef _name;
  bool _hasSegment = false;
  uint64_t _ordinal = 0;
  uint64_t _flags = 0;
  uint64_t _size = 0;
  uint64_t _memSize = 0;
  uint64_t _fileOffset = 0;
  uint64_t _virtualAddr = 0;
  int64_t _shInfo = 0;
  int64_t _entSize = 0;
  int64_t _link = 0;
  uint64_t _alignment = 1;
  int64_t _kind = 0;
  int64_t _type = 0;
  bool _isLoadableSection = false;
  std::vector<Section<ELFT> *> _sections;
};

/// \brief The class represents the ELF String Table
template <class ELFT> class StringTable : public Section<ELFT> {
public:
  StringTable(const ELFLinkingContext &, const char *str, int32_t order,
              bool dynamic = false);

  uint64_t addString(StringRef symname);

  void write(ELFWriter *writer, TargetLayout<ELFT> &layout,
             llvm::FileOutputBuffer &buffer) override;

  void setNumEntries(int64_t numEntries) { _stringMap.resize(numEntries); }

private:
  std::vector<StringRef> _strings;

  struct StringRefMappingInfo {
    static StringRef getEmptyKey() { return StringRef(); }
    static StringRef getTombstoneKey() { return StringRef(" ", 1); }
    static unsigned getHashValue(StringRef const val) {
      return llvm::HashString(val);
    }
    static bool isEqual(StringRef const lhs, StringRef const rhs) {
      return lhs.equals(rhs);
    }
  };
  typedef typename llvm::DenseMap<StringRef, uint64_t, StringRefMappingInfo>
      StringMapT;
  typedef typename StringMapT::iterator StringMapTIter;
  StringMapT _stringMap;
};

/// \brief The SymbolTable class represents the symbol table in a ELF file
template <class ELFT> class SymbolTable : public Section<ELFT> {
  typedef
      typename llvm::object::ELFDataTypeTypedefHelper<ELFT>::Elf_Addr Elf_Addr;

public:
  typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym;

  SymbolTable(const ELFLinkingContext &ctx, const char *str, int32_t order);

  /// \brief set the number of entries that would exist in the symbol
  /// table for the current link
  void setNumEntries(int64_t numEntries) const {
    if (_stringSection)
      _stringSection->setNumEntries(numEntries);
  }

  /// \brief return number of entries
  std::size_t size() const { return _symbolTable.size(); }

  void addSymbol(const Atom *atom, int32_t sectionIndex, uint64_t addr = 0,
                 const AtomLayout *layout = nullptr);

  /// \brief Get the symbol table index for an Atom. If it's not in the symbol
  /// table, return STN_UNDEF.
  uint32_t getSymbolTableIndex(const Atom *a) const {
    for (size_t i = 0, e = _symbolTable.size(); i < e; ++i)
      if (_symbolTable[i]._atom == a)
        return i;
    return STN_UNDEF;
  }

  void finalize() override { finalize(true); }

  virtual void sortSymbols() {
    std::stable_sort(_symbolTable.begin(), _symbolTable.end(),
                     [](const SymbolEntry &A, const SymbolEntry &B) {
                       return A._symbol.getBinding() < B._symbol.getBinding();
                     });
  }

  virtual void addAbsoluteAtom(Elf_Sym &sym, const AbsoluteAtom *aa,
                               int64_t addr);

  virtual void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da,
                              int64_t addr);

  virtual void addUndefinedAtom(Elf_Sym &sym, const UndefinedAtom *ua);

  virtual void addSharedLibAtom(Elf_Sym &sym, const SharedLibraryAtom *sla);

  virtual void finalize(bool sort);

  void write(ELFWriter *writer, TargetLayout<ELFT> &layout,
             llvm::FileOutputBuffer &buffer) override;

  void setStringSection(StringTable<ELFT> *s) { _stringSection = s; }

  StringTable<ELFT> *getStringTable() const { return _stringSection; }

protected:
  struct SymbolEntry {
    SymbolEntry(const Atom *a, const Elf_Sym &sym, const AtomLayout *layout)
        : _atom(a), _atomLayout(layout), _symbol(sym) {}

    const Atom *_atom;
    const AtomLayout *_atomLayout;
    Elf_Sym _symbol;
  };

  llvm::BumpPtrAllocator _symbolAllocate;
  StringTable<ELFT> *_stringSection;
  std::vector<SymbolEntry> _symbolTable;
};

template <class ELFT> class HashSection;

template <class ELFT> class DynamicSymbolTable : public SymbolTable<ELFT> {
public:
  DynamicSymbolTable(const ELFLinkingContext &ctx, TargetLayout<ELFT> &layout,
                     const char *str, int32_t order);

  // Set the dynamic hash table for symbols to be added into
  void setHashTable(HashSection<ELFT> *hashTable) { _hashTable = hashTable; }

  // Add all the dynamic symbos to the hash table
  void addSymbolsToHashTable();

  void finalize() override;

protected:
  HashSection<ELFT> *_hashTable = nullptr;
  TargetLayout<ELFT> &_layout;
};

template <class ELFT> class RelocationTable : public Section<ELFT> {
public:
  typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel;
  typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela;

  RelocationTable(const ELFLinkingContext &ctx, StringRef str, int32_t order);

  /// \returns the index of the relocation added.
  uint32_t addRelocation(const DefinedAtom &da, const Reference &r);

  bool getRelocationIndex(const Reference &r, uint32_t &res);

  void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable) {
    _symbolTable = symbolTable;
  }

  /// \brief Check if any relocation modifies a read-only section.
  bool canModifyReadonlySection() const;

  void finalize() override;

  void write(ELFWriter *writer, TargetLayout<ELFT> &layout,
             llvm::FileOutputBuffer &buffer) override;

protected:
  const DynamicSymbolTable<ELFT> *_symbolTable = nullptr;

  virtual void writeRela(ELFWriter *writer, Elf_Rela &r,
                         const DefinedAtom &atom, const Reference &ref);
  virtual void writeRel(ELFWriter *writer, Elf_Rel &r, const DefinedAtom &atom,
                        const Reference &ref);
  uint32_t getSymbolIndex(const Atom *a);

private:
  std::vector<std::pair<const DefinedAtom *, const Reference *>> _relocs;
};

template <class ELFT> class HashSection;

template <class ELFT> class DynamicTable : public Section<ELFT> {
public:
  typedef llvm::object::Elf_Dyn_Impl<ELFT> Elf_Dyn;
  typedef std::vector<Elf_Dyn> EntriesT;

  DynamicTable(const ELFLinkingContext &ctx, TargetLayout<ELFT> &layout,
               StringRef str, int32_t order);

  range<typename EntriesT::iterator> entries() { return _entries; }

  /// \returns the index of the entry.
  std::size_t addEntry(int64_t tag, uint64_t val);

  void write(ELFWriter *writer, TargetLayout<ELFT> &layout,
             llvm::FileOutputBuffer &buffer) override;

  virtual void createDefaultEntries();
  void doPreFlight() override;

  /// \brief Dynamic table tag for .got.plt section referencing.
  /// Usually but not always targets use DT_PLTGOT for that.
  virtual int64_t getGotPltTag() { return DT_PLTGOT; }

  void finalize() override;

  void setSymbolTable(DynamicSymbolTable<ELFT> *dynsym) {
    _dynamicSymbolTable = dynsym;
  }

  const DynamicSymbolTable<ELFT> *getSymbolTable() const {
    return _dynamicSymbolTable;
  }

  void setHashTable(HashSection<ELFT> *hsh) { _hashTable = hsh; }

  virtual void updateDynamicTable();

protected:
  EntriesT _entries;

  /// \brief Return a virtual address (maybe adjusted) for the atom layout
  /// Some targets like microMIPS and ARM Thumb use the last bit
  /// of a symbol's value to mark 'compressed' code. This function allows
  /// to adjust a virtal address before using it in the dynamic table tag.
  virtual uint64_t getAtomVirtualAddress(const AtomLayout *al) const {
    return al->_virtualAddr;
  }

private:
  std::size_t _dt_hash;
  std::size_t _dt_strtab;
  std::size_t _dt_symtab;
  std::size_t _dt_rela;
  std::size_t _dt_relasz;
  std::size_t _dt_relaent;
  std::size_t _dt_strsz;
  std::size_t _dt_syment;
  std::size_t _dt_pltrelsz;
  std::size_t _dt_pltgot;
  std::size_t _dt_pltrel;
  std::size_t _dt_jmprel;
  std::size_t _dt_init_array;
  std::size_t _dt_init_arraysz;
  std::size_t _dt_fini_array;
  std::size_t _dt_fini_arraysz;
  std::size_t _dt_textrel;
  std::size_t _dt_init;
  std::size_t _dt_fini;
  TargetLayout<ELFT> &_layout;
  DynamicSymbolTable<ELFT> *_dynamicSymbolTable;
  HashSection<ELFT> *_hashTable;

  const AtomLayout *getInitAtomLayout();

  const AtomLayout *getFiniAtomLayout();
};

template <class ELFT> class InterpSection : public Section<ELFT> {
public:
  InterpSection(const ELFLinkingContext &ctx, StringRef str, int32_t order,
                StringRef interp);

  void write(ELFWriter *writer, TargetLayout<ELFT> &layout,
             llvm::FileOutputBuffer &buffer);

private:
  StringRef _interp;
};

/// The hash table in the dynamic linker is organized into
///
///     [ nbuckets              ]
///     [ nchains               ]
///     [ buckets[0]            ]
///     .........................
///     [ buckets[nbuckets-1]   ]
///     [ chains[0]             ]
///     .........................
///     [ chains[nchains - 1]   ]
///
/// nbuckets - total number of hash buckets
/// nchains is equal to the number of dynamic symbols.
///
/// The symbol is searched by the dynamic linker using the below approach.
///  * Calculate the hash of the symbol that needs to be searched
///  * Take the value from the buckets[hash % nbuckets] as the index of symbol
///  * Compare the symbol's name, if true return, if false, look through the
///  * array since there was a collision
template <class ELFT> class HashSection : public Section<ELFT> {
  struct SymbolTableEntry {
    StringRef _name;
    uint32_t _index;
  };

public:
  HashSection(const ELFLinkingContext &ctx, StringRef name, int32_t order);

  /// \brief add the dynamic symbol into the table so that the
  /// hash could be calculated
  void addSymbol(StringRef name, uint32_t index);

  /// \brief Set the dynamic symbol table
  void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable);

  // The size of the section has to be determined so that fileoffsets
  // may be properly assigned. Let's calculate the buckets and the chains
  // and fill the chains and the buckets hash table used by the dynamic
  // linker and update the filesize and memory size accordingly
  void doPreFlight() override;

  void finalize() override;

  void write(ELFWriter *writer, TargetLayout<ELFT> &layout,
             llvm::FileOutputBuffer &buffer) override;

private:
  typedef
      typename llvm::object::ELFDataTypeTypedefHelper<ELFT>::Elf_Word Elf_Word;

  std::vector<SymbolTableEntry> _entries;
  std::vector<Elf_Word> _buckets;
  std::vector<Elf_Word> _chains;
  const DynamicSymbolTable<ELFT> *_symbolTable = nullptr;
};

template <class ELFT> class EHFrameHeader : public Section<ELFT> {
public:
  EHFrameHeader(const ELFLinkingContext &ctx, StringRef name,
                TargetLayout<ELFT> &layout, int32_t order);
  void doPreFlight() override;
  void finalize() override;
  void write(ELFWriter *writer, TargetLayout<ELFT> &layout,
             llvm::FileOutputBuffer &buffer) override;

private:
  int32_t _ehFrameOffset = 0;
  TargetLayout<ELFT> &_layout;
};

} // end namespace elf
} // end namespace lld

#endif
