https://github.com/bjosv created 
https://github.com/llvm/llvm-project/pull/201803

Adds `-fsanitize-compilation-dir=<dir>` to set the compilation directory for 
sanitizer metadata.
Absolute source paths that start with the given directory are made relative to 
it, affecting ASan module names and UBSan diagnostic source locations. This 
enables reproducible builds when using AddressSanitizer or 
UndefinedBehaviorSanitizer from different build directories.

The flag follows the established `-f*-compilation-dir` pattern used by 
`-fdebug-compilation-dir` and `-fcoverage-compilation-dir`. It is also implied 
by `-ffile-compilation-dir=`, which sets the compilation directory for debug 
info, coverage, and now sanitizer metadata in a single flag.

This addresses the issue where ASan and UBSan embed absolute source file paths 
in binary metadata (.rodata), causing non-reproducible builds when compiling 
from different directories.

This flag was suggeted in 
https://github.com/llvm/llvm-project/pull/186908#issuecomment-4071226467 as an 
alternative to adding a `-fsanitize-prefix-map` flag.


A short summary of the changes:
- New driver and CC1 flag -fsanitize-compilation-dir=
- -ffile-compilation-dir= implies -fsanitize-compilation-dir= 
(last-on-command-line wins)
- ASan module name (___asan_gen_module) is made relative when the flag is set
- UBSan source locations (EmitCheckSourceLocation) are made relative when the 
flag is set
- Dependency scanning resets the new option alongside the existing compilation 
dir options
- Documentation updates for AddressSanitizer, UndefinedBehaviorSanitizer, 
UsersManual, and ReleaseNotes
- Driver test, ASan codegen test, and UBSan codegen test added

From f45ee969f011915705771298f185c45e6dd29d42 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= <[email protected]>
Date: Mon, 23 Mar 2026 09:59:57 +0100
Subject: [PATCH] [Clang] Add -fsanitize-compilation-dir= for reproducible
 sanitizer builds
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add -fsanitize-compilation-dir=<dir> to set the compilation directory for
sanitizer metadata. Absolute source paths that start with the given
directory are made relative to it, affecting ASan module names and UBSan
diagnostic source locations. This enables reproducible builds when using
AddressSanitizer or UndefinedBehaviorSanitizer from different build
directories.

The flag follows the established -f*-compilation-dir pattern used by
-fdebug-compilation-dir and -fcoverage-compilation-dir. It is also implied
by -ffile-compilation-dir=, which sets the compilation directory for debug
info, coverage, and now sanitizer metadata in a single flag.

This addresses the issue where ASan and UBSan embed absolute source file
paths in binary metadata (.rodata), causing non-reproducible builds when
compiling from different directories.

Signed-off-by: Björn Svensson <[email protected]>
---
 clang/docs/AddressSanitizer.rst               | 29 +++++++++++++++++
 clang/docs/ReleaseNotes.rst                   |  5 +++
 clang/docs/UndefinedBehaviorSanitizer.rst     |  6 ++++
 clang/docs/UsersManual.rst                    |  8 +++++
 clang/include/clang/Basic/CodeGenOptions.h    |  3 ++
 clang/include/clang/Options/Options.td        |  6 +++-
 clang/lib/CodeGen/BackendUtil.cpp             |  1 +
 clang/lib/CodeGen/CGExpr.cpp                  | 17 ++++++++++
 .../DependencyScanning/ModuleDepCollector.cpp |  2 ++
 clang/lib/Driver/SanitizerArgs.cpp            |  7 ++++
 clang/lib/Frontend/CompilerInvocation.cpp     |  1 +
 .../CodeGen/asan-sanitize-compilation-dir.c   | 20 ++++++++++++
 .../ubsan-sanitize-compilation-dir.cpp        | 27 ++++++++++++++++
 clang/test/Driver/fsanitize-compilation-dir.c | 13 ++++++++
 .../Instrumentation/AddressSanitizer.h        |  2 ++
 .../Instrumentation/AddressSanitizer.cpp      | 32 ++++++++++++++++---
 16 files changed, 174 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/CodeGen/asan-sanitize-compilation-dir.c
 create mode 100644 clang/test/CodeGen/ubsan-sanitize-compilation-dir.cpp
 create mode 100644 clang/test/Driver/fsanitize-compilation-dir.c

