C++程序  |  385行  |  11.03 KB

/*
 * Copyright 2010-2012, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "bcc/Linker.h"
#include "bcc/Support/LinkerConfig.h"
#include "bcc/Support/MemoryFactory.h"
#include "bcc/Support/Log.h"

#include <llvm/Support/ELF.h>

#include <mcld/MC/MCLDDriver.h>
#include <mcld/MC/InputTree.h>
#include <mcld/MC/MCLinker.h>
#include <mcld/MC/InputTree.h>
#include <mcld/LD/LDSection.h>
#include <mcld/LD/LDContext.h>
#include <mcld/Target/TargetLDBackend.h>
#include <mcld/Support/Path.h>
#include <mcld/Support/MemoryArea.h>
#include <mcld/Support/FileHandle.h>
#include <mcld/Support/MemoryAreaFactory.h>
#include <mcld/Support/TargetRegistry.h>

using namespace bcc;

const char* Linker::GetErrorString(enum Linker::ErrorCode pErrCode) {
  static const char* ErrorString[] = {
    /* kSuccess */
    "Successfully compiled.",
    /* kDoubleConfig */
    "Configure Linker twice.",
    /* kCreateBackend */
    "Cannot create backend.",
    /* kDelegateLDInfo */
    "Cannot get linker information",
    /* kFindNameSpec */
    "Cannot find -lnamespec",
    /* kOpenNameSpec */
    "Cannot open -lnamespec",
    /* kOpenObjectFile */
    "Cannot open object file",
    /* kNotConfig */
    "Linker::config() is not called",
    /* kNotSetUpOutput */
    "Linker::setOutput() is not called before add input files",
    /* kOpenOutput */
    "Cannot open output file",
    /* kReadSections */
    "Cannot read sections",
    /* kReadSymbols */
    "Cannot read symbols",
    /* kAddAdditionalSymbols */
    "Cannot add standard and target symbols",
    /* kMaxErrorCode */
    "(Unknown error code)"
  };

  if (pErrCode > kMaxErrorCode) {
    pErrCode = kMaxErrorCode;
  }

  return ErrorString[ static_cast<size_t>(pErrCode) ];
}

//===----------------------------------------------------------------------===//
// Linker
//===----------------------------------------------------------------------===//
Linker::Linker()
  : mBackend(NULL), mDriver(NULL), mMemAreaFactory(NULL), mLDInfo(NULL),
    mRoot(NULL), mShared(false) {
}

Linker::Linker(const LinkerConfig& pConfig)
  : mBackend(NULL), mDriver(NULL), mMemAreaFactory(NULL), mLDInfo(NULL),
    mRoot(NULL), mShared(false) {

  const std::string &triple = pConfig.getTriple();

  enum ErrorCode err = config(pConfig);
  if (kSuccess != err) {
    ALOGE("%s (%s)", GetErrorString(err), triple.c_str());
    return;
  }

  return;
}

Linker::~Linker() {
  delete mDriver;
  delete mBackend;
  delete mMemAreaFactory;
  delete mRoot;
}

enum Linker::ErrorCode Linker::extractFiles(const LinkerConfig& pConfig) {
  mLDInfo = const_cast<mcld::MCLDInfo*>(pConfig.getLDInfo());
  if (mLDInfo == NULL) {
    return kDelegateLDInfo;
  }

  mRoot = new mcld::InputTree::iterator(mLDInfo->inputs().root());
  mShared = pConfig.isShared();
  mSOName = pConfig.getSOName();

  return kSuccess;
}

enum Linker::ErrorCode Linker::config(const LinkerConfig& pConfig) {
  if (mLDInfo != NULL) {
    return kDoubleConfig;
  }

  extractFiles(pConfig);

  mBackend = pConfig.getTarget()->createLDBackend(pConfig.getTriple());
  if (mBackend == NULL) {
    return kCreateBackend;
  }

  mMemAreaFactory = new MemoryFactory();

