Skip to content

fix: statically link build.mcpp to avoid missing DLL issues - #300

Closed
ZheFeng7110 wants to merge 1 commit into
mcpp-community:mainfrom
ZheFeng7110:fix/static-link-build-mcpp
Closed

fix: statically link build.mcpp to avoid missing DLL issues#300
ZheFeng7110 wants to merge 1 commit into
mcpp-community:mainfrom
ZheFeng7110:fix/static-link-build-mcpp

Conversation

@ZheFeng7110

Copy link
Copy Markdown
Member

Summary

  • Statically link the compiled build.mcpp binary so users don't need to configure dynamic library paths
  • Add -static flag to host_base_flags() for both Clang and GCC paths
  • Remove -Wl,-rpath, and -Wl,--dynamic-linker= flags that are unnecessary under static linking

Background

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.dll on Windows; libc++.so, libunwind.so on Linux).

If the toolchain's library paths are not in the system's search path (PATH on Windows, LD_LIBRARY_PATH on Linux), the binary fails at runtime.

Windows case: exit code -1073741511 (0xC0000135, STATUS_DLL_NOT_FOUND). Dumpbin /DEPENDENTS shows dependencies on libgcc_s_seh-1.dll and libstdc++-6.dll.

Changes

  • src/build/build_program.cppmhost_base_flags() function (lines 165-226):
    • Clang path: add -static, remove all -Wl,-rpath, and -Wl,--dynamic-linker=
    • GCC path: add -static, remove all -Wl,-rpath,
    • Keep -L flags (still needed to locate .a archives under static linking)

Test plan

  • mcpp build passes (Windows, LLVM 20.1.7)
  • E2E tests pass (Linux/macOS CI)
  • build.mcpp binary runs without requiring PATH/LD_LIBRARY_PATH setup

Closes #299

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>
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>
@Sunrisepeak

Copy link
Copy Markdown
Member

同类问题 合并到 298 一起合入

@ZheFeng7110
ZheFeng7110 deleted the fix/static-link-build-mcpp branch July 28, 2026 16:27
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.

fix: build.mcpp binary fails at runtime with missing DLL on Windows

2 participants