//===- ObjectLinker.cpp ---------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/Object/ObjectLinker.h>

#include <mcld/LinkerConfig.h>
#include <mcld/Module.h>
#include <mcld/InputTree.h>
#include <mcld/IRBuilder.h>
#include <mcld/LD/LDSection.h>
#include <mcld/LD/LDContext.h>
#include <mcld/LD/Archive.h>
#include <mcld/LD/ArchiveReader.h>
#include <mcld/LD/ObjectReader.h>
#include <mcld/LD/DynObjReader.h>
#include <mcld/LD/GroupReader.h>
#include <mcld/LD/BinaryReader.h>
#include <mcld/LD/ObjectWriter.h>
#include <mcld/LD/ResolveInfo.h>
#include <mcld/LD/RelocData.h>
#include <mcld/Support/RealPath.h>
#include <mcld/Support/MemoryArea.h>
#include <mcld/Support/MsgHandling.h>
#include <mcld/Target/TargetLDBackend.h>
#include <mcld/Fragment/FragmentLinker.h>
#include <mcld/Object/ObjectBuilder.h>

#include <llvm/Support/Casting.h>


using namespace llvm;
using namespace mcld;

ObjectLinker::ObjectLinker(const LinkerConfig& pConfig,
                           TargetLDBackend& pLDBackend)
  : m_Config(pConfig),
    m_pLinker(NULL),
    m_pModule(NULL),
    m_pBuilder(NULL),
    m_LDBackend(pLDBackend),
    m_pObjectReader(NULL),
    m_pDynObjReader(NULL),
    m_pArchiveReader(NULL),
    m_pGroupReader(NULL),
    m_pBinaryReader(NULL),
    m_pWriter(NULL) {
}

ObjectLinker::~ObjectLinker()
{
  delete m_pLinker;
  delete m_pObjectReader;
  delete m_pDynObjReader;
  delete m_pArchiveReader;
  delete m_pGroupReader;
  delete m_pBinaryReader;
  delete m_pWriter;
}

void ObjectLinker::setup(Module& pModule, IRBuilder& pBuilder)
{
  m_pModule = &pModule;
  m_pBuilder = &pBuilder;
  // set up soname
  if (!m_Config.options().soname().empty()) {
    m_pModule->setName(m_Config.options().soname());
  }
}

/// initFragmentLinker - initialize FragmentLinker
///  Connect all components with FragmentLinker
bool ObjectLinker::initFragmentLinker()
{
  if (NULL == m_pLinker) {
    m_pLinker = new FragmentLinker(m_Config,
                                   *m_pModule,
                                   m_LDBackend);
  }

  // initialize the readers and writers
  // Because constructor can not be failed, we initalize all readers and
  // writers outside the FragmentLinker constructors.
  m_pObjectReader  = m_LDBackend.createObjectReader(*m_pBuilder);
  m_pArchiveReader = m_LDBackend.createArchiveReader(*m_pModule);
  m_pDynObjReader  = m_LDBackend.createDynObjReader(*m_pBuilder);
  m_pGroupReader   = new GroupReader(*m_pModule, *m_pObjectReader,
                                     *m_pDynObjReader, *m_pArchiveReader);
  m_pBinaryReader  = m_LDBackend.createBinaryReader(*m_pBuilder);
  m_pWriter        = m_LDBackend.createWriter();

  // initialize Relocator
  m_LDBackend.initRelocator();
  return true;
}

/// initStdSections - initialize standard sections
bool ObjectLinker::initStdSections()
{
  ObjectBuilder builder(m_Config, *m_pModule);

  // initialize standard sections
  if (!m_LDBackend.initStdSections(builder))
    return false;

  // initialize target-dependent sections
  m_LDBackend.initTargetSections(*m_pModule, builder);

  return true;
}

