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

#include "ARMException.h"

#include "ARMLDBackend.h"

#include "mcld/Fragment/RegionFragment.h"
#include "mcld/LD/ELFFileFormat.h"
#include "mcld/LD/LDContext.h"
#include "mcld/Support/MsgHandling.h"

#include <memory>

namespace mcld {

static RegionFragment* findRegionFragment(LDSection& pSection) {
  SectionData* sectData = pSection.getSectionData();
  for (SectionData::iterator it = sectData->begin(),
                             end = sectData->end(); it != end; ++it) {
    if (it->getKind() == Fragment::Region) {
      return static_cast<RegionFragment*>(&*it);
    }
  }
  return NULL;
}

void ARMExData::addInputMap(Input* pInput,
                            std::unique_ptr<ARMInputExMap> pExMap) {
  assert(m_Inputs.find(pInput) == m_Inputs.end() &&
         "multiple maps for an input");

  ARMInputExMap* exMap = pExMap.get();

  // Add mapping to the input-to-exdata map.
  m_Inputs.insert(std::make_pair(pInput, std::move(pExMap)));

  // Add mapping to the fragment-to-exdata map.
  for (ARMInputExMap::iterator it = exMap->begin(), end = exMap->end();
       it != end; ++it) {
    ARMExSectionTuple* exTuple = it->second.get();
    m_ExIdxToTuple[exTuple->getExIdxFragment()] = exTuple;
  }
}

std::unique_ptr<ARMExData> ARMExData::create(Module& pModule) {
  std::unique_ptr<ARMExData> exData(new ARMExData());
  for (Module::obj_iterator it = pModule.obj_begin(),
                            end = pModule.obj_end(); it != end; ++it) {
    Input* input = *it;
    exData->addInputMap(input, ARMInputExMap::create(*input));
  }
  return exData;
}

std::unique_ptr<ARMInputExMap> ARMInputExMap::create(Input& pInput) {
  std::unique_ptr<ARMInputExMap> exMap(new ARMInputExMap());

  // Scan the input and collect all related sections.
  LDContext* ctx = pInput.context();
  for (LDContext::sect_iterator it = ctx->sectBegin(),
                                end = ctx->sectEnd(); it != end; ++it) {
    LDSection* sect = *it;
    if (sect->type() == llvm::ELF::SHT_ARM_EXIDX) {
      ARMExSectionTuple* exTuple = exMap->getOrCreateByExSection(*sect);
      exTuple->setExIdxSection(sect);
      exTuple->setTextSection(sect->getLink());
      if (sect->getLink() == NULL) {
        fatal(diag::eh_missing_text_section) << sect->name() << pInput.name();
      }
    }
  }

  // Remove the invalid exception tuples and convert LDSection to RegionFragment
  // or RelocData.
  ARMInputExMap::iterator it = exMap->begin();
  ARMInputExMap::iterator end = exMap->end();
  while (it != end) {
    ARMExSectionTuple* exTuple = it->second.get();
    LDSection* const text = exTuple->getTextSection();
    LDSection* const exIdx = exTuple->getExIdxSection();

    // Ignore the exception section if the text section is ignored.
    if ((text->kind() == LDFileFormat::Ignore) ||
        (text->kind() == LDFileFormat::Folded)) {
      // Set the related exception sections as LDFileFormat::Ignore.
      exIdx->setKind(LDFileFormat::Ignore);
      // Remove this tuple from the input exception map.
      ARMInputExMap::iterator deadIt = it++;
      exMap->erase(deadIt);
      continue;
    }

    // Get RegionFragment from ".text", ".ARM.exidx", and ".ARM.extab" sections.
    RegionFragment* textFrag = findRegionFragment(*text);
    RegionFragment* exIdxFrag = findRegionFragment(*exIdx);

    exTuple->setTextFragment(textFrag);
    exTuple->setExIdxFragment(exIdxFrag);

    // If there is no region fragment in the .ARM.extab section, then we can
    // skip this tuple.
    if (exIdxFrag == NULL) {
      ARMInputExMap::iterator deadIt = it++;
      exMap->erase(deadIt);
      continue;
    }

    // Check next tuple
    ++it;
  }

  return exMap;
}

}  // namespace mcld