diff --git a/clang/docs/AddressSanitizer.rst b/clang/docs/AddressSanitizer.rst
index 80b1cdd95d77a..80144206b2494 100644
--- a/clang/docs/AddressSanitizer.rst
+++ b/clang/docs/AddressSanitizer.rst
@@ -47,6 +47,11 @@ in error messages add ``-fno-omit-frame-pointer``.  To get 
perfect stack traces
 you may need to disable inlining (just use ``-O1``) and tail call elimination
 (``-fno-optimize-sibling-calls``).
 
+To produce reproducible builds, use ``-fsanitize-compilation-dir=<dir>`` (or
+``-ffile-compilation-dir=<dir>`` which also sets it for debug info and
+coverage) to make absolute paths in ASan metadata relative to the given
+directory. See `Remapping source paths`_ for details.
+
 .. code-block:: console
 
     % cat example_UseAfterFree.cc
@@ -408,6 +413,30 @@ run-time performance, which leads to increased binary 
size. Using the
 flag forces all code instrumentation to be outlined, which reduces the size
 of the generated code, but also reduces the run-time performance.
 
+Remapping source paths
+----------------------
+
+AddressSanitizer embeds the source file path in global metadata. For
+reproducible builds, the option ``-fsanitize-compilation-dir=<dir>`` can be
+used to set the compilation directory. Absolute source paths that start with
+``<dir>`` are made relative to it.
+
+The option ``-ffile-compilation-dir=<dir>`` can also be used, which
+additionally sets the compilation directory for debug info and coverage
+mapping. When both ``-ffile-compilation-dir`` and 
``-fsanitize-compilation-dir``
+are specified, the last one on the command line wins for sanitizer metadata.
+
+Example
+^^^^^^^
+
+.. code-block:: console
+
+  # Make absolute paths relative to the build directory
+  $ clang -fsanitize=address -fsanitize-compilation-dir=/build/dir source.c
+
+  # Using -ffile-compilation-dir to also cover debug info and coverage
+  $ clang -fsanitize=address -ffile-compilation-dir=/home/user/project source.c
+
 Limitations
 ===========
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9e4a47a5b18fc..b6395c6c9058a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -929,6 +929,11 @@ Sanitizers
 ----------
 - UndefinedBehaviorSanitizer now supports ``__ubsan_default_suppressions``.
 
+- Added ``-fsanitize-compilation-dir=`` to set the compilation directory for
+  sanitizer metadata (UBSan diagnostics and ASan module names), making
+  absolute paths relative for reproducible builds. Also implied by
+  ``-ffile-compilation-dir=``.
+
 Python Binding Changes
 ----------------------
 - Add deprecation warnings to ``CompletionChunk.isKind...`` methods.
diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst 
b/clang/docs/UndefinedBehaviorSanitizer.rst
index 3ff1a77e33a6c..4d5b760365a7d 100644
--- a/clang/docs/UndefinedBehaviorSanitizer.rst
+++ b/clang/docs/UndefinedBehaviorSanitizer.rst
@@ -522,6 +522,10 @@ information. If ``N`` is positive, file information 
emitted by
 UndefinedBehaviorSanitizer will drop the first ``N`` components from the file
 path. If ``N`` is negative, the last ``N`` components will be kept.
 
+Alternatively, ``-fsanitize-compilation-dir=<dir>`` (or
+``-ffile-compilation-dir=<dir>``) can be used to set the compilation directory,
+making absolute paths relative to it. This is useful for reproducible builds.
+
 Example
 -------
 
