Case 35: Field Rename¶
| Field | Value |
|---|---|
| Verdict | ๐ API_BREAK |
| Category | API Break |
| Platforms | Linux, macOS, Windows |
| Flags | API break |
Detected ChangeKinds |
field_renamed |
| Source files | examples/case35_field_rename/ |
Category: Struct API | Verdict: ๐ก API_BREAK (binary compatible)
Verdict and consumer impact¶
struct Point's fields are renamed (xโcol, yโrow) with identical
offsets and types. Field names are a compile-time concept only โ they're
resolved to byte offsets during compilation and never appear in the
compiled binary. An already-built consumer binary keeps linking and
running against v2 unmodified, since make_point() returns the exact same
struct layout. Source code referencing p.x or p.y fails to compile
against the v2 header, though, so recompilation forces every downstream
consumer to update field references before they can rebuild.
Old/new diff¶
| v1.h | v2.h |
|---|---|
struct Point { int x; int y; }; |
struct Point { int col; int row; }; |
abicheck command¶
gcc -shared -fPIC -g v1.c -o libfoo_v1.so
gcc -shared -fPIC -g v2.c -o libfoo_v2.so
abicheck compare libfoo_v1.so libfoo_v2.so
Expected abicheck finding¶
Verdict: API_BREAK (exit 2)
- field_renamed: Field renamed: Point::x -> col
> Field name changed but offset is the same; source code using old
name won't compile.
- field_renamed: Field renamed: Point::y -> row
> Field name changed but offset is the same; source code using old
name won't compile.
Minimum evidence¶
min_evidence: L1 โ DWARF's DW_TAG_member entries carry both a name and
an offset for every struct field, so abicheck can match old and new
members by identical offset+type and detect the name-only change directly
from debug info; -g alone (no public headers) is enough. The dedicated
field-rename detector suppresses the redundant field_removed/field_added
pair that a naive same-offset match would otherwise also report, so this
case surfaces cleanly as field_renamed rather than a spurious BREAKING
removal alongside it.
Why abicheck catches it¶
abicheck pairs struct members between the two Point snapshots by offset
and type (both have an int at offset 0 and an int at offset 4) and
compares the member names read from DW_TAG_member DIEs. A member that
keeps its offset and type but changes name is reported as a rename, which
is what routes this to the API_BREAK (source-level) bucket instead of a
binary-layout BREAKING finding.
Runtime failure demonstration¶
Severity: NONE (binary compatible) / compile-time failure on rebuild
Scenario: compile app against v1 headers, swap in the v2 .so without
recompiling.
# Build old library + app
gcc -shared -fPIC -g v1.c -o libv1.so
gcc -g app.c -I. -L. -lv1 -Wl,-rpath,. -o app
./app
# โ p.x = 10
# โ p.y = 20
# โ OK (API_BREAK only: field renamed, binary layout unchanged)
# Swap in new library (no recompile)
gcc -shared -fPIC -g v2.c -o libv1.so
./app
# โ p.x = 10
# โ p.y = 20
# โ OK (API_BREAK only: field renamed, binary layout unchanged)
Why no runtime failure: x/col and y/row sit at the exact same
offsets with the exact same type in both versions โ the app's field
accesses compile down to fixed-offset loads that are identical either way,
so there's nothing for the swapped library to disturb at runtime.
Source break verification (recompiling against v2 fails):
sed 's/#include "v1.h"/#include "v2.h"/' app.c > /tmp/app_v2_test.c
gcc -g /tmp/app_v2_test.c -I. -L. -lv1 -Wl,-rpath,. -o /tmp/app_v2_out
# โ error: 'struct Point' has no member named 'x'
# โ error: 'struct Point' has no member named 'y'
rm -f /tmp/app_v2_test.c /tmp/app_v2_out
Safe redesign¶
Avoid renaming public struct fields; if a clearer name is genuinely
needed, add the field under both names via a #define alias, or provide
accessor macros/functions and deprecate direct field access, giving
consumers a migration window before the old name disappears.
Real-world example: coordinate/geometry libraries that rename fields
for clarity (x/y โ domain-specific names like col/row or
lat/lon) hit this exact break โ every downstream consumer that
directly accesses the old field names fails to rebuild, even though the
struct's actual memory layout never changed.
Cross-tool comparison¶
abidw --out-file v1.xml libfoo_v1.so
abidw --out-file v2.xml libfoo_v2.so
abidiff v1.xml v2.xml
echo "exit: $?"
References¶
- C member access operator rules
- C struct type compatibility and declarations
- Semantic Versioning (policy rationale for public API source breaks)
Source files¶
CMakeLists.txtapp.cv1.cv1.hv2.cv2.h
See also: Examples overview ยท All API_BREAK cases ยท Category: API Break.