//===- X86GOTPLT.cpp ------------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "X86GOTPLT.h"

#include <new>

#include <llvm/Support/Casting.h>

#include <mcld/LD/LDFileFormat.h>
#include <mcld/Support/MsgHandling.h>

namespace {
  const uint64_t X86GOTPLTEntrySize = 4;
}

namespace mcld {

//===----------------------------------------------------------------------===//
// X86GOTPLT
//===----------------------------------------------------------------------===//
X86GOTPLT::X86GOTPLT(LDSection& pSection, SectionData& pSectionData)
  : GOT(pSection, pSectionData, X86GOTPLTEntrySize), m_GOTPLTIterator()
{
  GOTEntry* Entry = 0;

  // Create GOT0 entries.
  for (size_t i = 0; i < X86GOTPLT0Num; i++) {
    Entry = new (std::nothrow) GOTEntry(0, X86GOTPLTEntrySize,
                                        &m_SectionData);

    if (!Entry)
      fatal(diag::fail_allocate_memory_got);

    m_Section.setSize(m_Section.size() + X86GOTPLTEntrySize);
  }

  // Skip GOT0 entries.
  iterator it = m_SectionData.begin();

  for (size_t i = 1; i < X86GOTPLT0Num; ++i) {
    assert((it != m_SectionData.end()) &&
           "Generation of GOT0 entries is incomplete!");

    ++it;
  }

  m_GOTPLTIterator = it;
}

X86GOTPLT::~X86GOTPLT()
{
}

X86GOTPLT::iterator X86GOTPLT::begin()
{
  return m_SectionData.begin();
}

X86GOTPLT::const_iterator X86GOTPLT::begin() const
{
  return m_SectionData.begin();
}

X86GOTPLT::iterator X86GOTPLT::end()
{
  return m_SectionData.end();
}

X86GOTPLT::const_iterator X86GOTPLT::end() const
{
  return m_SectionData.end();
}

void X86GOTPLT::applyGOT0(uint64_t pAddress)
{
  llvm::cast<GOTEntry>
    (*(m_SectionData.getFragmentList().begin())).setContent(pAddress);
}

void X86GOTPLT::reserveEntry(size_t pNum)
{
  GOTEntry* got_entry = NULL;
  for (size_t i = 0; i < pNum; ++i) {
    got_entry = new GOTEntry(0, getEntrySize(),&(getSectionData()));
    if (!got_entry)
      fatal(diag::fail_allocate_memory_got);

    m_Section.setSize(m_Section.size() + getEntrySize());
  }
}

void X86GOTPLT::applyAllGOTPLT(uint64_t pPLTBase,
                               unsigned int pPLT0Size,
                               unsigned int pPLT1Size)
{
  iterator it = begin();
  // skip GOT0
  for (size_t i = 0; i < X86GOTPLT0Num; ++i)
    ++it;
  // address of corresponding plt entry
  uint64_t plt_addr = pPLTBase + pPLT0Size;
  for (; it != end() ; ++it) {
    llvm::cast<GOTEntry>(*it).setContent(plt_addr + 6);
    plt_addr += pPLT1Size;
  }
}

GOTEntry*& X86GOTPLT::lookupGOTPLTMap(const ResolveInfo& pSymbol)
{
  return m_GOTPLTMap[&pSymbol];
}

GOTEntry* X86GOTPLT::getEntry(const ResolveInfo& pInfo, bool& pExist)
{
  GOTEntry *&Entry = m_GOTPLTMap[&pInfo];
  pExist = 1;

  if (!Entry) {
    pExist = 0;

    ++m_GOTPLTIterator;
    assert(m_GOTPLTIterator != m_SectionData.getFragmentList().end()
           && "The number of GOT Entries and ResolveInfo doesn't match!");

    Entry = llvm::cast<GOTEntry>(&(*m_GOTPLTIterator));
  }

  return Entry;
}

} //end mcld namespace