]> granicus.if.org Git - clang/commitdiff
Use ld directly on linux. Changes from the previous try:
authorRafael Espindola <rafael.espindola@gmail.com>
Sun, 7 Nov 2010 20:14:31 +0000 (20:14 +0000)
committerRafael Espindola <rafael.espindola@gmail.com>
Sun, 7 Nov 2010 20:14:31 +0000 (20:14 +0000)
*) Try to detect as much as possible from the system itself, not the distro.
   This should make it easier to port to a new distro and more likely to
   work on a unknown one.
*) The distro enum now doesn't include the arch. Just use the existing
   host detection support in LLVM.
*) Correctly handle --sysroot.

A small regression is that now clang will pass bitcode file to the linker.
This is necessary for the gold plugin support to work.

It might be better to detect this at configure/cmake time, but doing it in
c++ first is a lot easier.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@118382 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Driver/Options.td
lib/Driver/ToolChains.cpp
lib/Driver/ToolChains.h
lib/Driver/Tools.cpp
lib/Driver/Tools.h
test/Driver/emit-llvm.c
test/Driver/sysroot-flags.c

index 907639f58b5d83c1cf4be67637f99ed48a298adf..c61d71b1203f0dc9f36d1412d4687e4143eb72f7 100644 (file)
@@ -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]>;
index 126e3fd95952bd5df758c44747cf011efd3667d2..adbac9029e16208bb4be05f5232714f53885e214 100644 (file)
@@ -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,217 @@ Tool &AuroraUX::SelectTool(const Compilation &C, const JobAction &JA) const {
 
 /// Linux toolchain (very bare-bones at the moment).
 
+enum LinuxDistro {
+  DebianLenny,
+  DebianSqueeze,
+  Fedora13,
+  Fedora14,
+  OpenSuse11_3,
+  UbuntuLucid,
+  UbuntuMaverick,
+  UnknownDistro
+};
+
+static bool IsFedora(enum LinuxDistro Distro) {
+  return Distro == Fedora13 || Distro == Fedora14;
+}
+
+static bool IsOpenSuse(enum LinuxDistro Distro) {
+  return Distro == OpenSuse11_3;
+}
+
+static bool IsDebian(enum LinuxDistro Distro) {
+  return Distro == DebianLenny || Distro == DebianSqueeze;
+}
+
+static bool IsUbuntu(enum LinuxDistro Distro) {
+  return Distro == UbuntuLucid || Distro == UbuntuMaverick;
+}
+
+static bool IsDebianBased(enum LinuxDistro Distro) {
+  return IsDebian(Distro) || IsUbuntu(Distro);
+}
+
+static bool HasMultilib(llvm::Triple::ArchType Arch, enum LinuxDistro Distro) {
+  if (Arch == llvm::Triple::x86_64)
+    return true;
+  if (Arch == llvm::Triple::x86 && 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")
+        return UbuntuMaverick;
+      else if (Lines[i] == "DISTRIB_CODENAME=lucid")
+        return UbuntuLucid;
+    }
+    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)"))
+      return Fedora14;
+    else if (Data.startswith("Fedora release 13 (Goddard)"))
+      return Fedora13;
+    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')
+      return DebianLenny;
+    else if (Data.startswith("squeeze/sid"))
+      return DebianSqueeze;
+    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"))
+      return OpenSuse11_3;
+    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.
+  llvm::Triple::ArchType Arch =
+    llvm::Triple(getDriver().DefaultHostTriple).getArch();
+
+  std::string Suffix32  = "";
+  if (Arch == llvm::Triple::x86_64)
+    Suffix32 = "/32";
+
+  std::string Suffix64  = "";
+  if (Arch == llvm::Triple::x86)
+    Suffix64 = "/64";
+
+  std::string Lib32 = "lib";
+
+  if (  llvm::sys::Path("/lib32").exists())
+    Lib32 = "lib32";
+
+  std::string Lib64 = "lib";
+  llvm::sys::Path Lib64Path("/lib64");
+  if (Lib64Path.exists() && !Lib64Path.isSymLink())
+    Lib64 = "lib64";
+
+  std::string GccTriple = "";
+  if (Arch == llvm::Triple::arm) {
+    if (llvm::sys::Path("/usr/lib/gcc/arm-linux-gnueabi").exists())
+      GccTriple = "arm-linux-gnueabi";
+  } else if (Arch == llvm::Triple::x86_64) {
+    if (llvm::sys::Path("/usr/lib/gcc/x86_64-linux-gnu").exists())
+      GccTriple = "x86_64-linux-gnu";
+    else if (llvm::sys::Path("/usr/lib/gcc/x86_64-redhat-linux").exists())
+      GccTriple = "x86_64-redhat-linux";
+    else if (llvm::sys::Path("/usr/lib64/gcc/x86_64-suse-linux").exists())
+      GccTriple = "x86_64-suse-linux";
+  } else if (Arch == llvm::Triple::x86) {
+    if (llvm::sys::Path("/usr/lib/gcc/i686-linux-gnu").exists())
+      GccTriple = "i686-linux-gnu";
+    else if (llvm::sys::Path("/usr/lib/gcc/i486-linux-gnu").exists())
+      GccTriple = "i486-linux-gnu";
+    else if (llvm::sys::Path("/usr/lib/gcc/i686-redhat-linux").exists())
+      GccTriple = "i686-redhat-linux";
+    else if (llvm::sys::Path("/usr/lib/gcc/i586-suse-linux").exists())
+      GccTriple = "i586-suse-linux";
+  }
+
+  const char* GccVersions[] = {"4.5.1", "4.5", "4.4.5", "4.4.4", "4.4.3",
+                               "4.3.2"};
+  std::string Base = "";
+  for (unsigned i = 0; i < sizeof(GccVersions)/sizeof(char*); ++i) {
+    std::string Suffix = GccTriple + "/" + GccVersions[i];
+    std::string t1 = "/usr/lib/gcc/" + Suffix;
+    if (llvm::sys::Path(t1 + "/crtbegin.o").exists()) {
+      Base = t1;
+      break;
+    }
+    std::string t2 = "/usr/lib64/gcc/" + Suffix;
+    if (llvm::sys::Path(t2 + "/crtbegin.o").exists()) {
+      Base = t2;
+      break;
+    }
+  }
+
+  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");
+
+  LinuxDistro Distro = DetectLinuxDistro(Arch);
+
+  if (IsUbuntu(Distro))
+    ExtraOpts.push_back("-z relro");
+
+  if (Arch == llvm::Triple::arm)
+    ExtraOpts.push_back("-X");
+
+  if (IsFedora(Distro) || Distro == UbuntuMaverick)
+    ExtraOpts.push_back("--hash-style=gnu");
+
+  if (IsDebian(Distro) || Distro == UbuntuLucid)
+    ExtraOpts.push_back("--hash-style=both");
+
+  if (IsFedora(Distro))
+    ExtraOpts.push_back("--no-add-needed");
+
+  if (Distro == DebianSqueeze || IsUbuntu(Distro) || IsOpenSuse(Distro) ||
+      IsFedora(Distro))
+    ExtraOpts.push_back("--build-id");
+
+  Paths.push_back(Base + Suffix);
+  if (HasMultilib(Arch, 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 + "/../../..");
+  if (Arch == getArch() && 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 +1403,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);
     }
index b98f1769c678ac925ffd1afe763dc416ae348906..04288a2ee06a0d233d5ca40e2aa53da98a2df1e9 100644 (file)
@@ -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;
 };
 
 