void ObjectLinker::normalize()
{
  // -----  set up inputs  ----- //
  Module::input_iterator input, inEnd = m_pModule->input_end();
  for (input = m_pModule->input_begin(); input!=inEnd; ++input) {
    // is a group node
    if (isGroup(input)) {
      getGroupReader()->readGroup(input, m_pBuilder->getInputBuilder(), m_Config);
      continue;
    }

    // already got type - for example, bitcode or external OIR (object
    // intermediate representation)
    if ((*input)->type() == Input::Script ||
        (*input)->type() == Input::Archive ||
        (*input)->type() == Input::External)
      continue;

    if (Input::Object == (*input)->type()) {
      m_pModule->getObjectList().push_back(*input);
      continue;
    }

    if (Input::DynObj == (*input)->type()) {
      m_pModule->getLibraryList().push_back(*input);
      continue;
    }

    // read input as a binary file
    if (m_Config.options().isBinaryInput()) {
      (*input)->setType(Input::Object);
      getBinaryReader()->readBinary(**input);
      m_pModule->getObjectList().push_back(*input);
    }
    // is a relocatable object file
    else if (getObjectReader()->isMyFormat(**input)) {
      (*input)->setType(Input::Object);
      getObjectReader()->readHeader(**input);
      getObjectReader()->readSections(**input);
      getObjectReader()->readSymbols(**input);
      m_pModule->getObjectList().push_back(*input);
    }
    // is a shared object file
    else if (getDynObjReader()->isMyFormat(**input)) {
      (*input)->setType(Input::DynObj);
      getDynObjReader()->readHeader(**input);
      getDynObjReader()->readSymbols(**input);
      m_pModule->getLibraryList().push_back(*input);
    }
    // is an archive
    else if (getArchiveReader()->isMyFormat(**input)) {
      (*input)->setType(Input::Archive);
      Archive archive(**input, m_pBuilder->getInputBuilder());
      getArchiveReader()->readArchive(archive);
      if(archive.numOfObjectMember() > 0) {
        m_pModule->getInputTree().merge<InputTree::Inclusive>(input,
                                                            archive.inputs());
      }
    }
    else {
      fatal(diag::err_unrecognized_input_file) << (*input)->path()
                                          << m_Config.targets().triple().str();
    }
  } // end of for
}

bool ObjectLinker::linkable() const
{
  // check we have input and output files
  if (m_pModule->getInputTree().empty()) {
    error(diag::err_no_inputs);
    return false;
  }

  // can not mix -static with shared objects
  Module::const_lib_iterator lib, libEnd = m_pModule->lib_end();
  for (lib = m_pModule->lib_begin(); lib != libEnd; ++lib) {
    if((*lib)->attribute()->isStatic()) {
      error(diag::err_mixed_shared_static_objects)
                                      << (*lib)->name() << (*lib)->path();
      return false;
    }
  }

  // --nmagic and --omagic options lead to static executable program.
  // These options turn off page alignment of sections. Because the
  // sections are not aligned to pages, these sections can not contain any
  // exported functions. Also, because the two options disable linking
  // against shared libraries, the output absolutely does not call outside
  // functions.
  if (m_Config.options().nmagic() && !m_Config.isCodeStatic()) {
    error(diag::err_nmagic_not_static);
    return false;
  }
  if (m_Config.options().omagic() && !m_Config.isCodeStatic()) {
    error(diag::err_omagic_not_static);
    return false;
  }

  return true;
}

/// readRelocations - read all relocation entries
///
/// All symbols should be read and resolved before this function.
bool ObjectLinker::readRelocations()
{
  // Bitcode is read by the other path. This function reads relocation sections
  // in object files.
  mcld::InputTree::bfs_iterator input, inEnd = m_pModule->getInputTree().bfs_end();
  for (input=m_pModule->getInputTree().bfs_begin(); input!=inEnd; ++input) {
    if ((*input)->type() == Input::Object && (*input)->hasMemArea()) {
      if (!getObjectReader()->readRelocations(**input))
        return false;
    }
    // ignore the other kinds of files.
  }
  return true;
}

