Skip to content

Fix $ORIGIN rpath on shared builds - #645

Open
abgoyal wants to merge 1 commit into
BtbN:masterfrom
abgoyal:fix-origin-rpath
Open

Fix $ORIGIN rpath on shared builds#645
abgoyal wants to merge 1 commit into
BtbN:masterfrom
abgoyal:fix-origin-rpath

Conversation

@abgoyal

@abgoyal abgoyal commented Aug 6, 2026

Copy link
Copy Markdown

Fixes #560.

scripts.d/99-rpath.sh has emitted $ORIGIN rpath flags for linux shared builds since a0384b8 (2021), but the escaped literal stopped surviving the pipeline in 2024, and every linux shared release since has shipped a mangled rpath:

$ readelf -d ffmpeg-master-latest-linux64-gpl-shared/bin/ffmpeg | grep RPATH
0x000000000000000f (RPATH)   Library rpath: [-Wl:../lib]

Confirmed against released tarballs. autobuild-2026-02-28 (N-123074), autobuild-2026-06-30 (N-125365) and autobuild-2026-08-03 (N-125953) all carry [-Wl:../lib].

Root cause

281ab29 (2024-03-14) added the xargs normalisation at generate.sh:230, which unescapes one level. That is the level 99-rpath.sh's literal depended on:

configure sees \$$ORIGIN     (with xargs)     ->  RPATH: $
configure sees \\\$\$ORIGIN  (without xargs)  ->  RPATH: $ORIGIN

What reaches the linker with the current escaping:

  1. configure's append() runs eval "$var=\"\$$var $*\"". Inside those quotes \$ becomes $, and the unset $ORIGIN expands to nothing, so config.mak gets -Wl,-rpath=$ -Wl,-rpath=$/../lib.
  2. make treats $ and $/ as single-character variable references, both empty, merging the two flags into one token: -Wl,-rpath=-Wl,-rpath=../lib.
  3. gcc splits that on commas, ld receives -rpath=-Wl and -rpath=../lib, giving RPATH [-Wl:../lib].

Step 2 explains both the merge and the missing leading slash.

Fix

Not by adding more backslashes. That is what caused the bug, and it cannot be made reliable: the chain has five unescaping layers, and one of them is to_df()'s printf "$@" (seperate issue in itself), which uses caller data as a format string.

Instead the value never enters the generate/Dockerfile pipeline at all:

  • 99-rpath.sh no longer emits the rpath flags (it keeps -pie), so there is nothing for xargs, printf or ENV parsing to mangle.

  • build.sh appends them inside the container, one shell layer from configure, where the required value is knowable and can be commented. The heredoc delimiter is quoted so the host expands nothing, and the fragment is single-quoted and concatenated so the container's shell substitutes nothing either:

    FF_LDEXEFLAGS="$FF_LDEXEFLAGS "'-Wl,-rpath=\\\$\$ORIGIN -Wl,-rpath=\\\$\$ORIGIN/../lib'

    Not ${FF_LDEXEFLAGS//@PLACEHOLDER@/$var}. Bash's pattern substitution consumes a backslash from the replacement on some bash versions but not others, which silently costs one escaping level.

  • build.sh also gains a post-build check for linux shared variants. A broken $ORIGIN rpath still builds, and still runs anywhere ld.so.cache knows the libraries, which is why this went unnoticed for two years. It now fails the build instead:

    readelf -d /ffbuild/prefix/bin/ffmpeg | grep -q ORIGIN || { ...; exit 1; }

Both additions are gated on linux* + *shared*, so win and static targets generate the same build script as before.

Verification

Traced end to end through the real chain: generate.sh output, Docker ENV parsing, configure's append()/eval, config.mak, make, sh, gcc.

Dockerfile ENV : FF_LDEXEFLAGS="-pie"
configure gets : -pie -Wl,-rpath=\\\$\$ORIGIN -Wl,-rpath=\\\$\$ORIGIN/../lib
config.mak     : LDEXEFLAGS= -pie -Wl,-rpath=\$$ORIGIN -Wl,-rpath=\$$ORIGIN/../lib
make emits     : gcc ... -pie -Wl,-rpath=\$ORIGIN -Wl,-rpath=\$ORIGIN/../lib
RESULTING RPATH: $ORIGIN:$ORIGIN/../lib

confirmed on real linux64 gpl-shared builds, twice: once against the prebuilt variant image, and once rebuilding the variant image from base-linux64 the way CI does.

+ FF_LDEXEFLAGS='-pie -Wl,-rpath=\\\$\$ORIGIN -Wl,-rpath=\\\$\$ORIGIN/../lib'
...
0x000000000000000f (RPATH)   Library rpath: [$ORIGIN:$ORIGIN/../lib]

Unpacked elsewhere and run from an unrelated working directory:

$ cd /tmp && ldd .../check/bin/ffmpeg
libavdevice.so.63 => .../check/bin/../lib/libavdevice.so.63

so the binary is self-locating, which is what #560 asked for.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux shared build, maybe use $ORIGIN/../lib as the rpath is a better solution

1 participant