  mDriver = new mcld::MCLDDriver(*mLDInfo, *mBackend, *mMemAreaFactory);

  mDriver->initMCLinker();

  return kSuccess;
}

void Linker::advanceRoot() {
  if (mRoot->isRoot()) {
    mRoot->move<mcld::TreeIteratorBase::Leftward>();
  } else {
    mRoot->move<mcld::TreeIteratorBase::Rightward>();
  }
  return;
}

enum Linker::ErrorCode Linker::openFile(const mcld::sys::fs::Path& pPath,
                                        enum Linker::ErrorCode pCode,
                                        mcld::Input& pInput) {
  mcld::MemoryArea *input_memory = mMemAreaFactory->produce(pPath,
                                                    mcld::FileHandle::ReadOnly);

  if (input_memory->handler()->isGood()) {
    pInput.setMemArea(input_memory);
  } else {
    return pCode;
  }

  mcld::LDContext *input_context = mLDInfo->contextFactory().produce(pPath);
  pInput.setContext(input_context);
  return kSuccess;
}

enum Linker::ErrorCode Linker::addNameSpec(const std::string &pNameSpec) {
  mcld::sys::fs::Path* path = NULL;
  // find out the real path of the namespec.
  if (mLDInfo->attrFactory().constraint().isSharedSystem()) {
    // In the system with shared object support, we can find both archive
    // and shared object.

    if (mLDInfo->attrFactory().last().isStatic()) {
      // with --static, we must search an archive.
      path = mLDInfo->options().directories().find(pNameSpec,
                                                   mcld::Input::Archive);
    }
    else {
      // otherwise, with --Bdynamic, we can find either an archive or a
      // shared object.
      path = mLDInfo->options().directories().find(pNameSpec,
                                                   mcld::Input::DynObj);
    }
  }
  else {
    // In the system without shared object support, we only look for an
    // archive.
    path = mLDInfo->options().directories().find(pNameSpec,
                                                 mcld::Input::Archive);
  }

  if (NULL == path)
    return kFindNameSpec;

  mcld::Input* input = mLDInfo->inputFactory().produce(pNameSpec, *path,
                                                       mcld::Input::Unknown);
  mLDInfo->inputs().insert<mcld::InputTree::Positional>(*mRoot, *input);

  advanceRoot();

  return openFile(*path, kOpenNameSpec, *input);
}

/// addObject - Add a object file by the filename.
enum Linker::ErrorCode Linker::addObject(const std::string &pObjectPath) {
  mcld::Input* input = mLDInfo->inputFactory().produce(pObjectPath,
                                                       pObjectPath,
                                                       mcld::Input::Unknown);

  mLDInfo->inputs().insert<mcld::InputTree::Positional>(*mRoot, *input);

  advanceRoot();

  return openFile(pObjectPath, kOpenObjectFile, *input);
}

/// addObject - Add a piece of memory. The memory is of ELF format.
enum Linker::ErrorCode Linker::addObject(void* pMemory, size_t pSize) {

  mcld::Input* input = mLDInfo->inputFactory().produce("memory object",
                                                       "NAN",
                                                       mcld::Input::Unknown);

  mLDInfo->inputs().insert<mcld::InputTree::Positional>(*mRoot, *input);

  advanceRoot();

  mcld::MemoryArea *input_memory = mMemAreaFactory->produce(pMemory, pSize);
  input->setMemArea(input_memory);

  mcld::LDContext *input_context = mLDInfo->contextFactory().produce();
  input->setContext(input_context);

  return kSuccess;
}

enum Linker::ErrorCode Linker::addCode(void* pMemory, size_t pSize) {
  mcld::Input* input = mLDInfo->inputFactory().produce("code object",
                                                       "NAN",
                                                       mcld::Input::External);

  mLDInfo->inputs().insert<mcld::InputTree::Positional>(*mRoot, *input);

  advanceRoot();

