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.txtapp.cv1.cv1.hv2.cv2.h
See also: Examples overview ยท All API_BREAK cases ยท Category: API Break.