Case 137: DT_RUNPATH Changed¶
| Field | Value |
|---|---|
| Verdict | ๐ข COMPATIBLE |
| Category | Quality (Compatible) |
| Platforms | Linux |
| Flags | โ |
Detected ChangeKinds |
runpath_changed |
| Source files | examples/case137_runpath_changed/ |
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¶
CMakeLists.txtapp.c
See also: Examples overview ยท All COMPATIBLE cases ยท Category: Quality (Compatible).