Driver: Add darwin::Link tool.
 - <rdar://problem/6717381> [driver] implement ld argument translation
   in new driver


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@67760 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp
index 31ff34f..1b0bdfa 100644
--- a/lib/Driver/ToolChains.cpp
+++ b/lib/Driver/ToolChains.cpp
@@ -38,6 +38,9 @@
   GCCVersion[1] = _GCCVersion[1];
   GCCVersion[2] = _GCCVersion[2];
 
+  llvm::raw_string_ostream(MacosxVersionMin)
+    << "10." << DarwinVersion[0] - 4 << '.' << DarwinVersion[1];
+
   ToolChainDir = "i686-apple-darwin";
   ToolChainDir += llvm::utostr(DarwinVersion[0]);
   ToolChainDir += "/";
@@ -93,13 +96,6 @@
     delete it->second;
 }
 
-std::string Darwin_X86::getMacosxVersionMin() const {
-  std::string Res;
-  llvm::raw_string_ostream OS(Res);
-  OS << "10." << DarwinVersion[0] - 4 << '.' << DarwinVersion[1];
-  return OS.str();
-}
-
 Tool &Darwin_X86::SelectTool(const Compilation &C, 
                               const JobAction &JA) const {
   Action::ActionClass Key;
@@ -125,7 +121,7 @@
     case Action::AssembleJobClass:
       T = new tools::darwin::Assemble(*this); break;
     case Action::LinkJobClass:
-      T = new tools::gcc::Link(*this); break;
+      T = new tools::darwin::Link(*this, MacosxVersionMin.c_str()); break;
     case Action::LipoJobClass:
       T = new tools::darwin::Lipo(*this); break;
     }
@@ -147,7 +143,7 @@
 
   if (!Args.hasArg(options::OPT_mmacosx_version_min_EQ, false)) {
     const Option *O = Opts.getOption(options::OPT_mmacosx_version_min_EQ);
-    DAL->append(DAL->MakeJoinedArg(O, getMacosxVersionMin().c_str()));
+    DAL->append(DAL->MakeJoinedArg(O, MacosxVersionMin.c_str()));
   }
   
   for (ArgList::iterator it = Args.begin(), ie = Args.end(); it != ie; ++it) {
diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h
index 7548b9e..5e7bdb5 100644
--- a/lib/Driver/ToolChains.h
+++ b/lib/Driver/ToolChains.h
@@ -56,7 +56,11 @@
   /// The directory suffix for this tool chain.
   std::string ToolChainDir;
 
-  std::string getMacosxVersionMin() const;
+  /// The default macosx-version-min of this tool chain; empty until
+  /// initialized.
+  mutable std::string MacosxVersionMin;
+
+  const char *getMacosxVersionMin() const;
 
 public:
   Darwin_X86(const HostInfo &Host, const char *Arch, const char *Platform, 
@@ -64,6 +68,26 @@
              const unsigned (&GCCVersion)[3]);
   ~Darwin_X86();
 
+  void getDarwinVersion(unsigned (&Res)[3]) const {
+    Res[0] = DarwinVersion[0];
+    Res[1] = DarwinVersion[1];
+    Res[2] = DarwinVersion[2];
+  }
+
+  void getMacosxVersion(unsigned (&Res)[3]) const {
+    Res[0] = 10;
+    Res[1] = DarwinVersion[0] - 4;
+    Res[2] = DarwinVersion[1];
+  }
+
+  const char *getMacosxVersionStr() const {
+    return MacosxVersionMin.c_str();
+  }
+
+  const std::string &getToolChainDir() const { 
+    return ToolChainDir;
+  }
+
   virtual DerivedArgList *TranslateArgs(InputArgList &Args) const;
 
   virtual Tool &SelectTool(const Compilation &C, const JobAction &JA) const;
@@ -72,9 +96,6 @@
   virtual bool IsUnwindTablesDefault() const;
   virtual const char *GetDefaultRelocationModel() const;
   virtual const char *GetForcedPicModel() const;
-
-private:
-  const std::string &getToolChainDir() const { return ToolChainDir; }
 };
 
   /// Darwin_GCC - Generic Darwin tool chain using gcc.
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index 4bc034f..248df27 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -22,8 +22,11 @@
 #include "clang/Driver/Util.h"
 
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
 
 #include "InputInfo.h"
