//===- MipsLDBackend.cpp --------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Mips.h"
#include "MipsGNUInfo.h"
#include "MipsELFDynamic.h"
#include "MipsLA25Stub.h"
#include "MipsLDBackend.h"
#include "MipsRelocator.h"

#include "mcld/IRBuilder.h"
#include "mcld/LinkerConfig.h"
#include "mcld/Module.h"
#include "mcld/Fragment/AlignFragment.h"
#include "mcld/Fragment/FillFragment.h"
#include "mcld/LD/BranchIslandFactory.h"
#include "mcld/LD/LDContext.h"
#include "mcld/LD/StubFactory.h"
#include "mcld/LD/ELFFileFormat.h"
#include "mcld/LD/ELFSegment.h"
#include "mcld/LD/ELFSegmentFactory.h"
#include "mcld/MC/Attribute.h"
#include "mcld/Object/ObjectBuilder.h"
#include "mcld/Support/MemoryRegion.h"
#include "mcld/Support/MemoryArea.h"
#include "mcld/Support/MsgHandling.h"
#include "mcld/Support/TargetRegistry.h"
#include "mcld/Target/OutputRelocSection.h"

#include <llvm/ADT/Triple.h>
#include <llvm/Object/ELFTypes.h>
#include <llvm/Support/Casting.h>
#include <llvm/Support/ELF.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/MipsABIFlags.h>

#include <vector>

