aboutsummaryrefslogtreecommitdiff
path: root/lib/Driver/ToolChains.cpp
diff options
context:
space:
mode:
authorRafael Espindola <rafael.espindola@gmail.com>2010-11-07 20:14:31 +0000
committerRafael Espindola <rafael.espindola@gmail.com>2010-11-07 20:14:31 +0000
commitc1da981bba867681004004b2d54ec2fdf3c08913 (patch)
tree96c2983ab5ee80187516aba0690a52f6ef7bdde3 /lib/Driver/ToolChains.cpp
parentdfdfc584f2a8d9f1eebd6e6eaa9b1bbff519d8f9 (diff)
Use ld directly on linux. Changes from the previous try:
*) 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
Diffstat (limited to 'lib/Driver/ToolChains.cpp')
-rw-r--r--lib/Driver/ToolChains.cpp232
1 files changed, 212 insertions, 20 deletions
diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp
index 126e3fd959..adbac9029e 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,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);
}