]> granicus.if.org Git - clang/commitdiff
Major rewrite of linking strategy for sanitizer runtimes on Linux.
authorAlexey Samsonov <vonosmas@gmail.com>
Mon, 15 Sep 2014 19:58:40 +0000 (19:58 +0000)
committerAlexey Samsonov <vonosmas@gmail.com>
Mon, 15 Sep 2014 19:58:40 +0000 (19:58 +0000)
Change 1: we used to add static sanitizer runtimes at the
very beginning of the linker invocation, even before crtbegin.o, which
is gross and not correct in general. Fix this: now addSanitizerRuntimes()
adds all sanitizer-related link flags to the end of the linker invocation
being constructed. It means, that we should call this function in the
correct place, namely, before AddLinkerInputs() to make sure sanitizer
versions of library functions will be preferred.

Change 2: Put system libraries sanitizer libraries depend on at the
end of the linker invocation, where all the rest system libraries are
located. Respect --nodefaultlibs and --nostdlib flags. This is another way
to fix PR15823. Original fix landed in r215940 put "-lpthread" and friends
immediately after static ASan runtime, before the user linker inputs.
This caused significant slowdown in dynamic linker for large binaries
linked against thousands of shared objects. Instead, to mark system
libraries as DT_NEEDED we prepend them with "--no-as-needed" flag,
discarding the "-Wl,--as-needed" flag that could be provided by the user.

Otherwise, this change is a code cleanup. Instead of having a special method
for each sanitizer, we introduce a function collectSanitizerRuntimes() that
analyzes -fsanitize= flags and returns the set of static and shared
libraries that needs to be linked.

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

lib/Driver/Tools.cpp
test/Driver/sanitizer-ld.c

index 408b2a895d1f0c851976ddab81cfaa79f0dd0465..02627645918b9b515dfc5a01bad973b59f7146b0 100644 (file)
@@ -2153,155 +2153,114 @@ static SmallString<128> getSanitizerRTLibName(const ToolChain &TC,
   return LibSanitizer;
 }
 
