diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -17,6 +17,7 @@ #include "clang/Basic/Sanitizers.h" #include "clang/Basic/XRayInstr.h" #include "llvm/ADT/FloatingPointMode.h" +#include "llvm/ADT/StringMap.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Regex.h" #include "llvm/Target/TargetOptions.h" @@ -160,6 +161,9 @@ std::map DebugPrefixMap; + /// Prefix mapping for file prefixes. + llvm::StringMap FilePrefixMap; + /// The ABI to use for passing floating point arguments. std::string FloatABI; diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp --- a/clang/lib/CodeGen/ModuleBuilder.cpp +++ b/clang/lib/CodeGen/ModuleBuilder.cpp @@ -65,11 +65,18 @@ private: SmallVector DeferredInlineMemberFuncDefs; - static llvm::StringRef ExpandModuleName(llvm::StringRef ModuleName, - const CodeGenOptions &CGO) { - if (ModuleName == "-" && !CGO.MainFileName.empty()) - return CGO.MainFileName; - return ModuleName; + static llvm::SmallString<64> ExpandModuleName(llvm::StringRef ModuleName, + const CodeGenOptions &CGO) { + llvm::SmallString<64> Name = + (ModuleName == "-" && !CGO.MainFileName.empty()) ? CGO.MainFileName + : ModuleName; + for (const auto &Entry : CGO.FilePrefixMap) { + if (llvm::sys::path::replace_path_prefix( + Name, Entry.getKey(), Entry.getValue(), + llvm::sys::path::Style::native, true)) + break; + } + return Name; } public: @@ -80,8 +87,9 @@ CoverageSourceInfo *CoverageInfo = nullptr) : Diags(diags), Ctx(nullptr), HeaderSearchOpts(HSO), PreprocessorOpts(PPO), CodeGenOpts(CGO), HandlingTopLevelDecls(0), - CoverageInfo(CoverageInfo), - M(new llvm::Module(ExpandModuleName(ModuleName, CGO), C)) { + CoverageInfo(CoverageInfo) { + auto ExpandedName = ExpandModuleName(ModuleName, CGO); + M.reset(new llvm::Module(ExpandedName, C)); C.setDiscardValueNames(CGO.DiscardValueNames); } @@ -129,7 +137,8 @@ llvm::Module *StartModule(llvm::StringRef ModuleName, llvm::LLVMContext &C) { assert(!M && "Replacing existing Module?"); - M.reset(new llvm::Module(ExpandModuleName(ModuleName, CodeGenOpts), C)); + auto ExpandedName = ExpandModuleName(ModuleName, CodeGenOpts); + M.reset(new llvm::Module(ExpandedName, C)); Initialize(*Ctx); return M.get(); } diff --git a/clang/lib/CodeGen/SanitizerMetadata.cpp b/clang/lib/CodeGen/SanitizerMetadata.cpp --- a/clang/lib/CodeGen/SanitizerMetadata.cpp +++ b/clang/lib/CodeGen/SanitizerMetadata.cpp @@ -20,6 +20,9 @@ using namespace clang; using namespace CodeGen; +static constexpr char kASanFilePrefixMapMetadata[] = + "llvm.asan.file.prefix.map"; + SanitizerMetadata::SanitizerMetadata(CodeGenModule &CGM) : CGM(CGM) {} static bool isAsanHwasanOrMemTag(const SanitizerSet& SS) { @@ -59,6 +62,30 @@ llvm::NamedMDNode *AsanGlobals = CGM.getModule().getOrInsertNamedMetadata("llvm.asan.globals"); AsanGlobals->addOperand(ThisGlobal); + + // Add metadata to propgate file prefix mappings down to ASan. We can't edit + // the existing metadata passed to llvm.asan.globals because the metadata + // contains StringRefs that we can't edit. These refs point to strings all the + // way back to strings from the SourceManager. + // + // This will be an array of key-value pairs from the prefix mapping stored in + // the codegen options. These can be used to adjust the paths used during the + // ASanGlobalsMetadata analysis. + + // Make sure not to add the prefix metadata more than once. + if (CGM.getModule().getNamedMetadata(kASanFilePrefixMapMetadata)) + return; + + llvm::NamedMDNode *AsanPrefixMapGlobals = + CGM.getModule().getOrInsertNamedMetadata(kASanFilePrefixMapMetadata); + for (const auto &Entry : CGM.getCodeGenOpts().FilePrefixMap) { + llvm::Metadata *PrefixPairMetadata[] = { + llvm::MDString::get(VMContext, Entry.getKey()), + llvm::MDString::get(VMContext, Entry.getValue()), + }; + AsanPrefixMapGlobals->addOperand( + llvm::MDNode::get(VMContext, PrefixPairMetadata)); + } } void SanitizerMetadata::reportGlobalToASan(llvm::GlobalVariable *GV, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -661,6 +661,20 @@ } } +/// Add a CC1 option to specify the file path prefix map. +static void addFilePrefixMapArgMapArg(const Driver &D, const ArgList &Args, + ArgStringList &CmdArgs) { + for (const Arg *A : Args.filtered(options::OPT_ffile_prefix_map_EQ)) { + StringRef Map = A->getValue(); + if (Map.find('=') == StringRef::npos) + D.Diag(diag::err_drv_invalid_argument_to_option) + << Map << A->getOption().getName(); + else + CmdArgs.push_back(Args.MakeArgString("-ffile-prefix-map=" + Map)); + A->claim(); + } +} + /// Vectorize at all optimization levels greater than 1 except for -Oz. /// For -Oz the loop vectorizer is disabled, while the slp vectorizer is /// enabled. @@ -4994,6 +5008,7 @@ addDebugCompDirArg(Args, CmdArgs, D.getVFS()); addDebugPrefixMapArg(D, Args, CmdArgs); + addFilePrefixMapArgMapArg(D, Args, CmdArgs); if (Arg *A = Args.getLastArg(options::OPT_ftemplate_depth_, options::OPT_ftemplate_depth_EQ)) { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -775,6 +775,12 @@ {std::string(Split.first), std::string(Split.second)}); } + for (const auto &Arg : Args.getAllArgValues(OPT_ffile_prefix_map_EQ)) { + auto Split = StringRef(Arg).split('='); + Opts.FilePrefixMap.insert( + {std::string(Split.first), std::string(Split.second)}); + } + if (const Arg *A = Args.getLastArg(OPT_emit_llvm_uselists, OPT_no_emit_llvm_uselists)) Opts.EmitLLVMUseLists = A->getOption().getID() == OPT_emit_llvm_uselists; diff --git a/clang/test/CodeGen/asan-globals-file-prefix-map.cpp b/clang/test/CodeGen/asan-globals-file-prefix-map.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/asan-globals-file-prefix-map.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 %s -fsanitize=address -ffile-prefix-map=%S=/tmp -S -emit-llvm -o - %s -triple x86_64-unknown-linux-gnu| FileCheck %s + +int global; + +// Check that the __asan_gen_* contants use the prefix map. +// CHECK: @global = global { i32, [60 x i8] } zeroinitializer, align 32 +// CHECK: @___asan_gen_{{\.?[0-9]*}} = private constant [38 x i8] c"/tmp/asan-globals-file-prefix-map.cpp\00", align 1 +// CHECK: @___asan_gen_{{\.?[0-9]*}} = private unnamed_addr constant [38 x i8] c"/tmp/asan-globals-file-prefix-map.cpp\00", align 1 + +// Check that the `llvm.asan.file.prefix.map` metadata is emitted. +// CHECK: !llvm.asan.globals = !{[[GLOBAL:\![0-9]+]]} +// CHECK: !llvm.asan.file.prefix.map = !{[[MAPPING:\![0-9]+]]} + +// CHECK: [[GLOBAL]] = !{i32* getelementptr{{.*}} [[MDLOC:![0-9]+]], !"global" +// CHECK: [[MDLOC]] = !{!"[[PATH:.+]]/asan-globals-file-prefix-map.cpp" +// CHECK: [[MAPPING]] = !{!"[[PATH]]", !"/tmp"} diff --git a/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizer.h b/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizer.h --- a/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizer.h +++ b/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizer.h @@ -22,7 +22,7 @@ /// Frontend-provided metadata for source location. struct LocationMetadata { - StringRef Filename; + llvm::SmallString<64> Filename; int LineNo = 0; int ColumnNo = 0; diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -1151,6 +1151,26 @@ mdconst::extract(MDN->getOperand(4)); E.IsBlacklisted |= IsBlacklisted->isOne(); } + + // Go through the file prefix map to replace prefixes in metadata that may + // contain filepaths. Do this once in the globals metadata pass so we don't + // need to run over the map in ASan for each metadata instance. + NamedMDNode *PrefixMap = M.getNamedMetadata("llvm.asan.file.prefix.map"); + if (!PrefixMap) + return; + for (auto &Entry : Entries) { + for (auto *MDN : PrefixMap->operands()) { + assert(MDN->getNumOperands() == 2 && + "Each node in the prefix map should be a pair"); + StringRef Prefix = cast(MDN->getOperand(0))->getString(); + StringRef Replacement = cast(MDN->getOperand(1))->getString(); + if (llvm::sys::path::replace_path_prefix( + Entry.second.SourceLoc.Filename, Prefix, Replacement, + llvm::sys::path::Style::native, true)) { + break; + } + } + } } AnalysisKey ASanGlobalsMetadataAnalysis::Key; diff --git a/llvm/test/Instrumentation/AddressSanitizer/asan_file_prefix_map.ll b/llvm/test/Instrumentation/AddressSanitizer/asan_file_prefix_map.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/asan_file_prefix_map.ll @@ -0,0 +1,21 @@ +; RUN: opt < %s -asan -asan-module -S | FileCheck %s + +target triple = "x86_64-unknown-linux-gnu" + +@global = global i32 0, align 4 +@global2 = global i32 0, align 4 + +; Check that the string used for the source location has it's prefix replaced if llvm.asan.file.prefix.map is present. +; CHECK: @___asan_gen_.{{[0-9]+}} = private unnamed_addr constant [33 x i8] c"/some/other/tmp/asan-globals.cpp\00", align 1 +; CHECK: @___asan_gen_.{{[0-9]+}} = private unnamed_addr constant [36 x i8] c"../some/other/blah/asan-globals.cpp\00", align 1 + +!llvm.asan.globals = !{!0, !1} +!llvm.asan.file.prefix.map = !{!101, !102} +!101 = !{!"/tmp", !"/some/other/tmp"} +!102 = !{!"blah", !"../some/other/blah"} + +!0 = !{i32* @global, !6, !"global", i1 false, i1 false} +!1 = !{i32* @global2, !7, !"global2", i1 false, i1 false} + +!6 = !{!"/tmp/asan-globals.cpp", i32 5, i32 5} +!7 = !{!"blah/asan-globals.cpp", i32 5, i32 5} diff --git a/llvm/test/Instrumentation/AddressSanitizer/asan_no_file_prefix_map.ll b/llvm/test/Instrumentation/AddressSanitizer/asan_no_file_prefix_map.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/asan_no_file_prefix_map.ll @@ -0,0 +1,20 @@ +; RUN: opt < %s -asan -asan-module -S | FileCheck %s + +target triple = "x86_64-unknown-linux-gnu" + +@global = global i32 0, align 4 +@global2 = global i32 0, align 4 + +; Check that the string used for the source location has its original prefix. +; CHECK: @___asan_gen_.{{[0-9]+}} = private unnamed_addr constant [22 x i8] c"/tmp/asan-globals.cpp\00", align 1 +; CHECK: @___asan_gen_.{{[0-9]+}} = private unnamed_addr constant [22 x i8] c"blah/asan-globals.cpp\00", align 1 + +!llvm.asan.globals = !{!0, !1} +!101 = !{!"/tmp", !"/some/other/tmp"} +!102 = !{!"blah", !"../some/other/blah"} + +!0 = !{i32* @global, !6, !"global", i1 false, i1 false} +!1 = !{i32* @global2, !7, !"global2", i1 false, i1 false} + +!6 = !{!"/tmp/asan-globals.cpp", i32 5, i32 5} +!7 = !{!"blah/asan-globals.cpp", i32 5, i32 5}