fix: statically link build.mcpp to avoid missing DLL issues - #300
Closed
ZheFeng7110 wants to merge 1 commit into
Closed
fix: statically link build.mcpp to avoid missing DLL issues#300ZheFeng7110 wants to merge 1 commit into
ZheFeng7110 wants to merge 1 commit into
Conversation
Add -static to host_base_flags() for both Clang and GCC paths so the compiled build.mcpp binary is fully static. Remove -Wl,-rpath and -Wl,--dynamic-linker flags that are unnecessary (and counterproductive) under static linking. This prevents runtime failures when users haven't configured LD_LIBRARY_PATH for the toolchain's shared libraries (libc++, libunwind, etc.).
Sunrisepeak
added a commit
to wellwei/mcpp
that referenced
this pull request
Jul 28, 2026
… (2026.7.28.2)
The compiled build.mcpp helper is exec'd by the host OS, outside anything
mcpp controls — no wrapper, no injected LD_LIBRARY_PATH, no PATH guarantee.
Whether it is actually runnable is a per-platform mechanism, and only two of
the four were closed:
Linux + glibc payload ok — host_base_flags bakes absolute payload paths
into -Wl,-rpath
Linux + musl (mcpp-community#295) bad — rpath cannot reach PT_INTERP, an absolute
/lib/ld-musl-<arch>.so.1 no payload installs
and no glibc distro ships -> execve ENOENT,
surfacing as a bare exit 127
Windows PE (mcpp-community#299) bad — PE has no rpath at all, so the helper resolves
libstdc++-6 / libgcc_s / libwinpthread via the
process PATH -> STATUS_DLL_NOT_FOUND
macOS ok — the system libc++ is always present
Both gaps are the same defect (build_program.cppm re-derives the helper link
policy instead of reusing the main build's, cf. prepare.cppm's musl ->
linkage="static" default), so they get ONE decision point rather than two
parallel predicates: a single staticHostHelper in run_build_program, with the
chosen policy folded into the build-program cache identity so pre-fix helpers
are rebuilt instead of reused. `-static` is appended to the final link argv
only — never to `base`, which also feeds the bundled module's compile and
precompile commands.
Also:
- process.cppm: keep the posix_spawnp error code and message in the captured
output instead of collapsing to an unexplained exit 127 (both capture_exec
and capture_exec_deadline). All three consumers of RunResult::output gate on
exit_code first, so nothing downstream changes.
- host_base_flags: stop emitting -Wl,-rpath on PE, where it is inert.
- tests/e2e/168 (was 167 — taken on main by 167_build_defines_module_scan):
discovers the installed musl-gcc payload instead of hardcoding 15.1.0, so it
runs on the aarch64 host where the regression actually lives; asserts static
+ no PT_INTERP. Verified by negative control: reverting the -static push
reproduces the exact mcpp-community#295 failure.
- tests/e2e/97: hold the build.mcpp helper to the same standalone bar as the
produced exe (import table + run with the toolchain bin off PATH). This runs
under the real MinGW GCC 16.1.0 on Windows CI, which is mcpp-community#299's configuration.
- ci-aarch64-fresh-install: build the PR source and run e2e 168 against it, and
leave the published-asset validation steps using the installed mcpp. The
duplicated inline assertions are gone, along with `! readelf … | grep -q …`,
which bash exempts from errexit and which therefore could never fail.
Closes mcpp-community#295
Closes mcpp-community#299
Supersedes mcpp-community#300
Co-authored-by: Zhe Feng <zhefeng7110@outlook.com>
7 tasks
Sunrisepeak
added a commit
to wellwei/mcpp
that referenced
this pull request
Jul 28, 2026
… (2026.7.28.2)
The compiled build.mcpp helper is exec'd by the host OS, outside anything
mcpp controls — no wrapper, no injected LD_LIBRARY_PATH, no PATH guarantee.
Whether it is actually runnable is a per-platform mechanism, and only two of
the four were closed:
Linux + glibc payload ok — host_base_flags bakes absolute payload paths
into -Wl,-rpath
Linux + musl (mcpp-community#295) bad — rpath cannot reach PT_INTERP, an absolute
/lib/ld-musl-<arch>.so.1 no payload installs
and no glibc distro ships -> execve ENOENT,
surfacing as a bare exit 127
Windows PE (mcpp-community#299) bad — PE has no rpath at all, so the helper resolves
libstdc++-6 / libgcc_s / libwinpthread via the
process PATH -> STATUS_DLL_NOT_FOUND
macOS ok — the system libc++ is always present
Both gaps are the same defect (build_program.cppm re-derives the helper link
policy instead of reusing the main build's, cf. prepare.cppm's musl ->
linkage="static" default), so they get ONE decision point rather than two
parallel predicates: a single staticHostHelper in run_build_program, with the
chosen policy folded into the build-program cache identity so pre-fix helpers
are rebuilt instead of reused. `-static` is appended to the final link argv
only — never to `base`, which also feeds the bundled module's compile and
precompile commands.
Also:
- process.cppm: keep the posix_spawnp error code and message in the captured
output instead of collapsing to an unexplained exit 127 (both capture_exec
and capture_exec_deadline). All three consumers of RunResult::output gate on
exit_code first, so nothing downstream changes.
- host_base_flags: stop emitting -Wl,-rpath on PE, where it is inert.
- tests/e2e/168 (was 167 — taken on main by 167_build_defines_module_scan):
discovers the installed musl-gcc payload instead of hardcoding 15.1.0, so it
runs on the aarch64 host where the regression actually lives; asserts static
+ no PT_INTERP. Verified by negative control: reverting the -static push
reproduces the exact mcpp-community#295 failure.
- tests/e2e/97: hold the build.mcpp helper to the same standalone bar as the
produced exe (import table + run with the toolchain bin off PATH). This runs
under the real MinGW GCC 16.1.0 on Windows CI, which is mcpp-community#299's configuration.
- ci-aarch64-fresh-install: append a PR regression gate that builds this source
and runs e2e 168 against it. The checkout must come AFTER the fresh-install
steps: .xlings.json declares an `mcpp` workspace pin, so a checkout present
in $GITHUB_WORKSPACE makes `xlings install mcpp` workspace-scoped — which
both voids the fresh-install charter (validating the pinned version, not the
published one) and leaves bare `mcpp` unresolvable outside the workspace.
Dropped the duplicated inline assertions, including
`! readelf … | grep -q …`, which bash exempts from errexit and which could
therefore never fail.
Closes mcpp-community#295
Closes mcpp-community#299
Supersedes mcpp-community#300
Co-authored-by: Zhe Feng <zhefeng7110@outlook.com>
Sunrisepeak
added a commit
that referenced
this pull request
Jul 28, 2026
…#298) * fix: statically link musl build helpers * ci: use self-built mcpp in ARM source builds * fix: make the build.mcpp host helper self-contained on musl AND MinGW (2026.7.28.2) The compiled build.mcpp helper is exec'd by the host OS, outside anything mcpp controls — no wrapper, no injected LD_LIBRARY_PATH, no PATH guarantee. Whether it is actually runnable is a per-platform mechanism, and only two of the four were closed: Linux + glibc payload ok — host_base_flags bakes absolute payload paths into -Wl,-rpath Linux + musl (#295) bad — rpath cannot reach PT_INTERP, an absolute /lib/ld-musl-<arch>.so.1 no payload installs and no glibc distro ships -> execve ENOENT, surfacing as a bare exit 127 Windows PE (#299) bad — PE has no rpath at all, so the helper resolves libstdc++-6 / libgcc_s / libwinpthread via the process PATH -> STATUS_DLL_NOT_FOUND macOS ok — the system libc++ is always present Both gaps are the same defect (build_program.cppm re-derives the helper link policy instead of reusing the main build's, cf. prepare.cppm's musl -> linkage="static" default), so they get ONE decision point rather than two parallel predicates: a single staticHostHelper in run_build_program, with the chosen policy folded into the build-program cache identity so pre-fix helpers are rebuilt instead of reused. `-static` is appended to the final link argv only — never to `base`, which also feeds the bundled module's compile and precompile commands. Also: - process.cppm: keep the posix_spawnp error code and message in the captured output instead of collapsing to an unexplained exit 127 (both capture_exec and capture_exec_deadline). All three consumers of RunResult::output gate on exit_code first, so nothing downstream changes. - host_base_flags: stop emitting -Wl,-rpath on PE, where it is inert. - tests/e2e/168 (was 167 — taken on main by 167_build_defines_module_scan): discovers the installed musl-gcc payload instead of hardcoding 15.1.0, so it runs on the aarch64 host where the regression actually lives; asserts static + no PT_INTERP. Verified by negative control: reverting the -static push reproduces the exact #295 failure. - tests/e2e/97: hold the build.mcpp helper to the same standalone bar as the produced exe (import table + run with the toolchain bin off PATH). This runs under the real MinGW GCC 16.1.0 on Windows CI, which is #299's configuration. - ci-aarch64-fresh-install: append a PR regression gate that builds this source and runs e2e 168 against it. The checkout must come AFTER the fresh-install steps: .xlings.json declares an `mcpp` workspace pin, so a checkout present in $GITHUB_WORKSPACE makes `xlings install mcpp` workspace-scoped — which both voids the fresh-install charter (validating the pinned version, not the published one) and leaves bare `mcpp` unresolvable outside the workspace. Dropped the duplicated inline assertions, including `! readelf … | grep -q …`, which bash exempts from errexit and which could therefore never fail. Closes #295 Closes #299 Supersedes #300 Co-authored-by: Zhe Feng <zhefeng7110@outlook.com> --------- Co-authored-by: wellwei <1827104243@qq.com> Co-authored-by: sunrisepeak <speakshen@163.com> Co-authored-by: Zhe Feng <zhefeng7110@outlook.com>
Member
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
build.mcppbinary so users don't need to configure dynamic library paths-staticflag tohost_base_flags()for both Clang and GCC paths-Wl,-rpath,and-Wl,--dynamic-linker=flags that are unnecessary under static linkingBackground
When mcpp compiles a user's
build.mcpp, it produces a native binary that runs before the main build. Currently this binary is dynamically linked against the toolchain's runtime libraries (e.g.libgcc_s_seh-1.dll,libstdc++-6.dllon Windows;libc++.so,libunwind.soon Linux).If the toolchain's library paths are not in the system's search path (
PATHon Windows,LD_LIBRARY_PATHon Linux), the binary fails at runtime.Windows case: exit code
-1073741511(0xC0000135,STATUS_DLL_NOT_FOUND).Dumpbin /DEPENDENTSshows dependencies onlibgcc_s_seh-1.dllandlibstdc++-6.dll.Changes
src/build/build_program.cppm—host_base_flags()function (lines 165-226):-static, remove all-Wl,-rpath,and-Wl,--dynamic-linker=-static, remove all-Wl,-rpath,-Lflags (still needed to locate.aarchives under static linking)Test plan
mcpp buildpasses (Windows, LLVM 20.1.7)build.mcppbinary runs without requiring PATH/LD_LIBRARY_PATH setupCloses #299