  mcld::MemoryArea *input_memory = mMemAreaFactory->produce(pMemory, pSize);
  input->setMemArea(input_memory);

  mcld::LDContext *input_context = mLDInfo->contextFactory().produce();
  input->setContext(input_context);

  // FIXME: So far, MCLinker must set up output before add input files.
  // set up LDContext
  if (mDriver->hasInitLinker()) {
    return kNotConfig;
  }

  if (!mLDInfo->output().hasContext()) {
    return kNotSetUpOutput;
  }

  // create NULL section
  mcld::LDSection& null =
      mDriver->getLinker()->createSectHdr("",
                                          mcld::LDFileFormat::Null,
                                          llvm::ELF::SHT_NULL,
                                          0);

  null.setSize(0);
  null.setOffset(0);
  null.setIndex(0);
  null.setInfo(0);
  null.setAlign(0);

  input_context->getSectionTable().push_back(&null);

  // create .text section
  mcld::LDSection& text = mDriver->getLinker()->createSectHdr(".text",
                              mcld::LDFileFormat::Regular,
                              llvm::ELF::SHT_PROGBITS,
                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR);

  text.setSize(pSize);
  text.setOffset(0x0);
  text.setIndex(1);
  text.setInfo(0);
  text.setAlign(1);

  input_context->getSectionTable().push_back(&text);

  return kSuccess;
}

enum Linker::ErrorCode Linker::setOutput(const std::string &pPath) {
  if (mLDInfo->output().hasContext()) {
    return kDoubleConfig;
  }

  // -----  initialize output file  ----- //

  mcld::FileHandle::Permission perm = 0755;

  mcld::MemoryArea* out_area = mMemAreaFactory->produce(
                      pPath,
                      mcld::FileHandle::ReadWrite |
                        mcld::FileHandle::Truncate |
                        mcld::FileHandle::Create,
                      perm);

  if (!out_area->handler()->isGood()) {
    return kOpenOutput;
  }

  if (mShared) {
    mLDInfo->output().setType(mcld::Output::DynObj);
  } else {
    mLDInfo->output().setType(mcld::Output::Exec);
  }

  mLDInfo->output().setSOName(mSOName);
  mLDInfo->output().setMemArea(out_area);
  mLDInfo->output().setContext(mLDInfo->contextFactory().produce(pPath));

  // FIXME: We must initialize MCLinker before setOutput, and initialize
  // standard sections here. This is because we have to build the section
  // map before input files using it.
  if (!mDriver->hasInitLinker()) {
    return kNotConfig;
  }

  mDriver->initStdSections();

  return kSuccess;
}

enum Linker::ErrorCode Linker::setOutput(int pFileHandler) {
  if (mLDInfo->output().hasContext()) {
    return kDoubleConfig;
  }

  // -----  initialize output file  ----- //
  mcld::MemoryArea* out_area = mMemAreaFactory->produce(pFileHandler);

  mLDInfo->output().setType(mcld::Output::DynObj);
  mLDInfo->output().setMemArea(out_area);
  mLDInfo->output().setContext(mLDInfo->contextFactory().produce());

  // FIXME: We must initialize MCLinker before setOutput, and initialize
  // standard sections here. This is because we have to build the section
  // map before input files using it.
  if (!mDriver->hasInitLinker()) {
    return kNotConfig;
  }

  mDriver->initStdSections();

  return kSuccess;
}

enum Linker::ErrorCode Linker::link() {
  mDriver->normalize();

  if (!mDriver->mergeSections()) {
    return kReadSections;
  }

  if (!mDriver->addStandardSymbols() || !mDriver->addTargetSymbols()) {
    return kAddAdditionalSymbols;
  }

  mDriver->readRelocations();
  mDriver->prelayout();
  mDriver->layout();
  mDriver->postlayout();
  mDriver->finalizeSymbolValue();
  mDriver->relocation();
  mDriver->emitOutput();
  mDriver->postProcessing();

  return kSuccess;
}