namespace mcld {

//===----------------------------------------------------------------------===//
// MipsGNULDBackend
//===----------------------------------------------------------------------===//
MipsGNULDBackend::MipsGNULDBackend(const LinkerConfig& pConfig,
                                   MipsGNUInfo* pInfo)
    : GNULDBackend(pConfig, pInfo),
      m_pRelocator(NULL),
      m_pGOT(NULL),
      m_pPLT(NULL),
      m_pGOTPLT(NULL),
      m_pInfo(*pInfo),
      m_pRelPlt(NULL),
      m_pRelDyn(NULL),
      m_pDynamic(NULL),
      m_pAbiFlags(NULL),
      m_pGOTSymbol(NULL),
      m_pPLTSymbol(NULL),
      m_pGpDispSymbol(NULL) {
}

MipsGNULDBackend::~MipsGNULDBackend() {
  delete m_pRelocator;
  delete m_pPLT;
  delete m_pRelPlt;
  delete m_pRelDyn;
  delete m_pDynamic;
}

bool MipsGNULDBackend::needsLA25Stub(Relocation::Type pType,
                                     const mcld::ResolveInfo* pSym) {
  if (config().isCodeIndep())
    return false;

  if (llvm::ELF::R_MIPS_26 != pType)
    return false;

  if (pSym->isLocal())
    return false;

  return true;
}

void MipsGNULDBackend::addNonPICBranchSym(ResolveInfo* rsym) {
  m_HasNonPICBranchSyms.insert(rsym);
}

bool MipsGNULDBackend::hasNonPICBranch(const ResolveInfo* rsym) const {
  return m_HasNonPICBranchSyms.count(rsym);
}

void MipsGNULDBackend::initTargetSections(Module& pModule,
                                          ObjectBuilder& pBuilder) {
  if (LinkerConfig::Object == config().codeGenType())
    return;

  ELFFileFormat* file_format = getOutputFormat();

  // initialize .rel.plt
  LDSection& relplt = file_format->getRelPlt();
  m_pRelPlt = new OutputRelocSection(pModule, relplt);

  // initialize .rel.dyn
  LDSection& reldyn = file_format->getRelDyn();
  m_pRelDyn = new OutputRelocSection(pModule, reldyn);

  // initialize .sdata
  m_psdata = pBuilder.CreateSection(
      ".sdata", LDFileFormat::Target, llvm::ELF::SHT_PROGBITS,
      llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE | llvm::ELF::SHF_MIPS_GPREL,
      4);

  // initialize .MIPS.abiflags
  m_pAbiFlags = pBuilder.CreateSection(".MIPS.abiflags", LDFileFormat::Target,
                                       llvm::ELF::SHT_MIPS_ABIFLAGS,
                                       llvm::ELF::SHF_ALLOC, 4);
}

void MipsGNULDBackend::initTargetSymbols(IRBuilder& pBuilder, Module& pModule) {
  // Define the symbol _GLOBAL_OFFSET_TABLE_ if there is a symbol with the
  // same name in input
  m_pGOTSymbol = pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>(
      "_GLOBAL_OFFSET_TABLE_",
      ResolveInfo::Object,
      ResolveInfo::Define,
      ResolveInfo::Local,
      0x0,                  // size
      0x0,                  // value
      FragmentRef::Null(),  // FragRef
      ResolveInfo::Hidden);

  // Define the symbol _PROCEDURE_LINKAGE_TABLE_ if there is a symbol with the
  // same name in input
  m_pPLTSymbol = pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>(
      "_PROCEDURE_LINKAGE_TABLE_",
      ResolveInfo::Object,
      ResolveInfo::Define,
      ResolveInfo::Local,
      0x0,                  // size
      0x0,                  // value
      FragmentRef::Null(),  // FragRef
      ResolveInfo::Hidden);

  m_pGpDispSymbol =
      pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>(
          "_gp_disp",
          ResolveInfo::Section,
          ResolveInfo::Define,
          ResolveInfo::Absolute,
          0x0,                  // size
          0x0,                  // value
          FragmentRef::Null(),  // FragRef
          ResolveInfo::Default);

  pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Unresolve>(
      "_gp",
      ResolveInfo::NoType,
      ResolveInfo::Define,
      ResolveInfo::Absolute,
      0x0,                  // size
      0x0,                  // value
      FragmentRef::Null(),  // FragRef
      ResolveInfo::Default);
}

const Relocator* MipsGNULDBackend::getRelocator() const {
  assert(m_pRelocator != NULL);
  return m_pRelocator;
}

Relocator* MipsGNULDBackend::getRelocator() {
  assert(m_pRelocator != NULL);
  return m_pRelocator;
}

void MipsGNULDBackend::doPreLayout(IRBuilder& pBuilder) {
  // initialize .dynamic data
  if (!config().isCodeStatic() && m_pDynamic == NULL)
    m_pDynamic = new MipsELFDynamic(*this, config());

  if (m_pAbiInfo.hasValue())
    m_pAbiFlags->setSize(m_pAbiInfo->size());

  // set .got size
  // when building shared object, the .got section is must.
  if (LinkerConfig::Object != config().codeGenType()) {
    if (LinkerConfig::DynObj == config().codeGenType() || m_pGOT->hasGOT1() ||
        m_pGOTSymbol != NULL) {
      m_pGOT->finalizeScanning(*m_pRelDyn);
      m_pGOT->finalizeSectionSize();

      defineGOTSymbol(pBuilder);
    }

    if (m_pGOTPLT->hasGOT1()) {
      m_pGOTPLT->finalizeSectionSize();

      defineGOTPLTSymbol(pBuilder);
    }

    if (m_pPLT->hasPLT1())
      m_pPLT->finalizeSectionSize();

    ELFFileFormat* file_format = getOutputFormat();

    // set .rel.plt size
    if (!m_pRelPlt->empty()) {
      assert(
          !config().isCodeStatic() &&
          "static linkage should not result in a dynamic relocation section");
      file_format->getRelPlt().setSize(m_pRelPlt->numOfRelocs() *
                                       getRelEntrySize());
    }

    // set .rel.dyn size
    if (!m_pRelDyn->empty()) {
      assert(
          !config().isCodeStatic() &&
          "static linkage should not result in a dynamic relocation section");
      file_format->getRelDyn().setSize(m_pRelDyn->numOfRelocs() *
                                       getRelEntrySize());
    }
  }
}

void MipsGNULDBackend::doPostLayout(Module& pModule, IRBuilder& pBuilder) {
  const ELFFileFormat* format = getOutputFormat();

  if (format->hasGOTPLT()) {
    assert(m_pGOTPLT != NULL && "doPostLayout failed, m_pGOTPLT is NULL!");
    m_pGOTPLT->applyAllGOTPLT(m_pPLT->addr());
  }

  if (format->hasPLT()) {
    assert(m_pPLT != NULL && "doPostLayout failed, m_pPLT is NULL!");
    m_pPLT->applyAllPLT(*m_pGOTPLT);
  }

  m_pInfo.setABIVersion(m_pPLT && m_pPLT->hasPLT1() ? 1 : 0);
}

/// dynamic - the dynamic section of the target machine.
/// Use co-variant return type to return its own dynamic section.
MipsELFDynamic& MipsGNULDBackend::dynamic() {
  assert(m_pDynamic != NULL);
  return *m_pDynamic;
}

/// dynamic - the dynamic section of the target machine.
/// Use co-variant return type to return its own dynamic section.
const MipsELFDynamic& MipsGNULDBackend::dynamic() const {
  assert(m_pDynamic != NULL);
  return *m_pDynamic;
}

uint64_t MipsGNULDBackend::emitSectionData(const LDSection& pSection,
                                           MemoryRegion& pRegion) const {
  assert(pRegion.size() && "Size of MemoryRegion is zero!");

  const ELFFileFormat* file_format = getOutputFormat();

  if (file_format->hasGOT() && (&pSection == &(file_format->getGOT()))) {
    return m_pGOT->emit(pRegion);
  }

  if (file_format->hasPLT() && (&pSection == &(file_format->getPLT()))) {
    return m_pPLT->emit(pRegion);
  }

  if (file_format->hasGOTPLT() && (&pSection == &(file_format->getGOTPLT()))) {
    return m_pGOTPLT->emit(pRegion);
  }

  if (&pSection == m_pAbiFlags && m_pAbiInfo.hasValue())
    return MipsAbiFlags::emit(*m_pAbiInfo, pRegion);

  if (&pSection == m_psdata && m_psdata->hasSectionData()) {
    const SectionData* sect_data = pSection.getSectionData();
    SectionData::const_iterator frag_iter, frag_end = sect_data->end();
    uint8_t* out_offset = pRegion.begin();
    for (frag_iter = sect_data->begin(); frag_iter != frag_end; ++frag_iter) {
      size_t size = frag_iter->size();
      switch (frag_iter->getKind()) {
        case Fragment::Fillment: {
          const FillFragment& fill_frag = llvm::cast<FillFragment>(*frag_iter);
          if (fill_frag.getValueSize() == 0) {
            // virtual fillment, ignore it.
            break;
          }
          memset(out_offset, fill_frag.getValue(), fill_frag.size());
          break;
        }
        case Fragment::Region: {
          const RegionFragment& region_frag =
              llvm::cast<RegionFragment>(*frag_iter);
          const char* start = region_frag.getRegion().begin();
          memcpy(out_offset, start, size);
          break;
        }
        case Fragment::Alignment: {
          const AlignFragment& align_frag =
              llvm::cast<AlignFragment>(*frag_iter);
          uint64_t count = size / align_frag.getValueSize();
          switch (align_frag.getValueSize()) {
            case 1u:
              std::memset(out_offset, align_frag.getValue(), count);
              break;
            default:
              llvm::report_fatal_error(
                  "unsupported value size for align fragment emission yet.\n");
              break;
          }  // end switch
          break;
        }
        case Fragment::Null: {
          assert(0x0 == size);
          break;
        }
        default:
          llvm::report_fatal_error("unsupported fragment type.\n");
          break;
      }  // end switch
      out_offset += size;
    }
    return pRegion.size();
  }

  fatal(diag::unrecognized_output_sectoin) << pSection.name()
                                           << "mclinker@googlegroups.com";
  return 0;
}

bool MipsGNULDBackend::hasEntryInStrTab(const LDSymbol& pSym) const {
  return ResolveInfo::Section != pSym.type() || m_pGpDispSymbol == &pSym;
}

namespace {
struct DynsymGOTCompare {
  const MipsGOT& m_pGOT;

  explicit DynsymGOTCompare(const MipsGOT& pGOT) : m_pGOT(pGOT) {}

  bool operator()(const LDSymbol* X, const LDSymbol* Y) const {
    return m_pGOT.dynSymOrderCompare(X, Y);
  }
};
}  // anonymous namespace

void MipsGNULDBackend::orderSymbolTable(Module& pModule) {
  if (config().options().hasGNUHash()) {
    // The MIPS ABI and .gnu.hash require .dynsym to be sorted
    // in different ways. The MIPS ABI requires a mapping between
    // the GOT and the symbol table. At the same time .gnu.hash
    // needs symbols to be grouped by hash code.
    llvm::errs() << ".gnu.hash is incompatible with the MIPS ABI\n";
  }

  Module::SymbolTable& symbols = pModule.getSymbolTable();

  std::stable_sort(
      symbols.dynamicBegin(), symbols.dynamicEnd(), DynsymGOTCompare(*m_pGOT));
}

}  // namespace mcld

