Case 137: DT_RUNPATH Changed¶
| Field | Value |
|---|---|
| Verdict | 🟢 COMPATIBLE |
| Category | Quality (Compatible) |
| Classification | Rule |
| Platforms | Linux |
| Flags | — |
Detected ChangeKinds |
runpath_changed |
| Source files | catalog/cases/case137_runpath_changed/ |
| Rule family | runpath-changed |
| Subject | ELF export and linker metadata |
Category: Quality | Verdict: 🟢 COMPATIBLE
Verdict and consumer impact¶
Both libraries export identical symbols with identical signatures — no
recompilation is ever required. v2 is linked with an embedded -rpath
(-Wl,-rpath,/opt/vendor/lib -Wl,--enable-new-dtags), which the linker
records as a DT_RUNPATH dynamic entry; v1 has none. The functional ABI is
unchanged, but the runtime library search path the dynamic loader uses to
resolve this library's own dependencies has changed — that can silently
alter which build of a transitive dependency gets picked up on a different
host, so it's worth surfacing without treating it as an ABI break.
Old/new diff¶
| old/lib.c | new/lib.c |
|---|---|
linked with no -rpath (DT_RUNPATH absent) |
linked with -Wl,-rpath,/opt/vendor/lib -Wl,--enable-new-dtags |
(library source is identical — see old/lib.c / new/lib.c) |
abicheck command¶
gcc -shared -fPIC -g old/lib.c -o libfoo_v1.so
gcc -shared -fPIC -g new/lib.c -o libfoo_v2.so -Wl,-rpath,/opt/vendor/lib -Wl,--enable-new-dtags
abicheck compare libfoo_v1.so libfoo_v2.so
Expected abicheck finding¶
Minimum evidence¶
min_evidence: L0 — DT_RUNPATH is a dynamic-section tag read directly
from the ELF .dynamic section; no debug info or headers are needed to see
the change.
Why abicheck catches it¶
abicheck reads each library's DT_RUNPATH/DT_RPATH entry from the ELF
dynamic section and diffs the string value (including the empty-string
"absent" case) between versions — a pure L0 ELF fact, unrelated to the
exported symbol surface.
Runtime failure demonstration¶
No observable effect on existing binaries — the exported symbols and their behavior are byte-identical between versions:
readelf -dW libfoo_v1.so | grep RUNPATH || echo "v1: none"
# → v1: none
readelf -dW libfoo_v2.so | grep RUNPATH
# → Library runpath: [/opt/vendor/lib]
gcc -shared -fPIC -g old/lib.c -o libfoo.so
gcc -g app.c -L. -lfoo -Wl,-rpath,. -o app
./app
# → compute(7) = 50
# → transform(3, 4) = 11
gcc -shared -fPIC -g new/lib.c -o libfoo.so -Wl,-rpath,/opt/vendor/lib -Wl,--enable-new-dtags
./app
# → compute(7) = 50
# → transform(3, 4) = 11 ← identical
compute()/transform() compute the same results with either .so
loaded; the change only affects which paths the loader searches when
resolving this library's own transitive dependencies.
Safe redesign¶
Avoid baking absolute RUNPATH/RPATH values into distributed shared
objects; prefer $ORIGIN-relative paths (see case52) or leave dependency
resolution to system search paths / loader configuration (/etc/ld.so.conf).
If an embedded RUNPATH is genuinely required for a vendored deployment,
document it in release notes so packagers and downstream integrators know
to check it against their environment.
References¶
Source files¶
new/old/CMakeLists.txtapp.c
See also: Compatibility Catalog · All COMPATIBLE cases · Category: Quality (Compatible) · Rule: RUNPATH changed · Subject: ELF export and linker metadata.