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

#include <mcld/LD/Relocation.h>
#include <mcld/LD/RelocationFactory.h>
#include <mcld/LD/Layout.h>
#include <mcld/Support/MsgHandling.h>

using namespace mcld;

Relocation::Relocation(Relocation::Type pType,
                       FragmentRef* pTargetRef,
                       Relocation::Address pAddend,
                       Relocation::DWord pTargetData)
  : Fragment(Fragment::Relocation),
    m_Type(pType),
    m_TargetData(pTargetData),
    m_pSymInfo(NULL),
    m_Addend(pAddend)
{
  if(NULL != pTargetRef)
     m_TargetAddress.assign(*pTargetRef->frag(), pTargetRef->offset()) ;
}

Relocation::~Relocation()
{
}

Relocation::Address Relocation::place(const Layout& pLayout) const
{
  Address sect_addr = pLayout.getOutputLDSection(*(m_TargetAddress.frag()))->addr();
  return sect_addr + pLayout.getOutputOffset(m_TargetAddress);
}

Relocation::Address Relocation::symValue() const
{
  if (m_pSymInfo->type() == ResolveInfo::Section &&
     m_pSymInfo->outSymbol()->hasFragRef()) {
    return m_pSymInfo->outSymbol()->fragRef()->frag()->getParent()->getSection().addr();
  }
  return m_pSymInfo->outSymbol()->value();
}

void Relocation::apply(RelocationFactory& pRelocFactory,
                       const MCLDInfo& pLDInfo)
{
  RelocationFactory::Result result =
                                 pRelocFactory.applyRelocation(*this, pLDInfo);

  switch (result) {
    case RelocationFactory::OK: {
      // do nothing
      return;
    }
    case RelocationFactory::Overflow: {
      error(diag::result_overflow) << pRelocFactory.getName(type())
                                   << symInfo()->name();
      return;
    }
    case RelocationFactory::BadReloc: {
      error(diag::result_badreloc) << pRelocFactory.getName(type())
                                   << symInfo()->name();
      return;
    }
    case RelocationFactory::Unsupport: {
      fatal(diag::unsupported_relocation) << type()
                                          << "mclinker@googlegroups.com";
      return;
    }
  } // end of switch
}

void Relocation::setType(Type pType)
{
  m_Type = pType;
}

void Relocation::setAddend(Address pAddend)
{
  m_Addend = pAddend;
}

void Relocation::setSymInfo(ResolveInfo* pSym)
{
  m_pSymInfo = pSym;
}

Relocation::DWord& Relocation::target()
{
  return m_TargetData;
}

const Relocation::DWord& Relocation::target() const
{
  return m_TargetData;
}