//===- MipsLDBackend.h ----------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef TARGET_MIPS_MIPSLDBACKEND_H_
#define TARGET_MIPS_MIPSLDBACKEND_H_
#include <llvm/Support/ELF.h>
#include "mcld/Target/GNULDBackend.h"
#include "MipsAbiFlags.h"
#include "MipsELFDynamic.h"
#include "MipsGOT.h"
#include "MipsGOTPLT.h"
#include "MipsPLT.h"

namespace mcld {

class LinkerConfig;
class MemoryArea;
class MipsGNUInfo;
class OutputRelocSection;
class SectionMap;

/** \class MipsGNULDBackend
 *  \brief Base linker backend of Mips target of GNU ELF format.
 */
class MipsGNULDBackend : public GNULDBackend {
 public:
  typedef std::vector<LDSymbol*> SymbolListType;

 public:
  MipsGNULDBackend(const LinkerConfig& pConfig, MipsGNUInfo* pInfo);
  ~MipsGNULDBackend();

  bool needsLA25Stub(Relocation::Type pType, const mcld::ResolveInfo* pSym);

  void addNonPICBranchSym(ResolveInfo* rsym);
  bool hasNonPICBranch(const ResolveInfo* rsym) const;

 public:
  /// initTargetSections - initialize target dependent sections in output
  void initTargetSections(Module& pModule, ObjectBuilder& pBuilder);

  /// initTargetSymbols - initialize target dependent symbols in output.
  void initTargetSymbols(IRBuilder& pBuilder, Module& pModule);

  /// getRelocator - return relocator.
  const Relocator* getRelocator() const;
  Relocator* getRelocator();

  /// preLayout - Backend can do any needed modification before layout
  void doPreLayout(IRBuilder& pBuilder);

  /// postLayout - Backend can do any needed modification after layout
  void doPostLayout(Module& pModule, IRBuilder& pBuilder);

  /// dynamic - the dynamic section of the target machine.
  /// Use co-variant return type to return its own dynamic section.
  MipsELFDynamic& dynamic();

  /// dynamic - the dynamic section of the target machine.
  /// Use co-variant return type to return its own dynamic section.
  const MipsELFDynamic& dynamic() const;

  /// emitSectionData - write out the section data into the memory region.
  /// When writers get a LDSection whose kind is LDFileFormat::Target, writers
  /// call back target backend to emit the data.
  ///
  /// Backends handle the target-special tables (plt, gp,...) by themselves.
  /// Backend can put the data of the tables in SectionData directly
  ///  - LDSection.getSectionData can get the section data.
  /// Or, backend can put the data into special data structure
  ///  - backend can maintain its own map<LDSection, table> to get the table
  /// from given LDSection.
  ///
  /// @param pSection - the given LDSection
  /// @param pRegion - the region to write out data
  /// @return the size of the table in the file.
  uint64_t emitSectionData(const LDSection& pSection,
                           MemoryRegion& pRegion) const;

  /// hasEntryInStrTab - symbol has an entry in a .strtab
  bool hasEntryInStrTab(const LDSymbol& pSym) const;

  /// orderSymbolTable - order symbol table before emitting
  void orderSymbolTable(Module& pModule);

  /// readSection - read a target dependent section.
  bool readSection(Input& pInput, SectionData& pSD);

  MipsGOT& getGOT();
  const MipsGOT& getGOT() const;

  MipsPLT& getPLT();
  const MipsPLT& getPLT() const;

  MipsGOTPLT& getGOTPLT();
  const MipsGOTPLT& getGOTPLT() const;

  OutputRelocSection& getRelPLT();
  const OutputRelocSection& getRelPLT() const;

  OutputRelocSection& getRelDyn();
  const OutputRelocSection& getRelDyn() const;

  LDSymbol* getGOTSymbol() { return m_pGOTSymbol; }
  const LDSymbol* getGOTSymbol() const { return m_pGOTSymbol; }

  LDSymbol* getGpDispSymbol() { return m_pGpDispSymbol; }
  const LDSymbol* getGpDispSymbol() const { return m_pGpDispSymbol; }

  SymbolListType& getGlobalGOTSyms() { return m_GlobalGOTSyms; }
  const SymbolListType& getGlobalGOTSyms() const { return m_GlobalGOTSyms; }

  /// getTargetSectionOrder - compute the layout order of ARM target sections
  unsigned int getTargetSectionOrder(const LDSection& pSectHdr) const;

  /// finalizeSymbol - finalize the symbol value
  bool finalizeTargetSymbols();

  /// allocateCommonSymbols - allocate common symbols in the corresponding
  /// sections.
  bool allocateCommonSymbols(Module& pModule);

  /// getTPOffset - return TP_OFFSET against the SHF_TLS
  /// section in the specified input.
  uint64_t getTPOffset(const Input& pInput) const;

  /// getDTPOffset - return DTP_OFFSET against the SHF_TLS
  /// section in the specified input.
  uint64_t getDTPOffset(const Input& pInput) const;

  /// getGP0 - the gp value used to create the relocatable objects
  /// in the specified input.
  uint64_t getGP0(const Input& pInput) const;

 private:
  void defineGOTSymbol(IRBuilder& pBuilder);
  void defineGOTPLTSymbol(IRBuilder& pBuilder);

  bool relaxRelocation(IRBuilder& pBuilder, Relocation& pRel);

  /// emitSymbol32 - emit an ELF32 symbol, override parent's function
  void emitSymbol32(llvm::ELF::Elf32_Sym& pSym32,
                    LDSymbol& pSymbol,
                    char* pStrtab,
                    size_t pStrtabsize,
                    size_t pSymtabIdx);