-static void addSanitizerRTLinkFlags(const ToolChain &TC, const ArgList &Args,
-                                    ArgStringList &CmdArgs,
-                                    StringRef Sanitizer,
-                                    bool ExportSymbols, bool LinkDeps) {
-  SmallString<128> LibSanitizer =
-      getSanitizerRTLibName(TC, Sanitizer, /*Shared*/ false);
-
-  // Sanitizer runtime may need to come before -lstdc++ (or -lc++, libstdc++.a,
-  // etc.) so that the linker picks custom versions of the global 'operator
-  // new' and 'operator delete' symbols. We take the extreme (but simple)
-  // strategy of inserting it at the front of the link command. It also
-  // needs to be forced to end up in the executable, so wrap it in
+static void addSanitizerRuntime(const ToolChain &TC, const ArgList &Args,
+                                ArgStringList &CmdArgs, StringRef Sanitizer,
+                                bool IsShared) {
+  SmallString<128> LibSanitizer = getSanitizerRTLibName(TC, Sanitizer, IsShared);
+  // Static runtimes must be forced into executable, so we wrap them in
   // whole-archive.
-  SmallVector<const char *, 8> LibSanitizerArgs;
-  LibSanitizerArgs.push_back("-whole-archive");
-  LibSanitizerArgs.push_back(Args.MakeArgString(LibSanitizer));
-  LibSanitizerArgs.push_back("-no-whole-archive");
-  if (LinkDeps) {
-    // Link sanitizer dependencies explicitly. These libraries should be added
-    // at the front of the link command, so that they will definitely
-    // participate in link even if user specified -Wl,-as-needed (see PR15823).
-    LibSanitizerArgs.push_back("-lpthread");
-    LibSanitizerArgs.push_back("-lrt");
-    LibSanitizerArgs.push_back("-lm");
-    // There's no libdl on FreeBSD.
-    if (TC.getTriple().getOS() != llvm::Triple::FreeBSD)
-      LibSanitizerArgs.push_back("-ldl");
-  }
-  // If possible, use a dynamic symbols file to export the symbols from the
-  // runtime library. If we can't do so, use -export-dynamic instead to export
-  // all symbols from the binary.
-  if (ExportSymbols) {
-    if (llvm::sys::fs::exists(LibSanitizer + ".syms"))
-      LibSanitizerArgs.push_back(
-          Args.MakeArgString("--dynamic-list=" + LibSanitizer + ".syms"));
-    else
-      LibSanitizerArgs.push_back("-export-dynamic");
+  if (!IsShared)
+    CmdArgs.push_back("-whole-archive");
+  CmdArgs.push_back(Args.MakeArgString(LibSanitizer));
+  if (!IsShared)
+    CmdArgs.push_back("-no-whole-archive");
+}
+
+// Tries to use a file with the list of dynamic symbols that need to be exported
+// from the runtime library. Returns true if the file was found.
+static bool addSanitizerDynamicList(const ToolChain &TC, const ArgList &Args,
+                                    ArgStringList &CmdArgs,
+                                    StringRef Sanitizer) {
+  SmallString<128> LibSanitizer = getSanitizerRTLibName(TC, Sanitizer, false);
+  if (llvm::sys::fs::exists(LibSanitizer + ".syms")) {
+    CmdArgs.push_back(
+        Args.MakeArgString("--dynamic-list=" + LibSanitizer + ".syms"));
+    return true;
   }
+  return false;
+}
 
-  CmdArgs.insert(CmdArgs.begin(), LibSanitizerArgs.begin(),
-                 LibSanitizerArgs.end());
+static void linkSanitizerRuntimeDeps(const ToolChain &TC,
+                                     ArgStringList &CmdArgs) {
+  // Force linking against the system libraries sanitizers depends on
+  // (see PR15823 why this is necessary).
+  CmdArgs.push_back("--no-as-needed");
+  CmdArgs.push_back("-lpthread");
+  CmdArgs.push_back("-lrt");
+  CmdArgs.push_back("-lm");
+  // There's no libdl on FreeBSD.
+  if (TC.getTriple().getOS() != llvm::Triple::FreeBSD)
+    CmdArgs.push_back("-ldl");
 }
 
-/// If AddressSanitizer is enabled, add appropriate linker flags (Linux).
-/// This needs to be called before we add the C run-time (malloc, etc).
-static void addAsanRT(const ToolChain &TC, const ArgList &Args,
-                      ArgStringList &CmdArgs, bool Shared, bool IsCXX) {
-  if (Shared) {
-    // Link dynamic runtime if necessary.
-    SmallString<128> LibSanitizer = getSanitizerRTLibName(TC, "asan", Shared);
-    CmdArgs.insert(CmdArgs.begin(), Args.MakeArgString(LibSanitizer));
+static void
+collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
+                         SmallVectorImpl<StringRef> &SharedRuntimes,
+                         SmallVectorImpl<StringRef> &StaticRuntimes,
+                         SmallVectorImpl<StringRef> &HelperStaticRuntimes) {
+  const SanitizerArgs &SanArgs = TC.getSanitizerArgs();
+  // Collect shared runtimes.
+  if (SanArgs.needsAsanRt() && SanArgs.needsSharedAsanRt()) {
+    SharedRuntimes.push_back("asan");
   }
 
-  // Do not link static runtime to DSOs or if compiling for Android.
+  // Collect static runtimes.
   if (Args.hasArg(options::OPT_shared) ||
-      (TC.getTriple().getEnvironment() == llvm::Triple::Android))
-    return;
-
-  if (Shared) {
-    addSanitizerRTLinkFlags(TC, Args, CmdArgs, "asan-preinit",
-                            /*ExportSymbols*/ false,
-                            /*LinkDeps*/ false);
-  } else {
-    addSanitizerRTLinkFlags(TC, Args, CmdArgs, "asan", /*ExportSymbols*/ true,
-                            /*LinkDeps*/ true);
-    if (IsCXX)
-      addSanitizerRTLinkFlags(TC, Args, CmdArgs, "asan_cxx",
-                              /*ExportSymbols*/ true, /*LinkDeps*/ false);
-  }
-}
-
-/// If ThreadSanitizer is enabled, add appropriate linker flags (Linux).
-/// This needs to be called before we add the C run-time (malloc, etc).
-static void addTsanRT(const ToolChain &TC, const ArgList &Args,
-                      ArgStringList &CmdArgs) {
-  if (!Args.hasArg(options::OPT_shared))
-    addSanitizerRTLinkFlags(TC, Args, CmdArgs, "tsan", /*ExportSymbols*/ true,
-                            /*LinkDeps*/ true);
-}
-
-/// If MemorySanitizer is enabled, add appropriate linker flags (Linux).
-/// This needs to be called before we add the C run-time (malloc, etc).
-static void addMsanRT(const ToolChain &TC, const ArgList &Args,
-                      ArgStringList &CmdArgs) {
-  if (!Args.hasArg(options::OPT_shared))
-    addSanitizerRTLinkFlags(TC, Args, CmdArgs, "msan", /*ExportSymbols*/ true,
-                            /*LinkDeps*/ true);
-}
-
-/// If LeakSanitizer is enabled, add appropriate linker flags (Linux).
-/// This needs to be called before we add the C run-time (malloc, etc).
-static void addLsanRT(const ToolChain &TC, const ArgList &Args,
-                      ArgStringList &CmdArgs) {
-  if (!Args.hasArg(options::OPT_shared))
-    addSanitizerRTLinkFlags(TC, Args, CmdArgs, "lsan", /*ExportSymbols */ true,
-                            /*LinkDeps*/ true);
-}
-
-/// If UndefinedBehaviorSanitizer is enabled, add appropriate linker flags
-/// (Linux).
-static void addUbsanRT(const ToolChain &TC, const ArgList &Args,
-                       ArgStringList &CmdArgs, bool IsCXX,
-                       bool HasOtherSanitizerRt) {
-  // Do not link runtime into shared libraries.
-  if (Args.hasArg(options::OPT_shared))
+      (TC.getTriple().getEnvironment() == llvm::Triple::Android)) {
+    // Don't link static runtimes into DSOs or if compiling for Android.
     return;
-
-  // Need a copy of sanitizer_common. This could come from another sanitizer
-  // runtime; if we're not including one, include our own copy.
-  if (!HasOtherSanitizerRt)
-    addSanitizerRTLinkFlags(TC, Args, CmdArgs, "san", /*ExportSymbols*/ false,
-                            /*LinkDeps*/ false);
-
-  addSanitizerRTLinkFlags(TC, Args, CmdArgs, "ubsan", /*ExportSymbols*/ true,
-                          /*LinkDeps*/ true);
-
-  // Only include the bits of the runtime which need a C++ ABI library if
-  // we're linking in C++ mode.
-  if (IsCXX)
-    addSanitizerRTLinkFlags(TC, Args, CmdArgs, "ubsan_cxx",
-                            /*ExportSymbols*/ true, /*LinkDeps*/ false);
-}
-
-static void addDfsanRT(const ToolChain &TC, const ArgList &Args,
-                       ArgStringList &CmdArgs) {
-  if (!Args.hasArg(options::OPT_shared))
-    addSanitizerRTLinkFlags(TC, Args, CmdArgs, "dfsan", /*ExportSymbols*/ true,
-                            /*LinkDeps*/ true);
-}
-
-// Should be called before we add C++ ABI library.
-static void addSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
+  }
+  if (SanArgs.needsAsanRt()) {
+    if (SanArgs.needsSharedAsanRt()) {
+      HelperStaticRuntimes.push_back("asan-preinit");
+    } else {
+      StaticRuntimes.push_back("asan");
+      if (SanArgs.linkCXXRuntimes())
+        StaticRuntimes.push_back("asan_cxx");
+    }
+  }
+  if (SanArgs.needsDfsanRt())
+    StaticRuntimes.push_back("dfsan");
+  if (SanArgs.needsLsanRt())
+    StaticRuntimes.push_back("lsan");
+  if (SanArgs.needsMsanRt())
+    StaticRuntimes.push_back("msan");
+  if (SanArgs.needsTsanRt())
+    StaticRuntimes.push_back("tsan");
+  // WARNING: UBSan should always go last.
+  if (SanArgs.needsUbsanRt()) {
+    // If UBSan is not combined with another sanitizer, we need to pull in
+    // sanitizer_common explicitly.
+    if (StaticRuntimes.empty())
+      HelperStaticRuntimes.push_back("san");
+    StaticRuntimes.push_back("ubsan");
+    if (SanArgs.linkCXXRuntimes())
+      StaticRuntimes.push_back("ubsan_cxx");
+  }
+}
+
+// Should be called before we add system libraries (C++ ABI, libstdc++/libc++,
+// C runtime, etc). Returns true if sanitizer system deps need to be linked in.
+static bool addSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
                                  ArgStringList &CmdArgs) {
-  const SanitizerArgs &Sanitize = TC.getSanitizerArgs();
-  if (Sanitize.needsUbsanRt())
-    addUbsanRT(TC, Args, CmdArgs, Sanitize.linkCXXRuntimes(),
-               Sanitize.needsAsanRt() || Sanitize.needsTsanRt() ||
-                   Sanitize.needsMsanRt() || Sanitize.needsLsanRt());
-  if (Sanitize.needsAsanRt())
-    addAsanRT(TC, Args, CmdArgs, Sanitize.needsSharedAsanRt(),
-              Sanitize.linkCXXRuntimes());
-  if (Sanitize.needsTsanRt())
-    addTsanRT(TC, Args, CmdArgs);
-  if (Sanitize.needsMsanRt())
-    addMsanRT(TC, Args, CmdArgs);
-  if (Sanitize.needsLsanRt())
-    addLsanRT(TC, Args, CmdArgs);
-  if (Sanitize.needsDfsanRt())
-    addDfsanRT(TC, Args, CmdArgs);
+  SmallVector<StringRef, 4> SharedRuntimes, StaticRuntimes,
+      HelperStaticRuntimes;
+  collectSanitizerRuntimes(TC, Args, SharedRuntimes, StaticRuntimes,
+                           HelperStaticRuntimes);
+  for (auto RT : SharedRuntimes)
+    addSanitizerRuntime(TC, Args, CmdArgs, RT, true);
+  for (auto RT : HelperStaticRuntimes)
+    addSanitizerRuntime(TC, Args, CmdArgs, RT, false);
+  bool AddExportDynamic = false;
+  for (auto RT : StaticRuntimes) {
+    addSanitizerRuntime(TC, Args, CmdArgs, RT, false);
+    AddExportDynamic |= !addSanitizerDynamicList(TC, Args, CmdArgs, RT);
+  }
+  // If there is a static runtime with no dynamic list, force all the symbols
+  // to be dynamic to be sure we export sanitizer interface functions.
+  if (AddExportDynamic)
+    CmdArgs.push_back("-export-dynamic");
+  return !StaticRuntimes.empty();
 }
 
 static bool shouldUseFramePointerForTarget(const ArgList &Args,
@@ -6641,6 +6600,7 @@ void freebsd::Link::ConstructJob(Compilation &C, const JobAction &JA,
   if (D.IsUsingLTO(Args))
     AddGoldPlugin(ToolChain, Args, CmdArgs);
 
+  bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs);
   AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs);
 
   if (!Args.hasArg(options::OPT_nostdlib) &&
@@ -6652,6 +6612,8 @@ void freebsd::Link::ConstructJob(Compilation &C, const JobAction &JA,
       else
         CmdArgs.push_back("-lm");
     }
+    if (NeedsSanitizerDeps)
+      linkSanitizerRuntimeDeps(ToolChain, CmdArgs);
     // FIXME: For some reason GCC passes -lgcc and -lgcc_s before adding
     // the default system libraries. Just mimic this for now.
     if (Args.hasArg(options::OPT_pg))
@@ -6706,8 +6668,6 @@ void freebsd::Link::ConstructJob(Compilation &C, const JobAction &JA,
     CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtn.o")));
   }
 
-  addSanitizerRuntimes(getToolChain(), Args, CmdArgs);
-
   addProfileRT(ToolChain, Args, CmdArgs);
 
   const char *Exec =
@@ -7465,9 +7425,8 @@ void gnutools::Link::ConstructJob(Compilation &C, const JobAction &JA,
   if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle))
     CmdArgs.push_back("--no-demangle");
 
