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

#include "MipsLinkingContext.h"
#include "MipsTargetLayout.h"

namespace lld {
namespace elf {

template <class ELFT>
MipsTargetLayout<ELFT>::MipsTargetLayout(MipsLinkingContext &ctx,
                                         MipsAbiInfoHandler<ELFT> &abi)
    : TargetLayout<ELFT>(ctx), _abiInfo(abi),
      _gotSection(new (this->_allocator) MipsGOTSection<ELFT>(ctx)),
      _pltSection(new (this->_allocator) MipsPLTSection<ELFT>(ctx)) {}

template <class ELFT>
AtomSection<ELFT> *MipsTargetLayout<ELFT>::createSection(
    StringRef name, int32_t type, DefinedAtom::ContentPermissions permissions,
    typename TargetLayout<ELFT>::SectionOrder order) {
  if (type == DefinedAtom::typeGOT && name == ".got")
    return _gotSection;
  if (type == DefinedAtom::typeStub && name == ".plt")
    return _pltSection;
  return TargetLayout<ELFT>::createSection(name, type, permissions, order);
}

template <class ELFT>
typename TargetLayout<ELFT>::SegmentType
MipsTargetLayout<ELFT>::getSegmentType(Section<ELFT> *section) const {
  switch (section->order()) {
  case ORDER_MIPS_REGINFO:
    return _abiInfo.hasMipsAbiSection() ? llvm::ELF::PT_LOAD
                                        : llvm::ELF::PT_MIPS_REGINFO;
  case ORDER_MIPS_OPTIONS:
    return llvm::ELF::PT_LOAD;
  case ORDER_MIPS_ABI_FLAGS:
    return llvm::ELF::PT_MIPS_ABIFLAGS;
  default:
    return TargetLayout<ELFT>::getSegmentType(section);
  }
}

template <class ELFT> uint64_t MipsTargetLayout<ELFT>::getGPAddr() {
  std::call_once(_gpOnce, [this]() {
    if (AtomLayout *a = this->findAbsoluteAtom("_gp"))
      _gpAddr = a->_virtualAddr;
  });
  return _gpAddr;
}

template <class ELFT>
typename TargetLayout<ELFT>::SectionOrder
MipsTargetLayout<ELFT>::getSectionOrder(StringRef name, int32_t contentType,
                                        int32_t contentPermissions) {
  if ((contentType == DefinedAtom::typeStub) && (name.startswith(".text")))
    return TargetLayout<ELFT>::ORDER_TEXT;

  return TargetLayout<ELFT>::getSectionOrder(name, contentType,
                                             contentPermissions);
}

template <class ELFT>
unique_bump_ptr<RelocationTable<ELFT>>
MipsTargetLayout<ELFT>::createRelocationTable(StringRef name, int32_t order) {
  return unique_bump_ptr<RelocationTable<ELFT>>(new (
      this->_allocator) MipsRelocationTable<ELFT>(this->_ctx, name, order));
}

template <class ELFT>
uint64_t MipsTargetLayout<ELFT>::getLookupSectionFlags(
    const OutputSection<ELFT> *os) const {
  uint64_t flags = TargetLayout<ELFT>::getLookupSectionFlags(os);
  return flags & ~llvm::ELF::SHF_MIPS_NOSTRIP;
}

template <class ELFT> void MipsTargetLayout<ELFT>::sortSegments() {
  using namespace llvm::ELF;
  TargetLayout<ELFT>::sortSegments();
  // Move PT_MIPS_ABIFLAGS or PT_MIPS_REGINFO right after PT_INTERP.
  auto abiIt =
      std::find_if(this->_segments.begin(), this->_segments.end(),
                   [](const Segment<ELFT> *s) {
                     auto typ = s->segmentType();
                     return typ == PT_MIPS_ABIFLAGS || typ == PT_MIPS_REGINFO;
                   });
  if (abiIt == this->_segments.end())
    return;
  Segment<ELFT> *abiSeg = *abiIt;
  this->_segments.erase(abiIt);
  auto outIt = std::find_if(this->_segments.begin(), this->_segments.end(),
                            [](const Segment<ELFT> *s) {
                              auto typ = s->segmentType();
                              return typ != PT_PHDR && typ != PT_INTERP;
                            });
  this->_segments.insert(outIt, abiSeg);
}

template class MipsTargetLayout<ELF32BE>;
template class MipsTargetLayout<ELF32LE>;
template class MipsTargetLayout<ELF64BE>;
template class MipsTargetLayout<ELF64LE>;

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