Skip to content

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


Source files

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

See also: Examples overview ยท All API_BREAK cases ยท Category: API Break.