https://github.com/kozemcak created 
https://github.com/llvm/llvm-project/pull/206465

Hi,

The ASAN globals instrumentation embeds the source filename into the
generated code. We hit this issue when we build our projects in 
Yocto/OpenEmbedded
cross build. We enable ASAN in our pipelines and then Yocto build fails QA 
check [buildpaths] because of the unmapped TMPDIR path. With AI help we found 
the issue and prepare a paths witch fix that issue. Each commit contains 
details descriptions about the issue in commit message. I don't understand 
llvm-project codebase well and have less knowledge about that problematic. This 
MR is more draft and RFC issue. We are interesting to know opinions what you 
think about the issue and commits, which we prepare with AI help.

Thank you

>From 98eecb04db7d05e0ad93ec332f3e65fc9f855372 Mon Sep 17 00:00:00 2001
From: Andrej Kozemcak <[email protected]>
Date: Tue, 23 Jun 2026 08:40:05 +0200
Subject: [PATCH 1/2] asan: use DICompileUnit filename for module name to
 honour   -ffile-prefix-map

The AddressSanitizer module-level instrumentation embeds the translation
unit filename into two places via getOrCreateModuleName():

  * the "module_name" field of every __asan_global descriptor, and
  * the "module_name" argument of __asan_before_dynamic_init() emitted
    for translation units that contain dynamically-initialised globals.

The string returned by M.getModuleIdentifier() is the raw source file
path as it was passed to the compiler frontend and is never remapped
through -ffile-prefix-map / -fdebug-prefix-map.  Using it directly means
that absolute build-directory paths leak into the compiled binary's
.rodata even when the user (or build system) has set -ffile-prefix-map
to strip them.

In a Yocto / OpenEmbedded cross build the sysroot lives inside TMPDIR.
Globals declared in sysroot headers (e.g. libstdc++ internals, gtest /
gmock headers, ...) cause the unmapped TMPDIR path to survive in the
final binary.  This trips OE's [buildpaths] QA check even when the
recipe correctly sets -ffile-prefix-map for every TMPDIR component.

When debug information is present (-g), the compiler frontend has already
applied the prefix map to the DICompileUnit source file path.  Prefer
that remapped path over the raw module identifier so that the ASan global
descriptors and __asan_before_dynamic_init() calls honour
-ffile-prefix-map.  When no debug info is available the existing
behaviour (M.getModuleIdentifier()) is preserved as a fallback.

The runtime impact is purely cosmetic: ASan error reports will show the
remapped filename, identical to what __FILE__ reports for the same
translation unit.

Signed-off-by: Andrej Kozemcak <[email protected]>
---
 .../Instrumentation/AddressSanitizer.cpp          | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp 
b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
index 65ab49d4e4273..83c809a1ebc9f 100644
--- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
@@ -2833,8 +2833,21 @@ GlobalVariable 
*ModuleAddressSanitizer::getOrCreateModuleName() {
   if (!ModuleName) {
     // We shouldn't merge same module names, as this string serves as unique
     // module ID in runtime.
+    //
+    // The raw M.getModuleIdentifier() is never remapped through
+    // -ffile-prefix-map, so using it here leaks absolute build-directory
+    // paths into ASan global descriptors and __asan_before_dynamic_init()
+    // arguments even when prefix maps are specified.  When debug info is
+    // present, the DICompileUnit source file already has the prefix map
+    // applied by the frontend; prefer it to avoid the leak.
+    StringRef ModuleId = M.getModuleIdentifier();
+    if (NamedMDNode *CUs = M.getNamedMetadata("llvm.dbg.cu"))
+      if (CUs->getNumOperands() > 0)
+        if (auto *CU = dyn_cast<DICompileUnit>(CUs->getOperand(0)))
+          if (!CU->getFilename().empty())
+            ModuleId = CU->getFilename();
     ModuleName =
-        createPrivateGlobalForString(M, M.getModuleIdentifier(),
+        createPrivateGlobalForString(M, ModuleId,
                                      /*AllowMerging*/ false, 
genName("module"));
   }
   return ModuleName;

>From 89c23b4df4883837681080abc6270d36a2ac608c Mon Sep 17 00:00:00 2001
From: Andrej Kozemcak <[email protected]>
Date: Tue, 23 Jun 2026 08:41:12 +0200
Subject: [PATCH 2/2] clang driver: apply -ffile-prefix-map to coverage data
 file  path

When -fprofile-arcs (-ftest-coverage / --coverage) is used, the clang
driver computes the absolute path to the .gcda file from the -o output
argument and stores it verbatim in llvm.gcov IR metadata via
-coverage-data-file=.  Unlike debug-info paths or macro __FILE__
expansions, this path is never remapped by -ffile-prefix-map /
-fcoverage-prefix-map: cc1 marshals the option straight into
CodeGenOpts.CoverageDataFile and CodeGenModule::EmitCoverageFile() embeds
it into llvm.gcov metadata without further processing.

As a result, absolute build-directory paths leak into the .gcda strings
stored in every instrumented binary even when -ffile-prefix-map= remaps
them everywhere else, tripping the Yocto [buildpaths] QA check.

Fix: after the driver sets the .gcda extension, iterate over
-ffile-prefix-map= / -fcoverage-prefix-map= arguments and apply the first
matching prefix replacement.  The .gcno path is left unchanged because
the compiler must be able to write that file to disk at build time.

Signed-off-by: Andrej Kozemcak <[email protected]>
---
 clang/lib/Driver/ToolChains/Clang.cpp | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index a3a3954bc464e..538ead6a2671d 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -698,6 +698,22 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, 
Compilation &C,
         llvm::sys::path::append(CoverageFilename, Gcno);
       }
       llvm::sys::path::replace_extension(CoverageFilename, "gcda");
+      // Apply -ffile-prefix-map / -fcoverage-prefix-map to the embedded .gcda
+      // path only.  The .gcno path must remain unchanged so the compiler can
+      // write it to disk at build time.  The .gcda path is stored verbatim in
+      // llvm.gcov metadata and embedded in the binary for runtime use; without
+      // remapping, the absolute build-directory path leaks into the binary
+      // even when -ffile-prefix-map= remaps everything else.
+      for (const Arg *A : Args.filtered(options::OPT_ffile_prefix_map_EQ,
+                                        options::OPT_fcoverage_prefix_map_EQ)) 
{
+        StringRef Map = A->getValue();
+        auto Sep = Map.find('=');
+        if (Sep == StringRef::npos)
+          continue;
+        if (llvm::sys::path::replace_path_prefix(
+                CoverageFilename, Map.substr(0, Sep), Map.substr(Sep + 1)))
+          break;
+      }
       CmdArgs.push_back(
           Args.MakeArgString("-coverage-data-file=" + CoverageFilename));
     }

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

Reply via email to