Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -359,6 +359,7 @@ HelpText<"Enable Apple gcc-compatible #pragma pack handling">; def faddress_sanitizer : Flag<["-"], "faddress-sanitizer">, Group; def fno_address_sanitizer : Flag<["-"], "fno-address-sanitizer">, Group; +def shared_libasan : Flag<["-"], "shared-libasan">; def fthread_sanitizer : Flag<["-"], "fthread-sanitizer">, Group; def fno_thread_sanitizer : Flag<["-"], "fno-thread-sanitizer">, Group; def fasm : Flag<["-"], "fasm">, Group; Index: include/clang/Driver/SanitizerArgs.h =================================================================== --- include/clang/Driver/SanitizerArgs.h +++ include/clang/Driver/SanitizerArgs.h @@ -51,6 +51,7 @@ bool MsanTrackOrigins; bool AsanZeroBaseShadow; bool UbsanTrapOnError; + bool AsanSharedRuntime; public: SanitizerArgs(); @@ -58,6 +59,7 @@ SanitizerArgs(const ToolChain &TC, const llvm::opt::ArgList &Args); bool needsAsanRt() const { return Kind & NeedsAsanRt; } + bool needsSharedAsanRt() const { return AsanSharedRuntime; } bool needsTsanRt() const { return Kind & NeedsTsanRt; } bool needsMsanRt() const { return Kind & NeedsMsanRt; } bool needsLeakDetection() const { return Kind & NeedsLeakDetection; } Index: lib/Driver/SanitizerArgs.cpp =================================================================== --- lib/Driver/SanitizerArgs.cpp +++ lib/Driver/SanitizerArgs.cpp @@ -26,6 +26,7 @@ MsanTrackOrigins = false; AsanZeroBaseShadow = false; UbsanTrapOnError = false; + AsanSharedRuntime = false; } SanitizerArgs::SanitizerArgs() { @@ -168,9 +169,12 @@ Args.hasFlag(options::OPT_fsanitize_memory_track_origins, options::OPT_fno_sanitize_memory_track_origins, /* Default */false); - if (NeedsAsan) + + if (NeedsAsan) { + AsanSharedRuntime = Args.hasArg(options::OPT_shared_libasan); AsanZeroBaseShadow = (TC.getTriple().getEnvironment() == llvm::Triple::Android); + } } void SanitizerArgs::addArgs(const llvm::opt::ArgList &Args, Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -1809,31 +1809,43 @@ static void addSanitizerRTLinkFlags( const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, const StringRef Sanitizer, bool BeforeLibStdCXX, - bool ExportSymbols = true) { + bool ExportSymbols = true, + bool NeedsSharedRT = false) { + bool Executable = !Args.hasArg(options::OPT_shared); + // Sanitizer runtime has name "libclang_rt.-.a". SmallString<128> LibSanitizer = getCompilerRTLibDir(TC); llvm::sys::path::append(LibSanitizer, - (Twine("libclang_rt.") + Sanitizer + "-" + - getArchNameForCompilerRTLib(TC) + ".a")); + Twine("libclang_rt.") + Sanitizer + "-" + + getArchNameForCompilerRTLib(TC) + + (NeedsSharedRT ? "-dynamic.so" : ".a")); // 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 - // whole-archive. - SmallVector LibSanitizerArgs; - LibSanitizerArgs.push_back("-whole-archive"); - LibSanitizerArgs.push_back(Args.MakeArgString(LibSanitizer)); - LibSanitizerArgs.push_back("-no-whole-archive"); + // strategy of inserting it at the front of the link command. + + SmallVector LibSanitizerArgs; + + if (Executable && !NeedsSharedRT) { + // Force static runtime to end up in the executable + // by wrapping it in whole-archive. + LibSanitizerArgs.push_back("-whole-archive"); + LibSanitizerArgs.push_back(Args.MakeArgString(LibSanitizer)); + LibSanitizerArgs.push_back("-no-whole-archive"); + } else { + LibSanitizerArgs.push_back(Args.MakeArgString(LibSanitizer)); + } CmdArgs.insert(BeforeLibStdCXX ? CmdArgs.begin() : CmdArgs.end(), LibSanitizerArgs.begin(), LibSanitizerArgs.end()); - CmdArgs.push_back("-lpthread"); - CmdArgs.push_back("-lrt"); - CmdArgs.push_back("-ldl"); - CmdArgs.push_back("-lm"); + if (Executable && !NeedsSharedRT) { + CmdArgs.push_back("-lpthread"); + CmdArgs.push_back("-lrt"); + CmdArgs.push_back("-ldl"); + CmdArgs.push_back("-lm"); + } // 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 @@ -1850,7 +1862,7 @@ /// 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) { + ArgStringList &CmdArgs, bool NeedsSharedRT) { if (TC.getTriple().getEnvironment() == llvm::Triple::Android) { SmallString<128> LibAsan = getCompilerRTLibDir(TC); llvm::sys::path::append(LibAsan, @@ -1858,8 +1870,33 @@ getArchNameForCompilerRTLib(TC) + "-android.so")); CmdArgs.insert(CmdArgs.begin(), Args.MakeArgString(LibAsan)); } else { - if (!Args.hasArg(options::OPT_shared)) - addSanitizerRTLinkFlags(TC, Args, CmdArgs, "asan", true); + bool Executable = !Args.hasArg(options::OPT_shared); + + // Do not link static runtime into shared libraries + if (Executable || NeedsSharedRT) + addSanitizerRTLinkFlags(TC, Args, CmdArgs, "asan", + /*BeforeLibStdCXX*/ true, + /*ExportSymbols*/ !NeedsSharedRT, + /*NeedsSharedRT*/ NeedsSharedRT); + + if (Executable) { + SmallVector PreinitArgs; + + SmallString<128> LibAsanPreinit = getCompilerRTLibDir(TC); + llvm::sys::path::append(LibAsanPreinit, + Twine("libclang_rt.asan-" + + getArchNameForCompilerRTLib(TC) + + "-preinit.a")); + + // Force initialization code to end up in the executable + // by wrapping it in whole-archive. + PreinitArgs.push_back("-whole-archive"); + PreinitArgs.push_back(Args.MakeArgString(LibAsanPreinit)); + PreinitArgs.push_back("-no-whole-archive"); + + CmdArgs.insert(CmdArgs.begin(), + PreinitArgs.begin(), PreinitArgs.end()); + } } } @@ -1921,7 +1958,7 @@ Sanitize.needsAsanRt() || Sanitize.needsTsanRt() || Sanitize.needsMsanRt() || Sanitize.needsLsanRt()); if (Sanitize.needsAsanRt()) - addAsanRT(TC, Args, CmdArgs); + addAsanRT(TC, Args, CmdArgs, Sanitize.needsSharedAsanRt()); if (Sanitize.needsTsanRt()) addTsanRT(TC, Args, CmdArgs); if (Sanitize.needsMsanRt()) Index: test/Driver/sanitizer-ld.c =================================================================== --- test/Driver/sanitizer-ld.c +++ test/Driver/sanitizer-ld.c @@ -15,6 +15,40 @@ // 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 \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree \ +// RUN: | FileCheck --check-prefix=CHECK-SHARED-ASAN-LINUX %s +// +// 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-i386-preinit.a" "-no-whole-archive" +// CHECK-SHARED-ASAN-LINUX: libclang_rt.asan-i386-dynamic.so" +// CHECK-SHARED-ASAN-LINUX-NOT: "-lpthread" +// CHECK-SHARED-ASAN-LINUX-NOT: "-lrt" +// CHECK-SHARED-ASAN-LINUX-NOT: "-ldl" +// CHECK-SHARED-ASAN-LINUX-NOT: "-export-dynamic" +// CHECK-SHARED-ASAN-LINUX-NOT: "--dynamic-list" + +// RUN: %clang -no-canonical-prefixes %s -### -o %t.so -shared 2>&1 \ +// RUN: -target i386-unknown-linux -fsanitize=address -shared-libasan \ +// RUN: -resource-dir=%S/Inputs/resource_dir \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree \ +// RUN: | FileCheck --check-prefix=CHECK-DSO-SHARED-ASAN-LINUX %s +// +// CHECK-DSO-SHARED-ASAN-LINUX: "{{(.*[^-.0-9A-Z_a-z])?}}ld{{(.exe)?}}" +// CHECK-DSO-SHARED-ASAN-LINUX-NOT: "-lc" +// CHECK-DSO-SHARED-ASAN-LINUX-NOT: libclang_rt.asan-i386.a" +// CHECK-DSO-SHARED-ASAN-LINUX-NOT: "libclang_rt.asan-i386-preinit.a" +// CHECK-DSO-SHARED-ASAN-LINUX: libclang_rt.asan-i386-dynamic.so" +// CHECK-DSO-SHARED-ASAN-LINUX-NOT: "-lpthread" +// CHECK-DSO-SHARED-ASAN-LINUX-NOT: "-lrt" +// CHECK-DSO-SHARED-ASAN-LINUX-NOT: "-ldl" +// CHECK-DSO-SHARED-ASAN-LINUX-NOT: "-export-dynamic" +// CHECK-DSO-SHARED-ASAN-LINUX-NOT: "--dynamic-list" + // RUN: %clangxx -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: -target i386-unknown-linux -fsanitize=address \ // RUN: -resource-dir=%S/Inputs/empty_resource_dir \ @@ -58,7 +92,6 @@ // CHECK-ASAN-ARMv7: "{{(.*[^.0-9A-Z_a-z])?}}ld{{(.exe)?}}" // CHECK-ASAN-ARMv7-NOT: "-lc" // CHECK-ASAN-ARMv7: libclang_rt.asan-arm.a" -// // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: -target arm-linux-androideabi -fsanitize=address \