Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -357,6 +357,7 @@ HelpText<"Use Apple's kernel extensions ABI">; def fapple_pragma_pack : Flag<["-"], "fapple-pragma-pack">, Group, Flags<[CC1Option]>, HelpText<"Enable Apple gcc-compatible #pragma pack handling">; +def shared_libasan : Flag<["-"], "shared-libasan">; def fasm : Flag<["-"], "fasm">, Group; def fasm_blocks : Flag<["-"], "fasm-blocks">, Group, Flags<[CC1Option]>; Index: include/clang/Driver/SanitizerArgs.h =================================================================== --- include/clang/Driver/SanitizerArgs.h +++ include/clang/Driver/SanitizerArgs.h @@ -51,6 +51,7 @@ int 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 @@ -27,6 +27,7 @@ MsanTrackOrigins = 0; AsanZeroBaseShadow = false; UbsanTrapOnError = false; + AsanSharedRuntime = false; } SanitizerArgs::SanitizerArgs() { @@ -168,9 +169,12 @@ } } - 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 @@ -1814,44 +1814,76 @@ CmdArgs.push_back(Args.MakeArgString(LibProfile)); } -static void addSanitizerRTLinkFlags( - const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, - const StringRef Sanitizer, bool BeforeLibStdCXX, - bool ExportSymbols = true) { +static const char *getSanitizerRTLibName( + const ToolChain &TC, const StringRef Sanitizer, const ArgList &Args, + bool NeedsSharedRT = false) { // 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 + + (NeedsSharedRT ? "-dynamic-" : "-") + + getArchNameForCompilerRTLib(TC) + + (NeedsSharedRT ? ".so" : ".a")); + return Args.MakeArgString(LibSanitizer); +} + +static void addSanitizerRTSharedLib( + const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, + const StringRef Sanitizer, bool BeforeLibStdCXX) { + const char *LibSanitizer = getSanitizerRTLibName(TC, Sanitizer, Args, + /*NeedsSharedRT*/ true); // 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. + // strategy of inserting it at the front of the link command. + + CmdArgs.insert(BeforeLibStdCXX ? CmdArgs.begin() : CmdArgs.end(), + LibSanitizer); +} + +static void addSanitizerRTStaticLib( + const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, + const StringRef Sanitizer, bool BeforeLibStdCXX) { + const char *LibSanitizer = getSanitizerRTLibName(TC, Sanitizer, Args, + /*NeedsSharedRT*/ false); + + // Static runtime 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(LibSanitizer); LibSanitizerArgs.push_back("-no-whole-archive"); CmdArgs.insert(BeforeLibStdCXX ? CmdArgs.begin() : CmdArgs.end(), LibSanitizerArgs.begin(), LibSanitizerArgs.end()); +} - 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"); +static void addSanitizerRTLinkFlags( + const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, + const StringRef Sanitizer, bool BeforeLibStdCXX, + bool ExportSymbols = true, bool NeedsSharedRT = false) { + if (NeedsSharedRT) + addSanitizerRTSharedLib(TC, Args, CmdArgs, Sanitizer, BeforeLibStdCXX); + else { + addSanitizerRTStaticLib(TC, Args, CmdArgs, Sanitizer, BeforeLibStdCXX); + + 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 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")) + const char *LibSanitizer = getSanitizerRTLibName(TC, Sanitizer, Args); + if (llvm::sys::fs::exists(StringRef(LibSanitizer) + ".syms")) CmdArgs.push_back( - Args.MakeArgString("--dynamic-list=" + LibSanitizer + ".syms")); + Args.MakeArgString("--dynamic-list=" + StringRef(LibSanitizer) + ".syms")); else CmdArgs.push_back("-export-dynamic"); } @@ -1860,7 +1892,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, @@ -1868,8 +1900,26 @@ 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 DSO = Args.hasArg(options::OPT_shared); + bool BeforeLibStdCXX = true; + + // Don't link static runtime to shared objects + + bool LinkMainRT = !DSO || NeedsSharedRT; + + if (LinkMainRT) { + bool ExportSymbols = !NeedsSharedRT; + addSanitizerRTLinkFlags(TC, Args, CmdArgs, "asan", + BeforeLibStdCXX, ExportSymbols, NeedsSharedRT); + } + + // Likewise + + bool LinkPreinitRT = !DSO; + + if (LinkPreinitRT) + addSanitizerRTStaticLib(TC, Args, CmdArgs, "asan-preinit", + BeforeLibStdCXX); } } @@ -1931,7 +1981,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 @@ -8,6 +8,7 @@ // // CHECK-ASAN-LINUX: "{{(.*[^-.0-9A-Z_a-z])?}}ld{{(.exe)?}}" // CHECK-ASAN-LINUX-NOT: "-lc" +// CHECK-ASAN-LINUX: libclang_rt.asan-preinit-i386.a" // CHECK-ASAN-LINUX: libclang_rt.asan-i386.a" // CHECK-ASAN-LINUX: "-lpthread" // CHECK-ASAN-LINUX: "-lrt" @@ -16,6 +17,40 @@ // 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-preinit-i386.a" "-no-whole-archive" +// CHECK-SHARED-ASAN-LINUX: libclang_rt.asan-dynamic-i386.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-preinit-i386.a" +// CHECK-DSO-SHARED-ASAN-LINUX: libclang_rt.asan-dynamic-i386.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: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: -target i386-unknown-freebsd -fsanitize=address \ // RUN: -resource-dir=%S/Inputs/resource_dir \ // RUN: --sysroot=%S/Inputs/basic_freebsd_tree \ @@ -23,6 +58,7 @@ // // CHECK-ASAN-FREEBSD: "{{(.*[^-.0-9A-Z_a-z])?}}ld{{(.exe)?}}" // CHECK-ASAN-FREEBSD-NOT: "-lc" +// CHECK-ASAN-FREEBSD: freebsd{{/|\\+}}libclang_rt.asan-preinit-i386.a" // CHECK-ASAN-FREEBSD: freebsd{{/|\\+}}libclang_rt.asan-i386.a" // CHECK-ASAN-FREEBSD: "-lpthread" // CHECK-ASAN-FREEBSD: "-lrt" @@ -46,6 +82,7 @@ // // 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-preinit-i386.a" "-no-whole-archive" // CHECK-ASAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.asan-i386.a" "-no-whole-archive" // CHECK-ASAN-LINUX-CXX: "-lpthread" // CHECK-ASAN-LINUX-CXX: "-lrt" @@ -61,6 +98,7 @@ // // CHECK-ASAN-LINUX-CXX-STATIC: "{{(.*[^-.0-9A-Z_a-z])?}}ld{{(.exe)?}}" // CHECK-ASAN-LINUX-CXX-STATIC-NOT: stdc++ +// CHECK-ASAN-LINUX-CXX-STATIC: "-whole-archive" "{{.*}}libclang_rt.asan-preinit-i386.a" "-no-whole-archive" // CHECK-ASAN-LINUX-CXX-STATIC: "-whole-archive" "{{.*}}libclang_rt.asan-i386.a" "-no-whole-archive" // CHECK-ASAN-LINUX-CXX-STATIC: stdc++ @@ -81,7 +119,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 \ @@ -170,6 +207,7 @@ // RUN: | FileCheck --check-prefix=CHECK-ASAN-UBSAN-LINUX %s // CHECK-ASAN-UBSAN-LINUX: "{{.*}}ld{{(.exe)?}}" // CHECK-ASAN-UBSAN-LINUX-NOT: libclang_rt.san +// CHECK-ASAN-UBSAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.asan-preinit-i386.a" "-no-whole-archive" // CHECK-ASAN-UBSAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.asan-i386.a" "-no-whole-archive" // CHECK-ASAN-UBSAN-LINUX-NOT: libclang_rt.san // CHECK-ASAN-UBSAN-LINUX: "-whole-archive" "{{.*}}libclang_rt.ubsan-i386.a" "-no-whole-archive" @@ -183,6 +221,7 @@ // RUN: | FileCheck --check-prefix=CHECK-ASAN-UBSAN-LINUX-CXX %s // CHECK-ASAN-UBSAN-LINUX-CXX: "{{.*}}ld{{(.exe)?}}" // CHECK-ASAN-UBSAN-LINUX-CXX-NOT: libclang_rt.san +// CHECK-ASAN-UBSAN-LINUX-CXX: "-whole-archive" "{{.*}}libclang_rt.asan-preinit-i386.a" "-no-whole-archive" // 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-i386.a" "-no-whole-archive" @@ -228,5 +267,6 @@ // RUN: | FileCheck --check-prefix=CHECK-LSAN-ASAN-LINUX %s // CHECK-LSAN-ASAN-LINUX: "{{.*}}ld{{(.exe)?}}" // CHECK-LSAN-ASAN-LINUX-NOT: libclang_rt.lsan +// CHECK-LSAN-ASAN-LINUX: libclang_rt.asan-preinit-x86_64 // CHECK-LSAN-ASAN-LINUX: libclang_rt.asan-x86_64 // CHECK-LSAN-ASAN-LINUX-NOT: libclang_rt.lsan