/* * 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; }