Skip to content

Case 103: Toolchain Flag Drift

Field Value
Verdict ๐ŸŸข COMPATIBLE
Category Quality (Compatible)
Platforms Linux
Flags Bad practice
Detected ChangeKinds toolchain_flag_drift
Source files examples/case103_toolchain_flag_drift/

Category: Build quality signal | Verdict: โœ… COMPATIBLE

Verdict and consumer impact

The public API and every exported symbol are byte-identical between v1 and v2 โ€” the source files themselves don't differ at all. The only change is the compiler flag set used to build the shared library: v2 adds -fshort-enums. In this minimal fixture there are no enums, so nothing in the symbol table or type layout actually changes and no consumer is affected today. The finding exists because flag drift is a leading indicator: the same drift applied to a library that does expose enums, packed structs, or a different data model would silently change layout without any source diff to review.

Old/new diff

Source is identical; only the build command differs:

 gcc -shared -fPIC -g -fvisibility=default               -o libv1.so v1.c
-gcc -shared -fPIC -g -fvisibility=default               -o libv2.so v2.c
+gcc -shared -fPIC -g -fvisibility=default -fshort-enums -o libv2.so v2.c

abicheck command

gcc -shared -fPIC -g -fvisibility=default               -o libv1.so v1.c
gcc -shared -fPIC -g -fvisibility=default -fshort-enums -o libv2.so v2.c
abicheck compare libv1.so libv2.so

Expected abicheck finding

Verdict: COMPATIBLE (exit 0)

## Quality Issues

- toolchain_flag_drift: ABI-affecting compiler flags changed: added: -fshort-enums

## Environment & Toolchain Drift

- toolchain_flag_drift: ABI-affecting compiler flags changed: added: -fshort-enums

Minimum evidence

min_evidence: L1 โ€” the flag set is read from DWARF's DW_AT_producer string (GCC embeds its full command-line switches there by default); no public headers are required. GCC-specific caveat: clang does not embed command-line flags in DW_AT_producer unless built with -grecord-command-line, so a default-clang build of this exact fixture would return NO_CHANGE for the flag drift โ€” the underlying source/ABI extraction gap is proven end-to-end by tests/test_compiler_record_cross_toolchain.py::test_clang_grecord_command_line_flag_drift_detected. This example is built with GCC deliberately, matching the case's own PLATFORMS linux / GCC-only scope.

Why abicheck catches it

abicheck parses DW_AT_producer from each binary's DWARF debug info and extracts the ABI-relevant flags it recognizes (-fshort-enums, -fpack-struct, -fno-common/-fcommon, -m32/-m64, -mabi=, -fabi-version=, -D_GLIBCXX_USE_CXX11_ABI=, among others), unioned across all compile units so a flag applied to only some translation units is not missed. When the extracted flag sets differ between the two versions it emits toolchain_flag_drift โ€” a compatible, informational signal, not a hard break, since nothing observable actually changed in this binary.

Runtime failure demonstration

This case is COMPATIBLE, not a hard break โ€” there is no crash to demonstrate. Verified directly: the consumer app behaves identically whether linked against libv1.so or hot-swapped to libv2.so (no recompile):

gcc app.c -I. -L. -lv1 -Wl,-rpath,. -o app
./app          # โ†’ 5 8.000000
cp libv2.so libv1.so   # swap in the v2 build
./app          # โ†’ 5 8.000000 (identical output)

No observable effect on existing binaries in this minimal fixture โ€” the signal is a leading indicator for what happens once the two builds' ABI-affecting flags actually diverge over a type the flag touches.

Safe redesign

Build every translation unit of a published library โ€” and every consumer that shares its ABI surface โ€” with the same ABI-affecting flag set. Pin the flags in the build system (not just compiler defaults) and treat any detected drift as a release-blocking review item rather than assuming it is harmless because a given snapshot happens not to expose an affected type.

Real-world example: -fshort-enums, -fpack-struct, and the libstdc++ dual-ABI macro (case 104) are exactly the class of flag that is invisible in a source diff or a symbol-table diff, yet silently changes struct/enum layout the moment a consumer's build picks up a slightly different toolchain default (e.g. an embedded/ARM target where -fshort-enums is sometimes the platform default).

Cross-tool comparison

abidiff/abi-compliance-checker compare binary artifacts and don't read DW_AT_producer as a distinct ABI-affecting-flags signal โ€” this compiler-driven-drift class of finding is unique to abicheck's DWARF parsing. Not re-verified in this environment (abidiff is not installed here).

References


Source files

  • CMakeLists.txt
  • app.c
  • v1.c
  • v1.h
  • v2.c
  • v2.h

See also: Examples overview ยท All COMPATIBLE cases ยท Category: Quality (Compatible).