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