index 744b0ae661f81a0ae192d178a75760b8e12cf701..4d6cbf747f99334c8947c0ed1c24f46e19a03fc9 100644 (file)
@@ -3136,6 +3136,150 @@ 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;
+
+  if (Arg *A = Args.getLastArg(options::OPT__sysroot_EQ)) {
+    CmdArgs.push_back("--sysroot");
+    CmdArgs.push_back(A->getValue(Args));
+  }
+
+  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,
index 63faf916549c68e2632674d2cef9c2eb8cffeb78..edfb2df38e17e6a3358ee959fccf06cd3b085c40 100644 (file)
@@ -342,6 +342,18 @@ namespace linuxtools {
 
     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;
+  };
+  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,
index 3439b58d85d7afb66ac50521c88807d24a8fad00..76ea05926c967f3427f002920f4f6df43be6aa1c 100644 (file)
@@ -1,6 +1,3 @@
-// RUN: not %clang -ccc-host-triple i386-pc-linux-gnu -emit-llvm -o %t %s 2> %t.log
-// RUN: grep 'unable to pass LLVM bit-code files to linker' %t.log
-
 // Check that -O4 is only honored as the effective -O option.
 // <rdar://problem/7046672> clang/loader problem
 
index eedd7627b360d17fe88e07020a682d4264115d5b..fde9105c10b7456f13f7dc245b33fb635d17e9da 100644 (file)
@@ -10,7 +10,7 @@
 // RUN:   --sysroot=/foo/bar -o /dev/null %s 2>&1 | \
 // RUN:   FileCheck %s -check-prefix=SYSROOT_EQ
 // SYSROOT_EQ: "-isysroot" "/foo/bar"
-// SYSROOT_EQ: "--sysroot=/foo/bar"
+// SYSROOT_EQ: "--sysroot" "/foo/bar"
 
 // Check for overriding the header sysroot by providing both --sysroot and
 // -isysroot.
 // RUN:   --sysroot=/foo/bar -o /dev/null %s 2>&1 | FileCheck %s \
 // RUN:   -check-prefix=ISYSROOT_AND_SYSROOT
 // ISYSROOT_AND_SYSROOT: "-isysroot" "/baz"
-// ISYSROOT_AND_SYSROOT: "--sysroot=/foo/bar"
+// ISYSROOT_AND_SYSROOT: "--sysroot" "/foo/bar"
 
 // Check that omitting the equals works as well.
 // RUN: %clang -### -ccc-host-triple x86_64-unknown-linux-gnu \
 // RUN:   --sysroot /foo/bar -o /dev/null %s 2>&1 | \
 // RUN:   FileCheck %s -check-prefix=SYSROOT_SEPARATE
 // SYSROOT_SEPARATE: "-isysroot" "/foo/bar"
-// SYSROOT_SEPARATE: "--sysroot=/foo/bar"
+// SYSROOT_SEPARATE: "--sysroot" "/foo/bar"