blob: 67d10ac39fb60bb8aa9b986bfda68526a677b46c [file] [log] [blame]
//===-- GnuTools.cpp ------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementations of gnutools Assembler and Linker classes.
//
//===----------------------------------------------------------------------===//
#include "GnuTools.h"
#include "Compilation.h"
#include "Driver.h"
#include "ToolChain.h"
#include "GollvmConfig.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/Path.h"
#include <set>
using namespace gollvm::driver;
namespace gnutools {
// This helper routine is used for constructing linker and
// assembler command lines. It combines input arguments with
// any escape-oriented command line arguments via "-Wl,..." or
// equivalent. For example, consider the following command line:
//
// llvm-goc -L /somepath foo.go -o qux \
// -Wl,--whole-archive mumble.a -Wl,--no-whole-archive blah.a
//
// This will result in three linker inputs (foo.o, mumble.a, and blah.a).
// Here in order to get the semantics we have to properly interleave
// the inputs with the flags, e.g.
//
// ld -o qux <...> foo.o --whole-archive mumble.a --no-whole-archive blah.a
//
// This helper routine walks through the command line arguments and picks
// out the corresponding "escaped" arguments and mixes them in with
// any args that appear in the input list.
static void combineInputsWithEscapes(gollvm::options::ID escape1,
gollvm::options::ID escape2,
const ArtifactList &inputArtifacts,
llvm::opt::ArgList &args,
llvm::opt::ArgStringList &cmdArgs)
{
// Collect the args mentioned in the input artifacts.
std::set<llvm::opt::Arg *> argset;
for (auto &inart : inputArtifacts) {
if (inart->type() == Artifact::A_Argument)
argset.insert(inart->arg());
else
cmdArgs.push_back(inart->file());
}
// Walk the args to sort things out.
for (auto arg : args) {
// If this is an arg that is part of the input set, append it now.
if (arg->getOption().getKind() == llvm::opt::Option::InputClass &&
argset.find(arg) != argset.end()) {
cmdArgs.push_back(arg->getValue());
continue;
}
// If this matches one of our escape options, then add its value(s) now.
if (arg->getOption().matches(escape1) ||
arg->getOption().matches(escape2))
for (auto &av : arg->getValues())
cmdArgs.push_back(av);
}
}
Assembler::Assembler(gollvm::driver::ToolChain &tc)
: ExternalTool("gnu-assembler", tc)
{
}
bool Assembler::constructCommand(Compilation &compilation,
const Action &jobAction,
const ArtifactList &inputArtifacts,
const Artifact &output)
{
llvm::opt::ArgList &args = toolchain().driver().args();
llvm::opt::ArgStringList cmdArgs;
// Executable path.
const char *executable =
args.MakeArgString(toolchain().getProgramPath("as"));
if (! executable) {
llvm::errs() << "error: unable to locate path for 'as'\n";
return false;
}
cmdArgs.push_back(executable);
// Add correct 32/64 option.
switch (toolchain().driver().triple().getArch()) {
case llvm::Triple::x86:
cmdArgs.push_back("--32");
break;
case llvm::Triple::x86_64:
// NB: no GNUX32 support yet
cmdArgs.push_back("--64");
break;
default:
break;
}
// Output file.
cmdArgs.push_back("-o");
cmdArgs.push_back(output.file());
// Incorporate inputs with -Wa,.. and -Xassembler args, in correct order.
combineInputsWithEscapes(gollvm::options::OPT_Wa_COMMA,
gollvm::options::OPT_Xassembler,
inputArtifacts, args, cmdArgs);
// Support for compressed debug.
llvm::opt::Arg *gzarg = args.getLastArg(gollvm::options::OPT_gz,
gollvm::options::OPT_gz_EQ);
if (gzarg != nullptr) {
if (gzarg->getOption().matches(gollvm::options::OPT_gz)) {
cmdArgs.push_back("-compress-debug-sections");
} else {
std::string cds("-compress-debug-sections=");
cds += gzarg->getValue();
cmdArgs.push_back(args.MakeArgString(cds));
}
}
cmdArgs.push_back(nullptr);
// Add final command.
compilation.addCommand(jobAction, *this,
executable, cmdArgs);
return true;
}
Linker::Linker(gollvm::driver::ToolChain &tc)
: ExternalTool("gnu-linker", tc)
{
}
void Linker::addBeginFiles(llvm::opt::ArgStringList &cmdArgs)
{
llvm::opt::ArgList &args = toolchain().driver().args();
// FIXME: no support yet for --nostdlib or --nostartfiles
bool isPIE = toolchain().driver().isPIE();
const char *crt1 = nullptr;
if (!args.hasArg(gollvm::options::OPT_shared)) {
// FIXME: no support yet for -pg
if (isPIE)
crt1 = "Scrt1.o";
else
crt1 = "crt1.o";
}
if (crt1)
cmdArgs.push_back(args.MakeArgString(toolchain().getFilePath(crt1)));
cmdArgs.push_back(args.MakeArgString(toolchain().getFilePath("crti.o")));
const char *crtbegin = nullptr;
if (args.hasArg(gollvm::options::OPT_static))
crtbegin = "crtbeginT.o";
else if (args.hasArg(gollvm::options::OPT_shared))
crtbegin = "crtbeginS.o";
else if (isPIE)
crtbegin = "crtbeginS.o";
else
crtbegin = "crtbegin.o";
cmdArgs.push_back(args.MakeArgString(toolchain().getFilePath(crtbegin)));
}
void Linker::addEndFiles(llvm::opt::ArgStringList &cmdArgs)
{
llvm::opt::ArgList &args = toolchain().driver().args();
// FIXME: no support yet for --nostdlib or --nostartfiles
const char *crtend = nullptr;
if (args.hasArg(gollvm::options::OPT_shared) ||
toolchain().driver().isPIE())
crtend = "crtendS.o";
else
crtend = "crtend.o";
cmdArgs.push_back(args.MakeArgString(toolchain().getFilePath(crtend)));
cmdArgs.push_back(args.MakeArgString(toolchain().getFilePath("crtn.o")));
}
void Linker::addLDM(llvm::opt::ArgStringList &cmdArgs)
{
cmdArgs.push_back("-m");
switch (toolchain().driver().triple().getArch()) {
case llvm::Triple::x86:
cmdArgs.push_back("elf_i386");
break;
case llvm::Triple::x86_64:
// NB: no GNUX32 support
cmdArgs.push_back("elf_x86_64");
break;
default:
// unhandled architecture
cmdArgs.push_back("%unknown%");
assert(false);
}
}
void Linker::addSharedAndOrStaticFlags(llvm::opt::ArgStringList &cmdArgs)
{
llvm::opt::ArgList &args = toolchain().driver().args();
if (!args.hasArg(gollvm::options::OPT_static)) {
cmdArgs.push_back("--eh-frame-hdr");
if (!args.hasArg(gollvm::options::OPT_shared)) {
// NB: no support for --dyld-prefix= option
const std::string Loader = toolchain().getDynamicLinker(args);
cmdArgs.push_back("-dynamic-linker");
cmdArgs.push_back(args.MakeArgString(Loader));
} else {
cmdArgs.push_back("-shared");
}
if (toolchain().driver().isPIE())
cmdArgs.push_back("-pie");
} else {
cmdArgs.push_back("-static");
}
}
void Linker::addFilePathArgs(llvm::opt::ArgStringList &cmdArgs)
{
llvm::opt::ArgList &args = toolchain().driver().args();
for (auto & fp : toolchain().filePaths())
if (fp.length() > 0)
cmdArgs.push_back(args.MakeArgString(llvm::StringRef("-L") + fp));
}
bool Linker::constructCommand(Compilation &compilation,
const Action &jobAction,
const ArtifactList &inputArtifacts,
const Artifact &output)
{
llvm::opt::ArgList &args = compilation.driver().args();
llvm::opt::ArgStringList cmdArgs;
// Honor -fuse-ld=XXX
const char *executable = "ld.gold";
llvm::opt::Arg *ldarg = args.getLastArg(gollvm::options::OPT_fuse_ld_EQ);
if (ldarg != nullptr)
executable = ldarg->getValue();
cmdArgs.push_back(executable);
// Perform program path lookup if needed.
if (!llvm::sys::path::is_absolute(executable))
executable = args.MakeArgString(toolchain().getProgramPath(executable));
// Output file.
cmdArgs.push_back("-o");
cmdArgs.push_back(output.file());
// Select proper options depending on presence of -static/-shared, etc.
// Dynamic linker selection is also done here.
addSharedAndOrStaticFlags(cmdArgs);
// Add crtbegin*.
addBeginFiles(cmdArgs);
// Incorporate inputs with -Wl,.. and -Xlinker args, in correct order.
combineInputsWithEscapes(gollvm::options::OPT_Wl_COMMA,
gollvm::options::OPT_Xlinker,
inputArtifacts, args, cmdArgs);
// FIXME: add "-dynamic-linker /lib64/ld-linux-x86-64.so.2"
// Incorporate any -L, -l options from the user
args.AddAllArgs(cmdArgs, gollvm::options::OPT_L, gollvm::options::OPT_l);
// Add each thing in the toolchain filepath as an -L option.
addFilePathArgs(cmdArgs);
// Add -m flag.
addLDM(cmdArgs);
// FIXME: common this up with the assembler sequence above.
// Support for compressed debug.
llvm::opt::Arg *gzarg = args.getLastArg(gollvm::options::OPT_gz,
gollvm::options::OPT_gz_EQ);
if (gzarg != nullptr) {
if (gzarg->getOption().matches(gollvm::options::OPT_gz)) {
cmdArgs.push_back("-compress-debug-sections");
} else {
std::string cds("-compress-debug-sections=");
cds += gzarg->getValue();
cmdArgs.push_back(args.MakeArgString(cds));
}
}
// Pick up correct directory for Go libraries.
std::string golib("-L");
golib += GOLLVM_INSTALL_LIBDIR;
cmdArgs.push_back(args.MakeArgString(golib.c_str()));
// Incorporate linker arguments needed for Go.
cmdArgs.push_back("-lgobegin");
cmdArgs.push_back("-lgo");
// Pull in pthread and math library.
bool isStatic = args.hasArg(gollvm::options::OPT_static);
if (isStatic)
cmdArgs.push_back("-lpthread");
cmdArgs.push_back("-lm");
if (isStatic) {
cmdArgs.push_back("-u");
cmdArgs.push_back("pthread_create");
}
cmdArgs.push_back("--wrap=pthread_create");
// Libgcc and libc.
bool isShared = args.hasArg(gollvm::options::OPT_shared);
if (isStatic) {
cmdArgs.push_back("--start-group");
cmdArgs.push_back("-lgcc");
cmdArgs.push_back("-lgcc_eh");
cmdArgs.push_back("-lc");
cmdArgs.push_back("--end-group");
} else {
cmdArgs.push_back("-lgcc_s");
if (!isShared)
cmdArgs.push_back("-lgcc");
cmdArgs.push_back("-lc");
cmdArgs.push_back("-lgcc_s");
if (!isShared)
cmdArgs.push_back("-lgcc");
}
// crtend files.
addEndFiles(cmdArgs);
// end of args.
cmdArgs.push_back(nullptr);
// Add final command.
compilation.addCommand(jobAction, *this, executable, cmdArgs);
cmdArgs.push_back(nullptr);
return true;
}
} // end namespace gnutools