//===- SectLinker.cpp -----------------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the SectLinker class.
//
//===----------------------------------------------------------------------===//
#include <mcld/Support/FileHandle.h>
#include <mcld/MC/InputTree.h>
#include <mcld/MC/MCLDDriver.h>
#include <mcld/Support/FileSystem.h>
#include <mcld/Support/MsgHandling.h>
#include <mcld/Support/FileHandle.h>
#include <mcld/Support/raw_ostream.h>
#include <mcld/Support/MemoryAreaFactory.h>
#include <mcld/Support/DerivedPositionDependentOptions.h>
#include <mcld/Target/TargetLDBackend.h>
#include <mcld/CodeGen/SectLinker.h>
#include <mcld/CodeGen/SectLinkerOption.h>
#include <llvm/Module.h>
#include <algorithm>
#include <stack>
#include <string>
using namespace mcld;
using namespace llvm;
//===----------------------------------------------------------------------===//
// Forward declarations
char SectLinker::m_ID = 0;
static bool CompareOption(const PositionDependentOption* X,
const PositionDependentOption* Y);
//===----------------------------------------------------------------------===//
// SectLinker
SectLinker::SectLinker(SectLinkerOption &pOption,
TargetLDBackend& pLDBackend)
: MachineFunctionPass(m_ID),
m_pOption(&pOption),
m_pLDBackend(&pLDBackend),
m_pLDDriver(NULL),
m_pMemAreaFactory(NULL)
{
m_pMemAreaFactory = new MemoryAreaFactory(32);
}
SectLinker::~SectLinker()
{
delete m_pLDDriver;
// FIXME: current implementation can not change the order of delete.
//
// Instance of TargetLDBackend was created outside and is not managed by
// SectLinker. It should not be destroyed here and by SectLinker. However, in
// order to follow the LLVM convention - that is, the pass manages all the
// objects it used during the processing, we destroy the object of
// TargetLDBackend here.
delete m_pLDBackend;
delete m_pMemAreaFactory;
}
bool SectLinker::doInitialization(Module &pM)
{
MCLDInfo &info = m_pOption->info();
// ----- convert position dependent options into tree of input files ----- //
PositionDependentOptions &PosDepOpts = m_pOption->pos_dep_options();
std::stable_sort(PosDepOpts.begin(), PosDepOpts.end(), CompareOption);
initializeInputTree(PosDepOpts);
initializeInputOutput(info);
// Now, all input arguments are prepared well, send it into MCLDDriver
m_pLDDriver = new MCLDDriver(info, *m_pLDBackend, *memAreaFactory());
return false;
}
bool SectLinker::doFinalization(Module &pM)
{
const MCLDInfo &info = m_pOption->info();
// 2. - initialize MCLinker
if (!m_pLDDriver->initMCLinker())
return true;
// 3. - initialize output's standard sections
if (!m_pLDDriver->initStdSections())
return true;
// 4. - normalize the input tree
m_pLDDriver->normalize();
if (info.options().trace()) {
static int counter = 0;
mcld::outs() << "** name\ttype\tpath\tsize (" << info.inputs().size() << ")\n";
InputTree::const_dfs_iterator input, inEnd = info.inputs().dfs_end();
for (input=info.inputs().dfs_begin(); input!=inEnd; ++input) {
mcld::outs() << counter++ << " * " << (*input)->name();
switch((*input)->type()) {
case Input::Archive:
mcld::outs() << "\tarchive\t(";
break;
case Input::Object:
mcld::outs() << "\tobject\t(";
break;
case Input::DynObj:
mcld::outs() << "\tshared\t(";
break;
case Input::Script:
mcld::outs() << "\tscript\t(";
break;
case Input::External:
mcld::outs() << "\textern\t(";
break;
default:
unreachable(diag::err_cannot_trace_file) << (*input)->type()
<< (*input)->name()
<< (*input)->path();
}
mcld::outs() << (*input)->path() << ")\n";
}
}
// 5. - check if we can do static linking and if we use split-stack.
if (!m_pLDDriver->linkable())
return true;
// 6. - merge all sections
if (!m_pLDDriver->mergeSections())
return true;
// 7. - add standard symbols and target-dependent symbols
// m_pLDDriver->addUndefSymbols();
if (!m_pLDDriver->addStandardSymbols() ||
!m_pLDDriver->addTargetSymbols())
return true;
// 8. - read all relocation entries from input files
m_pLDDriver->readRelocations();
// 9. - pre-layout
m_pLDDriver->prelayout();
// 10. - linear layout
m_pLDDriver->layout();
// 10.b - post-layout (create segment, instruction relaxing)
m_pLDDriver->postlayout();
// 11. - finalize symbol value
m_pLDDriver->finalizeSymbolValue();
// 12. - apply relocations
m_pLDDriver->relocation();
// 13. - write out output
m_pLDDriver->emitOutput();
// 14. - post processing
m_pLDDriver->postProcessing();
return false;
}
bool SectLinker::runOnMachineFunction(MachineFunction& pF)
{
// basically, linkers do nothing during function is generated.
return false;
}
void SectLinker::initializeInputOutput(MCLDInfo &pLDInfo)
{
// ----- initialize output file ----- //
FileHandle::Permission perm;
if (Output::Object == pLDInfo.output().type())
perm = 0544;
else
perm = 0755;
MemoryArea* out_area = memAreaFactory()->produce(pLDInfo.output().path(),
FileHandle::ReadWrite,
perm);
if (!out_area->handler()->isGood()) {
// make sure output is openend successfully.
fatal(diag::err_cannot_open_output_file) << pLDInfo.output().name()
<< pLDInfo.output().path();
}
pLDInfo.output().setMemArea(out_area);
pLDInfo.output().setContext(pLDInfo.contextFactory().produce());
// ----- initialize input files ----- //
InputTree::dfs_iterator input, inEnd = pLDInfo.inputs().dfs_end();
for (input = pLDInfo.inputs().dfs_begin(); input!=inEnd; ++input) {
// already got type - for example, bitcode
if ((*input)->type() == Input::Script ||
(*input)->type() == Input::Object ||
(*input)->type() == Input::DynObj ||
(*input)->type() == Input::Archive)
continue;
MemoryArea *input_memory =
memAreaFactory()->produce((*input)->path(), FileHandle::ReadOnly);
if (input_memory->handler()->isGood()) {
(*input)->setMemArea(input_memory);
}
else {
error(diag::err_cannot_open_input) << (*input)->name() << (*input)->path();
return;
}
LDContext *input_context =
pLDInfo.contextFactory().produce((*input)->path());
(*input)->setContext(input_context);
}
}
void SectLinker::initializeInputTree(const PositionDependentOptions &pPosDepOptions) const
{
if (pPosDepOptions.empty())
fatal(diag::err_no_inputs);
MCLDInfo &info = m_pOption->info();
PositionDependentOptions::const_iterator option = pPosDepOptions.begin();
if (1 == pPosDepOptions.size() &&
((*option)->type() != PositionDependentOption::INPUT_FILE &&
(*option)->type() != PositionDependentOption::NAMESPEC) &&
(*option)->type() != PositionDependentOption::BITCODE) {
// if we only have one positional options, and the option is
// not an input file, then emit error message.
fatal(diag::err_no_inputs);
}
// ----- Input tree insertion algorithm ----- //
// The type of the previsou node indicates the direction of the current
// insertion.
//
// root : the parent node who being inserted.
// mover : the direcion of current movement.
//
// for each positional options:
// insert the options in current root.
// calculate the next movement
// Initialization
InputTree::Mover *move = &InputTree::Downward;
InputTree::iterator root = info.inputs().root();
PositionDependentOptions::const_iterator optionEnd = pPosDepOptions.end();
std::stack<InputTree::iterator> returnStack;
while (option != optionEnd ) {
switch ((*option)->type()) {
/** bitcode **/
case PositionDependentOption::BITCODE: {
const BitcodeOption *bitcode_option =
static_cast<const BitcodeOption*>(*option);
// threat bitcode as an external IR in this version.
info.inputs().insert(root, *move,
bitcode_option->path()->native(),
*(bitcode_option->path()),
Input::External);
info.setBitcode(**root);
// move root on the new created node.
move->move(root);
// the next file is appended after bitcode file.
move = &InputTree::Afterward;
break;
}
/** input object file **/
case PositionDependentOption::INPUT_FILE: {
const InputFileOption *input_file_option =
static_cast<const InputFileOption*>(*option);
info.inputs().insert(root, *move,
input_file_option->path()->native(),
*(input_file_option->path()));
// move root on the new created node.
move->move(root);
// the next file is appended after object file.
move = &InputTree::Afterward;
break;
}
/** -lnamespec **/
case PositionDependentOption::NAMESPEC: {
sys::fs::Path* path = NULL;
const NamespecOption *namespec_option =
static_cast<const NamespecOption*>(*option);
// find out the real path of the namespec.
if (info.attrFactory().constraint().isSharedSystem()) {
// In the system with shared object support, we can find both archive
// and shared object.
if (info.attrFactory().last().isStatic()) {
// with --static, we must search an archive.
path = info.options().directories().find(namespec_option->namespec(),
Input::Archive);
}
else {
// otherwise, with --Bdynamic, we can find either an archive or a
// shared object.
path = info.options().directories().find(namespec_option->namespec(),
Input::DynObj);
}
}
else {
// In the system without shared object support, we only look for an
// archive.
path = info.options().directories().find(namespec_option->namespec(),
Input::Archive);
}
if (NULL == path)
fatal(diag::err_cannot_find_namespec) << namespec_option->namespec();
info.inputs().insert(root, *move,
namespec_option->namespec(),
*path);
// iterate root on the new created node.
move->move(root);
// the file after a namespec must be appended afterward.
move = &InputTree::Afterward;
break;
}
/** start group **/
case PositionDependentOption::START_GROUP:
info.inputs().enterGroup(root, *move);
move->move(root);
returnStack.push(root);
move = &InputTree::Downward;
break;
/** end group **/
case PositionDependentOption::END_GROUP:
root = returnStack.top();
returnStack.pop();
move = &InputTree::Afterward;
break;
case PositionDependentOption::WHOLE_ARCHIVE:
info.attrFactory().last().setWholeArchive();
break;
case PositionDependentOption::NO_WHOLE_ARCHIVE:
info.attrFactory().last().unsetWholeArchive();
break;
case PositionDependentOption::AS_NEEDED:
info.attrFactory().last().setAsNeeded();
break;
case PositionDependentOption::NO_AS_NEEDED:
info.attrFactory().last().unsetAsNeeded();
break;
case PositionDependentOption::ADD_NEEDED:
info.attrFactory().last().setAddNeeded();
break;
case PositionDependentOption::NO_ADD_NEEDED:
info.attrFactory().last().unsetAddNeeded();
break;
case PositionDependentOption::BSTATIC:
info.attrFactory().last().setStatic();
break;
case PositionDependentOption::BDYNAMIC:
info.attrFactory().last().setDynamic();
break;
default:
fatal(diag::err_cannot_identify_option) << (*option)->position()
<< (uint32_t)(*option)->type();
} // end of switch
++option;
} // end of while
if (!returnStack.empty()) {
report_fatal_error("no matched --start-group and --end-group");
}
}
//===----------------------------------------------------------------------===//
// Non-member functions
static bool CompareOption(const PositionDependentOption* X,
const PositionDependentOption* Y)
{
return (X->position() < Y->position());
}