  /// doCreateProgramHdrs - backend can implement this function to create the
  /// target-dependent segments
  void doCreateProgramHdrs(Module& pModule);

  /// mayRelax - Backends should override this function if they need relaxation
  bool mayRelax() { return true; }

  /// doRelax - Backend can orevride this function to add its relaxation
  /// implementation. Return true if the output (e.g., .text) is "relaxed"
  /// (i.e. layout is changed), and set pFinished to true if everything is fit,
  /// otherwise set it to false.
  bool doRelax(Module& pModule, IRBuilder& pBuilder, bool& pFinished);

  /// initTargetStubs
  bool initTargetStubs();

  /// readRelocation - read ELF32_Rel entry
  bool readRelocation(const llvm::ELF::Elf32_Rel& pRel,
                      Relocation::Type& pType,
                      uint32_t& pSymIdx,
                      uint32_t& pOffset) const;

  /// readRelocation - read ELF32_Rela entry
  bool readRelocation(const llvm::ELF::Elf32_Rela& pRel,
                      Relocation::Type& pType,
                      uint32_t& pSymIdx,
                      uint32_t& pOffset,
                      int32_t& pAddend) const;

  /// readRelocation - read ELF64_Rel entry
  bool readRelocation(const llvm::ELF::Elf64_Rel& pRel,
                      Relocation::Type& pType,
                      uint32_t& pSymIdx,
                      uint64_t& pOffset) const;

  /// readRel - read ELF64_Rela entry
  bool readRelocation(const llvm::ELF::Elf64_Rela& pRel,
                      Relocation::Type& pType,
                      uint32_t& pSymIdx,
                      uint64_t& pOffset,
                      int64_t& pAddend) const;

  /// emitRelocation - write data to the ELF32_Rel entry
  void emitRelocation(llvm::ELF::Elf32_Rel& pRel,
                      Relocation::Type pType,
                      uint32_t pSymIdx,
                      uint32_t pOffset) const;

  /// emitRelocation - write data to the ELF32_Rela entry
  void emitRelocation(llvm::ELF::Elf32_Rela& pRel,
                      Relocation::Type pType,
                      uint32_t pSymIdx,
                      uint32_t pOffset,
                      int32_t pAddend) const;

  /// emitRelocation - write data to the ELF64_Rel entry
  void emitRelocation(llvm::ELF::Elf64_Rel& pRel,
                      Relocation::Type pType,
                      uint32_t pSymIdx,
                      uint64_t pOffset) const;

  /// emitRelocation - write data to the ELF64_Rela entry
  void emitRelocation(llvm::ELF::Elf64_Rela& pRel,
                      Relocation::Type pType,
                      uint32_t pSymIdx,
                      uint64_t pOffset,
                      int64_t pAddend) const;

  /// preMergeSections - hooks to be executed before merging sections
  void preMergeSections(Module& pModule);

  /// mergeSection - merge target dependent sections
  bool mergeSection(Module& pModule, const Input& pInput, LDSection& pSection);

 protected:
  virtual void mergeFlags(Input& pInput, const char* ELF_hdr);

 private:
  typedef llvm::DenseSet<const ResolveInfo*> ResolveInfoSetType;
  typedef llvm::DenseMap<const Input*, llvm::ELF::Elf64_Addr> InputNumMapType;
  typedef llvm::DenseMap<const Input*, uint64_t> ElfFlagsMapType;

 protected:
  Relocator* m_pRelocator;
  MipsGOT* m_pGOT;        // .got
  MipsPLT* m_pPLT;        // .plt
  MipsGOTPLT* m_pGOTPLT;  // .got.plt

 private:
  MipsGNUInfo& m_pInfo;
  llvm::Optional<MipsAbiFlags> m_pAbiInfo;

  OutputRelocSection* m_pRelPlt;  // .rel.plt
  OutputRelocSection* m_pRelDyn;  // .rel.dyn

  MipsELFDynamic* m_pDynamic;
  LDSection* m_psdata;
  LDSection* m_pAbiFlags;
  LDSymbol* m_pGOTSymbol;
  LDSymbol* m_pPLTSymbol;
  LDSymbol* m_pGpDispSymbol;

  SymbolListType m_GlobalGOTSyms;
  ResolveInfoSetType m_HasNonPICBranchSyms;
  InputNumMapType m_GP0Map;
  InputNumMapType m_TpOffsetMap;
  InputNumMapType m_DtpOffsetMap;
  ElfFlagsMapType m_ElfFlagsMap;

  void moveSectionData(SectionData& pFrom, SectionData& pTo);
  void saveTPOffset(const Input& pInput);
};

/** \class Mips32GNULDBackend
 *  \brief Base linker backend of Mips 32-bit target of GNU ELF format.
 */
class Mips32GNULDBackend : public MipsGNULDBackend {
 public:
  Mips32GNULDBackend(const LinkerConfig& pConfig, MipsGNUInfo* pInfo);

 private:
  // MipsGNULDBackend

  bool initRelocator();
  void initTargetSections(Module& pModule, ObjectBuilder& pBuilder);
  size_t getRelEntrySize();
  size_t getRelaEntrySize();
};

/** \class Mips64GNULDBackend
 *  \brief Base linker backend of Mips 64-bit target of GNU ELF format.
 */
class Mips64GNULDBackend : public MipsGNULDBackend {
 public:
  Mips64GNULDBackend(const LinkerConfig& pConfig, MipsGNUInfo* pInfo);

 private:
  // MipsGNULDBackend

  bool initRelocator();
  void initTargetSections(Module& pModule, ObjectBuilder& pBuilder);
  size_t getRelEntrySize();
  size_t getRelaEntrySize();
};

}  // namespace mcld

#endif  // TARGET_MIPS_MIPSLDBACKEND_H_