namespace llvm {
namespace ELF {
// SHT_MIPS_OPTIONS section's block descriptor.
struct Elf_Options {
  unsigned char kind;  // Determines interpretation of variable
                       // part of descriptor. See ODK_xxx enumeration.
  unsigned char size;  // Byte size of descriptor, including this header.
  Elf64_Half section;  // Section header index of section affected,
                       // or 0 for global options.
  Elf64_Word info;     // Kind-specific information.
};

// Content of ODK_REGINFO block in SHT_MIPS_OPTIONS section on 32 bit ABI.
struct Elf32_RegInfo {
  Elf32_Word ri_gprmask;     // Mask of general purpose registers used.
  Elf32_Word ri_cprmask[4];  // Mask of co-processor registers used.
  Elf32_Addr ri_gp_value;    // GP register value for this object file.
};

// Content of ODK_REGINFO block in SHT_MIPS_OPTIONS section on 64 bit ABI.
struct Elf64_RegInfo {
  Elf32_Word ri_gprmask;     // Mask of general purpose registers used.
  Elf32_Word ri_pad;         // Padding.
  Elf32_Word ri_cprmask[4];  // Mask of co-processor registers used.
  Elf64_Addr ri_gp_value;    // GP register value for this object file.
};

}  // namespace ELF
}  // namespace llvm

