mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Static-link MinGW Release for a self-contained exe (fixes #1)
A default MinGW build dynamically imports three non-system DLLs (SDL2.dll, libgcc_s_seh-1.dll, libstdc++-6.dll) that get bundled side-by-side. On a user machine with a mismatched-architecture copy of one of those earlier on the DLL search path, Windows loads the wrong one and the process dies at launch with 0xc000007b (STATUS_INVALID_IMAGE_FORMAT) before any of our code runs. Add PSX_STATIC_RUNTIME (default ON for MinGW Release/MinSizeRel): link -static -static-libgcc -static-libstdc++ and pull SDL2 from the pkg-config --static line (libSDL2.a + its Windows system-lib chain) instead of the import lib. The resulting exe imports only Windows system DLLs, so it runs from any folder and 0xc000007b from a mismatched runtime DLL becomes structurally impossible. package_release.ps1 no longer bundles the runtime DLLs and instead asserts self-containment via objdump (fails the build if any non-system import remains). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+36
-1
@@ -43,6 +43,26 @@ if(NOT SDL2_INCLUDE_DIRS OR NOT SDL2_LIBRARIES)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# PSX_STATIC_RUNTIME: produce a 100% self-contained MinGW exe.
|
||||
#
|
||||
# A default MinGW build dynamically imports three NON-system DLLs —
|
||||
# SDL2.dll, libgcc_s_seh-1.dll, libstdc++-6.dll — which must be shipped
|
||||
# next to the exe. On a user's machine that side-by-side scheme breaks
|
||||
# when a different-architecture copy of one of those DLLs is found earlier
|
||||
# on the DLL search path (System32, another app on PATH), producing the
|
||||
# 0xc000007b STATUS_INVALID_IMAGE_FORMAT crash on launch.
|
||||
#
|
||||
# Linking those runtimes (and SDL2) statically removes every non-system
|
||||
# import, so the exe runs from any folder with zero bundled DLLs and the
|
||||
# 0xc000007b failure mode becomes structurally impossible. Default ON for
|
||||
# MinGW Release/MinSizeRel (the configs used to cut releases); override
|
||||
# with -DPSX_STATIC_RUNTIME=OFF to force dynamic linking.
|
||||
if(MINGW AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel"))
|
||||
option(PSX_STATIC_RUNTIME "Statically link SDL2 + libgcc/libstdc++ for a self-contained exe" ON)
|
||||
else()
|
||||
option(PSX_STATIC_RUNTIME "Statically link SDL2 + libgcc/libstdc++ for a self-contained exe" OFF)
|
||||
endif()
|
||||
|
||||
set(PSXRECOMP_RUNTIME_SOURCES
|
||||
${PSXRECOMP_ROOT}/runtime/src/main.cpp
|
||||
${PSXRECOMP_ROOT}/runtime/src/memory.c
|
||||
@@ -151,7 +171,15 @@ function(psxrecomp_add_runtime_target target)
|
||||
if(SDL2_LIBRARY_DIRS)
|
||||
target_link_directories(${target} PRIVATE ${SDL2_LIBRARY_DIRS})
|
||||
endif()
|
||||
target_link_libraries(${target} PRIVATE ${SDL2_LIBRARIES})
|
||||
# For a self-contained MinGW build, link SDL2 statically via pkg-config's
|
||||
# --static link line (libSDL2.a + the full Windows system-lib chain SDL2
|
||||
# needs: winmm, imm32, ole32, oleaut32, version, setupapi, dinput8, ...).
|
||||
# Otherwise link the SDL2 import lib (needs SDL2.dll at runtime).
|
||||
if(PSX_STATIC_RUNTIME AND SDL2_STATIC_LDFLAGS)
|
||||
target_link_libraries(${target} PRIVATE ${SDL2_STATIC_LDFLAGS})
|
||||
else()
|
||||
target_link_libraries(${target} PRIVATE ${SDL2_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${target} PRIVATE
|
||||
DEFAULT_DEBUG_PORT=${PSXRT_DEBUG_PORT}
|
||||
@@ -188,6 +216,13 @@ function(psxrecomp_add_runtime_target target)
|
||||
target_link_options(${target} PRIVATE -Wl,--stack,67108864)
|
||||
# No console window in Release MinGW builds.
|
||||
target_link_options(${target} PRIVATE $<$<CONFIG:Release>:-mwindows>)
|
||||
if(PSX_STATIC_RUNTIME)
|
||||
# Fold the GCC / C++ / winpthread runtimes into the exe so it
|
||||
# imports only Windows system DLLs (no libgcc_s_seh-1.dll /
|
||||
# libstdc++-6.dll dependency). Pairs with the static SDL2 link
|
||||
# above to make the exe fully self-contained.
|
||||
target_link_options(${target} PRIVATE -static -static-libgcc -static-libstdc++)
|
||||
endif()
|
||||
elseif(MSVC)
|
||||
target_compile_options(${target} PRIVATE /GS- /guard:cf-)
|
||||
target_link_options(${target} PRIVATE /STACK:67108864,67108864 /GUARD:NO)
|
||||
|
||||
@@ -30,13 +30,26 @@ if (Test-Path (Join-Path $Root "RELEASE_NOTES.md")) {
|
||||
Copy-Item (Join-Path $Root "RELEASE_NOTES.md") $Stage
|
||||
}
|
||||
|
||||
foreach ($Dll in @("SDL2.dll", "libgcc_s_seh-1.dll", "libstdc++-6.dll")) {
|
||||
$Source = Join-Path $MingwBin $Dll
|
||||
if (!(Test-Path $Source)) {
|
||||
throw "Required runtime DLL not found: $Source"
|
||||
}
|
||||
Copy-Item $Source $Stage
|
||||
# The Release build is statically linked (PSX_STATIC_RUNTIME defaults ON for
|
||||
# MinGW Release in runtime.cmake), so the exe imports ONLY Windows system DLLs
|
||||
# — no SDL2.dll / libgcc_s_seh-1.dll / libstdc++-6.dll to bundle. Shipping
|
||||
# those side-by-side was the cause of the 0xc000007b launch crash on user
|
||||
# machines that had a mismatched copy earlier on the DLL search path.
|
||||
#
|
||||
# Assert self-containment rather than trust it: fail packaging if the exe
|
||||
# imports any non-system DLL.
|
||||
$objdump = Join-Path $MingwBin "objdump.exe"
|
||||
$imports = & $objdump -p (Join-Path $Stage "PSXRecomp.exe") |
|
||||
Select-String "DLL Name: (.+)" | ForEach-Object { $_.Matches[0].Groups[1].Value.Trim() }
|
||||
$systemDlls = @("kernel32.dll","user32.dll","gdi32.dll","shell32.dll","msvcrt.dll",
|
||||
"advapi32.dll","ws2_32.dll","comdlg32.dll","dbghelp.dll","ole32.dll",
|
||||
"oleaut32.dll","winmm.dll","imm32.dll","version.dll","setupapi.dll",
|
||||
"dinput8.dll","rpcrt4.dll","hid.dll","cfgmgr32.dll")
|
||||
$nonSystem = $imports | Where-Object { $systemDlls -notcontains $_.ToLower() }
|
||||
if ($nonSystem) {
|
||||
throw "Release exe is NOT self-contained — imports non-system DLL(s): $($nonSystem -join ', ')"
|
||||
}
|
||||
Write-Host "Verified self-contained: imports only system DLLs ($($imports.Count) total)"
|
||||
|
||||
@"
|
||||
; PSXRecomp input mapping. PSX buttons are active when any listed source is pressed.
|
||||
|
||||
Reference in New Issue
Block a user