Skip to content

Commit 9c98736

Browse files
jbachorikclaude
andcommitted
fix(build): use clang's ASan runtime when linking with clang
locateLibasan now returns libclang_rt.asan-<arch>.so when the compiler is clang, falling back to GCC's libasan only for GCC builds. configureAsan derives the -l flag from the located library filename: - clang: -lclang_rt.asan-<arch> satisfies -z defs for both __asan_* and __ubsan_* (clang's runtime includes both) and matches the runtime that -fsanitize=address links into executables — one runtime, no conflict. - gcc: -lasan -lubsan as before. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 5f410b7 commit 9c98736

2 files changed

Lines changed: 33 additions & 13 deletions

File tree

build-logic/conventions/src/main/kotlin/com/datadoghq/native/config/ConfigurationPresets.kt

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -187,18 +187,22 @@ object ConfigurationPresets {
187187
config.compilerArgs.set(asanCompilerArgs + commonLinuxCompilerArgs(version))
188188

189189
val libasan = PlatformUtils.locateLibasan(compiler)
190-
// Do not add -lasan/-lubsan explicitly: clang links its own
191-
// libclang_rt.asan via -fsanitize=address, and combining that
192-
// with an explicit -lasan (GCC's runtime) puts two incompatible
193-
// ASan runtimes into the binary's NEEDED entries, causing an
194-
// immediate "incompatible ASan runtimes" abort at startup.
195-
// -fsanitize=address/-fsanitize=undefined handle the runtime
196-
// link correctly for both GCC and clang.
197-
val asanLinkerArgs = listOf(
198-
"-fsanitize=address",
199-
"-fsanitize=undefined",
200-
"-fno-omit-frame-pointer"
201-
)
190+
// Link against the sanitizer runtime that matches the compiler:
191+
// - clang: locateLibasan returns libclang_rt.asan-<arch>.so, which
192+
// includes UBSan symbols; -lclang_rt.asan-<arch> satisfies -z defs
193+
// for both __asan_* and __ubsan_* and matches the runtime that
194+
// -fsanitize=address links into executables — one runtime, no conflict.
195+
// - gcc: locateLibasan returns libasan.so; -lasan + -lubsan as before.
196+
val asanLinkerArgs = if (libasan != null) {
197+
val asanLibName = File(libasan).nameWithoutExtension.removePrefix("lib")
198+
val ubsanLibs = if (asanLibName.startsWith("clang_rt")) emptyList()
199+
else listOf("-lubsan")
200+
listOf("-L${File(libasan).parent}", "-l$asanLibName") +
201+
ubsanLibs +
202+
listOf("-fsanitize=address", "-fsanitize=undefined", "-fno-omit-frame-pointer")
203+
} else {
204+
listOf("-fsanitize=address", "-fsanitize=undefined", "-fno-omit-frame-pointer")
205+
}
202206

203207
config.linkerArgs.set(commonLinuxLinkerArgs() + asanLinkerArgs)
204208

build-logic/conventions/src/main/kotlin/com/datadoghq/native/util/PlatformUtils.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,23 @@ object PlatformUtils {
125125
return null
126126
}
127127

128-
fun locateLibasan(compiler: String = "gcc"): String? = locateLibrary("libasan", compiler)
128+
fun locateLibasan(compiler: String = "gcc"): String? {
129+
if (currentPlatform != Platform.LINUX) return null
130+
// For clang, prefer the architecture-specific clang_rt.asan library over
131+
// GCC's libasan. Using GCC's runtime alongside clang's libclang_rt.asan
132+
// (which -fsanitize=address links for executables) causes "incompatible
133+
// ASan runtimes" at startup. The clang runtime also includes UBSan symbols,
134+
// so no separate -lubsan is needed.
135+
if (compiler.contains("clang")) {
136+
val archSuffix = when (currentArchitecture) {
137+
Architecture.X64 -> "x86_64"
138+
Architecture.ARM64 -> "aarch64"
139+
}
140+
val clangAsan = locateLibrary("libclang_rt.asan-$archSuffix", compiler)
141+
if (clangAsan != null) return clangAsan
142+
}
143+
return locateLibrary("libasan", compiler)
144+
}
129145

130146
fun locateLibtsan(compiler: String = "gcc"): String? = locateLibrary("libtsan", compiler)
131147

0 commit comments

Comments
 (0)