namespace mcld {

static const char* ArchName(uint64_t flagBits) {
  switch (flagBits) {
    case llvm::ELF::EF_MIPS_ARCH_1:
      return "mips1";
    case llvm::ELF::EF_MIPS_ARCH_2:
      return "mips2";
    case llvm::ELF::EF_MIPS_ARCH_3:
      return "mips3";
    case llvm::ELF::EF_MIPS_ARCH_4:
      return "mips4";
    case llvm::ELF::EF_MIPS_ARCH_5:
      return "mips5";
    case llvm::ELF::EF_MIPS_ARCH_32:
      return "mips32";
    case llvm::ELF::EF_MIPS_ARCH_64:
      return "mips64";
    case llvm::ELF::EF_MIPS_ARCH_32R2:
      return "mips32r2";
    case llvm::ELF::EF_MIPS_ARCH_64R2:
      return "mips64r2";
    case llvm::ELF::EF_MIPS_ARCH_32R6:
      return "mips32r6";
    case llvm::ELF::EF_MIPS_ARCH_64R6:
      return "mips64r6";
    default:
      return "Unknown Arch";
  }
}

void MipsGNULDBackend::mergeFlags(Input& pInput, const char* ELF_hdr) {
  bool isTarget64Bit = config().targets().triple().isArch64Bit();
  bool isInput64Bit = ELF_hdr[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS64;

  if (isTarget64Bit != isInput64Bit) {
    fatal(diag::error_Mips_incompatible_class)
        << (isTarget64Bit ? "ELFCLASS64" : "ELFCLASS32")
        << (isInput64Bit ? "ELFCLASS64" : "ELFCLASS32") << pInput.name();
    return;
  }

  m_ElfFlagsMap[&pInput] =
      isInput64Bit ?
          reinterpret_cast<const llvm::ELF::Elf64_Ehdr*>(ELF_hdr)->e_flags :
          reinterpret_cast<const llvm::ELF::Elf32_Ehdr*>(ELF_hdr)->e_flags;
}

bool MipsGNULDBackend::readSection(Input& pInput, SectionData& pSD) {
  if ((pSD.getSection().flag() & llvm::ELF::SHF_MIPS_GPREL) ||
      (pSD.getSection().type() == llvm::ELF::SHT_MIPS_ABIFLAGS)) {
    uint64_t offset = pInput.fileOffset() + pSD.getSection().offset();
    uint64_t size = pSD.getSection().size();

    Fragment* frag = IRBuilder::CreateRegion(pInput, offset, size);
    ObjectBuilder::AppendFragment(*frag, pSD);
    return true;
  }

  if (pSD.getSection().type() == llvm::ELF::SHT_MIPS_OPTIONS) {
    uint32_t offset = pInput.fileOffset() + pSD.getSection().offset();
    uint32_t size = pSD.getSection().size();

    llvm::StringRef region = pInput.memArea()->request(offset, size);
    if (region.size() > 0) {
      const llvm::ELF::Elf_Options* optb =
          reinterpret_cast<const llvm::ELF::Elf_Options*>(region.begin());
      const llvm::ELF::Elf_Options* opte =
          reinterpret_cast<const llvm::ELF::Elf_Options*>(region.begin() +
                                                          size);

      for (const llvm::ELF::Elf_Options* opt = optb; opt < opte;
           opt += opt->size) {
        switch (opt->kind) {
          default:
            // Nothing to do.
            break;
          case llvm::ELF::ODK_REGINFO:
            if (config().targets().triple().isArch32Bit()) {
              const llvm::ELF::Elf32_RegInfo* reg =
                  reinterpret_cast<const llvm::ELF::Elf32_RegInfo*>(opt + 1);
              m_GP0Map[&pInput] = reg->ri_gp_value;
            } else {
              const llvm::ELF::Elf64_RegInfo* reg =
                  reinterpret_cast<const llvm::ELF::Elf64_RegInfo*>(opt + 1);
              m_GP0Map[&pInput] = reg->ri_gp_value;
            }
            break;
        }
      }
    }

    return true;
  }

  return GNULDBackend::readSection(pInput, pSD);
}

MipsGOT& MipsGNULDBackend::getGOT() {
  assert(m_pGOT != NULL);
  return *m_pGOT;
}

const MipsGOT& MipsGNULDBackend::getGOT() const {
  assert(m_pGOT != NULL);
  return *m_pGOT;
}

MipsPLT& MipsGNULDBackend::getPLT() {
  assert(m_pPLT != NULL);
  return *m_pPLT;
}

const MipsPLT& MipsGNULDBackend::getPLT() const {
  assert(m_pPLT != NULL);
  return *m_pPLT;
}

MipsGOTPLT& MipsGNULDBackend::getGOTPLT() {
  assert(m_pGOTPLT != NULL);
  return *m_pGOTPLT;
}

const MipsGOTPLT& MipsGNULDBackend::getGOTPLT() const {
  assert(m_pGOTPLT != NULL);
  return *m_pGOTPLT;
}

OutputRelocSection& MipsGNULDBackend::getRelPLT() {
  assert(m_pRelPlt != NULL);
  return *m_pRelPlt;
}

const OutputRelocSection& MipsGNULDBackend::getRelPLT() const {
  assert(m_pRelPlt != NULL);
  return *m_pRelPlt;
}

OutputRelocSection& MipsGNULDBackend::getRelDyn() {
  assert(m_pRelDyn != NULL);
  return *m_pRelDyn;
}

const OutputRelocSection& MipsGNULDBackend::getRelDyn() const {
  assert(m_pRelDyn != NULL);
  return *m_pRelDyn;
}

unsigned int MipsGNULDBackend::getTargetSectionOrder(
    const LDSection& pSectHdr) const {
  const ELFFileFormat* file_format = getOutputFormat();

  if (file_format->hasGOT() && (&pSectHdr == &file_format->getGOT()))
    return SHO_DATA;

  if (file_format->hasGOTPLT() && (&pSectHdr == &file_format->getGOTPLT()))
    return SHO_DATA;

  if (file_format->hasPLT() && (&pSectHdr == &file_format->getPLT()))
    return SHO_PLT;

  if (&pSectHdr == m_psdata)
    return SHO_SMALL_DATA;

  if (&pSectHdr == m_pAbiFlags)
    return SHO_RO_NOTE;

  return SHO_UNDEFINED;
}

/// finalizeSymbol - finalize the symbol value
bool MipsGNULDBackend::finalizeTargetSymbols() {
  if (m_pGpDispSymbol != NULL)
    m_pGpDispSymbol->setValue(m_pGOT->getGPDispAddress());

  return true;
}

/// allocateCommonSymbols - allocate common symbols in the corresponding
/// sections. This is called at pre-layout stage.
/// FIXME: Mips needs to allocate small common symbol
bool MipsGNULDBackend::allocateCommonSymbols(Module& pModule) {
  SymbolCategory& symbol_list = pModule.getSymbolTable();

  if (symbol_list.emptyCommons() && symbol_list.emptyFiles() &&
      symbol_list.emptyLocals() && symbol_list.emptyLocalDyns())
    return true;

  SymbolCategory::iterator com_sym, com_end;

  // FIXME: If the order of common symbols is defined, then sort common symbols
  // std::sort(com_sym, com_end, some kind of order);

  // get corresponding BSS LDSection
  ELFFileFormat* file_format = getOutputFormat();
  LDSection& bss_sect = file_format->getBSS();
  LDSection& tbss_sect = file_format->getTBSS();

  // get or create corresponding BSS SectionData
  SectionData* bss_sect_data = NULL;
  if (bss_sect.hasSectionData())
    bss_sect_data = bss_sect.getSectionData();
  else
    bss_sect_data = IRBuilder::CreateSectionData(bss_sect);

  SectionData* tbss_sect_data = NULL;
  if (tbss_sect.hasSectionData())
    tbss_sect_data = tbss_sect.getSectionData();
  else
    tbss_sect_data = IRBuilder::CreateSectionData(tbss_sect);

  // remember original BSS size
  uint64_t bss_offset = bss_sect.size();
  uint64_t tbss_offset = tbss_sect.size();

  // allocate all local common symbols
  com_end = symbol_list.localEnd();

  for (com_sym = symbol_list.localBegin(); com_sym != com_end; ++com_sym) {
    if (ResolveInfo::Common == (*com_sym)->desc()) {
      // We have to reset the description of the symbol here. When doing
      // incremental linking, the output relocatable object may have common
      // symbols. Therefore, we can not treat common symbols as normal symbols
      // when emitting the regular name pools. We must change the symbols'
      // description here.
      (*com_sym)->resolveInfo()->setDesc(ResolveInfo::Define);
      Fragment* frag = new FillFragment(0x0, 1, (*com_sym)->size());

      if (ResolveInfo::ThreadLocal == (*com_sym)->type()) {
        // allocate TLS common symbol in tbss section
        tbss_offset += ObjectBuilder::AppendFragment(
            *frag, *tbss_sect_data, (*com_sym)->value());
        ObjectBuilder::UpdateSectionAlign(tbss_sect, (*com_sym)->value());
        (*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0));
      } else {
        // FIXME: how to identify small and large common symbols?
        bss_offset += ObjectBuilder::AppendFragment(
            *frag, *bss_sect_data, (*com_sym)->value());
        ObjectBuilder::UpdateSectionAlign(bss_sect, (*com_sym)->value());
        (*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0));
      }
    }
  }

  // allocate all global common symbols
  com_end = symbol_list.commonEnd();
  for (com_sym = symbol_list.commonBegin(); com_sym != com_end; ++com_sym) {
    // We have to reset the description of the symbol here. When doing
    // incremental linking, the output relocatable object may have common
    // symbols. Therefore, we can not treat common symbols as normal symbols
    // when emitting the regular name pools. We must change the symbols'
    // description here.
    (*com_sym)->resolveInfo()->setDesc(ResolveInfo::Define);
    Fragment* frag = new FillFragment(0x0, 1, (*com_sym)->size());

    if (ResolveInfo::ThreadLocal == (*com_sym)->type()) {
      // allocate TLS common symbol in tbss section
      tbss_offset += ObjectBuilder::AppendFragment(
          *frag, *tbss_sect_data, (*com_sym)->value());
      ObjectBuilder::UpdateSectionAlign(tbss_sect, (*com_sym)->value());
      (*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0));
    } else {
      // FIXME: how to identify small and large common symbols?
      bss_offset += ObjectBuilder::AppendFragment(
          *frag, *bss_sect_data, (*com_sym)->value());
      ObjectBuilder::UpdateSectionAlign(bss_sect, (*com_sym)->value());
      (*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0));
    }
  }

  bss_sect.setSize(bss_offset);
  tbss_sect.setSize(tbss_offset);
  symbol_list.changeCommonsToGlobal();
  return true;
}