+  bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs);
   AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs);
-
-  addSanitizerRuntimes(getToolChain(), Args, CmdArgs);
   // The profile runtime also needs access to system libraries.
   addProfileRT(getToolChain(), Args, CmdArgs);
 
@@ -7489,6 +7448,9 @@ void gnutools::Link::ConstructJob(Compilation &C, const JobAction &JA,
       if (Args.hasArg(options::OPT_static))
         CmdArgs.push_back("--start-group");
 
+      if (NeedsSanitizerDeps)
+        linkSanitizerRuntimeDeps(ToolChain, CmdArgs);
+
       LibOpenMP UsedOpenMPLib = LibUnknown;
       if (Args.hasArg(options::OPT_fopenmp)) {
         UsedOpenMPLib = LibGOMP;
index 247a925c6a18bc16896773f07a6fe275d68189cd..dc8e93e50f26117fa389eb053281dc3a31de974b 100644 (file)
@@ -9,11 +9,12 @@
 // CHECK-ASAN-LINUX: "{{(.*[^-.0-9A-Z_a-z])?}}ld{{(.exe)?}}"
 // CHECK-ASAN-LINUX-NOT: "-lc"
 // CHECK-ASAN-LINUX: libclang_rt.asan-i386.a"
+// CHECK-ASAN-LINUX-NOT: "-export-dynamic"
+// CHECK-ASAN-LINUX: "--dynamic-list={{.*}}libclang_rt.asan-i386.a.syms"
+// CHECK-ASAN-LINUX-NOT: "-export-dynamic"
 // CHECK-ASAN-LINUX: "-lpthread"
 // CHECK-ASAN-LINUX: "-lrt"
 // CHECK-ASAN-LINUX: "-ldl"
-// CHECK-ASAN-LINUX-NOT: "-export-dynamic"
-// CHECK-ASAN-LINUX: "--dynamic-list={{.*}}libclang_rt.asan-i386.a.syms"
 
 // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
 // RUN:     -target i386-unknown-linux -fsanitize=address -shared-libasan \
@@ -24,8 +25,8 @@
 // CHECK-SHARED-ASAN-LINUX: "{{(.*[^-.0-9A-Z_a-z])?}}ld{{(.exe)?}}"
 // CHECK-SHARED-ASAN-LINUX-NOT: "-lc"
 // CHECK-SHARED-ASAN-LINUX-NOT: libclang_rt.asan-i386.a"
-// CHECK-SHARED-ASAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.asan-preinit-i386.a" "-no-whole-archive"
 // CHECK-SHARED-ASAN-LINUX: libclang_rt.asan-i386.so"
+// CHECK-SHARED-ASAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.asan-preinit-i386.a" "-no-whole-archive"
 // CHECK-SHARED-ASAN-LINUX-NOT: "-lpthread"
 // CHECK-SHARED-ASAN-LINUX-NOT: "-lrt"
 // CHECK-SHARED-ASAN-LINUX-NOT: "-ldl"
 // CHECK-ASAN-FREEBSD-NOT: libclang_rt.asan_cxx
 // CHECK-ASAN-FREEBSD: freebsd{{/|\\+}}libclang_rt.asan-i386.a"
 // CHECK-ASAN-FREEBSD-NOT: libclang_rt.asan_cxx
+// CHECK-ASAN-FREEBSD-NOT: "--dynamic-list"
+// CHECK-ASAN-FREEBSD: "-export-dynamic"
 // CHECK-ASAN-FREEBSD: "-lpthread"
 // CHECK-ASAN-FREEBSD: "-lrt"
-// CHECK-ASAN-FREEBSD: "-export-dynamic"
-// CHECK-ASAN-FREEBSD-NOT: "--dynamic-list"
 
 // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
 // RUN:     -target i386-unknown-freebsd -fsanitize=address \
 //
 // CHECK-ASAN-LINUX-CXX: "{{(.*[^-.0-9A-Z_a-z])?}}ld{{(.exe)?}}"
 // CHECK-ASAN-LINUX-CXX-NOT: "-lc"
-// CHECK-ASAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.asan_cxx-i386.a" "-no-whole-archive"
 // CHECK-ASAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.asan-i386.a" "-no-whole-archive"
+// CHECK-ASAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.asan_cxx-i386.a" "-no-whole-archive"
+// CHECK-ASAN-LINUX-CXX-NOT: "--dynamic-list"
+// CHECK-ASAN-LINUX-CXX: "-export-dynamic"
+// CHECK-ASAN-LINUX-CXX: stdc++
 // CHECK-ASAN-LINUX-CXX: "-lpthread"
 // CHECK-ASAN-LINUX-CXX: "-lrt"
 // CHECK-ASAN-LINUX-CXX: "-ldl"
-// CHECK-ASAN-LINUX-CXX: "-export-dynamic"
-// CHECK-ASAN-LINUX-CXX-NOT: "--dynamic-list"
-// CHECK-ASAN-LINUX-CXX: stdc++
 
 // RUN: %clang -no-canonical-prefixes %s -### -o /dev/null -fsanitize=address \
 // RUN:     -target i386-unknown-linux --sysroot=%S/Inputs/basic_linux_tree \
 //
 // CHECK-ASAN-ANDROID: "{{(.*[^.0-9A-Z_a-z])?}}ld{{(.exe)?}}"
 // CHECK-ASAN-ANDROID-NOT: "-lc"
-// CHECK-ASAN-ANDROID: libclang_rt.asan-arm-android.so"
-// CHECK-ASAN-ANDROID-NOT: "-lpthread"
 // CHECK-ASAN-ANDROID: "-pie"
 // CHECK-ASAN-ANDROID-NOT: "-lpthread"
+// CHECK-ASAN-ANDROID: libclang_rt.asan-arm-android.so"
+// CHECK-ASAN-ANDROID-NOT: "-lpthread"
 //
 // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \
 // RUN:     -target arm-linux-androideabi -fsanitize=address \
 // CHECK-TSAN-LINUX-CXX: "{{(.*[^-.0-9A-Z_a-z])?}}ld{{(.exe)?}}"
 // CHECK-TSAN-LINUX-CXX-NOT: stdc++
 // CHECK-TSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.tsan-x86_64.a" "-no-whole-archive"
-// CHECK-TSAN-LINUX-CXX: "-lpthread"
-// CHECK-TSAN-LINUX-CXX: "-lrt"
-// CHECK-TSAN-LINUX-CXX: "-ldl"
 // CHECK-TSAN-LINUX-CXX-NOT: "-export-dynamic"
 // CHECK-TSAN-LINUX-CXX: "--dynamic-list={{.*}}libclang_rt.tsan-x86_64.a.syms"
+// CHECK-TSAN-LINUX-CXX-NOT: "-export-dynamic"
 // CHECK-TSAN-LINUX-CXX: stdc++
+// CHECK-TSAN-LINUX-CXX: "-lpthread"
+// CHECK-TSAN-LINUX-CXX: "-lrt"
+// CHECK-TSAN-LINUX-CXX: "-ldl"
 
 // RUN: %clangxx -no-canonical-prefixes %s -### -o %t.o 2>&1 \
 // RUN:     -target x86_64-unknown-linux -lstdc++ -fsanitize=memory \
 // CHECK-MSAN-LINUX-CXX: "{{(.*[^-.0-9A-Z_a-z])?}}ld{{(.exe)?}}"
 // CHECK-MSAN-LINUX-CXX-NOT: stdc++
 // CHECK-MSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.msan-x86_64.a" "-no-whole-archive"
-// CHECK-MSAN-LINUX-CXX: "-lpthread"
-// CHECK-MSAN-LINUX-CXX: "-lrt"
-// CHECK-MSAN-LINUX-CXX: "-ldl"
 // CHECK-MSAN-LINUX-CXX-NOT: "-export-dynamic"
 // CHECK-MSAN-LINUX-CXX: "--dynamic-list={{.*}}libclang_rt.msan-x86_64.a.syms"
+// CHECK-MSAN-LINUX-CXX-NOT: "-export-dynamic"
 // CHECK-MSAN-LINUX-CXX: stdc++
+// CHECK-MSAN-LINUX-CXX: "-lpthread"
+// CHECK-MSAN-LINUX-CXX: "-lrt"
+// CHECK-MSAN-LINUX-CXX: "-ldl"
 
 // RUN: %clang -fsanitize=undefined %s -### -o %t.o 2>&1 \
 // RUN:     -target i386-unknown-linux \
 // CHECK-UBSAN-LINUX: "{{.*}}ld{{(.exe)?}}"
 // CHECK-UBSAN-LINUX-NOT: libclang_rt.asan
 // CHECK-UBSAN-LINUX-NOT: libclang_rt.ubsan_cxx
-// CHECK-UBSAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.ubsan-i386.a" "-no-whole-archive"
-// CHECK-UBSAN-LINUX-NOT: libclang_rt.asan
-// CHECK-UBSAN-LINUX-NOT: libclang_rt.ubsan_cxx
-// CHECK-UBSAN-LINUX: "-lpthread"
+// CHECK-UBSAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.san-i386.a" "-no-whole-archive"
 // CHECK-UBSAN-LINUX-NOT: libclang_rt.asan
 // CHECK-UBSAN-LINUX-NOT: libclang_rt.ubsan_cxx
-// CHECK-UBSAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.san-i386.a" "-no-whole-archive"
+// CHECK-UBSAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.ubsan-i386.a" "-no-whole-archive"
 // CHECK-UBSAN-LINUX-NOT: libclang_rt.asan
 // CHECK-UBSAN-LINUX-NOT: libclang_rt.ubsan_cxx
 // CHECK-UBSAN-LINUX-NOT: "-lstdc++"
+// CHECK-UBSAN-LINUX: "-lpthread"
 
 // RUN: %clang -fsanitize=undefined -fsanitize-link-c++-runtime %s -### -o %t.o 2>&1 \
 // RUN:     -target i386-unknown-linux \
 // RUN:   | FileCheck --check-prefix=CHECK-UBSAN-LINUX-CXX %s
 // CHECK-UBSAN-LINUX-CXX: "{{.*}}ld{{(.exe)?}}"
 // CHECK-UBSAN-LINUX-CXX-NOT: libclang_rt.asan
-// CHECK-UBSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.ubsan_cxx-i386.a" "-no-whole-archive"
-// CHECK-UBSAN-LINUX-CXX: "--dynamic-list={{.*}}libclang_rt.ubsan_cxx-i386.a.syms"
+// CHECK-UBSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.san-i386.a" "-no-whole-archive"
 // CHECK-UBSAN-LINUX-CXX-NOT: libclang_rt.asan
 // CHECK-UBSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.ubsan-i386.a" "-no-whole-archive"
-// CHECK-UBSAN-LINUX-CXX: "-lpthread"
+// CHECK-UBSAN-LINUX-CXX-NOT: libclang_rt.asan
 // CHECK-UBSAN-LINUX-CXX: "--dynamic-list={{.*}}libclang_rt.ubsan-i386.a.syms"
 // CHECK-UBSAN-LINUX-CXX-NOT: libclang_rt.asan
-// CHECK-UBSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.san-i386.a" "-no-whole-archive"
+// CHECK-UBSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.ubsan_cxx-i386.a" "-no-whole-archive"
+// CHECK-UBSAN-LINUX-CXX-NOT: libclang_rt.asan
+// CHECK-UBSAN-LINUX-CXX: "--dynamic-list={{.*}}libclang_rt.ubsan_cxx-i386.a.syms"
 // CHECK-UBSAN-LINUX-CXX-NOT: libclang_rt.asan
 // CHECK-UBSAN-LINUX-CXX: "-lstdc++"
+// CHECK-UBSAN-LINUX-CXX-NOT: libclang_rt.asan
+// CHECK-UBSAN-LINUX-CXX: "-lpthread"
 
 // RUN: %clang -fsanitize=address,undefined %s -### -o %t.o 2>&1 \
 // RUN:     -target i386-unknown-linux \
 // CHECK-ASAN-UBSAN-LINUX-NOT: libclang_rt.san
 // CHECK-ASAN-UBSAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.ubsan-i386.a" "-no-whole-archive"
 // CHECK-ASAN-UBSAN-LINUX-NOT: libclang_rt.ubsan_cxx
-// CHECK-ASAN-UBSAN-LINUX: "-lpthread"
 // CHECK-ASAN-UBSAN-LINUX-NOT: "-lstdc++"
+// CHECK-ASAN-UBSAN-LINUX: "-lpthread"
 
 // RUN: %clangxx -fsanitize=address,undefined %s -### -o %t.o 2>&1 \
 // RUN:     -target i386-unknown-linux \
 // CHECK-ASAN-UBSAN-LINUX-CXX-NOT: libclang_rt.san
 // CHECK-ASAN-UBSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.asan-i386.a" "-no-whole-archive"
 // CHECK-ASAN-UBSAN-LINUX-CXX-NOT: libclang_rt.san
-// CHECK-ASAN-UBSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.ubsan_cxx-i386.a" "-no-whole-archive"
 // CHECK-ASAN-UBSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.ubsan-i386.a" "-no-whole-archive"
-// CHECK-ASAN-UBSAN-LINUX-CXX: "-lpthread"
+// CHECK-ASAN-UBSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.ubsan_cxx-i386.a" "-no-whole-archive"
 // CHECK-ASAN-UBSAN-LINUX-CXX: "-lstdc++"
+// CHECK-ASAN-UBSAN-LINUX-CXX: "-lpthread"
 
 // RUN: %clang -fsanitize=undefined %s -### -o %t.o 2>&1 \
 // RUN:     -target i386-unknown-linux \
 // CHECK-LSAN-UBSAN-LINUX-NOT: libclang_rt.san
 // CHECK-LSAN-UBSAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.ubsan-x86_64.a" "-no-whole-archive"
 // CHECK-LSAN-UBSAN-LINUX-NOT: libclang_rt.ubsan_cxx
-// CHECK-LSAN-UBSAN-LINUX: "-lpthread"
 // CHECK-LSAN-UBSAN-LINUX-NOT: "-lstdc++"
+// CHECK-LSAN-UBSAN-LINUX: "-lpthread"
 
 // RUN: %clang -fsanitize=leak,address %s -### -o %t.o 2>&1 \
 // RUN:     -target x86_64-unknown-linux \