Skip to content

Case 31: Enum Member Rename

Field Value
Verdict ๐ŸŸ  API_BREAK
Category API Break
Platforms Linux, macOS, Windows
Flags API break
Detected ChangeKinds enum_member_renamed
Source files examples/case31_enum_rename/

Category: Enum API | Verdict: ๐ŸŸ  API_BREAK (binary compatible)

Verdict and consumer impact

Enum constants in C compile to immediate integer values โ€” renaming LOG_ERR to LOG_ERROR doesn't change a single byte of generated code, since the integer 1 is identical either way. An already-built binary calling set_log_level(1) links and runs against v2 exactly as before: binary compatibility is fully preserved. Source compatibility is not: any code that references LOG_ERR, LOG_WARN, or LOG_DBG by name fails to compile against the v2 header, because those identifiers no longer exist. Every downstream consumer must update source before they can rebuild.

Old/new diff

v1.h v2.h Value
LOG_ERR LOG_ERROR 1 (unchanged)
LOG_WARN LOG_WARNING 2 (unchanged)
LOG_DBG LOG_DEBUG 3 (unchanged)
LOG_NONE, LOG_MAX unchanged 0, 4

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)

- enum_member_renamed: Enum member renamed: log_level_t::LOG_ERR -> LOG_ERROR (value=1)
  > Enumerator name changed but value is the same; source code using old
    name won't compile.
- enum_member_renamed: Enum member renamed: log_level_t::LOG_WARN -> LOG_WARNING (value=2)
  > Enumerator name changed but value is the same; source code using old
    name won't compile.
- enum_member_renamed: Enum member renamed: log_level_t::LOG_DBG -> LOG_DEBUG (value=3)
  > Enumerator name changed but value is the same; source code using old
    name won't compile.

Minimum evidence

min_evidence: L1 โ€” DWARF's DW_TAG_enumerator entries carry both the name and the constant value for each enum member, so abicheck can match old and new enumerators by value and detect the name-only rename directly from debug info; -g alone (no public headers) is enough.

Why abicheck catches it

abicheck pairs up enumerators between the two log_level_t snapshots by their constant value (both have a member valued 1, 2, 3) and compares the associated enumerator names read from DW_TAG_enumerator DIEs. A value that keeps its integer but changes name is reported as a rename rather than a paired removal + addition.

Runtime failure demonstration

Severity: MODERATE (source break only)

Scenario: compile app against v1 headers, swap in the v2 .so without recompiling.

# Build old library + app
gcc -shared -fPIC -g v1.c -o libfoo.so
gcc -g app.c -I. -L. -lfoo -Wl,-rpath,. -o app
./app
# โ†’ Calling set_log_level(LOG_ERR)  [value=1] ... OK
# โ†’ Calling set_log_level(LOG_WARN) [value=2] ... OK
# โ†’ Calling set_log_level(LOG_DBG)  [value=3] ... OK

# Swap in new library (no recompile)
gcc -shared -fPIC -g v2.c -o libfoo.so
./app
# โ†’ identical output โ€” the compiled-in integers 1, 2, 3 are still valid
#   enum values in v2, so the existing binary runs unchanged.

Why no runtime failure: the enum values baked into the old binary (1, 2, 3) mean exactly the same thing to v2's set_log_level โ€” only the source-level name changed. This case has no binary crash to demonstrate; the failure only appears at recompile time.

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. -lfoo -Wl,-rpath,. -o app_v2
# โ†’ error: 'LOG_ERR' undeclared (first use in this function); did you mean 'LOG_ERROR'?
# โ†’ error: 'LOG_WARN' undeclared (first use in this function)
# โ†’ error: 'LOG_DBG' undeclared (first use in this function); did you mean 'LOG_DEBUG'?
rm -f /tmp/app_v2_test.c

Safe redesign

Keep the old names as aliases (#define LOG_ERR LOG_ERROR), or add both old and new names in the enum with matching values (LOG_ERR = 1, LOG_ERROR = LOG_ERR). Only remove the old names on a major SONAME bump, after a deprecation period.

Real-world example: logging and status-code enums are renamed for naming-convention consistency ("clean up the API") more often than almost any other symbol category โ€” every downstream consumer that pattern-matches on the old identifiers breaks at the next rebuild even though nothing actually changed behaviorally.

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.