uint64_t MipsGNULDBackend::getTPOffset(const Input& pInput) const {
  return m_TpOffsetMap.lookup(&pInput);
}

uint64_t MipsGNULDBackend::getDTPOffset(const Input& pInput) const {
  return m_DtpOffsetMap.lookup(&pInput);
}

uint64_t MipsGNULDBackend::getGP0(const Input& pInput) const {
  return m_GP0Map.lookup(&pInput);
}

void MipsGNULDBackend::defineGOTSymbol(IRBuilder& pBuilder) {
  // If we do not reserve any GOT entries, we do not need to re-define GOT
  // symbol.
  if (!m_pGOT->hasGOT1())
    return;

  // define symbol _GLOBAL_OFFSET_TABLE_
  if (m_pGOTSymbol != NULL) {
    pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>(
        "_GLOBAL_OFFSET_TABLE_",
        ResolveInfo::Object,
        ResolveInfo::Define,
        ResolveInfo::Local,
        0x0,  // size
        0x0,  // value
        FragmentRef::Create(*(m_pGOT->begin()), 0x0),
        ResolveInfo::Hidden);
  } else {
    m_pGOTSymbol = pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Resolve>(
        "_GLOBAL_OFFSET_TABLE_",
        ResolveInfo::Object,
        ResolveInfo::Define,
        ResolveInfo::Local,
        0x0,  // size
        0x0,  // value
        FragmentRef::Create(*(m_pGOT->begin()), 0x0),
        ResolveInfo::Hidden);
  }
}

void MipsGNULDBackend::defineGOTPLTSymbol(IRBuilder& pBuilder) {
  // define symbol _PROCEDURE_LINKAGE_TABLE_
  if (m_pPLTSymbol != NULL) {
    pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>(
        "_PROCEDURE_LINKAGE_TABLE_",
        ResolveInfo::Object,
        ResolveInfo::Define,
        ResolveInfo::Local,
        0x0,  // size
        0x0,  // value
        FragmentRef::Create(*(m_pPLT->begin()), 0x0),
        ResolveInfo::Hidden);
  } else {
    m_pPLTSymbol = pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Resolve>(
        "_PROCEDURE_LINKAGE_TABLE_",
        ResolveInfo::Object,
        ResolveInfo::Define,
        ResolveInfo::Local,
        0x0,  // size
        0x0,  // value
        FragmentRef::Create(*(m_pPLT->begin()), 0x0),
        ResolveInfo::Hidden);
  }
}

/// doCreateProgramHdrs - backend can implement this function to create the
/// target-dependent segments
void MipsGNULDBackend::doCreateProgramHdrs(Module& pModule) {
  if (!m_pAbiFlags || m_pAbiFlags->size() == 0)
    return;

  // create PT_MIPS_ABIFLAGS segment
  ELFSegmentFactory::iterator sit =
      elfSegmentTable().find(llvm::ELF::PT_INTERP, 0x0, 0x0);
  if (sit == elfSegmentTable().end())
    sit = elfSegmentTable().find(llvm::ELF::PT_PHDR, 0x0, 0x0);
  if (sit == elfSegmentTable().end())
    sit = elfSegmentTable().begin();
  else
    ++sit;

  ELFSegment* abiSeg = elfSegmentTable().insert(sit,
                                                llvm::ELF::PT_MIPS_ABIFLAGS,
                                                llvm::ELF::PF_R);
  abiSeg->setAlign(8);
  abiSeg->append(m_pAbiFlags);
}

bool MipsGNULDBackend::relaxRelocation(IRBuilder& pBuilder, Relocation& pRel) {
  uint64_t sym_value = 0x0;

  LDSymbol* symbol = pRel.symInfo()->outSymbol();
  if (symbol->hasFragRef()) {
    uint64_t value = symbol->fragRef()->getOutputOffset();
    uint64_t addr = symbol->fragRef()->frag()->getParent()->getSection().addr();
    sym_value = addr + value;
  }

  Stub* stub = getStubFactory()->create(
      pRel, sym_value, pBuilder, *getBRIslandFactory());

  if (stub == NULL)
    return false;

  assert(stub->symInfo() != NULL);
  // reset the branch target of the reloc to this stub instead
  pRel.setSymInfo(stub->symInfo());

  // increase the size of .symtab and .strtab
  LDSection& symtab = getOutputFormat()->getSymTab();
  LDSection& strtab = getOutputFormat()->getStrTab();
  symtab.setSize(symtab.size() + sizeof(llvm::ELF::Elf32_Sym));
  strtab.setSize(strtab.size() + stub->symInfo()->nameSize() + 1);

  return true;
}

bool MipsGNULDBackend::doRelax(Module& pModule,
                               IRBuilder& pBuilder,
                               bool& pFinished) {
  assert(getStubFactory() != NULL && getBRIslandFactory() != NULL);

  bool isRelaxed = false;

  for (Module::obj_iterator input = pModule.obj_begin();
       input != pModule.obj_end();
       ++input) {
    LDContext* context = (*input)->context();

    for (LDContext::sect_iterator rs = context->relocSectBegin();
         rs != context->relocSectEnd();
         ++rs) {
      LDSection* sec = *rs;

      if (LDFileFormat::Ignore == sec->kind() || !sec->hasRelocData())
        continue;

      for (RelocData::iterator reloc = sec->getRelocData()->begin();
           reloc != sec->getRelocData()->end();
           ++reloc) {
        if (llvm::ELF::R_MIPS_26 != reloc->type())
          continue;

        if (relaxRelocation(pBuilder, *llvm::cast<Relocation>(reloc)))
          isRelaxed = true;
      }
    }
  }

  // find the first fragment w/ invalid offset due to stub insertion
  std::vector<Fragment*> invalid_frags;
  pFinished = true;
  for (BranchIslandFactory::iterator ii = getBRIslandFactory()->begin(),
                                     ie = getBRIslandFactory()->end();
       ii != ie;
       ++ii) {
    BranchIsland& island = *ii;
    if (island.size() > stubGroupSize()) {
      error(diag::err_no_space_to_place_stubs) << stubGroupSize();
      return false;
    }

    if (island.numOfStubs() == 0) {
      continue;
    }

    Fragment* exit = &*island.end();
    if (exit == &*island.begin()->getParent()->end()) {
      continue;
    }

    if ((island.offset() + island.size()) > exit->getOffset()) {
      if (invalid_frags.empty() ||
          (invalid_frags.back()->getParent() != island.getParent())) {
        invalid_frags.push_back(exit);
        pFinished = false;
      }
      continue;
    }
  }

  // reset the offset of invalid fragments
  for (auto it = invalid_frags.begin(), ie = invalid_frags.end(); it != ie;
       ++it) {
    Fragment* invalid = *it;
    while (invalid != NULL) {
      invalid->setOffset(invalid->getPrevNode()->getOffset() +
                         invalid->getPrevNode()->size());
      invalid = invalid->getNextNode();
    }
  }

  // reset the size of section that has stubs inserted.
  if (isRelaxed) {
    SectionData* prev = NULL;
    for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(),
                                       island_end = getBRIslandFactory()->end();
         island != island_end;
         ++island) {
      SectionData* sd = (*island).begin()->getParent();
      if ((*island).numOfStubs() != 0) {
        if (sd != prev) {
          sd->getSection().setSize(sd->back().getOffset() + sd->back().size());
        }
      }
      prev = sd;
    }
  }

  return isRelaxed;
}

