Build portability and the target-architecture baseline
The analyzer (stig-code-analyzer, linking PhASAR) is built once and run elsewhere: CI bakes it into the prebuilt toolchain image on one runner, then the build/test and release jobs run it on a different runner; release artifacts also ship to customer machines we don't control. A binary compiled for the build machine's exact CPU is therefore a portability hazard.
The rule: never -march=native for the analyzer
-march=native makes the compiler emit instructions for the CPU doing the compile (AVX2, BMI2, FMA, …). Run that binary on a CPU missing one of those and it SIGILLs at runtime — illegal instruction, process dies with rc=132 (128 + SIGILL(4)).
This bit CI. PhASAR's CMakeLists.txt silently defaults Release builds to -march=native (the PHASAR_TARGET_ARCH block — despite a comment claiming it is "disabled by default"), and the analyzer inherited it. The rc=132 matrix crashes (ifds-fieldsens-taint NO_REPORT, production combo degraded) were exactly this: the image was baked on a runner with BMI2 and the matrix ran on a runner without it.
It was not the host compiler. clang-18 builds a fully correct analyzer (matrix 94.4 %, rc=132×0) whenever build-host == run-host — a same-machine test can never reproduce a cross-runner crash, which is why an early "switch to clang-22 / Fedora" attempt looked like a fix but only moved the build and run onto the same CPU. The portability lever is -march, not the compiler version.
The fix: pin x86-64-v2
stig-code-analyzer/scripts/build-analyzer.sh pins a portable micro-architecture baseline for both native components:
Component | How |
|---|---|
PhASAR |
|
stig-code-analyzer |
|
x86-64-v2 (SSE4.2 + POPCNT + CMPXCHG16B — universal on server/desktop CPUs since ~2009) is safe on every realistic CI runner and customer machine, and is ~free on performance for PhASAR's workload: pointer-chasing graph analysis with no auto-vectorizable hot loops (libphasar emits zero ymm/zmm).
Verifying portability
objdump -d the binary and count the genuinely-unsafe instructions — the VEX-encoded BMI1/BMI2 set, which faults on a CPU without those features:
Two classes are not a hazard and are expected to remain:
tzcnt/lzcnt— theirREP-prefixed encodings execute asbsf/bsron older CPUs, so they degrade instead of faulting.AVX
ymm/zmminside LLVM's BLAKE3 hash (llvm_blake3_hash_many_avx2, …) — LLVM selects these at runtime via CPU-feature dispatch, so they only execute where supported.
A dynamically-linked dependency (e.g. libz3.so) is not part of the binary and is the distro's portability concern, not ours.
Versions (recap)
Host compiler (Job 1 — builds PhASAR/the analyzer): not pinned to one version across distros, and that's fine — any reasonably modern clang produces a correct analyzer. See LLVM toolchain. Fedora's libstdc++-16 forces host clang ≥ 22; Ubuntu noble's stock is clang-18; both work.
LLVM library + bitcode target: pinned to 18 via
stig-code-analyzer/scripts/pins.env(LLVM_MAJOR).Customer note: customer builds on Ubuntu/clang-18 — supported, because the fix is the portable
-march, not a newer compiler.