Case 11: Global Variable Type Change¶
| Field | Value |
|---|---|
| Verdict | ๐ด BREAKING |
| Category | Breaking |
| Platforms | Linux |
| Flags | ABI break, API break |
Detected ChangeKinds |
var_type_changed |
| Source files | browse on GitHub |
Category: Type Layout | Verdict: ๐ก ABI CHANGE (exit 4)
Note on abidiff 2.4.0: Returns exit 4. Semantically breaking โ the symbol size changes from 4 to 8 bytes; consumers that read it as
intget only half the data on big-endian or the wrong bits on little-endian for large values.
What breaks¶
Any binary that accesses lib_version as a 4-byte int now reads only half the
variable. On little-endian (x86), the low word happens to be correct for small values,
masking the bug โ until lib_version exceeds INT_MAX.
Why abidiff catches it¶
Reports size of symbol changed from 4 to 8 and type of variable changed: int โ long int.
Code diff¶
| v1.c | v2.c |
|---|---|
int lib_version = 1; |
long lib_version = 1; |
Reproduce manually¶
gcc -shared -fPIC -g v1.c -o libfoo_v1.so
gcc -shared -fPIC -g v2.c -o libfoo_v2.so
abidw --out-file v1.xml libfoo_v1.so
abidw --out-file v2.xml libfoo_v2.so
abidiff v1.xml v2.xml
echo "exit: $?" # โ 4
How to fix¶
Use a fixed-width type from the start (int32_t, int64_t, or uint32_t). If you
must change a global's type, introduce a new symbol with a new name and deprecate the
old one.
Real-world example¶
The errno global in glibc is deliberately typed as int and will never change;
glibc uses __thread int errno internally but the public type is ABI-frozen.
Real Failure Demo¶
Severity: CRITICAL
Scenario: app reads lib_version as int (v1 type); v2 declares it long 5000000000.
# Build v1 + app
gcc -shared -fPIC -g v1.c -o libfoo.so
gcc -g app.c -I. -L. -lfoo -Wl,-rpath,. -o app
./app
# โ lib_version = 1 (as int)
# Swap in v2
gcc -shared -fPIC -g v2.c -o libfoo.so
./app
# โ lib_version = 705032704 (as int) โ wrong (5000000000 truncated to 32 bits)
# ld.so may also warn: "size of symbol changed"
Why CRITICAL: The app accesses only the lower 4 bytes of a now-8-byte symbol.
On little-endian x86 this reads the low 32 bits โ garbage for values above INT_MAX.
References¶
Source files¶
See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.