Index: lib/Driver/SanitizerArgs.cpp =================================================================== --- lib/Driver/SanitizerArgs.cpp +++ lib/Driver/SanitizerArgs.cpp @@ -246,14 +246,15 @@ const llvm::opt::Arg *A, bool DiagnoseErrors, unsigned &DiagnosedKinds) { - bool IsLinux = TC.getTriple().getOS() == llvm::Triple::Linux; + bool IsLinuxOrFreeBSD = TC.getTriple().getOS() == llvm::Triple::Linux || + TC.getTriple().getOS() == llvm::Triple::FreeBSD; bool IsX86 = TC.getTriple().getArch() == llvm::Triple::x86; bool IsX86_64 = TC.getTriple().getArch() == llvm::Triple::x86_64; - if (!(IsLinux && IsX86_64)) { + if (!(IsLinuxOrFreeBSD && IsX86_64)) { filterUnsupportedMask(TC, Kinds, Thread | Memory | DataFlow, Args, A, DiagnoseErrors, DiagnosedKinds); } - if (!(IsLinux && (IsX86 || IsX86_64))) { + if (!(IsLinuxOrFreeBSD && (IsX86 || IsX86_64))) { filterUnsupportedMask(TC, Kinds, Function, Args, A, DiagnoseErrors, DiagnosedKinds); } Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -224,8 +224,23 @@ // the link line. We cannot do the same thing because unlike gcov there is a // libprofile_rt.so. We used to use the -l:libprofile_rt.a syntax, but that is // not supported by old linkers. - std::string ProfileRT = - std::string(TC.getDriver().Dir) + "/../lib/libprofile_rt.a"; + SmallString<128> ProfileRT; + if (Triple.getOS() == llvm::Triple::FreeBSD) { + // The profile runtime is located in the freebsd library directory and has + // name "libclang_rt.profile-.a". + llvm::sys::path::append(ProfileRT, + TC.getDriver().ResourceDir, + "lib", + "freebsd", + Twine("libclang_rt.profile-") + + TC.getArchName() + ".a"); + } else { + llvm::sys::path::append(ProfileRT, + TC.getDriver().Dir, + "..", + "lib", + "libprofile_rt.a"); + } CmdArgs.push_back(Args.MakeArgString(ProfileRT)); } @@ -1794,15 +1809,18 @@ CmdArgs.push_back(Args.MakeArgString(LibProfile)); } -static void addSanitizerRTLinkFlagsLinux( +static void addSanitizerRTLinkFlagsLinuxOrFreeBSD( const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, const StringRef Sanitizer, bool BeforeLibStdCXX, bool ExportSymbols = true) { - // Sanitizer runtime is located in the Linux library directory and + const bool IsFreeBSD = (TC.getTriple().getOS() == llvm::Triple::FreeBSD); + + // Sanitizer runtime is located in the Linux or FreeBSD library directory and // has name "libclang_rt.-.a". SmallString<128> LibSanitizer(TC.getDriver().ResourceDir); llvm::sys::path::append( - LibSanitizer, "lib", "linux", + LibSanitizer, "lib", + IsFreeBSD ? "freebsd" : "linux", (Twine("libclang_rt.") + Sanitizer + "-" + getArchNameForCompilerRTLib(TC) + ".a")); @@ -1820,19 +1838,25 @@ 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 (!IsFreeBSD) { + 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 // all symbols from the binary. if (ExportSymbols) { - if (llvm::sys::fs::exists(LibSanitizer + ".syms")) + if (llvm::sys::fs::exists(LibSanitizer + ".syms")) { CmdArgs.push_back( Args.MakeArgString("--dynamic-list=" + LibSanitizer + ".syms")); - else + } else if (IsFreeBSD) { + if (!Args.hasArg(options::OPT_rdynamic) && + !Args.hasArg(options::OPT_static)) + CmdArgs.push_back("--export-dynamic"); + } else CmdArgs.push_back("-export-dynamic"); } } @@ -1849,7 +1873,7 @@ CmdArgs.insert(CmdArgs.begin(), Args.MakeArgString(LibAsan)); } else { if (!Args.hasArg(options::OPT_shared)) - addSanitizerRTLinkFlagsLinux(TC, Args, CmdArgs, "asan", true); + addSanitizerRTLinkFlagsLinuxOrFreeBSD(TC, Args, CmdArgs, "asan", true); } } @@ -1858,7 +1882,7 @@ static void addTsanRTLinux(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { if (!Args.hasArg(options::OPT_shared)) - addSanitizerRTLinkFlagsLinux(TC, Args, CmdArgs, "tsan", true); + addSanitizerRTLinkFlagsLinuxOrFreeBSD(TC, Args, CmdArgs, "tsan", true); } /// If MemorySanitizer is enabled, add appropriate linker flags (Linux). @@ -1866,7 +1890,7 @@ static void addMsanRTLinux(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { if (!Args.hasArg(options::OPT_shared)) - addSanitizerRTLinkFlagsLinux(TC, Args, CmdArgs, "msan", true); + addSanitizerRTLinkFlagsLinuxOrFreeBSD(TC, Args, CmdArgs, "msan", true); } /// If LeakSanitizer is enabled, add appropriate linker flags (Linux). @@ -1874,7 +1898,7 @@ static void addLsanRTLinux(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { if (!Args.hasArg(options::OPT_shared)) - addSanitizerRTLinkFlagsLinux(TC, Args, CmdArgs, "lsan", true); + addSanitizerRTLinkFlagsLinuxOrFreeBSD(TC, Args, CmdArgs, "lsan", true); } /// If UndefinedBehaviorSanitizer is enabled, add appropriate linker flags @@ -1885,20 +1909,23 @@ // 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) - addSanitizerRTLinkFlagsLinux(TC, Args, CmdArgs, "san", true, false); + addSanitizerRTLinkFlagsLinuxOrFreeBSD(TC, Args, CmdArgs, + "san", true, false); - addSanitizerRTLinkFlagsLinux(TC, Args, CmdArgs, "ubsan", false); + addSanitizerRTLinkFlagsLinuxOrFreeBSD(TC, Args, CmdArgs, "ubsan", false); // Only include the bits of the runtime which need a C++ ABI library if // we're linking in C++ mode. if (IsCXX) - addSanitizerRTLinkFlagsLinux(TC, Args, CmdArgs, "ubsan_cxx", false); + addSanitizerRTLinkFlagsLinuxOrFreeBSD(TC, Args, CmdArgs, + "ubsan_cxx", false); } static void addDfsanRTLinux(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { if (!Args.hasArg(options::OPT_shared)) - addSanitizerRTLinkFlagsLinux(TC, Args, CmdArgs, "dfsan", true); + addSanitizerRTLinkFlagsLinuxOrFreeBSD(TC, Args, CmdArgs, + "dfsan", true); } static bool shouldUseFramePointerForTarget(const ArgList &Args, @@ -5960,9 +5987,28 @@ const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { - const toolchains::FreeBSD& ToolChain = + const toolchains::FreeBSD &ToolChain = static_cast(getToolChain()); const Driver &D = ToolChain.getDriver(); + const SanitizerArgs &Sanitize = ToolChain.getSanitizerArgs(); + const bool IsPIE = + !Args.hasArg(options::OPT_shared) && + (Args.hasArg(options::OPT_pie) || Sanitize.hasZeroBaseShadow()); + const bool NeedsDefaultLibs = + !Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs); + const bool NeedsCXXSanRt = + !Args.hasArg(options::OPT_shared) && + (Sanitize.needsAsanRt() || + Sanitize.needsTsanRt() || + Sanitize.needsMsanRt() || + Sanitize.needsLsanRt()); + const bool NeedsUbsanRt = Sanitize.needsUbsanRt(); + const bool NeedsSanRt = NeedsCXXSanRt || Sanitize.needsDfsanRt(); + const bool NeedsCXXStdlib = + NeedsDefaultLibs && + (D.CCCIsCXX() || NeedsUbsanRt); + ArgStringList CmdArgs; // Silence warning for "clang -g foo.o -o foo" @@ -5976,7 +6022,7 @@ if (!D.SysRoot.empty()) CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); - if (Args.hasArg(options::OPT_pie)) + if (IsPIE) CmdArgs.push_back("-pie"); if (Args.hasArg(options::OPT_static)) { @@ -6020,13 +6066,12 @@ assert(Output.isNothing() && "Invalid output."); } - if (!Args.hasArg(options::OPT_nostdlib) && - !Args.hasArg(options::OPT_nostartfiles)) { + if (NeedsDefaultLibs) { const char *crt1 = NULL; if (!Args.hasArg(options::OPT_shared)) { if (Args.hasArg(options::OPT_pg)) crt1 = "gcrt1.o"; - else if (Args.hasArg(options::OPT_pie)) + else if (IsPIE) crt1 = "Scrt1.o"; else crt1 = "crt1.o"; @@ -6039,7 +6084,7 @@ const char *crtbegin = NULL; if (Args.hasArg(options::OPT_static)) crtbegin = "crtbeginT.o"; - else if (Args.hasArg(options::OPT_shared) || Args.hasArg(options::OPT_pie)) + else if (Args.hasArg(options::OPT_shared) || IsPIE) crtbegin = "crtbeginS.o"; else crtbegin = "crtbegin.o"; @@ -6064,10 +6109,58 @@ AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs); - if (!Args.hasArg(options::OPT_nostdlib) && - !Args.hasArg(options::OPT_nodefaultlibs)) { + // Call these before we add the C++ ABI library. + // 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. + if (NeedsSanRt || NeedsUbsanRt) { + if (NeedsUbsanRt) { + // Need a copy of sanitizer_common. This could come from another + // sanitizer runtime; if we're not including one, include our own copy. + if (!NeedsSanRt) + addSanitizerRTLinkFlagsLinuxOrFreeBSD(ToolChain, Args, CmdArgs, + "san", false); + addSanitizerRTLinkFlagsLinuxOrFreeBSD(ToolChain, Args, CmdArgs, + "ubsan", false); + } + if (Sanitize.needsAsanRt()) + addSanitizerRTLinkFlagsLinuxOrFreeBSD(ToolChain, Args, CmdArgs, + "asan", false); + if (Sanitize.needsTsanRt()) + addSanitizerRTLinkFlagsLinuxOrFreeBSD(ToolChain, Args, CmdArgs, + "tsan", false); + if (Sanitize.needsMsanRt()) + addSanitizerRTLinkFlagsLinuxOrFreeBSD(ToolChain, Args, CmdArgs, + "msan", false); + if (Sanitize.needsLsanRt()) + addSanitizerRTLinkFlagsLinuxOrFreeBSD(ToolChain, Args, CmdArgs, + "lsan", false); + if (Sanitize.needsDfsanRt()) + addSanitizerRTLinkFlagsLinuxOrFreeBSD(ToolChain, Args, CmdArgs, + "dfsan", false); + + if (NeedsDefaultLibs) + CmdArgs.push_back("-lrt"); + } + + // The profile runtime also needs access to system libraries. + addProfileRT(ToolChain, Args, CmdArgs, ToolChain.getTriple()); + + if (NeedsCXXStdlib) + ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); + + if (NeedsUbsanRt && D.CCCIsCXX()) { + // Only include the bits of the runtime which need a C++ ABI library if + // we're linking in C++ mode. + addSanitizerRTLinkFlagsLinuxOrFreeBSD(ToolChain, Args, CmdArgs, + "ubsan_cxx", false); + } + + if (NeedsDefaultLibs) { if (D.CCCIsCXX()) { - ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); if (Args.hasArg(options::OPT_pg)) CmdArgs.push_back("-lm_p"); else @@ -6089,7 +6182,7 @@ CmdArgs.push_back("--no-as-needed"); } - if (Args.hasArg(options::OPT_pthread)) { + if (Args.hasArg(options::OPT_pthread) || NeedsSanRt || NeedsUbsanRt) { if (Args.hasArg(options::OPT_pg)) CmdArgs.push_back("-lpthread_p"); else @@ -6118,19 +6211,15 @@ } } - if (!Args.hasArg(options::OPT_nostdlib) && - !Args.hasArg(options::OPT_nostartfiles)) { - if (Args.hasArg(options::OPT_shared) || Args.hasArg(options::OPT_pie)) + if (NeedsDefaultLibs) { + if (Args.hasArg(options::OPT_shared) || IsPIE) CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtendS.o"))); else CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtend.o"))); CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtn.o"))); } - addProfileRT(ToolChain, Args, CmdArgs, ToolChain.getTriple()); - - const char *Exec = - Args.MakeArgString(ToolChain.GetProgramPath("ld")); + const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("ld")); C.addCommand(new Command(JA, *this, Exec, CmdArgs)); } Index: test/Driver/freebsd-sanitizers.c =================================================================== --- test/Driver/freebsd-sanitizers.c +++ test/Driver/freebsd-sanitizers.c @@ -0,0 +1,32 @@ + +// RUN: %clang -no-canonical-prefixes -target i386-pc-freebsd \ +// RUN: -fsanitize=address %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-ASAN-I386 %s +// CHECK-ASAN-I386: "-whole-archive" "{{.*}}/lib/freebsd/libclang_rt.asan-i386.a" +// CHECK-ASAN-I386: "--export-dynamic" "-lrt" "-lgcc" + +// RUN: %clang -no-canonical-prefixes -target x86_64-pc-freebsd \ +// RUN: -fsanitize=address %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-ASAN-X86_64 %s +// CHECK-ASAN-X86_64: "-whole-archive" "{{.*}}/lib/freebsd/libclang_rt.asan-x86_64.a" +// CHECK-ASAN-X86_64: "-lrt" "-lgcc" + +// RUN: %clang -no-canonical-prefixes -target i386-pc-freebsd \ +// RUN: -fsanitize=undefined %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-UBSAN-I386 %s +// CHECK-UBSAN-I386: "-whole-archive" "{{.*}}/freebsd/libclang_rt.san-i386.a" "{{.*}}/freebsd/libclang_rt.ubsan-i386.a" +// CHECK-UBSAN-I386: "-lrt" "-lstdc++" + +// RUN: %clang -no-canonical-prefixes -target x86_64-pc-freebsd \ +// RUN: -fsanitize=undefined %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-UBSAN-X86_64 %s +// CHECK-UBSAN-X86_64: "-whole-archive" "{{.*}}/freebsd/libclang_rt.san-x86_64.a" "{{.*}}/freebsd/libclang_rt.ubsan-x86_64.a" +// CHECK-UBSAN-X86_64: "-lrt" "-lstdc++" +// CHECK-UBSAN-X86_64-NOT: libclang_rt.ubsan_cxx + +// RUN: %clangxx -no-canonical-prefixes -target x86_64-pc-freebsd \ +// RUN: -fsanitize=undefined %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-UBSAN-CXX %s +// CHECK-UBSAN-CXX: "-whole-archive" "{{.*}}/freebsd/libclang_rt.san-x86_64.a" "{{.*}}/freebsd/libclang_rt.ubsan-x86_64.a" +// CHECK-UBSAN-CXX: "-lrt" "-lstdc++" +// CHECK-UBSAN-CXX: "-whole-archive" "{{.*}}/freebsd/libclang_rt.ubsan_cxx-x86_64.a"