+#include "ToolChains.h"
 
 using namespace clang::driver;
 using namespace clang::driver::tools;
@@ -490,6 +493,447 @@
   Dest.addCommand(new Command(Exec, CmdArgs));
 }
 
+static const char *MakeFormattedString(const ArgList &Args,
+                                       const llvm::format_object_base &Fmt) {
+  std::string Str;
+  llvm::raw_string_ostream(Str) << Fmt;
+  return Args.MakeArgString(Str.c_str());
+}
+
+/// Helper routine for seeing if we should use dsymutil; this is a
+/// gcc compatible hack, we should remove it and use the input
+/// type information.
+static bool isSourceSuffix(const char *Str) {
+  // match: 'C', 'CPP', 'c', 'cc', 'cp', 'c++', 'cpp', 'cxx', 'm',
+  // 'mm'.
+  switch (strlen(Str)) {
+  default:
+    return false;
+  case 1:
+    return (memcmp(Str, "C", 1) == 0 ||
+            memcmp(Str, "c", 1) == 0 ||
+            memcmp(Str, "m", 1) == 0);
+  case 2:
+    return (memcmp(Str, "cc", 2) == 0 ||
+            memcmp(Str, "cp", 2) == 0 ||
+            memcmp(Str, "mm", 2) == 0);
+  case 3:
+    return (memcmp(Str, "CPP", 3) == 0 ||
+            memcmp(Str, "c++", 3) == 0 ||
+            memcmp(Str, "cpp", 3) == 0 ||
+            memcmp(Str, "cxx", 3) == 0);
+  }
+}
+
+static bool isMacosxVersionLT(unsigned (&A)[3], unsigned (&B)[3]) {
+  for (unsigned i=0; i < 3; ++i) {
+    if (A[i] > B[i]) return false;
+    if (A[i] < B[i]) return true;
+  }
+  return false;
+}
+
+static bool isMacosxVersionLT(unsigned (&A)[3], 
+                              unsigned V0, unsigned V1=0, unsigned V2=0) {
+  unsigned B[3] = { V0, V1, V2 };
+  return isMacosxVersionLT(A, B);
+}
+
+static bool isMacosxVersionGTE(unsigned(&A)[3], 
+                              unsigned V0, unsigned V1=0, unsigned V2=0) {
+  return !isMacosxVersionLT(A, V0, V1, V2);
+}
+
+const toolchains::Darwin_X86 &darwin::Link::getDarwinToolChain() const {
+  return reinterpret_cast<const toolchains::Darwin_X86&>(getToolChain());
+}
+
+void darwin::Link::AddDarwinArch(const ArgList &Args, 
+                                 ArgStringList &CmdArgs) const {
+  // Derived from darwin_arch spec.
+  CmdArgs.push_back("-arch");
+  CmdArgs.push_back(getToolChain().getArchName().c_str());
+}
+
+void darwin::Link::AddDarwinSubArch(const ArgList &Args, 
+                                    ArgStringList &CmdArgs) const {
+  // Derived from darwin_subarch spec, not sure what the distinction
+  // exists for but at least for this chain it is the same.
+  AddDarwinArch(Args, CmdArgs);
+}
+
+void darwin::Link::AddLinkArgs(const ArgList &Args, 
+                               ArgStringList &CmdArgs) const {
+  const Driver &D = getToolChain().getHost().getDriver();
+
+  // Derived from the "link" spec.
+  Args.AddAllArgs(CmdArgs, options::OPT_static);
+  if (!Args.hasArg(options::OPT_static))
+    CmdArgs.push_back("-dynamic");
+  if (Args.hasArg(options::OPT_fgnu_runtime)) {
+    // FIXME: gcc replaces -lobjc in forward args with -lobjc-gnu
+    // here. How do we wish to handle such things?
+  }
+    
+  if (!Args.hasArg(options::OPT_dynamiclib)) {
+    if (Args.hasArg(options::OPT_force__cpusubtype__ALL)) {
+      AddDarwinArch(Args, CmdArgs);
+      CmdArgs.push_back("-force_cpusubtype_ALL");
+    } else
+      AddDarwinSubArch(Args, CmdArgs);
+
+    Args.AddLastArg(CmdArgs, options::OPT_bundle);
+    Args.AddAllArgs(CmdArgs, options::OPT_bundle__loader);
+    Args.AddAllArgs(CmdArgs, options::OPT_client__name);
+
+    Arg *A;
+    if ((A = Args.getLastArg(options::OPT_compatibility__version)) ||
+        (A = Args.getLastArg(options::OPT_current__version)) ||
+        (A = Args.getLastArg(options::OPT_install__name)))
+      D.Diag(clang::diag::err_drv_argument_only_allowed_with)
+        << A->getAsString(Args) << "-dynamiclib";
+
+    Args.AddLastArg(CmdArgs, options::OPT_force__flat__namespace);
+    Args.AddLastArg(CmdArgs, options::OPT_keep__private__externs);
+    Args.AddLastArg(CmdArgs, options::OPT_private__bundle);
+  } else {
+    CmdArgs.push_back("-dylib");
+
+    Arg *A;
+    if ((A = Args.getLastArg(options::OPT_bundle)) ||
+        (A = Args.getLastArg(options::OPT_bundle__loader)) ||
+        (A = Args.getLastArg(options::OPT_client__name)) ||
+        (A = Args.getLastArg(options::OPT_force__flat__namespace)) ||
+        (A = Args.getLastArg(options::OPT_keep__private__externs)) ||
+        (A = Args.getLastArg(options::OPT_private__bundle)))
+      D.Diag(clang::diag::err_drv_argument_not_allowed_with)
+        << A->getAsString(Args) << "-dynamiclib";
+    
+    Args.AddAllArgsTranslated(CmdArgs, options::OPT_compatibility__version,
+                              "-dylib_compatibility_version");
+    Args.AddAllArgsTranslated(CmdArgs, options::OPT_current__version,
+                              "-dylib_current_version");
+
+    if (Args.hasArg(options::OPT_force__cpusubtype__ALL)) {
+      AddDarwinArch(Args, CmdArgs);
+          // NOTE: We don't add -force_cpusubtype_ALL on this path. Ok.
+    } else
+      AddDarwinSubArch(Args, CmdArgs);
+
+    Args.AddAllArgsTranslated(CmdArgs, options::OPT_install__name,
+                              "-dylib_install_name");
+  }
+
+  Args.AddLastArg(CmdArgs, options::OPT_all__load);
+  Args.AddAllArgs(CmdArgs, options::OPT_allowable__client);
+  Args.AddLastArg(CmdArgs, options::OPT_bind__at__load);
+  Args.AddLastArg(CmdArgs, options::OPT_dead__strip);
+  Args.AddLastArg(CmdArgs, options::OPT_no__dead__strip__inits__and__terms);
+  Args.AddAllArgs(CmdArgs, options::OPT_dylib__file);
+  Args.AddLastArg(CmdArgs, options::OPT_dynamic);
+  Args.AddAllArgs(CmdArgs, options::OPT_exported__symbols__list);
+  Args.AddLastArg(CmdArgs, options::OPT_flat__namespace);
+  Args.AddAllArgs(CmdArgs, options::OPT_headerpad__max__install__names);
+  Args.AddAllArgs(CmdArgs, options::OPT_image__base);
+  Args.AddAllArgs(CmdArgs, options::OPT_init);
+
+  if (!Args.hasArg(options::OPT_mmacosx_version_min_EQ)) {
+    if (!Args.hasArg(options::OPT_miphoneos_version_min_EQ)) {
+        // FIXME: I don't understand what is going on here. This is
+        // supposed to come from darwin_ld_minversion, but gcc doesn't
+        // seem to be following that; it must be getting overridden
+        // somewhere.
+        CmdArgs.push_back("-macosx_version_min");
+        CmdArgs.push_back(getDarwinToolChain().getMacosxVersionStr());
+      }
+  } else {
+    // Adding all arguments doesn't make sense here but this is what
+    // gcc does.
+    Args.AddAllArgsTranslated(CmdArgs, options::OPT_mmacosx_version_min_EQ,
+                              "-macosx_version_min");
+  }
+
+  Args.AddAllArgsTranslated(CmdArgs, options::OPT_miphoneos_version_min_EQ,
+                            "-iphoneos_version_min");
+  Args.AddLastArg(CmdArgs, options::OPT_nomultidefs);
+  Args.AddLastArg(CmdArgs, options::OPT_multi__module);
+  Args.AddLastArg(CmdArgs, options::OPT_single__module);
+  Args.AddAllArgs(CmdArgs, options::OPT_multiply__defined);
+  Args.AddAllArgs(CmdArgs, options::OPT_multiply__defined__unused);
+  
+  if (Args.hasArg(options::OPT_fpie))
+    CmdArgs.push_back("-pie");
+
+  Args.AddLastArg(CmdArgs, options::OPT_prebind);
+  Args.AddLastArg(CmdArgs, options::OPT_noprebind);
+  Args.AddLastArg(CmdArgs, options::OPT_nofixprebinding);
+  Args.AddLastArg(CmdArgs, options::OPT_prebind__all__twolevel__modules);
+  Args.AddLastArg(CmdArgs, options::OPT_read__only__relocs);
+  Args.AddAllArgs(CmdArgs, options::OPT_sectcreate);
+  Args.AddAllArgs(CmdArgs, options::OPT_sectorder);
+  Args.AddAllArgs(CmdArgs, options::OPT_seg1addr);
+  Args.AddAllArgs(CmdArgs, options::OPT_segprot);
+  Args.AddAllArgs(CmdArgs, options::OPT_segaddr);
+  Args.AddAllArgs(CmdArgs, options::OPT_segs__read__only__addr);
+  Args.AddAllArgs(CmdArgs, options::OPT_segs__read__write__addr);
+  Args.AddAllArgs(CmdArgs, options::OPT_seg__addr__table);
+  Args.AddAllArgs(CmdArgs, options::OPT_seg__addr__table__filename);
+  Args.AddAllArgs(CmdArgs, options::OPT_sub__library);
+  Args.AddAllArgs(CmdArgs, options::OPT_sub__umbrella);
+  Args.AddAllArgsTranslated(CmdArgs, options::OPT_isysroot, "-syslibroot");
+  Args.AddLastArg(CmdArgs, options::OPT_twolevel__namespace);
+  Args.AddLastArg(CmdArgs, options::OPT_twolevel__namespace__hints);
+  Args.AddAllArgs(CmdArgs, options::OPT_umbrella);
+  Args.AddAllArgs(CmdArgs, options::OPT_undefined);
+  Args.AddAllArgs(CmdArgs, options::OPT_unexported__symbols__list);
+  Args.AddAllArgs(CmdArgs, options::OPT_weak__reference__mismatches);
+
+  if (!Args.hasArg(options::OPT_weak__reference__mismatches)) {
+    CmdArgs.push_back("-weak_reference_mismatches");
+    CmdArgs.push_back("non-weak");
+  }
+
+  Args.AddLastArg(CmdArgs, options::OPT_X_Flag);
+  Args.AddAllArgs(CmdArgs, options::OPT_y);
+  Args.AddLastArg(CmdArgs, options::OPT_w);
+  Args.AddAllArgs(CmdArgs, options::OPT_pagezero__size);
+  Args.AddAllArgs(CmdArgs, options::OPT_segs__read__);
+  Args.AddLastArg(CmdArgs, options::OPT_seglinkedit);
+  Args.AddLastArg(CmdArgs, options::OPT_noseglinkedit);
+  Args.AddAllArgs(CmdArgs, options::OPT_sectalign);
+  Args.AddAllArgs(CmdArgs, options::OPT_sectobjectsymbols);
+  Args.AddAllArgs(CmdArgs, options::OPT_segcreate);
+  Args.AddLastArg(CmdArgs, options::OPT_whyload);
+  Args.AddLastArg(CmdArgs, options::OPT_whatsloaded);
+  Args.AddAllArgs(CmdArgs, options::OPT_dylinker__install__name);
+  Args.AddLastArg(CmdArgs, options::OPT_dylinker);
+  Args.AddLastArg(CmdArgs, options::OPT_Mach);
+}
+
+void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA,
+                                Job &Dest, const InputInfo &Output, 
+                                const InputInfoList &Inputs, 
+                                const ArgList &Args, 
+                                const char *LinkingOutput) const {
+  assert(Output.getType() == types::TY_Image && "Invalid linker output type.");
+  // The logic here is derived from gcc's behavior; most of which
+  // comes from specs (starting with link_command). Consult gcc for
+  // more information.
+
+  // FIXME: The spec references -fdump= which seems to have
+  // disappeared?
+
+  ArgStringList CmdArgs;
+
+  // I'm not sure why this particular decomposition exists in gcc, but
+  // we follow suite for ease of comparison.
+  AddLinkArgs(Args, CmdArgs);
+
+  // FIXME: gcc has %{x} in here. How could this ever happen?  Cruft?
+  Args.AddAllArgs(CmdArgs, options::OPT_d_Flag);
+  Args.AddAllArgs(CmdArgs, options::OPT_s);
+  Args.AddAllArgs(CmdArgs, options::OPT_t);
+  Args.AddAllArgs(CmdArgs, options::OPT_Z_Flag);
+  Args.AddAllArgs(CmdArgs, options::OPT_u_Group);
+  Args.AddAllArgs(CmdArgs, options::OPT_A);
+  Args.AddLastArg(CmdArgs, options::OPT_e);
+  Args.AddAllArgs(CmdArgs, options::OPT_m_Separate);
+  Args.AddAllArgs(CmdArgs, options::OPT_r);
+
+  // FIXME: This is just being pedantically bug compatible, gcc
+  // doesn't *mean* to forward this, it just does (yay for pattern
+  // matching). It doesn't work, of course.
+  Args.AddAllArgs(CmdArgs, options::OPT_object);
+
+  CmdArgs.push_back("-o");
+  CmdArgs.push_back(Output.getFilename());
+
+  unsigned MacosxVersion[3];
+  if (Arg *A = Args.getLastArg(options::OPT_mmacosx_version_min_EQ)) {
+    bool HadExtra;
+    if (!Driver::GetReleaseVersion(A->getValue(Args), MacosxVersion[0], 
+                                   MacosxVersion[1], MacosxVersion[2],
+                                   HadExtra) ||
+        HadExtra) {
+      const Driver &D = getToolChain().getHost().getDriver();
+      D.Diag(clang::diag::err_drv_invalid_version_number)
+        << A->getAsString(Args);
+    }
+  } else {
+    getDarwinToolChain().getMacosxVersion(MacosxVersion);
+  }
+    
+  if (!Args.hasArg(options::OPT_A) &&
+      !Args.hasArg(options::OPT_nostdlib) &&
+      !Args.hasArg(options::OPT_nostartfiles)) {
+    // Derived from startfile spec.
+    if (Args.hasArg(options::OPT_dynamiclib)) {
+        // Derived from darwin_dylib1 spec.
+      if (Args.hasArg(options::OPT_miphoneos_version_min_EQ) ||
+          isMacosxVersionLT(MacosxVersion, 10, 5))
+        CmdArgs.push_back("-ldylib1.o");
+      else
+        CmdArgs.push_back("-ldylib1.10.5.o");
+    } else {
+      if (Args.hasArg(options::OPT_bundle)) {
+        if (!Args.hasArg(options::OPT_static))
+          CmdArgs.push_back("-lbundle1.o");
+      } else {
+        if (Args.hasArg(options::OPT_pg)) {
+          if (Args.hasArg(options::OPT_static) ||
+              Args.hasArg(options::OPT_object) ||
+              Args.hasArg(options::OPT_preload)) {
+            CmdArgs.push_back("-lgcrt0.o");
+          } else {
+            CmdArgs.push_back("-lgcrt1.o");
+                
+            // darwin_crt2 spec is empty.
+          } 
+        } else {
+          if (Args.hasArg(options::OPT_static) ||
+              Args.hasArg(options::OPT_object) ||
+              Args.hasArg(options::OPT_preload)) {
+            CmdArgs.push_back("-lcrt0.o");
+          } else {
+            // Derived from darwin_crt1 spec.
+            if (Args.hasArg(options::OPT_miphoneos_version_min_EQ) ||
+                isMacosxVersionLT(MacosxVersion, 10, 5)) {
+              CmdArgs.push_back("-lcrt1.o");
+            } else {
+              CmdArgs.push_back("-lcrt1.10.5.o");
+              
+              // darwin_crt2 spec is empty.
+            }
+          }
+        }
+      }
+    }
+
+    if (Args.hasArg(options::OPT_shared_libgcc) &&
+        !Args.hasArg(options::OPT_miphoneos_version_min_EQ) &&
+        isMacosxVersionLT(MacosxVersion, 10, 5)) {
+      const char *Str = getToolChain().GetFilePath(C, "crt3.o").c_str();
+      CmdArgs.push_back(Args.MakeArgString(Str));
+    }
+  }
+
+  Args.AddAllArgs(CmdArgs, options::OPT_L);
+  
+  if (Args.hasArg(options::OPT_fopenmp))
+    // This is more complicated in gcc...
+    CmdArgs.push_back("-lgomp");
+
+  // FIXME: Derive these correctly.
+  const char *TCDir = getDarwinToolChain().getToolChainDir().c_str();
+  if (getToolChain().getArchName() == "x86_64") {
+    CmdArgs.push_back(MakeFormattedString(Args,
+                              llvm::format("-L/usr/lib/gcc/%s/x86_64", TCDir)));
+    // Intentionally duplicated for (temporary) gcc bug compatibility.
+    CmdArgs.push_back(MakeFormattedString(Args,
+                              llvm::format("-L/usr/lib/gcc/%s/x86_64", TCDir)));
+  }
+  CmdArgs.push_back(MakeFormattedString(Args, 
+                                        llvm::format("-L/usr/lib/%s", TCDir)));
+  CmdArgs.push_back(MakeFormattedString(Args, 
+                                     llvm::format("-L/usr/lib/gcc/%s", TCDir)));
+  // Intentionally duplicated for (temporary) gcc bug compatibility.
+  CmdArgs.push_back(MakeFormattedString(Args, 
+                                     llvm::format("-L/usr/lib/gcc/%s", TCDir)));
+  CmdArgs.push_back(MakeFormattedString(Args, 
+                  llvm::format("-L/usr/lib/gcc/%s/../../../%s", TCDir, TCDir)));
+  CmdArgs.push_back(MakeFormattedString(Args,
+                            llvm::format("-L/usr/lib/gcc/%s/../../..", TCDir)));
+  
+  for (InputInfoList::const_iterator
+         it = Inputs.begin(), ie = Inputs.end(); it != ie; ++it) {
+    const InputInfo &II = *it;
+    if (II.isFilename())
+      CmdArgs.push_back(II.getFilename());
+    else 
+      II.getInputArg().renderAsInput(Args, CmdArgs);
+  }
+
+  if (LinkingOutput) {
+    CmdArgs.push_back("-arch_multiple");
+    CmdArgs.push_back("-final_output");
+    CmdArgs.push_back(LinkingOutput);
+  }
+
+  if (Args.hasArg(options::OPT_fprofile_arcs) ||
+      Args.hasArg(options::OPT_fprofile_generate) ||
+      Args.hasArg(options::OPT_fcreate_profile) ||
+      Args.hasArg(options::OPT_coverage))
+    CmdArgs.push_back("-lgcov");
+  
+  if (Args.hasArg(options::OPT_fnested_functions))
+    CmdArgs.push_back("-allow_stack_execute");
+  
+  if (!Args.hasArg(options::OPT_nostdlib) &&
+      !Args.hasArg(options::OPT_nodefaultlibs)) {
+    // link_ssp spec is empty.
+
+    // Derived from libgcc spec.
+    if (Args.hasArg(options::OPT_static)) {
+      CmdArgs.push_back("-lgcc_static");
+    } else if (Args.hasArg(options::OPT_static_libgcc)) {
+      CmdArgs.push_back("-lgcc_eh");
+      CmdArgs.push_back("-lgcc");
+    } else if (Args.hasArg(options::OPT_miphoneos_version_min_EQ)) {
+      // Derived from darwin_iphoneos_libgcc spec.
+      CmdArgs.push_back("-lgcc_s.10.5");
+      CmdArgs.push_back("-lgcc");
+    } else if (Args.hasArg(options::OPT_shared_libgcc) ||
+               Args.hasArg(options::OPT_fexceptions) ||
+               Args.hasArg(options::OPT_fgnu_runtime)) {
+      if (isMacosxVersionLT(MacosxVersion, 10, 5))
+        CmdArgs.push_back("-lgcc_s.10.4");
+      else
+        CmdArgs.push_back("-lgcc_s.10.5");
+      CmdArgs.push_back("-lgcc");
+    } else {
+      if (isMacosxVersionLT(MacosxVersion, 10, 5) &&
+          isMacosxVersionGTE(MacosxVersion, 10, 3, 9))
+        CmdArgs.push_back("-lgcc_s.10.4");
+      if (isMacosxVersionGTE(MacosxVersion, 10, 5))
+        CmdArgs.push_back("-lgcc_s.10.5");
+      CmdArgs.push_back("-lgcc");
+    }
+
+    // Derived from lib spec.
+    if (!Args.hasArg(options::OPT_static))
+      CmdArgs.push_back("-lSystem");
+  }
+
+  if (!Args.hasArg(options::OPT_A) &&
+      !Args.hasArg(options::OPT_nostdlib) &&
+      !Args.hasArg(options::OPT_nostartfiles)) {
+    // endfile_spec is empty.
+  }
+
+  Args.AddAllArgs(CmdArgs, options::OPT_T_Group);
+  Args.AddAllArgs(CmdArgs, options::OPT_F);
+
+  const char *Exec = 
+    Args.MakeArgString(getToolChain().GetProgramPath(C, "collect2").c_str());
+  Dest.addCommand(new Command(Exec, CmdArgs));  
+
+  if (Args.getLastArg(options::OPT_g_Group) &&
+      !Args.getLastArg(options::OPT_gstabs) &&
+      !Args.getLastArg(options::OPT_g0)) {
+    // FIXME: This is gross, but matches gcc. The test only considers
+    // the suffix (not the -x type), and then only of the first
+    // input. Awesome.
+    const char *Suffix = strchr(Inputs[0].getBaseInput(), '.');
+    if (Suffix && isSourceSuffix(Suffix + 1)) {
+      const char *Exec = 
+       Args.MakeArgString(getToolChain().GetProgramPath(C, "dsymutil").c_str());
+      ArgStringList CmdArgs;
+      CmdArgs.push_back(Output.getFilename());
+      C.getJobs().addCommand(new Command(Exec, CmdArgs));
+    }
+  }
+}
+
 void darwin::Lipo::ConstructJob(Compilation &C, const JobAction &JA,
                                 Job &Dest, const InputInfo &Output, 
                                 const InputInfoList &Inputs, 
diff --git a/lib/Driver/Tools.h b/lib/Driver/Tools.h
index 672096e..2b06a3d 100644
--- a/lib/Driver/Tools.h
+++ b/lib/Driver/Tools.h
@@ -17,6 +17,10 @@
 
 namespace clang {
 namespace driver {
+namespace toolchains {
+  class Darwin_X86;
+}
+
 namespace tools {
 
   class VISIBILITY_HIDDEN Clang : public Tool {
@@ -127,6 +131,34 @@
                               const char *LinkingOutput) const;
   };
 
+  class VISIBILITY_HIDDEN Link : public Tool  {
+    void AddDarwinArch(const ArgList &Args, ArgStringList &CmdArgs) const;
+    void AddDarwinSubArch(const ArgList &Args, ArgStringList &CmdArgs) const;
+    void AddLinkArgs(const ArgList &Args, ArgStringList &CmdArgs) const;
+
+    /// The default macosx-version-min.
+    const char *MacosxVersionMin;
+
+    const toolchains::Darwin_X86 &getDarwinToolChain() const;
+
+  public:
+    Link(const ToolChain &TC,
+         const char *_MacosxVersionMin) 
+      : Tool("darwin::Link", TC), MacosxVersionMin(_MacosxVersionMin) {
+    }
+
+    virtual bool acceptsPipedInput() const { return false; }
+    virtual bool canPipeOutput() const { return false; }
+    virtual bool hasIntegratedCPP() const { return false; }
+
+    virtual void ConstructJob(Compilation &C, const JobAction &JA,
+                              Job &Dest,
+                              const InputInfo &Output, 
+                              const InputInfoList &Inputs, 
+                              const ArgList &TCArgs, 
+                              const char *LinkingOutput) const;
+  };
+
   class VISIBILITY_HIDDEN Lipo : public Tool  {
   public:
     Lipo(const ToolChain &TC) : Tool("darwin::Lipo", TC) {}