Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down Expand Up @@ -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: |
Expand Down
55 changes: 55 additions & 0 deletions PACKAGING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <install-dir> --flag "-static"
```

or equivalently set the `FPM_FFLAGS` environment variable:

```
set FPM_FFLAGS=-static
fpm install --prefix <install-dir>
```

#### 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.

82 changes: 82 additions & 0 deletions ci/verify_static_linking.sh
Original file line number Diff line number Diff line change
@@ -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 <path-to-fpm-binary>
#
# 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 <path-to-binary>"
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