Skip to content

Case 26 โ€” Union Field Added

Field Value
Verdict ๐Ÿ”ด BREAKING
Category Breaking
Platforms Linux, macOS, Windows
Flags ABI break
Detected ChangeKinds โ€”
Source files browse on GitHub

Verdict: ๐Ÿ”ด BREAKING abicheck verdict: BREAKING (TYPE_SIZE_CHANGED: union grows 4โ†’8 bytes)

What changes

Version Definition
v1 union Value { int i; float f; };
v2 union Value { int i; float f; double d; };

Why this IS a binary ABI break

Adding double d makes double the largest member, so sizeof(union Value) grows from 4 bytes (max of int=4, float=4) to 8 bytes (double=8).

This is a TYPE_SIZE_CHANGED break because: - Old callers allocate 4 bytes for union Value on the stack or in a struct. - The v2 library's fill() may write 8 bytes, overwriting adjacent memory. - Array indexing breaks: Value arr[10] โ†’ old caller: 40 bytes, library expects 80 bytes.

All union fields still share offset 0, so the field layout is unchanged โ€” but the size change makes this BREAKING, not just informational.

What it does affect

  • Union size: sizeof(union Value) grows 4โ†’8 bytes โ†’ TYPE_SIZE_CHANGED โ†’ BREAKING.
  • Struct embedding: any struct containing union Value also grows, shifting later fields.
  • Array indexing: Value arr[N] stride changes from 4 to 8 bytes.

Contrast with other union changes

Change Verdict Why
Field added (no size change) COMPATIBLE Existing fields at offset 0 unchanged, sizeof same
Field added (size grows) BREAKING TYPE_SIZE_CHANGED โ€” callers under-allocate
Field removed BREAKING Removes a valid representation
Field type changed BREAKING Reinterpretation of shared storage

Code diff

 union Value {
     int i;
     float f;
+    double d;   /* sizeof grows 4 โ†’ 8 bytes: BREAKING */
 };

Real Failure Demo

Severity: BREAKING (TYPE_SIZE_CHANGED)

Scenario: app reads v.i after fill(). Adding a double d field doesn't affect int i access โ€” but the union is now 8 bytes. Any caller that stack-allocated 4 bytes and passes &v to a v2 function that writes 8 bytes has a stack overflow.

# Build old lib + app
gcc -shared -fPIC -g old/lib.c -Iold -o libval.so
gcc -g app.c -Iold -L. -lval -Wl,-rpath,. -o app
./app
# โ†’ v.i = 42 (expected 42)

# Swap in new lib (double field added โ€” sizeof grows 4โ†’8 bytes)
gcc -shared -fPIC -g new/lib.c -Inew -o libval.so
./app
# โ†’ v.i = 42 (expected 42)  โ† may still appear correct for int-only access
# But: abicheck reports TYPE_SIZE_CHANGED (4โ†’8) โ†’ BREAKING

Why BREAKING: sizeof(union Value) grows from 4 to 8 bytes due to double d. Old callers that allocate union Value on the stack or embed it in a struct under-allocate memory when running against the v2 library. abicheck correctly reports TYPE_SIZE_CHANGED โ†’ BREAKING verdict.

Why runtime result may differ from verdict

Union field added (double): sizeof(Value) grows 4โ†’8 bytes, layout broken

Runtime note

v2 now writes the newly added double field so old-layout callers observe incompatible behavior.

References


Source files

See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.