|
@@ -10,7 +10,14 @@
|
|
#include "CommonArgs.h"
|
|
#include "CommonArgs.h"
|
|
#include "InputInfo.h"
|
|
#include "InputInfo.h"
|
|
#include "clang/Driver/Compilation.h"
|
|
#include "clang/Driver/Compilation.h"
|
|
|
|
+#include "clang/Driver/DriverDiagnostic.h"
|
|
|
|
+#include "clang/Driver/Options.h"
|
|
|
|
+#include "llvm/ADT/Optional.h"
|
|
|
|
+#include "llvm/ADT/StringSwitch.h"
|
|
|
|
+#include "llvm/MC/MCSubtargetInfo.h"
|
|
|
|
+#include "llvm/MC/SubtargetFeature.h"
|
|
#include "llvm/Option/ArgList.h"
|
|
#include "llvm/Option/ArgList.h"
|
|
|
|
+#include "llvm/Support/FileSystem.h"
|
|
|
|
|
|
using namespace clang::driver;
|
|
using namespace clang::driver;
|
|
using namespace clang::driver::toolchains;
|
|
using namespace clang::driver::toolchains;
|
|
@@ -18,12 +25,76 @@ using namespace clang::driver::tools;
|
|
using namespace clang;
|
|
using namespace clang;
|
|
using namespace llvm::opt;
|
|
using namespace llvm::opt;
|
|
|
|
|
|
|
|
+namespace {
|
|
|
|
+
|
|
|
|
+// TODO: Consider merging this into the AVR device table
|
|
|
|
+// array in Targets/AVR.cpp.
|
|
|
|
+llvm::Optional<StringRef> GetMcuFamilyName(StringRef MCU) {
|
|
|
|
+ return llvm::StringSwitch<llvm::Optional<StringRef>>(MCU)
|
|
|
|
+ .Case("atmega328", Optional<StringRef>("avr5"))
|
|
|
|
+ .Case("atmega328p", Optional<StringRef>("avr5"))
|
|
|
|
+ .Default(Optional<StringRef>());
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const StringRef PossibleAVRLibcLocations[] = {
|
|
|
|
+ "/usr/avr",
|
|
|
|
+ "/usr/lib/avr",
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+} // end anonymous namespace
|
|
|
|
+
|
|
/// AVR Toolchain
|
|
/// AVR Toolchain
|
|
AVRToolChain::AVRToolChain(const Driver &D, const llvm::Triple &Triple,
|
|
AVRToolChain::AVRToolChain(const Driver &D, const llvm::Triple &Triple,
|
|
const ArgList &Args)
|
|
const ArgList &Args)
|
|
- : Generic_ELF(D, Triple, Args) { }
|
|
|
|
|
|
+ : Generic_ELF(D, Triple, Args), LinkStdlib(false) {
|
|
|
|
+ GCCInstallation.init(Triple, Args);
|
|
|
|
+
|
|
|
|
+ // Only add default libraries if the user hasn't explicitly opted out.
|
|
|
|
+ if (!Args.hasArg(options::OPT_nostdlib) &&
|
|
|
|
+ !Args.hasArg(options::OPT_nodefaultlibs) &&
|
|
|
|
+ !Args.hasArg(options::OPT_c /* does not apply when not linking */)) {
|
|
|
|
+ std::string CPU = getCPUName(Args, Triple);
|
|
|
|
+
|
|
|
|
+ if (CPU.empty()) {
|
|
|
|
+ // We cannot link any standard libraries without an MCU specified.
|
|
|
|
+ D.Diag(diag::warn_drv_avr_mcu_not_specified);
|
|
|
|
+ } else {
|
|
|
|
+ Optional<StringRef> FamilyName = GetMcuFamilyName(CPU);
|
|
|
|
+ Optional<std::string> AVRLibcRoot = findAVRLibcInstallation();
|
|
|
|
+
|
|
|
|
+ if (!FamilyName.hasValue()) {
|
|
|
|
+ // We do not have an entry for this CPU in the family
|
|
|
|
+ // mapping table yet.
|
|
|
|
+ D.Diag(diag::warn_drv_avr_family_linking_stdlibs_not_implemented)
|
|
|
|
+ << CPU;
|
|
|
|
+ } else if (!GCCInstallation.isValid()) {
|
|
|
|
+ // No avr-gcc found and so no runtime linked.
|
|
|
|
+ D.Diag(diag::warn_drv_avr_gcc_not_found);
|
|
|
|
+ } else if (!AVRLibcRoot.hasValue()) {
|
|
|
|
+ // No avr-libc found and so no runtime linked.
|
|
|
|
+ D.Diag(diag::warn_drv_avr_libc_not_found);
|
|
|
|
+ } else { // We have enough information to link stdlibs
|
|
|
|
+ std::string GCCRoot = GCCInstallation.getInstallPath();
|
|
|
|
+ std::string LibcRoot = AVRLibcRoot.getValue();
|
|
|
|
+
|
|
|
|
+ getFilePaths().push_back(LibcRoot + std::string("/lib/") +
|
|
|
|
+ std::string(*FamilyName));
|
|
|
|
+ getFilePaths().push_back(LibcRoot + std::string("/lib/") +
|
|
|
|
+ std::string(*FamilyName));
|
|
|
|
+ getFilePaths().push_back(GCCRoot + std::string("/") +
|
|
|
|
+ std::string(*FamilyName));
|
|
|
|
+
|
|
|
|
+ LinkStdlib = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!LinkStdlib)
|
|
|
|
+ D.Diag(diag::warn_drv_avr_stdlib_not_linked);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
Tool *AVRToolChain::buildLinker() const {
|
|
Tool *AVRToolChain::buildLinker() const {
|
|
- return new tools::AVR::Linker(*this);
|
|
|
|
|
|
+ return new tools::AVR::Linker(getTriple(), *this, LinkStdlib);
|
|
}
|
|
}
|
|
|
|
|
|
void AVR::Linker::ConstructJob(Compilation &C, const JobAction &JA,
|
|
void AVR::Linker::ConstructJob(Compilation &C, const JobAction &JA,
|
|
@@ -31,13 +102,58 @@ void AVR::Linker::ConstructJob(Compilation &C, const JobAction &JA,
|
|
const InputInfoList &Inputs,
|
|
const InputInfoList &Inputs,
|
|
const ArgList &Args,
|
|
const ArgList &Args,
|
|
const char *LinkingOutput) const {
|
|
const char *LinkingOutput) const {
|
|
|
|
+ // Compute information about the target AVR.
|
|
|
|
+ std::string CPU = getCPUName(Args, getToolChain().getTriple());
|
|
|
|
+ llvm::Optional<StringRef> FamilyName = GetMcuFamilyName(CPU);
|
|
|
|
|
|
std::string Linker = getToolChain().GetProgramPath(getShortName());
|
|
std::string Linker = getToolChain().GetProgramPath(getShortName());
|
|
ArgStringList CmdArgs;
|
|
ArgStringList CmdArgs;
|
|
AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA);
|
|
AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA);
|
|
|
|
+
|
|
CmdArgs.push_back("-o");
|
|
CmdArgs.push_back("-o");
|
|
CmdArgs.push_back(Output.getFilename());
|
|
CmdArgs.push_back(Output.getFilename());
|
|
|
|
+
|
|
|
|
+ // Enable garbage collection of unused sections.
|
|
|
|
+ CmdArgs.push_back("--gc-sections");
|
|
|
|
+
|
|
|
|
+ // Add library search paths before we specify libraries.
|
|
|
|
+ Args.AddAllArgs(CmdArgs, options::OPT_L);
|
|
|
|
+ getToolChain().AddFilePathLibArgs(Args, CmdArgs);
|
|
|
|
+
|
|
|
|
+ // If the family name is known, we can link with the device-specific libgcc.
|
|
|
|
+ // Without it, libgcc will simply not be linked. This matches avr-gcc
|
|
|
|
+ // behavior.
|
|
|
|
+ if (LinkStdlib) {
|
|
|
|
+ assert(!CPU.empty() && "CPU name must be known in order to link stdlibs");
|
|
|
|
+
|
|
|
|
+ // Add the object file for the CRT.
|
|
|
|
+ std::string CrtFileName = std::string("-l:crt") + CPU + std::string(".o");
|
|
|
|
+ CmdArgs.push_back(Args.MakeArgString(CrtFileName));
|
|
|
|
+
|
|
|
|
+ CmdArgs.push_back("-lgcc");
|
|
|
|
+ CmdArgs.push_back("-lm");
|
|
|
|
+ CmdArgs.push_back("-lc");
|
|
|
|
+
|
|
|
|
+ // Add the link library specific to the MCU.
|
|
|
|
+ CmdArgs.push_back(Args.MakeArgString(std::string("-l") + CPU));
|
|
|
|
+
|
|
|
|
+ // Specify the family name as the emulation mode to use.
|
|
|
|
+ // This is almost always required because otherwise avr-ld
|
|
|
|
+ // will assume 'avr2' and warn about the program being larger
|
|
|
|
+ // than the bare minimum supports.
|
|
|
|
+ CmdArgs.push_back(Args.MakeArgString(std::string("-m") + *FamilyName));
|
|
|
|
+ }
|
|
|
|
+
|
|
C.addCommand(llvm::make_unique<Command>(JA, *this, Args.MakeArgString(Linker),
|
|
C.addCommand(llvm::make_unique<Command>(JA, *this, Args.MakeArgString(Linker),
|
|
CmdArgs, Inputs));
|
|
CmdArgs, Inputs));
|
|
}
|
|
}
|
|
-// AVR tools end.
|
|
|
|
|
|
+
|
|
|
|
+llvm::Optional<std::string> AVRToolChain::findAVRLibcInstallation() const {
|
|
|
|
+ for (StringRef PossiblePath : PossibleAVRLibcLocations) {
|
|
|
|
+ // Return the first avr-libc installation that exists.
|
|
|
|
+ if (llvm::sys::fs::is_directory(PossiblePath))
|
|
|
|
+ return Optional<std::string>(std::string(PossiblePath));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return llvm::None;
|
|
|
|
+}
|