/// mergeSections - put allinput sections into output sections
bool ObjectLinker::mergeSections()
{
  ObjectBuilder builder(m_Config, *m_pModule);
  Module::obj_iterator obj, objEnd = m_pModule->obj_end();
  for (obj = m_pModule->obj_begin(); obj != objEnd; ++obj) {
    LDContext::sect_iterator sect, sectEnd = (*obj)->context()->sectEnd();
    for (sect = (*obj)->context()->sectBegin(); sect != sectEnd; ++sect) {
      switch ((*sect)->kind()) {
        // Some *INPUT sections should not be merged.
        case LDFileFormat::Ignore:
        case LDFileFormat::Null:
        case LDFileFormat::Relocation:
        case LDFileFormat::NamePool:
        case LDFileFormat::Group:
        case LDFileFormat::StackNote:
          // skip
          continue;
        case LDFileFormat::Target:
          if (!m_LDBackend.mergeSection(*m_pModule, **sect)) {
            error(diag::err_cannot_merge_section) << (*sect)->name()
                                                  << (*obj)->name();
            return false;
          }
          break;
        case LDFileFormat::EhFrame: {
          if (!(*sect)->hasEhFrame())
            continue; // skip

          if (NULL == builder.MergeSection(**sect)) {
            error(diag::err_cannot_merge_section) << (*sect)->name()
                                                  << (*obj)->name();
            return false;
          }
          break;
        }
        default: {
          if (!(*sect)->hasSectionData())
            continue; // skip

          LDSection* out_sect = builder.MergeSection(**sect);
          if (NULL != out_sect) {
            if (!m_LDBackend.updateSectionFlags(*out_sect, **sect)) {
              error(diag::err_cannot_merge_section) << (*sect)->name()
                                                    << (*obj)->name();
              return false;
            }
          }
          else {
            error(diag::err_cannot_merge_section) << (*sect)->name()
                                                  << (*obj)->name();
            return false;
          }
          break;
        }
      } // end of switch
    } // for each section
  } // for each obj
  return true;
}

/// addStandardSymbols - shared object and executable files need some
/// standard symbols
///   @return if there are some input symbols with the same name to the
///   standard symbols, return false
bool ObjectLinker::addStandardSymbols()
{
  // create and add section symbols for each output section
  Module::iterator iter, iterEnd = m_pModule->end();
  for (iter = m_pModule->begin(); iter != iterEnd; ++iter) {
    m_pModule->getSectionSymbolSet().add(**iter, m_pModule->getNamePool());
  }

  return m_LDBackend.initStandardSymbols(*m_pBuilder, *m_pModule);
}

/// addTargetSymbols - some targets, such as MIPS and ARM, need some
/// target-dependent symbols
///   @return if there are some input symbols with the same name to the
///   target symbols, return false
bool ObjectLinker::addTargetSymbols()
{
  m_LDBackend.initTargetSymbols(*m_pBuilder, *m_pModule);
  return true;
}

/// addScriptSymbols - define symbols from the command line option or linker
/// scripts.
///   @return if there are some existing symbols with identical name to the
///   script symbols, return false.
bool ObjectLinker::addScriptSymbols()
{
  return true;
}

bool ObjectLinker::scanRelocations()
{
  // apply all relocations of all inputs
  Module::obj_iterator input, inEnd = m_pModule->obj_end();
  for (input = m_pModule->obj_begin(); input != inEnd; ++input) {
    LDContext::sect_iterator rs, rsEnd = (*input)->context()->relocSectEnd();
    for (rs = (*input)->context()->relocSectBegin(); rs != rsEnd; ++rs) {
      // bypass the reloc section if
      // 1. its section kind is changed to Ignore. (The target section is a
      // discarded group section.)
      // 2. it has no reloc data. (All symbols in the input relocs are in the
      // discarded group sections)
      if (LDFileFormat::Ignore == (*rs)->kind() || !(*rs)->hasRelocData())
        continue;
      RelocData::iterator reloc, rEnd = (*rs)->getRelocData()->end();
      for (reloc = (*rs)->getRelocData()->begin(); reloc != rEnd; ++reloc) {
        Relocation* relocation = llvm::cast<Relocation>(reloc);
        // scan relocation
        if (LinkerConfig::Object != m_Config.codeGenType())
          m_LDBackend.scanRelocation(*relocation, *m_pBuilder, *m_pModule, **rs);
        else
          m_LDBackend.partialScanRelocation(*relocation, *m_pModule, **rs);
      } // for all relocations
    } // for all relocation section
  } // for all inputs
  return true;
}

