/*
* Copyright 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/Support/LinkerConfig.h"
#include "bcc/Support/Log.h"
#include <llvm/Support/Signals.h>
#include <mcld/LinkerConfig.h>
#include <mcld/MC/MCLDDirectory.h>
#include <mcld/MC/ZOption.h>
#include <mcld/LD/TextDiagnosticPrinter.h>
#include <mcld/Support/Path.h>
#include <mcld/Support/MsgHandling.h>
#include <mcld/Support/raw_ostream.h>
using namespace bcc;
LinkerConfig::LinkerConfig(const std::string &pTriple)
: mTriple(pTriple), mSOName(), mTarget(NULL), mLDConfig(NULL),
mDiagLineInfo(NULL), mDiagPrinter(NULL) {
initializeTarget();
initializeLDInfo();
initializeDiagnostic();
}
LinkerConfig::~LinkerConfig() {
delete mLDConfig;
if (mDiagPrinter->getNumErrors() != 0) {
// If here, the program failed ungracefully. Run the interrupt handlers to
// ensure any other cleanups (e.g., files that registered by
// RemoveFileOnSignal(...)) getting done before exit.
llvm::sys::RunInterruptHandlers();
}
mDiagPrinter->finish();
delete mDiagLineInfo;
delete mDiagPrinter;
}
bool LinkerConfig::initializeTarget() {
std::string error;
mTarget = mcld::TargetRegistry::lookupTarget(mTriple, error);
if (NULL != mTarget) {
return true;
} else {
ALOGE("Cannot initialize mcld::Target for given triple '%s'! (%s)\n",
mTriple.c_str(), error.c_str());
return false;
}
}
bool LinkerConfig::initializeLDInfo() {
if (NULL != mLDConfig) {
ALOGE("Cannot initialize mcld::MCLDInfo for given triple '%s!\n",
mTriple.c_str());
return false;
}
mLDConfig = new mcld::LinkerConfig(getTriple());
mLDConfig->setCodeGenType(mcld::LinkerConfig::Exec);
struct NameMap {
const char* from;
const char* to;
};
static const NameMap map[] =
{
{".text", ".text"},
{".rodata", ".rodata"},
{".data.rel.ro.local", ".data.rel.ro.local"},
{".data.rel.ro", ".data.rel.ro"},
{".data", ".data"},
{".bss", ".bss"},
{".tdata", ".tdata"},
{".tbss", ".tbss"},
{".init_array", ".init_array"},
{".fini_array", ".fini_array"},
// TODO: Support DT_INIT_ARRAY for all constructors?
{".ctors", ".ctors"},
{".dtors", ".dtors"},
// FIXME: in GNU ld, if we are creating a shared object .sdata2 and .sbss2
// sections would be handled differently.
{".sdata2", ".sdata"},
{".sbss2", ".sbss"},
{".sdata", ".sdata"},
{".sbss", ".sbss"},
{".lrodata", ".lrodata"},
{".ldata", ".ldata"},
{".lbss", ".lbss"},
{".gcc_except_table", ".gcc_except_table"},
{".gnu.linkonce.d.rel.ro.local", ".data.rel.ro.local"},
{".gnu.linkonce.d.rel.ro", ".data.rel.ro"},
{".gnu.linkonce.r", ".rodata"},
{".gnu.linkonce.d", ".data"},
{".gnu.linkonce.b", ".bss"},
{".gnu.linkonce.sb2", ".sbss"},
{".gnu.linkonce.sb", ".sbss"},
{".gnu.linkonce.s2", ".sdata"},
{".gnu.linkonce.s", ".sdata"},
{".gnu.linkonce.wi", ".debug_info"},
{".gnu.linkonce.td", ".tdata"},
{".gnu.linkonce.tb", ".tbss"},
{".gnu.linkonce.t", ".text"},
{".gnu.linkonce.lr", ".lrodata"},
{".gnu.linkonce.lb", ".lbss"},
{".gnu.linkonce.l", ".ldata"},
};
if (mLDConfig->codeGenType() != mcld::LinkerConfig::Object) {
const unsigned int map_size = (sizeof(map) / sizeof(map[0]) );
for (unsigned int i = 0; i < map_size; ++i) {
bool exist = false;
mLDConfig->scripts().sectionMap().append(map[i].from,
map[i].to,
exist);
}
}
return true;
}
bool LinkerConfig::initializeDiagnostic() {
// Set up MsgHandler.
mDiagPrinter = new mcld::TextDiagnosticPrinter(mcld::errs(), *mLDConfig);
mcld::InitializeDiagnosticEngine(*mLDConfig, mDiagPrinter);
mDiagLineInfo = mTarget->createDiagnosticLineInfo(*mTarget, mTriple);
mcld::getDiagnosticEngine().setLineInfo(*mDiagLineInfo);
return true;
}
bool LinkerConfig::isShared() const {
return (mcld::LinkerConfig::DynObj == mLDConfig->codeGenType());
}
void LinkerConfig::setShared(bool pEnable) {
if (pEnable)
mLDConfig->setCodeGenType(mcld::LinkerConfig::DynObj);
else
mLDConfig->setCodeGenType(mcld::LinkerConfig::Exec);
return;
}
void LinkerConfig::setBsymbolic(bool pEnable) {
mLDConfig->options().setBsymbolic(pEnable);
return;
}
void LinkerConfig::setDefineCommon(bool pEnable) {
mLDConfig->options().setDefineCommon(pEnable);
return;
}
void LinkerConfig::setSOName(const std::string &pSOName) {
mLDConfig->options().setSOName(pSOName);
return;
}
void LinkerConfig::setDyld(const std::string &pDyld) {
mLDConfig->options().setDyld(pDyld);
return;
}
void LinkerConfig::setSysRoot(const std::string &pSysRoot) {
mLDConfig->options().setSysroot(mcld::sys::fs::Path(pSysRoot));
return;
}
void LinkerConfig::setZOption(unsigned int pOptions) {
mcld::ZOption option;
if (pOptions & kCombReloc) {
option.setKind(mcld::ZOption::CombReloc);
mLDConfig->options().addZOption(option);
}
else {
option.setKind(mcld::ZOption::NoCombReloc);
mLDConfig->options().addZOption(option);
}
if (pOptions & kDefs) {
option.setKind(mcld::ZOption::Defs);
mLDConfig->options().addZOption(option);
}
if (pOptions & kExecStack) {
option.setKind(mcld::ZOption::ExecStack);
mLDConfig->options().addZOption(option);
}
else {
option.setKind(mcld::ZOption::NoExecStack);
mLDConfig->options().addZOption(option);
}
if (pOptions & kInitFirst) {
option.setKind(mcld::ZOption::InitFirst);
mLDConfig->options().addZOption(option);
}
if (pOptions & kInterPose) {
option.setKind(mcld::ZOption::InterPose);
mLDConfig->options().addZOption(option);
}
if (pOptions & kLoadFltr) {
option.setKind(mcld::ZOption::LoadFltr);
mLDConfig->options().addZOption(option);
}
if (pOptions & kMulDefs) {
option.setKind(mcld::ZOption::MulDefs);
mLDConfig->options().addZOption(option);
}
if (pOptions & kNoCopyReloc) {
option.setKind(mcld::ZOption::NoCopyReloc);
mLDConfig->options().addZOption(option);
}
if (pOptions & kNoDefaultLib) {
option.setKind(mcld::ZOption::NoDefaultLib);
mLDConfig->options().addZOption(option);
}
if (pOptions & kNoDelete) {
option.setKind(mcld::ZOption::NoDelete);
mLDConfig->options().addZOption(option);
}
if (pOptions & kNoDLOpen) {
option.setKind(mcld::ZOption::NoDLOpen);
mLDConfig->options().addZOption(option);
}
if (pOptions & kNoDump) {
option.setKind(mcld::ZOption::NoDump);
mLDConfig->options().addZOption(option);
}
if (pOptions & kRelro) {
option.setKind(mcld::ZOption::Relro);
mLDConfig->options().addZOption(option);
}
else {
option.setKind(mcld::ZOption::NoRelro);
mLDConfig->options().addZOption(option);
}
if (pOptions & kLazy) {
option.setKind(mcld::ZOption::Lazy);
mLDConfig->options().addZOption(option);
}
else {
option.setKind(mcld::ZOption::Now);
mLDConfig->options().addZOption(option);
}
if (pOptions & kOrigin) {
option.setKind(mcld::ZOption::Origin);
mLDConfig->options().addZOption(option);
}
}
void LinkerConfig::addWrap(const std::string &pWrapSymbol) {
bool exist = false;
// Add wname -> __wrap_wname.
mcld::StringEntry<llvm::StringRef>* to_wrap =
mLDConfig->scripts().renameMap().insert(pWrapSymbol, exist);
std::string to_wrap_str = "__wrap_" + pWrapSymbol;
to_wrap->setValue(to_wrap_str);
if (exist) {
mcld::warning(mcld::diag::rewrap) << pWrapSymbol << to_wrap_str;
}
// Add __real_wname -> wname.
std::string from_real_str = "__real_" + pWrapSymbol;
mcld::StringEntry<llvm::StringRef>* from_real =
mLDConfig->scripts().renameMap().insert(from_real_str, exist);
from_real->setValue(pWrapSymbol);
if (exist) {
mcld::warning(mcld::diag::rewrap) << pWrapSymbol << from_real_str;
}
return;
}
void LinkerConfig::addPortable(const std::string &pPortableSymbol) {
bool exist = false;
// Add pname -> pname_portable.
mcld::StringEntry<llvm::StringRef>* to_port =
mLDConfig->scripts().renameMap().insert(pPortableSymbol, exist);
std::string to_port_str = pPortableSymbol + "_portable";
to_port->setValue(to_port_str);
if (exist) {
mcld::warning(mcld::diag::rewrap) << pPortableSymbol << to_port_str;
}
// Add __real_pname -> pname.
std::string from_real_str = "__real_" + pPortableSymbol;
mcld::StringEntry<llvm::StringRef>* from_real =
mLDConfig->scripts().renameMap().insert(from_real_str, exist);
from_real->setValue(pPortableSymbol);
if (exist) {
mcld::warning(mcld::diag::rewrap) << pPortableSymbol << from_real_str;
}
return;
}
void LinkerConfig::addSearchDir(const std::string &pDirPath) {
// SearchDirs will remove the created MCLDDirectory.
if (!mLDConfig->options().directories().insert(pDirPath)) {
mcld::warning(mcld::diag::warn_cannot_open_search_dir) << pDirPath;
}
}