diff options
-rw-r--r-- | include/clang/Driver/Options.td | 1 | ||||
-rw-r--r-- | lib/Driver/ToolChains.cpp | 353 | ||||
-rw-r--r-- | lib/Driver/ToolChains.h | 5 | ||||
-rw-r--r-- | lib/Driver/Tools.cpp | 139 | ||||
-rw-r--r-- | lib/Driver/Tools.h | 12 |
5 files changed, 490 insertions, 20 deletions
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index da762becb6..728a9e169b 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -610,6 +610,7 @@ def undefined : JoinedOrSeparate<"-undefined">, Group<u_Group>; def undef : Flag<"-undef">, Group<u_Group>; def unexported__symbols__list : Separate<"-unexported_symbols_list">; def u : JoinedOrSeparate<"-u">, Group<u_Group>; +def use_gold_plugin : Flag<"-use-gold-plugin">; def v : Flag<"-v">, HelpText<"Show commands to run and use verbose output">; def weak_l : Joined<"-weak-l">, Flags<[LinkerInput]>; diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 126e3fd959..1acdda72a3 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -23,6 +23,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "llvm/System/Path.h" @@ -1177,28 +1178,338 @@ Tool &AuroraUX::SelectTool(const Compilation &C, const JobAction &JA) const { /// Linux toolchain (very bare-bones at the moment). +enum LinuxDistro { + DebianLennyAmd64, + DebianLennyi386, + DebianSqueezeAmd64, + DebianSqueezei386, + DebianSqueezeArm, + Fedora13X86_64, + Fedora13i686, + Fedora14X86_64, + Fedora14i686, + OpenSuse11_3X86_64, + OpenSuse11_3i686, + UbuntuLucidAmd64, + UbuntuLucidi386, + UbuntuMaverickAmd64, + UbuntuMavericki386, + UnknownDistro +}; + +static bool IsFedora13(enum LinuxDistro Distro) { + return Distro == Fedora13X86_64 || Distro == Fedora13i686; +} + +static bool IsFedora14(enum LinuxDistro Distro) { + return Distro == Fedora14X86_64 || Distro == Fedora14i686; +} + +static bool IsFedora(enum LinuxDistro Distro) { + return IsFedora13(Distro) || IsFedora14(Distro); +} + +static bool IsOpenSuse(enum LinuxDistro Distro) { + return Distro == OpenSuse11_3X86_64 || Distro == OpenSuse11_3i686; +} + +static bool IsUbuntuLucid(enum LinuxDistro Distro) { + return Distro == UbuntuLucidi386 || Distro == UbuntuLucidAmd64; +} + +static bool IsUbuntuMaveric(enum LinuxDistro Distro) { + return Distro == UbuntuMavericki386 || Distro == UbuntuMaverickAmd64; +} + +static bool IsDebianLenny(enum LinuxDistro Distro) { + return Distro == DebianLennyAmd64 || Distro == DebianLennyi386; +} + +static bool IsDebianSqueeze(enum LinuxDistro Distro) { + return Distro == DebianSqueezeAmd64 || + Distro == DebianSqueezei386 || + Distro == DebianSqueezeArm; +} + +static bool IsDebian(enum LinuxDistro Distro) { + return IsDebianLenny(Distro) || IsDebianSqueeze(Distro); +} + +static bool IsUbuntu(enum LinuxDistro Distro) { + return IsUbuntuMaveric(Distro) || IsUbuntuLucid(Distro); +} + +static bool IsDebianBased(enum LinuxDistro Distro) { + return IsDebian(Distro) || IsUbuntu(Distro); +} + +static bool IsArm(enum LinuxDistro Distro) { + return Distro == DebianSqueezeArm; +} + +static bool IsX86_64(enum LinuxDistro Distro) { + switch (Distro) { + default: + return false; + case DebianLennyAmd64: + case DebianSqueezeAmd64: + case Fedora13X86_64: + case Fedora14X86_64: + case OpenSuse11_3X86_64: + case UbuntuLucidAmd64: + case UbuntuMaverickAmd64: + return true; + } +} + +static bool IsX86(enum LinuxDistro Distro) { + return !IsArm(Distro) && !IsX86_64(Distro); +} + +static bool HasMultilib(enum LinuxDistro Distro) { + if (IsX86_64(Distro)) + return true; + if (IsArm(Distro)) + return false; + if (IsDebianBased(Distro)) + return true; + return false; +} + +static LinuxDistro DetectLinuxDistro( llvm::Triple::ArchType Arch) { + llvm::OwningPtr<const llvm::MemoryBuffer> + LsbRelease(llvm::MemoryBuffer::getFile("/etc/lsb-release")); + if (LsbRelease) { + llvm::StringRef Data = LsbRelease.get()->getBuffer(); + llvm::SmallVector<llvm::StringRef, 8> Lines; + Data.split(Lines, "\n"); + for (unsigned int i = 0, s = Lines.size(); i < s; ++ i) { + if (Lines[i] == "DISTRIB_CODENAME=maverick") { + switch (Arch) { + default: + return UnknownDistro; + case llvm::Triple::x86: + return UbuntuMavericki386; + case llvm::Triple::x86_64: + return UbuntuMaverickAmd64; + } + } + if (Lines[i] == "DISTRIB_CODENAME=lucid") { + switch (Arch) { + default: + return UnknownDistro; + case llvm::Triple::x86: + return UbuntuLucidi386; + case llvm::Triple::x86_64: + return UbuntuLucidAmd64; + } + } + } + return UnknownDistro; + } + + llvm::OwningPtr<const llvm::MemoryBuffer> + RHRelease(llvm::MemoryBuffer::getFile("/etc/redhat-release")); + if (RHRelease) { + llvm::StringRef Data = RHRelease.get()->getBuffer(); + if (Data.startswith("Fedora release 14 (Laughlin)")) { + switch (Arch) { + default: + return UnknownDistro; + case llvm::Triple::x86: + return Fedora14i686; + case llvm::Triple::x86_64: + return Fedora14X86_64; + } + } + + if (Data.startswith("Fedora release 13 (Goddard)")) { + switch (Arch) { + default: + return UnknownDistro; + case llvm::Triple::x86: + return Fedora13i686; + case llvm::Triple::x86_64: + return Fedora13X86_64; + } + } + return UnknownDistro; + } + + llvm::OwningPtr<const llvm::MemoryBuffer> + DebianVersion(llvm::MemoryBuffer::getFile("/etc/debian_version")); + if (DebianVersion) { + llvm::StringRef Data = DebianVersion.get()->getBuffer(); + if (Data[0] == '5') { + switch (Arch) { + default: + return UnknownDistro; + case llvm::Triple::x86: + return DebianLennyi386; + case llvm::Triple::x86_64: + return DebianLennyAmd64; + } + } else if (Data.startswith("squeeze/sid")) { + switch (Arch) { + default: + return UnknownDistro; + case llvm::Triple::arm: + return DebianSqueezeArm; + case llvm::Triple::x86: + return DebianSqueezei386; + case llvm::Triple::x86_64: + return DebianSqueezeAmd64; + } + } + return UnknownDistro; + } + + llvm::OwningPtr<const llvm::MemoryBuffer> + SuseRelease(llvm::MemoryBuffer::getFile("/etc/SuSE-release")); + if (SuseRelease) { + llvm::StringRef Data = SuseRelease.get()->getBuffer(); + if (Data.startswith("openSUSE 11.3")) { + switch (Arch) { + default: + return UnknownDistro; + case llvm::Triple::x86: + return OpenSuse11_3i686; + case llvm::Triple::x86_64: + return OpenSuse11_3X86_64; + } + } + return UnknownDistro; + } + + return UnknownDistro; +} + Linux::Linux(const HostInfo &Host, const llvm::Triple& Triple) : Generic_ELF(Host, Triple) { - getFilePaths().push_back(getDriver().Dir + - "/../lib/clang/" CLANG_VERSION_STRING "/"); - getFilePaths().push_back("/lib/"); - getFilePaths().push_back("/usr/lib/"); - - // Depending on the Linux distribution, any combination of lib{,32,64} is - // possible. E.g. Debian uses lib and lib32 for mixed i386/x86-64 systems, - // openSUSE uses lib and lib64 for the same purpose. - getFilePaths().push_back("/lib32/"); - getFilePaths().push_back("/usr/lib32/"); - getFilePaths().push_back("/lib64/"); - getFilePaths().push_back("/usr/lib64/"); - - // FIXME: Figure out some way to get gcc's libdir - // (e.g. /usr/lib/gcc/i486-linux-gnu/4.3/ for Ubuntu 32-bit); we need - // crtbegin.o/crtend.o/etc., and want static versions of various - // libraries. If we had our own crtbegin.o/crtend.o/etc, we could probably - // get away with using shared versions in /usr/lib, though. - // We could fall back to the approach we used for includes (a massive - // list), but that's messy at best. + + std::string Lib32 = "lib"; + std::string Lib64 = "lib"; + std::string Suffix32 = ""; + std::string Suffix64 = ""; + std::string Base = ""; + std::string GccTriple = ""; + + llvm::Triple::ArchType Arch = + llvm::Triple(getDriver().DefaultHostTriple).getArch(); + LinuxDistro Distro = DetectLinuxDistro(Arch); + + if (Distro == UnknownDistro) + llvm::report_fatal_error("Unknown linux distribution."); + + if (IsUbuntu(Distro)) + ExtraOpts.push_back("-z relro"); + + if (IsArm(Distro)) + ExtraOpts.push_back("-X"); + + if (IsFedora(Distro) || IsUbuntuMaveric(Distro)) + ExtraOpts.push_back("--hash-style=gnu"); + + if (IsDebian(Distro) || IsUbuntuLucid(Distro)) + ExtraOpts.push_back("--hash-style=both"); + + if (IsX86_64(Distro)) + Suffix32 = "/32"; + + if (IsX86(Distro)) + Suffix64 = "/64"; + + if (IsDebianBased(Distro) && IsX86_64(Distro)) + Lib32 = "lib32"; + + if ((IsDebianBased(Distro) && IsX86(Distro)) || + IsFedora(Distro) || IsOpenSuse(Distro)) + Lib64 = "lib64"; + + if (IsFedora(Distro)) + ExtraOpts.push_back("--no-add-needed"); + + if (IsDebianSqueeze(Distro) || IsUbuntu(Distro) || IsOpenSuse(Distro) || + IsFedora(Distro)) + ExtraOpts.push_back("--build-id"); + + if (IsArm(Distro)) { + if (IsDebianBased(Distro)) + GccTriple = "arm-linux-gnueabi"; + } else if (IsX86_64(Distro)) { + if (IsDebianBased(Distro)) + GccTriple = "x86_64-linux-gnu"; + else if (IsFedora(Distro)) + GccTriple = "x86_64-redhat-linux"; + else if (IsOpenSuse(Distro)) + GccTriple = "x86_64-suse-linux"; + } else if (IsX86(Distro)) { + if (Distro == UbuntuMavericki386) + GccTriple = "i686-linux-gnu"; + else if (IsDebianBased(Distro)) + GccTriple = "i486-linux-gnu"; + if (IsFedora(Distro)) + GccTriple = "i686-redhat-linux"; + if (IsOpenSuse(Distro)) + GccTriple = "i586-suse-linux"; + } + + if (IsDebianLenny(Distro)) + Base = "/usr/lib/gcc/" + GccTriple +"/4.3.2"; + if (IsDebianSqueeze(Distro) || IsUbuntuMaveric(Distro)) + Base = "/usr/lib/gcc/" + GccTriple +"/4.4.5"; + if (IsUbuntuLucid(Distro)) + Base = "/usr/lib/gcc/" + GccTriple +"/4.4.3"; + if (IsFedora13(Distro)) + Base = "/usr/lib/gcc/" + GccTriple +"/4.4.4"; + if (IsFedora14(Distro)) + Base = "/usr/lib/gcc/" + GccTriple +"/4.5.1"; + if (IsOpenSuse(Distro) && IsX86_64(Distro)) + Base = "/usr/lib64/gcc/" + GccTriple + "/4.5"; + if (IsOpenSuse(Distro) && IsX86(Distro)) + Base = "/usr/lib/gcc/" + GccTriple + "/4.5"; + + path_list &Paths = getFilePaths(); + bool Is32Bits = getArch() == llvm::Triple::x86; + + std::string Suffix; + std::string Lib; + + if (Is32Bits) { + Suffix = Suffix32; + Lib = Lib32; + } else { + Suffix = Suffix64; + Lib = Lib64; + } + + llvm::sys::Path LinkerPath(Base + "/../../../../" + GccTriple + "/bin/ld"); + if (LinkerPath.exists()) + Linker = LinkerPath.str(); + else + Linker = GetProgramPath("ld"); + + Paths.push_back(Base + Suffix); + if (HasMultilib(Distro)) { + if (IsOpenSuse(Distro) && Is32Bits) + Paths.push_back(Base + "/../../../../" + GccTriple + "/lib/../lib"); + Paths.push_back(Base + "/../../../../" + Lib); + Paths.push_back("/lib/../" + Lib); + Paths.push_back("/usr/lib/../" + Lib); + } + if (!Suffix.empty()) + Paths.push_back(Base); + if (IsOpenSuse(Distro)) + Paths.push_back(Base + "/../../../../" + GccTriple + "/lib"); + Paths.push_back(Base + "/../../.."); + bool IsNative = (Is32Bits && IsX86(Distro)) || + (!Is32Bits && IsX86_64(Distro)); + if (IsNative && IsUbuntu(Distro)) + Paths.push_back("/usr/lib/" + GccTriple); +} + +bool Linux::HasNativeLLVMSupport() const { + return true; } Tool &Linux::SelectTool(const Compilation &C, const JobAction &JA) const { @@ -1213,6 +1524,8 @@ Tool &Linux::SelectTool(const Compilation &C, const JobAction &JA) const { switch (Key) { case Action::AssembleJobClass: T = new tools::linuxtools::Assemble(*this); break; + case Action::LinkJobClass: + T = new tools::linuxtools::Link(*this); break; default: T = &Generic_GCC::SelectTool(C, JA); } diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index b98f1769c6..04288a2ee0 100644 --- a/lib/Driver/ToolChains.h +++ b/lib/Driver/ToolChains.h @@ -317,7 +317,12 @@ class LLVM_LIBRARY_VISIBILITY Linux : public Generic_ELF { public: Linux(const HostInfo &Host, const llvm::Triple& Triple); + virtual bool HasNativeLLVMSupport() const; + virtual Tool &SelectTool(const Compilation &C, const JobAction &JA) const; + + std::string Linker; + std::vector<std::string> ExtraOpts; }; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 7ba1dc8be7..1fcae40076 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -3134,6 +3134,145 @@ void linuxtools::Assemble::ConstructJob(Compilation &C, const JobAction &JA, C.addCommand(new Command(JA, *this, Exec, CmdArgs)); } +void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + const toolchains::Linux& ToolChain = + static_cast<const toolchains::Linux&>(getToolChain()); + const Driver &D = ToolChain.getDriver(); + ArgStringList CmdArgs; + + for (std::vector<std::string>::const_iterator i = ToolChain.ExtraOpts.begin(), + e = ToolChain.ExtraOpts.end(); + i != e; ++i) + CmdArgs.push_back(i->c_str()); + + if (!Args.hasArg(options::OPT_static)) { + CmdArgs.push_back("--eh-frame-hdr"); + } + + CmdArgs.push_back("-m"); + if (ToolChain.getArch() == llvm::Triple::x86) + CmdArgs.push_back("elf_i386"); + else if (ToolChain.getArch() == llvm::Triple::arm) + CmdArgs.push_back("armelf_linux_eabi"); + else + CmdArgs.push_back("elf_x86_64"); + + if (Args.hasArg(options::OPT_static)) { + if (ToolChain.getArch() == llvm::Triple::arm) + CmdArgs.push_back("-Bstatic"); + else + CmdArgs.push_back("-static"); + } else if (Args.hasArg(options::OPT_shared)) { + CmdArgs.push_back("-shared"); + } + + if (ToolChain.getArch() == llvm::Triple::arm || + (!Args.hasArg(options::OPT_static) && + !Args.hasArg(options::OPT_shared))) { + CmdArgs.push_back("-dynamic-linker"); + if (ToolChain.getArch() == llvm::Triple::x86) + CmdArgs.push_back("/lib/ld-linux.so.2"); + else if (ToolChain.getArch() == llvm::Triple::arm) + CmdArgs.push_back("/lib/ld-linux.so.3"); + else + CmdArgs.push_back("/lib64/ld-linux-x86-64.so.2"); + } + + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + + if (!Args.hasArg(options::OPT_shared)) + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crt1.o"))); + + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crti.o"))); + + const char *crtbegin; + if (Args.hasArg(options::OPT_static)) + crtbegin = "crtbeginT.o"; + else if (Args.hasArg(options::OPT_shared)) + crtbegin = "crtbeginS.o"; + else + crtbegin = "crtbegin.o"; + + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crtbegin))); + + Args.AddAllArgs(CmdArgs, options::OPT_L); + + const ToolChain::path_list Paths = ToolChain.getFilePaths(); + + for (ToolChain::path_list::const_iterator i = Paths.begin(), + e = Paths.end(); + i != e; ++i) { + const std::string &s = *i; + CmdArgs.push_back(Args.MakeArgString(std::string("-L") + s)); + } + + AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs); + + if (D.CCCIsCXX) { + ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); + CmdArgs.push_back("-lm"); + } + + if (Args.hasArg(options::OPT_static)) + CmdArgs.push_back("--start-group"); + + if (!D.CCCIsCXX) + CmdArgs.push_back("-lgcc"); + + if (Args.hasArg(options::OPT_static)) { + if (D.CCCIsCXX) + CmdArgs.push_back("-lgcc"); + } else { + if (!D.CCCIsCXX) + CmdArgs.push_back("--as-needed"); + CmdArgs.push_back("-lgcc_s"); + if (!D.CCCIsCXX) + CmdArgs.push_back("--no-as-needed"); + } + + if (Args.hasArg(options::OPT_static)) + CmdArgs.push_back("-lgcc_eh"); + else if (!Args.hasArg(options::OPT_shared) && D.CCCIsCXX) + CmdArgs.push_back("-lgcc"); + + CmdArgs.push_back("-lc"); + + if (Args.hasArg(options::OPT_static)) + CmdArgs.push_back("--end-group"); + else { + if (!D.CCCIsCXX) + CmdArgs.push_back("-lgcc"); + + if (!D.CCCIsCXX) + CmdArgs.push_back("--as-needed"); + CmdArgs.push_back("-lgcc_s"); + if (!D.CCCIsCXX) + CmdArgs.push_back("--no-as-needed"); + + if (!Args.hasArg(options::OPT_shared) && D.CCCIsCXX) + CmdArgs.push_back("-lgcc"); + } + + if (Args.hasArg(options::OPT_shared)) + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtendS.o"))); + else + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtend.o"))); + + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtn.o"))); + + if (Args.hasArg(options::OPT_use_gold_plugin)) { + CmdArgs.push_back("-plugin"); + std::string Plugin = ToolChain.getDriver().Dir + "/../lib/LLVMgold.so"; + CmdArgs.push_back(Args.MakeArgString(Plugin)); + } + + C.addCommand(new Command(JA, *this, ToolChain.Linker.c_str(), CmdArgs)); +} void minix::Assemble::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, diff --git a/lib/Driver/Tools.h b/lib/Driver/Tools.h index 63faf91654..edfb2df38e 100644 --- a/lib/Driver/Tools.h +++ b/lib/Driver/Tools.h @@ -348,6 +348,18 @@ namespace linuxtools { const ArgList &TCArgs, const char *LinkingOutput) const; }; + class LLVM_LIBRARY_VISIBILITY Link : public Tool { + public: + Link(const ToolChain &TC) : Tool("linux::Link", "linker", TC) {} + + virtual bool hasIntegratedCPP() const { return false; } + + virtual void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &TCArgs, + const char *LinkingOutput) const; + }; } /// minix -- Directly call GNU Binutils assembler and linker namespace minix { |