bool MipsGNULDBackend::initTargetStubs() {
  if (getStubFactory() == NULL)
    return false;

  getStubFactory()->addPrototype(new MipsLA25Stub(*this));
  return true;
}

bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf32_Rel& pRel,
                                      Relocation::Type& pType,
                                      uint32_t& pSymIdx,
                                      uint32_t& pOffset) const {
  return GNULDBackend::readRelocation(pRel, pType, pSymIdx, pOffset);
}

bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf32_Rela& pRel,
                                      Relocation::Type& pType,
                                      uint32_t& pSymIdx,
                                      uint32_t& pOffset,
                                      int32_t& pAddend) const {
  return GNULDBackend::readRelocation(pRel, pType, pSymIdx, pOffset, pAddend);
}

bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf64_Rel& pRel,
                                      Relocation::Type& pType,
                                      uint32_t& pSymIdx,
                                      uint64_t& pOffset) const {
  uint64_t r_info = 0x0;
  if (llvm::sys::IsLittleEndianHost) {
    pOffset = pRel.r_offset;
    r_info = pRel.r_info;
  } else {
    pOffset = mcld::bswap64(pRel.r_offset);
    r_info = mcld::bswap64(pRel.r_info);
  }

  // MIPS 64 little endian (we do not support big endian now)
  // has a "special" encoding of r_info relocation
  // field. Instead of one 64 bit little endian number, it is a little
  // endian 32 bit number followed by a 32 bit big endian number.
  pType = mcld::bswap32(r_info >> 32);
  pSymIdx = r_info & 0xffffffff;
  return true;
}

bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf64_Rela& pRel,
                                      Relocation::Type& pType,
                                      uint32_t& pSymIdx,
                                      uint64_t& pOffset,
                                      int64_t& pAddend) const {
  uint64_t r_info = 0x0;
  if (llvm::sys::IsLittleEndianHost) {
    pOffset = pRel.r_offset;
    r_info = pRel.r_info;
    pAddend = pRel.r_addend;
  } else {
    pOffset = mcld::bswap64(pRel.r_offset);
    r_info = mcld::bswap64(pRel.r_info);
    pAddend = mcld::bswap64(pRel.r_addend);
  }

  pType = mcld::bswap32(r_info >> 32);
  pSymIdx = r_info & 0xffffffff;
  return true;
}

void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf32_Rel& pRel,
                                      Relocation::Type pType,
                                      uint32_t pSymIdx,
                                      uint32_t pOffset) const {
  GNULDBackend::emitRelocation(pRel, pType, pSymIdx, pOffset);
}

void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf32_Rela& pRel,
                                      Relocation::Type pType,
                                      uint32_t pSymIdx,
                                      uint32_t pOffset,
                                      int32_t pAddend) const {
  GNULDBackend::emitRelocation(pRel, pType, pSymIdx, pOffset, pAddend);
}

void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf64_Rel& pRel,
                                      Relocation::Type pType,
                                      uint32_t pSymIdx,
                                      uint64_t pOffset) const {
  uint64_t r_info = mcld::bswap32(pType);
  r_info <<= 32;
  r_info |= pSymIdx;

  pRel.r_info = r_info;
  pRel.r_offset = pOffset;
}

void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf64_Rela& pRel,
                                      Relocation::Type pType,
                                      uint32_t pSymIdx,
                                      uint64_t pOffset,
                                      int64_t pAddend) const {
  uint64_t r_info = mcld::bswap32(pType);
  r_info <<= 32;
  r_info |= pSymIdx;

  pRel.r_info = r_info;
  pRel.r_offset = pOffset;
  pRel.r_addend = pAddend;
}

namespace {
struct ISATreeEdge {
  unsigned child;
  unsigned parent;
};
}

static ISATreeEdge isaTree[] = {
    // MIPS32R6 and MIPS64R6 are not compatible with other extensions

    // MIPS64 extensions.
    {llvm::ELF::EF_MIPS_ARCH_64R2, llvm::ELF::EF_MIPS_ARCH_64},
    // MIPS V extensions.
    {llvm::ELF::EF_MIPS_ARCH_64, llvm::ELF::EF_MIPS_ARCH_5},
    // MIPS IV extensions.
    {llvm::ELF::EF_MIPS_ARCH_5, llvm::ELF::EF_MIPS_ARCH_4},
    // MIPS III extensions.
    {llvm::ELF::EF_MIPS_ARCH_4, llvm::ELF::EF_MIPS_ARCH_3},
    // MIPS32 extensions.
    {llvm::ELF::EF_MIPS_ARCH_32R2, llvm::ELF::EF_MIPS_ARCH_32},
    // MIPS II extensions.
    {llvm::ELF::EF_MIPS_ARCH_3, llvm::ELF::EF_MIPS_ARCH_2},
    {llvm::ELF::EF_MIPS_ARCH_32, llvm::ELF::EF_MIPS_ARCH_2},
    // MIPS I extensions.
    {llvm::ELF::EF_MIPS_ARCH_2, llvm::ELF::EF_MIPS_ARCH_1},
};

