fix(loader): skip --no-undefined for sanitizer grammar builds

Compiling a grammar with `-fsanitize=address` (or any other sanitizer)
emits a module constructor in the parser object file that references
runtime symbols like `__asan_init`. These runtime-resolved symbols are
incompatible with `-Wl,--no-undefined`, which breaking sanitized test
runs on clang. (gcc happens to paper over this by auto-linking libasan
into the .so)

To fix, detect `-fsanitize=` in the compile flags and omit `--no-undefined`
in that case.
This commit is contained in:
Will Lillis 2026-05-25 02:46:49 -04:00
parent ceef7d179f
commit cd338a7a43
2 changed files with 30 additions and 1 deletions

View file

@ -1306,7 +1306,15 @@ impl Loader {
command.arg("-UTREE_SITTER_REUSE_ALLOCATOR");
} else {
command.arg("-shared");
command.arg("-Wl,--no-undefined");
// Sanitizer builds reference runtime symbols that `--no-undefined`
// would reject at link time.
let sanitizing = compiler
.args()
.iter()
.any(|a| a.to_str().is_some_and(|s| s.starts_with("-fsanitize=")));
if !sanitizing {
command.arg("-Wl,--no-undefined");
}
#[cfg(target_os = "openbsd")]
command.arg("-lc");
}

View file

@ -184,6 +184,27 @@ You can enable these helpers by importing them:
(lldb) command script import /path/to/tree-sitter/lib/lldb_pretty_printers/tree_sitter_types.py
```
#### Sanitizers
Tests can be run with AddressSanitizer or UndefinedBehaviorSanitizer to track down memory or UB issues in the C library
or in grammar parsers. The standard recipe is
```sh
CFLAGS=-fsanitize=address \
RUSTFLAGS="-lasan --cfg sanitizing" \
ASAN_OPTIONS=verify_asan_link_order=0 \
cargo test
```
Swap `address` -> `undefined` (and `-lasan` -> `-lubsan`) for UBSAN, or combine them with `-fsanitize=undefined,address`
and both `-l` flags.
If the loader detects `-fsanitize=` in the compile flags, `-Wl,--no-undefined` is dropped from the grammar link step. This
lets the grammar's sanitizer runtime symbols be resolved at runtime rather than be rejected at link time.
If you flip between sanitizer and non-sanitizer runs you may pick up cached parser builds from the previous mode in `target/scratch/`.
Clear that directory to resolve any inconsistencies.
## Published Packages
The main [`tree-sitter/tree-sitter`][ts repo] repository contains the source code for