diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b30bc30fa8..8d8042f262 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -259,6 +259,15 @@ jobs: env: EXE: fpm-${{ env.VERSION }}-${{ matrix.os-arch }}-gcc-${{ matrix.toolchain.version }}${{ matrix.exe }} + # Verify Windows release binary is statically linked (Issue #1204) + # Dynamic linking against GCC runtime DLLs causes ABI mismatch crashes + # when users have different GCC versions with incompatible threading models. + - name: Verify static linking (Windows) + if: contains(matrix.os, 'windows') + shell: bash + run: | + bash ci/verify_static_linking.sh "${{ env.FPM_RELEASE }}" + - name: Run release version shell: bash run: | @@ -339,6 +348,12 @@ jobs: fi cp "$exe_path" ./ci/fpm.exe + # Verify the binary going into the installer is statically linked (Issue #1204) + - name: Verify static linking (installer binary) + shell: msys2 {0} + run: | + bash ci/verify_static_linking.sh ./ci/fpm.exe + - name: Fetch Git for Windows shell: msys2 {0} run: | diff --git a/PACKAGING.md b/PACKAGING.md index 32bc51bfda..130dbfb0b1 100644 --- a/PACKAGING.md +++ b/PACKAGING.md @@ -716,3 +716,58 @@ that should be produced as the command line argument. > Note: All file and directory names are specified with their full canonical > path. + +### Windows distribution builds: static linking requirement + +When building *fpm* for distribution on **Windows** (e.g., for conda-forge, +MSYS2, Spack, package installers, or any other distribution channel), you +**must** statically link the GCC runtime libraries into the `fpm.exe` binary. +Use the `--static` flag: + +``` +fpm install --prefix --flag "-static" +``` + +or equivalently set the `FPM_FFLAGS` environment variable: + +``` +set FPM_FFLAGS=-static +fpm install --prefix +``` + +#### Why this is required + +GCC on Windows supports multiple **threading models** (Win32, POSIX, MCF), each +producing runtime DLLs (`libgfortran-5.dll`, `libgcc_s_seh-1.dll`, etc.) with +the **same filename** but **incompatible ABIs**. If `fpm.exe` is dynamically +linked against these DLLs, it will crash with an "Entry point not found" error +when a user installs a GCC compiler built with a different threading model: + +``` +fpm.exe - Entry point not found +The entry point of procedure __gthr_win32_create cannot be found in the +dynamic link library ...\libgfortran-5.dll +``` + +This occurs because the older `m2w64-gcc` toolchain uses **Win32 threading**, +while modern GCC 14+ compilers typically use **POSIX threading** via +`winpthreads`. Both produce a `libgfortran-5.dll`, but the POSIX version lacks +the `__gthr_win32_*` symbols expected by binaries built with the Win32 model. + +Static linking eliminates the runtime DLL dependency entirely, making the +binary self-contained and immune to this class of ABI mismatch. + +See [Issue #1204](https://github.com/fortran-lang/fpm/issues/1204) for details. + +#### Verifying static linking + +You can verify that a Windows binary is properly statically linked using +the CI script provided in this repository: + +```bash +bash ci/verify_static_linking.sh path/to/fpm.exe +``` + +This checks that the binary has no dynamic dependencies on `libgfortran`, +`libgcc`, `libwinpthread`, or `libquadmath` DLLs. + diff --git a/ci/verify_static_linking.sh b/ci/verify_static_linking.sh new file mode 100644 index 0000000000..9d5636165f --- /dev/null +++ b/ci/verify_static_linking.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# verify_static_linking.sh — Verify that an fpm binary has no dynamic GCC runtime dependencies. +# +# This script checks a Windows PE binary (fpm.exe) to ensure it does not dynamically +# link against GCC runtime DLLs (libgfortran, libgcc, libwinpthread, libquadmath). +# +# Dynamic linking against these DLLs causes ABI mismatch crashes when users install +# GCC compilers with different threading models (e.g., Win32 vs POSIX threads). +# +# Usage: +# ./ci/verify_static_linking.sh +# +# Exit codes: +# 0 — Binary is statically linked (no GCC runtime DLL dependencies) +# 1 — Binary has dynamic GCC runtime dependencies (FAIL) +# 2 — Usage error or missing tools + +set -euo pipefail + +# --- Usage --- +if [ $# -lt 1 ]; then + echo "Usage: $0 " + echo " Verifies that a Windows binary has no dynamic GCC runtime DLL dependencies." + exit 2 +fi + +BINARY="$1" + +if [ ! -f "$BINARY" ]; then + echo "Error: Binary not found: $BINARY" + exit 2 +fi + +# --- Check for objdump --- +if ! command -v objdump &> /dev/null; then + echo "Warning: objdump not found. Skipping static linking verification." + echo "Install binutils to enable this check." + exit 0 +fi + +echo "=== Static Linking Verification ===" +echo "Binary: $BINARY" +echo "" + +# List all DLL dependencies +echo "DLL dependencies:" +DLL_LIST=$(objdump -p "$BINARY" 2>/dev/null | grep -i "DLL Name" || true) + +if [ -z "$DLL_LIST" ]; then + echo " (none found — binary may be fully static or not a PE file)" + echo "" + echo "✓ PASS: No dynamic DLL dependencies detected." + exit 0 +fi + +echo "$DLL_LIST" | sed 's/^/ /' +echo "" + +# Check for GCC runtime DLLs that indicate dynamic linking +# These are the DLLs that cause ABI mismatch issues (Issue #1204): +# - libgfortran-*.dll (Fortran runtime — threading model dependent) +# - libgcc_s_seh-*.dll (GCC support library) +# - libwinpthread-*.dll (POSIX threading shim) +# - libquadmath-*.dll (Quad-precision math library) +GCC_RUNTIME_PATTERN="libgfortran\|libgcc\|libwinpthread\|libquadmath" + +if echo "$DLL_LIST" | grep -qi "$GCC_RUNTIME_PATTERN"; then + echo "✗ FAIL: Binary has dynamic dependencies on GCC runtime DLLs!" + echo "" + echo "The following GCC runtime DLLs were found:" + echo "$DLL_LIST" | grep -i "$GCC_RUNTIME_PATTERN" | sed 's/^/ /' + echo "" + echo "This will cause 'Entry point not found' errors when users have" + echo "different GCC versions with incompatible threading models installed." + echo "See: https://github.com/fortran-lang/fpm/issues/1204" + echo "" + echo "Fix: Build with '--flag \"-static\"' to statically link GCC runtime libraries." + exit 1 +fi + +echo "✓ PASS: No GCC runtime DLL dependencies found. Binary is safe for distribution." +exit 0