static bool isIsaMatched(uint32_t base, uint32_t ext) {
  if (base == ext)
    return true;
  if (base == llvm::ELF::EF_MIPS_ARCH_32 &&
      isIsaMatched(llvm::ELF::EF_MIPS_ARCH_64, ext))
    return true;
  if (base == llvm::ELF::EF_MIPS_ARCH_32R2 &&
      isIsaMatched(llvm::ELF::EF_MIPS_ARCH_64R2, ext))
    return true;
  for (const auto &edge : isaTree) {
    if (ext == edge.child) {
      ext = edge.parent;
      if (ext == base)
        return true;
    }
  }
  return false;
}

static bool getAbiFlags(const Input& pInput, uint64_t elfFlags, bool& hasFlags,
                        MipsAbiFlags& pFlags) {
  MipsAbiFlags pElfFlags = {};
  if (!MipsAbiFlags::fillByElfFlags(pInput, elfFlags, pElfFlags))
    return false;

  const LDContext* ctx = pInput.context();
  for (auto it = ctx->sectBegin(), ie = ctx->sectEnd(); it != ie; ++it)
    if ((*it)->type() == llvm::ELF::SHT_MIPS_ABIFLAGS) {
      if (!MipsAbiFlags::fillBySection(pInput, **it, pFlags))
        return false;
      if (!MipsAbiFlags::isCompatible(pInput, pElfFlags, pFlags))
        return false;
      hasFlags = true;
      return true;
    }

  pFlags = pElfFlags;
  return true;
}

static const char* getNanName(uint64_t flags) {
  return flags & llvm::ELF::EF_MIPS_NAN2008 ? "2008" : "legacy";
}

static bool mergeElfFlags(const Input& pInput, uint64_t& oldElfFlags,
                          uint64_t newElfFlags) {
  // PIC code is inherently CPIC and may not set CPIC flag explicitly.
  // Ensure that this flag will exist in the linked file.
  if (newElfFlags & llvm::ELF::EF_MIPS_PIC)
    newElfFlags |= llvm::ELF::EF_MIPS_CPIC;

  if (newElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE_M16) {
    error(diag::error_Mips_m16_unsupported) << pInput.name();
    return false;
  }

  if (!oldElfFlags) {
    oldElfFlags = newElfFlags;
    return true;
  }

  uint64_t newPic =
      newElfFlags & (llvm::ELF::EF_MIPS_PIC | llvm::ELF::EF_MIPS_CPIC);
  uint64_t oldPic =
      oldElfFlags & (llvm::ELF::EF_MIPS_PIC | llvm::ELF::EF_MIPS_CPIC);

  // Check PIC / CPIC flags compatibility.
  if ((newPic != 0) != (oldPic != 0))
    warning(diag::warn_Mips_abicalls_linking) << pInput.name();

  if (!(newPic & llvm::ELF::EF_MIPS_PIC))
    oldElfFlags &= ~llvm::ELF::EF_MIPS_PIC;
  if (newPic)
    oldElfFlags |= llvm::ELF::EF_MIPS_CPIC;

  // Check ISA compatibility.
  uint64_t newArch = newElfFlags & llvm::ELF::EF_MIPS_ARCH;
  uint64_t oldArch = oldElfFlags & llvm::ELF::EF_MIPS_ARCH;
  if (!isIsaMatched(newArch, oldArch)) {
    if (!isIsaMatched(oldArch, newArch)) {
      error(diag::error_Mips_inconsistent_arch)
          << ArchName(oldArch) << ArchName(newArch) << pInput.name();
      return false;
    }
    oldElfFlags &= ~llvm::ELF::EF_MIPS_ARCH;
    oldElfFlags |= newArch;
  }

  // Check ABI compatibility.
  uint32_t newAbi = newElfFlags & llvm::ELF::EF_MIPS_ABI;
  uint32_t oldAbi = oldElfFlags & llvm::ELF::EF_MIPS_ABI;
  if (newAbi != oldAbi && newAbi && oldAbi) {
    error(diag::error_Mips_inconsistent_abi) << pInput.name();
    return false;
  }

  // Check -mnan flags compatibility.
  if ((newElfFlags & llvm::ELF::EF_MIPS_NAN2008) !=
      (oldElfFlags & llvm::ELF::EF_MIPS_NAN2008)) {
    // Linking -mnan=2008 and -mnan=legacy modules
    error(diag::error_Mips_inconsistent_mnan)
        << getNanName(oldElfFlags) << getNanName(newElfFlags) << pInput.name();
    return false;
  }

  // Check ASE compatibility.
  uint64_t newAse = newElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE;
  uint64_t oldAse = oldElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE;
  if (newAse != oldAse)
    oldElfFlags |= newAse;

  // Check FP64 compatibility.
  if ((newElfFlags & llvm::ELF::EF_MIPS_FP64) !=
      (oldElfFlags & llvm::ELF::EF_MIPS_FP64)) {
    // Linking -mnan=2008 and -mnan=legacy modules
    error(diag::error_Mips_inconsistent_fp64) << pInput.name();
    return false;
  }

  oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_NOREORDER;
  oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_MICROMIPS;
  oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_NAN2008;
  oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_32BITMODE;

  return true;
}

void MipsGNULDBackend::saveTPOffset(const Input& pInput) {
  const LDContext* ctx = pInput.context();
  for (auto it = ctx->sectBegin(), ie = ctx->sectEnd(); it != ie; ++it) {
    LDSection* sect = *it;
    if (sect->flag() & llvm::ELF::SHF_TLS) {
      m_TpOffsetMap[&pInput] = sect->addr() + 0x7000;
      m_DtpOffsetMap[&pInput] = sect->addr() + 0x8000;
      break;
    }
  }
}

void MipsGNULDBackend::preMergeSections(Module& pModule) {
  uint64_t elfFlags = 0;
  bool hasAbiFlags = false;
  MipsAbiFlags abiFlags = {};
  for (const Input *input : pModule.getObjectList()) {
    if (input->type() != Input::Object)
      continue;

    uint64_t newElfFlags = m_ElfFlagsMap[input];

    MipsAbiFlags newAbiFlags = {};
    if (!getAbiFlags(*input, newElfFlags, hasAbiFlags, newAbiFlags))
      continue;

    if (!mergeElfFlags(*input, elfFlags, newElfFlags))
      continue;

    if (!MipsAbiFlags::merge(*input, abiFlags, newAbiFlags))
      continue;

    saveTPOffset(*input);
  }

  m_pInfo.setElfFlags(elfFlags);
  if (hasAbiFlags)
    m_pAbiInfo = abiFlags;
}

