A $ORIGIN in a user link flag reaches the program as /../lib: link flags are escaped for ninja and not quoted for the shell #703
Copy link
Copy link
Closed
Labels
bugSomething isn't workingSomething isn't working
Description
Activity
- added a commit that references this issue
on Sep 26, 2026 Released in mcpp 2026.9.26.2 (#702, squash
c109fdd6).SPEC-004 §8's element reading (v1.7) now applies to
[build] ldflags, tomcpp::link_flag, and to what dependencies propagate:- Each element is read into words, and each word reaches the linker quoted for the host and escaped for ninja.
$ORIGINreaches the run path as written.- Dependency link flags propagate word by word.
link_search,link_libandlink_scriptspell engine-built paths as one word, so a directory with a space is one argument.- The first plan names an element whose words differ from what 2026.9.26.1 passed (
build/flag-words), which covers an element escaped by hand for the shell or for ninja. - The build-program cache epoch moves to 3.
Before the release, the index and mcpp-plugins were searched for link flags escaped by hand, and none were found.
Readings:
- e2e 795 fails on 2026.9.26.1. There,
readelfshows/../libin the run path of the reproduction from this issue. - 795 passes on 2026.9.26.2 locally, in CI, and in the xlings sandbox against the published binary.
- added a commit that references this issue
on Sep 26, 2026
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working
Summary
A run path written with
$ORIGINin[build] ldflags, or passed by a build program throughmcpp::link_flag, does not reach the linker.build.ninjacarries the ninja-escaped$$ORIGIN, ninja hands the command tosh, andshexpands$ORIGINto nothing. The program's run path then holds/../lib, which is the host's/lib. The build succeeds and says nothing.Reproduction
mcpp 2026.9.26.1, gcc@16.1.0, Linux x86_64:
A build program that calls
mcpp::link_flag("-Wl,-rpath,$ORIGIN/../lib")produces the samebuild.ninjaline and the same run path.Cause
SPEC-004 §8 states how an element of
cflags,cxxflagsandasmflagsbecomes words, forbids an implementation to interpret$(rule 7), and requires every word to reach the compiler verbatim whatever the host's command-line reader.ldflagsand the link directives have no such statement, andnormalize_ldflag(src/build/flags.cppm:367-385) escapes an element for ninja only. The engine's own run path is written-Wl,-rpath,'$$ORIGIN'(src/build/plan.cppm:745,:2125), quoted for ninja and for the shell, which is why mcpp's own$ORIGINentries are correct.Consequence
The entry is not merely lost.
/../libnames the host's/lib, so a program linked against a payload or graph closure searches a host directory at run time: the class of defect #696 closed for link-time search.Found through the mcpp-plugins
deps-*work, whose design record gave up$ORIGINfor this reason and links by full path instead.Proposed repair
SPEC-004 §8's element reading applies to
ldflagsand tomcpp::link_flag: an element is read into words, and each word reaches the linker verbatim, escaped for ninja and then quoted for the host. The same reading splits an element that packs several tokens, which the rendering of link-unit flags guards against case by case today (src/build/ninja_backend.cppm:342-370). Windows is unaffected, because its commands run without a shell.Compatibility: an element already written for the shell or for ninja by hand (
\$ORIGIN,'$$ORIGIN') changes meaning. The index is searched for such spellings before the release.Criterion: an e2e in which
$ORIGINfrom the manifest and fromlink_flagreaches the program's run path verbatim. It fails on 2026.9.26.1.This is item F1 of the design record for the next release (
.agents/docs/2026-09-26-compile-database-and-issue-699-design.md), which lands with the fix.