/// initStubs - initialize stub-related stuff.
bool ObjectLinker::initStubs()
{
  // initialize BranchIslandFactory
  m_LDBackend.initBRIslandFactory();

  // initialize StubFactory
  m_LDBackend.initStubFactory();

  // initialize target stubs
  m_LDBackend.initTargetStubs();
  return true;
}

/// allocateCommonSymobols - allocate fragments for common symbols to the
/// corresponding sections
bool ObjectLinker::allocateCommonSymbols()
{
  if (LinkerConfig::Object != m_Config.codeGenType() ||
      m_Config.options().isDefineCommon())
    return m_LDBackend.allocateCommonSymbols(*m_pModule);
  return true;
}

/// prelayout - help backend to do some modification before layout
bool ObjectLinker::prelayout()
{
  // finalize the section symbols, set their fragment reference and push them
  // into output symbol table
  Module::iterator sect, sEnd = m_pModule->end();
  for (sect = m_pModule->begin(); sect != sEnd; ++sect) {
    m_pModule->getSectionSymbolSet().finalize(**sect, m_pModule->getSymbolTable());
  }

  m_LDBackend.preLayout(*m_pModule, *m_pBuilder);

  /// check program interpreter - computer the name size of the runtime dyld
  if (!m_Config.isCodeStatic() &&
      (LinkerConfig::Exec == m_Config.codeGenType() ||
       m_Config.options().isPIE() ||
       m_Config.options().hasDyld()))
    m_LDBackend.sizeInterp();

  /// measure NamePools - compute the size of name pool sections
  /// In ELF, will compute  the size of.symtab, .strtab, .dynsym, .dynstr,
  /// .hash and .shstrtab sections.
  ///
  /// dump all symbols and strings from FragmentLinker and build the format-dependent
  /// hash table.
  m_LDBackend.sizeNamePools(*m_pModule, m_Config.isCodeStatic());

  return true;
}

/// layout - linearly layout all output sections and reserve some space
/// for GOT/PLT
///   Because we do not support instruction relaxing in this early version,
///   if there is a branch can not jump to its target, we return false
///   directly
bool ObjectLinker::layout()
{
  m_LDBackend.layout(*m_pModule);
  return true;
}

/// prelayout - help backend to do some modification after layout
bool ObjectLinker::postlayout()
{
  m_LDBackend.postLayout(*m_pModule, *m_pBuilder);
  return true;
}

/// finalizeSymbolValue - finalize the resolved symbol value.
///   Before relocate(), after layout(), FragmentLinker should correct value of all
///   symbol.
bool ObjectLinker::finalizeSymbolValue()
{
  return (m_pLinker->finalizeSymbols() && m_LDBackend.finalizeSymbols());
}

/// relocate - applying relocation entries and create relocation
/// section in the output files
/// Create relocation section, asking TargetLDBackend to
/// read the relocation information into RelocationEntry
/// and push_back into the relocation section
bool ObjectLinker::relocation()
{
  return m_pLinker->applyRelocations();
}

/// emitOutput - emit the output file.
bool ObjectLinker::emitOutput(MemoryArea& pOutput)
{
  return llvm::errc::success == getWriter()->writeObject(*m_pModule, pOutput);
}

/// postProcessing - do modification after all processes
bool ObjectLinker::postProcessing(MemoryArea& pOutput)
{
  m_pLinker->syncRelocationResult(pOutput);

  // emit .eh_frame_hdr
  // eh_frame_hdr should be emitted after syncRelocation, because eh_frame_hdr
  // needs FDE PC value, which will be corrected at syncRelocation
  m_LDBackend.postProcessing(pOutput);
  return true;
}