@@ -532,6 +536,8 @@ For a file called ``/code/library/file.cpp``, here is what 
would be emitted:
 * ``-fsanitize-undefined-strip-path-components=2``: ``library/file.cpp``
 * ``-fsanitize-undefined-strip-path-components=-1``: ``file.cpp``
 * ``-fsanitize-undefined-strip-path-components=-2``: ``library/file.cpp``
+* ``-fsanitize-compilation-dir=/code``: ``library/file.cpp``
+* ``-fsanitize-compilation-dir=/code/library``: ``file.cpp``
 
 More Information
 ================
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 3392a210f0bb0..c6f25a34c06d7 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -2277,6 +2277,14 @@ are listed below.
 
    See :doc: `AddressSanitizer` for more details.
 
+.. option:: -fsanitize-compilation-dir=<dir>
+
+   Set the compilation directory for sanitizer metadata. Absolute source paths
+   that start with the given directory are made relative to it, affecting
+   UBSan diagnostic source locations and ASan module names. This can be used
+   to achieve reproducible builds regardless of the build directory. Also
+   implied by ``-ffile-compilation-dir=``.
+
 .. option:: -f[no-]sanitize-type-outline-instrumentation
 
    Controls how type sanitizer code is generated. If enabled will always use
diff --git a/clang/include/clang/Basic/CodeGenOptions.h 
b/clang/include/clang/Basic/CodeGenOptions.h
index e43112b4bb98b..aaacca4c5190c 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -260,6 +260,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// The string to embed in coverage mapping as the current working directory.
   std::string CoverageCompilationDir;
 
+  /// The compilation directory to use for sanitizer metadata.
+  std::string SanitizeCompilationDir;
+
   /// The string to embed in the debug information for the compile unit, if
   /// non-empty.
   std::string DwarfDebugFlags;
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 8451a3698ef17..989a86f84728a 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -1811,9 +1811,13 @@ def fcoverage_compilation_dir_EQ : Joined<["-"], 
"fcoverage-compilation-dir=">,
     Group<f_Group>, Visibility<[ClangOption, CC1Option, CC1AsOption, 
CLOption]>,
     HelpText<"The compilation directory to embed in the coverage mapping.">,
     MarshallingInfoString<CodeGenOpts<"CoverageCompilationDir">>;
+def fsanitize_compilation_dir_EQ : Joined<["-"], "fsanitize-compilation-dir=">,
+    Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
+    HelpText<"The compilation directory to use for sanitizer metadata.">,
+    MarshallingInfoString<CodeGenOpts<"SanitizeCompilationDir">>;
 def ffile_compilation_dir_EQ : Joined<["-"], "ffile-compilation-dir=">, 
Group<f_Group>,
     Visibility<[ClangOption, CLOption, DXCOption]>,
-    HelpText<"The compilation directory to embed in the debug info and 
coverage mapping.">;
+    HelpText<"The compilation directory to embed in the debug info, coverage 
mapping, and sanitizer metadata.">;
 defm debug_info_for_profiling : BoolFOption<"debug-info-for-profiling",
   CodeGenOpts<"DebugInfoForProfiling">, DefaultFalse,
   PosFlag<SetTrue, [], [ClangOption, CC1Option, FlangOption, FC1Option],
diff --git a/clang/lib/CodeGen/BackendUtil.cpp 
b/clang/lib/CodeGen/BackendUtil.cpp
index a46a25c4492f2..bb8218af31e92 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -750,6 +750,7 @@ static void addSanitizers(const Triple &TargetTriple,
         Opts.Recover = CodeGenOpts.SanitizeRecover.has(Mask);
         Opts.UseAfterScope = CodeGenOpts.SanitizeAddressUseAfterScope;
         Opts.UseAfterReturn = CodeGenOpts.getSanitizeAddressUseAfterReturn();
+        Opts.CompilationDir = CodeGenOpts.SanitizeCompilationDir;
         MPM.addPass(AddressSanitizerPass(Opts, UseGlobalGC, UseOdrIndicator,
                                          DestructorKind));
       }
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 325902f2127bc..c3bea476732d7 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -49,6 +49,7 @@
 #include "llvm/IR/MatrixBuilder.h"
 #include "llvm/Support/ConvertUTF.h"
 #include "llvm/Support/Endian.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/xxhash.h"