bool MipsGNULDBackend::mergeSection(Module& pModule, const Input& pInput,
                                    LDSection& pSection) {
  if (pSection.flag() & llvm::ELF::SHF_MIPS_GPREL) {
    SectionData* sd = NULL;
    if (!m_psdata->hasSectionData()) {
      sd = IRBuilder::CreateSectionData(*m_psdata);
      m_psdata->setSectionData(sd);
    }
    sd = m_psdata->getSectionData();
    moveSectionData(*pSection.getSectionData(), *sd);
  } else if (pSection.type() == llvm::ELF::SHT_MIPS_ABIFLAGS) {
    // Nothing to do because we handle all .MIPS.abiflags sections
    // in the preMergeSections method.
  } else {
    ObjectBuilder builder(pModule);
    builder.MergeSection(pInput, pSection);
  }
  return true;
}

void MipsGNULDBackend::moveSectionData(SectionData& pFrom, SectionData& pTo) {
  assert(&pFrom != &pTo && "Cannot move section data to itself!");

  uint64_t offset = pTo.getSection().size();
  AlignFragment* align = NULL;
  if (pFrom.getSection().align() > 1) {
    // if the align constraint is larger than 1, append an alignment
    unsigned int alignment = pFrom.getSection().align();
    align = new AlignFragment(/*alignment*/ alignment,
                              /*the filled value*/ 0x0,
                              /*the size of filled value*/ 1u,
                              /*max bytes to emit*/ alignment - 1);
    align->setOffset(offset);
    align->setParent(&pTo);
    pTo.getFragmentList().push_back(align);
    offset += align->size();
  }

  // move fragments from pFrom to pTO
  SectionData::FragmentListType& from_list = pFrom.getFragmentList();
  SectionData::FragmentListType& to_list = pTo.getFragmentList();
  SectionData::FragmentListType::iterator frag, fragEnd = from_list.end();
  for (frag = from_list.begin(); frag != fragEnd; ++frag) {
    frag->setParent(&pTo);
    frag->setOffset(offset);
    offset += frag->size();
  }
  to_list.splice(to_list.end(), from_list);

  // set up pTo's header
  pTo.getSection().setSize(offset);
}

//===----------------------------------------------------------------------===//
// Mips32GNULDBackend
//===----------------------------------------------------------------------===//
Mips32GNULDBackend::Mips32GNULDBackend(const LinkerConfig& pConfig,
                                       MipsGNUInfo* pInfo)
    : MipsGNULDBackend(pConfig, pInfo) {
}

bool Mips32GNULDBackend::initRelocator() {
  if (m_pRelocator == NULL)
    m_pRelocator = new Mips32Relocator(*this, config());

  return true;
}

void Mips32GNULDBackend::initTargetSections(Module& pModule,
                                            ObjectBuilder& pBuilder) {
  MipsGNULDBackend::initTargetSections(pModule, pBuilder);

  if (LinkerConfig::Object == config().codeGenType())
    return;

  ELFFileFormat* fileFormat = getOutputFormat();

  // initialize .got
  LDSection& got = fileFormat->getGOT();
  m_pGOT = new Mips32GOT(got);

  // initialize .got.plt
  LDSection& gotplt = fileFormat->getGOTPLT();
  m_pGOTPLT = new MipsGOTPLT(gotplt);

  // initialize .plt
  LDSection& plt = fileFormat->getPLT();
  m_pPLT = new MipsPLT(plt);
}

size_t Mips32GNULDBackend::getRelEntrySize() {
  return 8;
}

size_t Mips32GNULDBackend::getRelaEntrySize() {
  return 12;
}

//===----------------------------------------------------------------------===//
// Mips64GNULDBackend
//===----------------------------------------------------------------------===//
Mips64GNULDBackend::Mips64GNULDBackend(const LinkerConfig& pConfig,
                                       MipsGNUInfo* pInfo)
    : MipsGNULDBackend(pConfig, pInfo) {
}

bool Mips64GNULDBackend::initRelocator() {
  if (m_pRelocator == NULL)
    m_pRelocator = new Mips64Relocator(*this, config());

  return true;
}

void Mips64GNULDBackend::initTargetSections(Module& pModule,
                                            ObjectBuilder& pBuilder) {
  MipsGNULDBackend::initTargetSections(pModule, pBuilder);

  if (LinkerConfig::Object == config().codeGenType())
    return;

  ELFFileFormat* fileFormat = getOutputFormat();

  // initialize .got
  LDSection& got = fileFormat->getGOT();
  m_pGOT = new Mips64GOT(got);

  // initialize .got.plt
  LDSection& gotplt = fileFormat->getGOTPLT();
  m_pGOTPLT = new MipsGOTPLT(gotplt);

  // initialize .plt
  LDSection& plt = fileFormat->getPLT();
  m_pPLT = new MipsPLT(plt);
}

size_t Mips64GNULDBackend::getRelEntrySize() {
  return 16;
}

size_t Mips64GNULDBackend::getRelaEntrySize() {
  return 24;
}

//===----------------------------------------------------------------------===//
/// createMipsLDBackend - the help funtion to create corresponding MipsLDBackend
///
static TargetLDBackend* createMipsLDBackend(const LinkerConfig& pConfig) {
  const llvm::Triple& triple = pConfig.targets().triple();

  if (triple.isOSDarwin()) {
    assert(0 && "MachO linker is not supported yet");
  }
  if (triple.isOSWindows()) {
    assert(0 && "COFF linker is not supported yet");
  }

  llvm::Triple::ArchType arch = triple.getArch();

  if (llvm::Triple::mips64el == arch)
    return new Mips64GNULDBackend(pConfig, new MipsGNUInfo(triple));

  assert(arch == llvm::Triple::mipsel);
  return new Mips32GNULDBackend(pConfig, new MipsGNUInfo(triple));
}

}  // namespace mcld

//===----------------------------------------------------------------------===//
// Force static initialization.
//===----------------------------------------------------------------------===//
extern "C" void MCLDInitializeMipsLDBackend() {
  mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheMipselTarget,
                                                mcld::createMipsLDBackend);
  mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheMips64elTarget,
                                                mcld::createMipsLDBackend);
}