Case 51: Protected Visibility (DEFAULT to PROTECTED)¶
| Field | Value |
|---|---|
| Verdict | ๐ข COMPATIBLE |
| Category | Quality (Compatible) |
| Platforms | Linux |
| Flags | โ |
Detected ChangeKinds |
symbol_elf_visibility_changed |
| Source files | examples/case51_protected_visibility/ |
Category: Quality | Verdict: ๐ข COMPATIBLE
Verdict and consumer impact¶
Existing binaries that call hook_point() keep working unmodified โ the symbol
is still exported and resolves normally for external callers. The only effect
is on interposition: with STV_PROTECTED, the library's own internal calls
to hook_point() always bind to its local definition, even under LD_PRELOAD.
Tooling that relies on overriding hook_point() from outside the library (sanitizers,
profilers, mocks) silently stops intercepting calls made from within the library.
Old/new diff¶
| old/lib.c | new/lib.c |
|---|---|
int hook_point(int x) { return x * 2; } (DEFAULT visibility) |
__attribute__((visibility("protected")))int hook_point(int x) { return x * 2; } |
abicheck command¶
gcc -shared -fPIC -g old/lib.c -Iold -o libfoo_v1.so
gcc -shared -fPIC -g new/lib.c -Inew -o libfoo_v2.so
abicheck compare libfoo_v1.so libfoo_v2.so
Expected abicheck finding¶
Verdict: COMPATIBLE (exit 0)
- symbol_elf_visibility_changed: ELF visibility changed: hook_point (default -> protected)
> Symbol still exported and resolvable; intra-library calls to hook_point
no longer honor LD_PRELOAD/interposition.
Minimum evidence¶
min_evidence: L0 โ ELF symbol visibility (STV_DEFAULT vs STV_PROTECTED)
is recorded directly in .dynsym; no debug info or headers needed to see the
change. (-g above is only there so DWARF-aware confidence is reported.)
Why abicheck catches it¶
abicheck reads each symbol's st_other visibility byte from the ELF dynamic
symbol table and diffs it between versions โ a pure L0 ELF fact, independent
of DWARF or headers.
Runtime failure demonstration¶
Severity: INFORMATIONAL โ no observable effect on existing binaries.
# Build old library + app
gcc -shared -fPIC -g old/lib.c -Iold -o libfoo.so
gcc -g app.c -Iold -L. -lfoo -Wl,-rpath,. -o app
./app
# โ hook_point(5) = 10
# โ compute(5) = 11
# Swap in new library (no recompile)
gcc -shared -fPIC -g new/lib.c -Inew -o libfoo.so
./app
# โ hook_point(5) = 10 โ same result
# โ compute(5) = 11 โ same result
Normal callers see identical output before and after the swap. The change
only surfaces for LD_PRELOAD-based interposition of hook_point(), where
v2's library-internal call from compute() no longer honors the preloaded
override โ a policy concern, not an ABI break.
Safe redesign¶
If interposition of an internal helper is a supported extension point,
document it explicitly and keep the symbol STV_DEFAULT. If protected
visibility is intentional (performance, hardening), call it out in release
notes so profiling/mocking tooling that depends on interposition isn't
surprised.
Real-world example: GCC's -fno-semantic-interposition makes the same
trade-off at compiler-flag granularity โ assuming interposed implementations
are semantically equivalent so intra-TU calls can bind directly, trading
interposability for codegen freedom.
References¶
Source files¶
CMakeLists.txtapp.c
See also: Examples overview ยท All COMPATIBLE cases ยท Category: Quality (Compatible).