@@ -4050,6 +4051,22 @@ llvm::Constant 
*CodeGenFunction::EmitCheckSourceLocation(SourceLocation Loc) {
   if (PLoc.isValid()) {
     StringRef FilenameString = PLoc.getFilename();
 
+    // If a sanitize compilation dir is set, make absolute paths relative to 
it.
+    llvm::SmallString<256> FileBuf(FilenameString);
+    if (!CGM.getCodeGenOpts().SanitizeCompilationDir.empty() &&
+        llvm::sys::path::is_absolute(FilenameString)) {
+      llvm::SmallString<256> CompDir(
+          CGM.getCodeGenOpts().SanitizeCompilationDir);
+      if (!llvm::sys::path::is_absolute(CompDir))
+        llvm::sys::fs::make_absolute(CompDir);
+      llvm::sys::path::remove_dots(CompDir, /*remove_dot_dot=*/true);
+      // Ensure trailing separator so "/foo" doesn't match "/foobar/x.c".
+      if (!CompDir.empty() && !llvm::sys::path::is_separator(CompDir.back()))
+        CompDir += llvm::sys::path::get_separator();
+      if (llvm::sys::path::replace_path_prefix(FileBuf, CompDir, ""))
+        FilenameString = FileBuf;
+    }
+
     int PathComponentsToStrip =
         CGM.getCodeGenOpts().EmitCheckPathComponentsToStrip;
     if (PathComponentsToStrip < 0) {
diff --git a/clang/lib/DependencyScanning/ModuleDepCollector.cpp 
b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
index f9fe50564a5ae..24ce027a8004f 100644
--- a/clang/lib/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/DependencyScanning/ModuleDepCollector.cpp
@@ -136,6 +136,7 @@ static void optimizeCWD(CowCompilerInvocation 
&BuildInvocation, StringRef CWD) {
   BuildInvocation.getMutFileSystemOpts().WorkingDir.clear();
   BuildInvocation.getMutCodeGenOpts().DebugCompilationDir.clear();
   BuildInvocation.getMutCodeGenOpts().CoverageCompilationDir.clear();
+  BuildInvocation.getMutCodeGenOpts().SanitizeCompilationDir.clear();
 }
 
 static std::vector<std::string> splitString(std::string S, char Separator) {
@@ -185,6 +186,7 @@ void 
dependencies::resetBenignCodeGenOptions(frontend::ActionKind ProgramAction,
       (ProgramAction == frontend::GenerateModule && !LangOpts.ModulesCodegen)) 
{
     CGOpts.DebugCompilationDir.clear();
     CGOpts.CoverageCompilationDir.clear();
+    CGOpts.SanitizeCompilationDir.clear();
     CGOpts.CoverageDataFile.clear();
     CGOpts.CoverageNotesFile.clear();
     CGOpts.ProfileInstrumentUsePath.clear();
diff --git a/clang/lib/Driver/SanitizerArgs.cpp 
b/clang/lib/Driver/SanitizerArgs.cpp
index 31660dd29407c..b80f90b2909a0 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -1697,6 +1697,13 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const 
llvm::opt::ArgList &Args,
   if (Sanitizers.has(SanitizerKind::MemtagStack) &&
       !hasTargetFeatureMTE(CmdArgs))
     TC.getDriver().Diag(diag::err_stack_tagging_requires_hardware_feature);
+
+  // Add sanitize compilation dir.
+  if (Arg *A = Args.getLastArg(options::OPT_ffile_compilation_dir_EQ,
+                               options::OPT_fsanitize_compilation_dir_EQ)) {
+    CmdArgs.push_back(Args.MakeArgString("-fsanitize-compilation-dir=" +
+                                         Twine(A->getValue())));
+  }
 }
 
 SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp 
b/clang/lib/Frontend/CompilerInvocation.cpp
index 9fc695a74a3c7..384dc6482ae5c 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -5396,6 +5396,7 @@ void CompilerInvocationBase::visitPathsImpl(
   auto &CodeGenOpts = *this->CodeGenOpts;
   RETURN_IF(CodeGenOpts.DebugCompilationDir);
   RETURN_IF(CodeGenOpts.CoverageCompilationDir);
+  RETURN_IF(CodeGenOpts.SanitizeCompilationDir);
 
   // Sanitizer options.
   RETURN_IF_MANY(LangOpts->NoSanitizeFiles);
diff --git a/clang/test/CodeGen/asan-sanitize-compilation-dir.c 
b/clang/test/CodeGen/asan-sanitize-compilation-dir.c
new file mode 100644
index 0000000000000..c173c74640fae
--- /dev/null
+++ b/clang/test/CodeGen/asan-sanitize-compilation-dir.c
@@ -0,0 +1,20 @@
+// Verify that -fsanitize-compilation-dir makes absolute filenames relative
+// in the ASan module name embedded in instrumented code.
+
+// RUN: mkdir -p %t.dir/sub
+// RUN: cp %s %t.dir/sub/test.c
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fsanitize=address 
%t.dir/sub/test.c -o - | FileCheck -check-prefix=CHECK-DEFAULT %s
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fsanitize=address 
-fsanitize-compilation-dir=%t.dir/ %t.dir/sub/test.c -o - | FileCheck 
-check-prefix=CHECK-STRIPPED %s
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fsanitize=address 
-fsanitize-compilation-dir=%t.dir %t.dir/sub/test.c -o - | FileCheck 
-check-prefix=CHECK-STRIPPED %s
+// RUN: cd %t.dir && %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm 
-fsanitize=address -fsanitize-compilation-dir=. %t.dir/sub/test.c -o - | 
FileCheck -check-prefix=CHECK-STRIPPED %s
+// RUN: cd %t.dir/sub && %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm 
-fsanitize=address -fsanitize-compilation-dir=.. %t.dir/sub/test.c -o - | 
FileCheck -check-prefix=CHECK-STRIPPED %s
+
+// Verify that a partial prefix match does not strip (e.g. /tmp/dir vs 
/tmp/directory).
+// RUN: mkdir -p %t.directory
+// RUN: cp %s %t.directory/test.c
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fsanitize=address 
-fsanitize-compilation-dir=%t.dir %t.directory/test.c -o - | FileCheck 
-check-prefix=CHECK-NO-FALSE-MATCH %s
+
+// CHECK-DEFAULT: @___asan_gen_module = private constant [{{.*}} x i8] 
c"{{.+}}test.c\00"
+// CHECK-STRIPPED: @___asan_gen_module = private constant [{{.*}} x i8] 
c"sub{{.}}test.c\00"
+// CHECK-NO-FALSE-MATCH: @___asan_gen_module = private constant [{{.*}} x i8] 
c"{{.+}}directory{{.}}test.c\00"
+int x;
diff --git a/clang/test/CodeGen/ubsan-sanitize-compilation-dir.cpp 
b/clang/test/CodeGen/ubsan-sanitize-compilation-dir.cpp
new file mode 100644
index 0000000000000..0dcd65a8664aa
--- /dev/null
+++ b/clang/test/CodeGen/ubsan-sanitize-compilation-dir.cpp
@@ -0,0 +1,27 @@
+// Verify that -fsanitize-compilation-dir makes absolute filenames relative
+// in UBSan check metadata.
+//
+// We use -fsanitize=unreachable (one of the checks under -fsanitize=undefined)
+// rather than -fsanitize=undefined because the latter is a driver-level 
umbrella
+// flag not accepted by cc1. Any individual sanitizer check exercises the same
+// EmitCheckSourceLocation path.
+
+// RUN: mkdir -p %t.dir/sub
+// RUN: cp %s %t.dir/sub/test.c
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fsanitize=unreachable 
%t.dir/sub/test.c -o - | FileCheck -check-prefix=CHECK-DEFAULT %s
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fsanitize=unreachable 
-fsanitize-compilation-dir=%t.dir/ %t.dir/sub/test.c -o - | FileCheck 
-check-prefix=CHECK-STRIPPED %s
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fsanitize=unreachable 
-fsanitize-compilation-dir=%t.dir %t.dir/sub/test.c -o - | FileCheck 
-check-prefix=CHECK-STRIPPED %s
+// RUN: cd %t.dir && %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm 
-fsanitize=unreachable -fsanitize-compilation-dir=. %t.dir/sub/test.c -o - | 
FileCheck -check-prefix=CHECK-STRIPPED %s
+// RUN: cd %t.dir/sub && %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm 
-fsanitize=unreachable -fsanitize-compilation-dir=.. %t.dir/sub/test.c -o - | 
FileCheck -check-prefix=CHECK-STRIPPED %s
+
+// Verify that a partial prefix match does not strip.
+// RUN: mkdir -p %t.directory
+// RUN: cp %s %t.directory/test.c
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fsanitize=unreachable 
-fsanitize-compilation-dir=%t.dir %t.directory/test.c -o - | FileCheck 
-check-prefix=CHECK-NO-FALSE-MATCH %s
+
+// CHECK-DEFAULT: @{{.*}} = private unnamed_addr constant [{{.*}} x i8] 
c"{{.+}}test.c\00"
+// CHECK-STRIPPED: @{{.*}} = private unnamed_addr constant [{{.*}} x i8] 
c"sub{{.}}test.c\00"
+// CHECK-NO-FALSE-MATCH: @{{.*}} = private unnamed_addr constant [{{.*}} x i8] 
c"{{.+}}directory{{.}}test.c\00"
+void f(void) {
+  __builtin_unreachable();
+}
diff --git a/clang/test/Driver/fsanitize-compilation-dir.c 
b/clang/test/Driver/fsanitize-compilation-dir.c
new file mode 100644
index 0000000000000..6037f55e58d2f
--- /dev/null
+++ b/clang/test/Driver/fsanitize-compilation-dir.c
@@ -0,0 +1,13 @@
+// RUN: %clang -### -fsanitize=address -fsanitize-compilation-dir=/foo %s 2>&1 
| FileCheck %s --check-prefix=SANITIZE
+// RUN: %clang -### -fsanitize=undefined -fsanitize-compilation-dir=/foo %s 
2>&1 | FileCheck %s --check-prefix=SANITIZE
+// RUN: %clang -### -fsanitize=address -ffile-compilation-dir=/foo %s 2>&1 | 
FileCheck %s --check-prefix=FILE
+// RUN: %clang -### -fsanitize=undefined -ffile-compilation-dir=/foo %s 2>&1 | 
FileCheck %s --check-prefix=FILE
+
+// Verify that -fsanitize-compilation-dir wins over -ffile-compilation-dir 
when it comes last.
+// RUN: %clang -### -fsanitize=address -ffile-compilation-dir=/bar 
-fsanitize-compilation-dir=/foo %s 2>&1 | FileCheck %s --check-prefix=OVERRIDE
+// RUN: %clang -### -fsanitize=address -fsanitize-compilation-dir=/foo 
-ffile-compilation-dir=/bar %s 2>&1 | FileCheck %s --check-prefix=OVERRIDE-FILE
+
+// SANITIZE: "-fsanitize-compilation-dir=/foo"
+// FILE: "-fsanitize-compilation-dir=/foo"
+// OVERRIDE: "-fsanitize-compilation-dir=/foo"
+// OVERRIDE-FILE: "-fsanitize-compilation-dir=/bar"
diff --git a/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizer.h 
b/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizer.h
index 781b88175f562..aa75976a4c06f 100644
--- a/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizer.h
+++ b/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizer.h
@@ -16,6 +16,7 @@
 #include "llvm/IR/PassManager.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h"
+#include <string>
 
 namespace llvm {
 class Module;
@@ -30,6 +31,7 @@ struct AddressSanitizerOptions {
   int InstrumentationWithCallsThreshold = 7000;
   uint32_t MaxInlinePoisoningSize = 64;
   bool InsertVersionCheck = true;
+  std::string CompilationDir;
 };
 
 /// Public interface to the address sanitizer module pass for instrumenting 
code
diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp 
b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
index e75b5ccdf612c..6a985f8923074 100644
--- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
@@ -70,8 +70,10 @@
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/ModRef.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TargetParser/Triple.h"
 #include "llvm/Transforms/Instrumentation/AddressSanitizerCommon.h"
@@ -961,7 +963,8 @@ class ModuleAddressSanitizer {
                          bool CompileKernel = false, bool Recover = false,
                          bool UseGlobalsGC = true, bool UseOdrIndicator = true,
                          AsanDtorKind DestructorKind = AsanDtorKind::Global,
-                         AsanCtorKind ConstructorKind = AsanCtorKind::Global)
+                         AsanCtorKind ConstructorKind = AsanCtorKind::Global,
+                         StringRef CompilationDir = "")
       : M(M), Inserter(M),
         CompileKernel(ClEnableKasan.getNumOccurrences() > 0 ? ClEnableKasan
                                                             : CompileKernel),
@@ -988,7 +991,8 @@ class ModuleAddressSanitizer {
         DestructorKind(DestructorKind),
         ConstructorKind(ClConstructorKind.getNumOccurrences() > 0
                             ? ClConstructorKind
-                            : ConstructorKind) {
+                            : ConstructorKind),
+        CompilationDir(CompilationDir) {
     C = &(M.getContext());
     int LongSize = M.getDataLayout().getPointerSizeInBits();
     IntptrTy = Type::getIntNTy(*C, LongSize);
@@ -1069,6 +1073,7 @@ class ModuleAddressSanitizer {
   Function *AsanCtorFunction = nullptr;
   Function *AsanDtorFunction = nullptr;
   GlobalVariable *ModuleName = nullptr;
+  std::string CompilationDir;
 };
 
 // Stack poisoning does not play well with exception handling.
@@ -1339,7 +1344,8 @@ PreservedAnalyses AddressSanitizerPass::run(Module &M,
 
   ModuleAddressSanitizer ModuleSanitizer(
       M, Options.InsertVersionCheck, Options.CompileKernel, Options.Recover,
-      UseGlobalGC, UseOdrIndicator, DestructorKind, ConstructorKind);
+      UseGlobalGC, UseOdrIndicator, DestructorKind, ConstructorKind,
+      Options.CompilationDir);
   bool Modified = false;
   auto &FAM = 
MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
   const StackSafetyGlobalInfo *const SSGI =
@@ -2831,8 +2837,26 @@ GlobalVariable 
*ModuleAddressSanitizer::getOrCreateModuleName() {
   if (!ModuleName) {
     // We shouldn't merge same module names, as this string serves as unique
     // module ID in runtime.
+    std::string ModuleNameStr = M.getModuleIdentifier();
+
+    // If a compilation dir is set, make absolute paths relative to it.
+    if (!CompilationDir.empty()) {
+      SmallString<256> Path(ModuleNameStr);
+      if (sys::path::is_absolute(Path)) {
+        SmallString<256> CompDir(CompilationDir);
+        if (!sys::path::is_absolute(CompDir))
+          sys::fs::make_absolute(CompDir);
+        sys::path::remove_dots(CompDir, /*remove_dot_dot=*/true);
+        // Ensure trailing separator so "/foo" doesn't match "/foobar/x.c".
+        if (!CompDir.empty() && !sys::path::is_separator(CompDir.back()))
+          CompDir += sys::path::get_separator();
+        if (sys::path::replace_path_prefix(Path, CompDir, ""))
+          ModuleNameStr = std::string(Path);
+      }
+    }
+
     ModuleName =
-        createPrivateGlobalForString(M, M.getModuleIdentifier(),
+        createPrivateGlobalForString(M, ModuleNameStr,
                                      /*AllowMerging*/ false, 
genName("module"));
   }
   return ModuleName;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to