diff --git a/app/igneum-app/Cargo.lock b/app/igneum-app/Cargo.lock index d0e2e8f7..16aeb43c 100644 --- a/app/igneum-app/Cargo.lock +++ b/app/igneum-app/Cargo.lock @@ -219,7 +219,7 @@ dependencies = [ [[package]] name = "igneum-app" -version = "0.3.6" +version = "0.3.7" dependencies = [ "ed25519-dalek", "getrandom", diff --git a/app/igneum-app/Cargo.toml b/app/igneum-app/Cargo.toml index 53894ca2..5c6630a8 100644 --- a/app/igneum-app/Cargo.toml +++ b/app/igneum-app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "igneum-app" -version = "0.3.6" +version = "0.3.7" edition = "2021" description = "Igneum Miner engine: supervises the node, the miner and the GPU workers, and serves the dashboard on 127.0.0.1" license = "MIT" diff --git a/app/igneum-app/resources/igneum-app.rc b/app/igneum-app/resources/igneum-app.rc index 36d8212a..154d5524 100644 --- a/app/igneum-app/resources/igneum-app.rc +++ b/app/igneum-app/resources/igneum-app.rc @@ -6,8 +6,8 @@ 1 ICON "igneum.ico" 1 VERSIONINFO -FILEVERSION 0,3,6,0 -PRODUCTVERSION 0,3,6,0 +FILEVERSION 0,3,7,0 +PRODUCTVERSION 0,3,7,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS VOS_NT_WINDOWS32 @@ -20,12 +20,12 @@ BEGIN BEGIN VALUE "CompanyName", "Igneum" VALUE "FileDescription", "Igneum Miner engine" - VALUE "FileVersion", "0.3.6" + VALUE "FileVersion", "0.3.7" VALUE "InternalName", "igneum-app" VALUE "LegalCopyright", "Igneum contributors" VALUE "OriginalFilename", "igneum-app.exe" VALUE "ProductName", "Igneum Miner" - VALUE "ProductVersion", "0.3.6" + VALUE "ProductVersion", "0.3.7" END END BLOCK "VarFileInfo" diff --git a/app/igneum-app/src/jobbuild.rs b/app/igneum-app/src/jobbuild.rs index 7fe122d7..ff6e73f8 100644 --- a/app/igneum-app/src/jobbuild.rs +++ b/app/igneum-app/src/jobbuild.rs @@ -330,6 +330,17 @@ pub fn build_script(p: &BuildParams, job_id: &str, m: &Manifest, target: &str) - s.push_str(&format!(" if [ -f \"$CARGO_TARGET_DIR/{rel}/{name}\" ]; then cp -f \"$CARGO_TARGET_DIR/{rel}/{name}\" \"$OUT/{target}/{name}\"; echo \"RESULT {target} {name} $(stat -c %s \"$OUT/{target}/{name}\") bytes sha256 $(sha256sum \"$OUT/{target}/{name}\" | cut -c1-64)\"; else echo \"RESULT {target} {name} missing after the build\"; rc=3; fi\n")); } s.push_str("fi\n"); + if windows && u.bins.iter().any(|b| b == "igneumd") { + // the three mingw runtime DLLs from THIS toolchain go next to the exes (5 October 2026, 0.3.6: the PC's + // GCC 13 exes shipped with the Mac's GCC 16 libstdc++-6.dll, which lacks five codecvt symbols; the node + // did not start on either PC). make-payload.sh takes DLLs next to the exes first. + s.push_str("if [ \"$rc\" = 0 ]; then\n"); + s.push_str(" gccdir=$(dirname \"$(x86_64-w64-mingw32-gcc-posix -print-file-name=libstdc++-6.dll)\")\n"); + s.push_str(" for dll in \"$gccdir/libstdc++-6.dll\" \"$gccdir/libgcc_s_seh-1.dll\" /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll; do\n"); + s.push_str(&format!(" if [ -f \"$dll\" ]; then cp -f \"$dll\" \"$OUT/{target}/\"; echo \"RESULT {target} $(basename \"$dll\") $(stat -c %s \"$dll\") bytes sha256 $(sha256sum \"$dll\" | cut -c1-64) from $(dirname \"$dll\")\"; else echo \"RESULT {target} runtime dll missing: $dll\"; rc_all=1; fi\n")); + s.push_str(" done\n"); + s.push_str("fi\n"); + } if optional { s.push_str(&format!("[ \"$rc\" = 0 ] || echo \"RESULT {target} {dir} optional on {target}: not fatal\"\n", dir = u.dir)); } else { @@ -543,6 +554,10 @@ mod tests { assert!(lin.contains("cd \"$SRC/app/igneum-app\"")); assert!(lin.contains("optional on linux: not fatal")); assert!(!lin.contains("--target x86_64-pc-windows-gnu")); + // the windows stage ships the runtime DLLs of its own toolchain next to the exes; the linux stage does not + let win = build_script(&p, id, &m, "windows"); + assert!(win.contains("-print-file-name=libstdc++-6.dll") && win.contains("libgcc_s_seh-1.dll") && win.contains("libwinpthread-1.dll"), "{win}"); + assert!(!lin.contains("libstdc++-6.dll")); assert!(lin.contains("RESULT linux igneumd $(stat")); let win = build_script(&p, id, &m, "windows"); assert!(win.contains("--target x86_64-pc-windows-gnu") && win.contains("x86_64-w64-mingw32-gcc-posix") && win.contains("link-arg=-static")); diff --git a/app/windows/version.h b/app/windows/version.h index 4eae6d67..4aed77d7 100644 --- a/app/windows/version.h +++ b/app/windows/version.h @@ -3,6 +3,6 @@ // packaging/windows/Igneum-Miner.iss when the app version moves. Include guards, not #pragma once: rc.exe reads it too. #ifndef IGNEUM_HOST_VERSION_H #define IGNEUM_HOST_VERSION_H -#define IGNEUM_HOST_VERSION_STR "0.3.6" -#define IGNEUM_HOST_VERSION_RC 0,3,6,0 +#define IGNEUM_HOST_VERSION_STR "0.3.7" +#define IGNEUM_HOST_VERSION_RC 0,3,7,0 #endif diff --git a/packaging/mac/app/Info.plist b/packaging/mac/app/Info.plist index 1688b88d..890fe96a 100644 --- a/packaging/mac/app/Info.plist +++ b/packaging/mac/app/Info.plist @@ -11,7 +11,7 @@ CFBundleVersion VERSION_STAMP CFBundleShortVersionString - 0.3.6 + 0.3.7 CFBundlePackageType APPL CFBundleExecutable diff --git a/packaging/windows/Igneum-Miner.iss b/packaging/windows/Igneum-Miner.iss index c28370b8..3855fbaf 100644 --- a/packaging/windows/Igneum-Miner.iss +++ b/packaging/windows/Igneum-Miner.iss @@ -9,7 +9,7 @@ #define ArtDir "..\..\brand\icons" #endif #ifndef AppVersion - #define AppVersion "0.3.6" + #define AppVersion "0.3.7" #endif #define AppName "Igneum Miner" #define Publisher "Igneum" diff --git a/packaging/windows/check-runtime-dlls.sh b/packaging/windows/check-runtime-dlls.sh new file mode 100755 index 00000000..fcce6c1f --- /dev/null +++ b/packaging/windows/check-runtime-dlls.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Refuses a Windows payload whose exes import a symbol the shipped runtime DLL does not export. +# +# packaging/windows/check-runtime-dlls.sh +# +# 5 October 2026, Igneum Miner 0.3.6: the PC-built igneumd.exe (GCC 13's mingw) was shipped with libstdc++-6.dll from +# the Mac's toolchain (GCC 16), which no longer exports five std::codecvt symbols the exe imports +# (_ZNKSt25__codecvt_utf8_utf16_baseIwE10do_unshiftERiPcS2_RS2_ and four siblings); Windows refused to start the node +# ("Entry Point Not Found") on both PCs. `-C link-arg=-static` had not removed the import. Rule from here: the DLLs +# in a payload come from the toolchain that linked the exes, and this check runs before anything is signed +# (push-inputs.sh) and before the installer is packed (make-payload.sh). Needs x86_64-w64-mingw32-objdump (Homebrew +# mingw-w64 on the Mac); prints a note and exits 0 where it is missing (the runner), exits 1 on a missing symbol. +set -euo pipefail +DIR="${1:?folder with the exes and the DLLs}" +OBJDUMP="${OBJDUMP:-x86_64-w64-mingw32-objdump}" +if ! command -v "$OBJDUMP" >/dev/null 2>&1; then echo "runtime DLL check: no $OBJDUMP here, skipped (the Mac's push-inputs.sh ran it before signing)"; exit 0; fi +# objdump -p prints, under "DLL Name: x.dll", one import per line: " > "; and under +# "[Ordinal/Name Pointer] Table" one export per line: "[ n] +base[ m] " +imports_from() { "$OBJDUMP" -p "$1" | awk -v dll="$2" 'index(tolower($0), "dll name: " tolower(dll)) > 0 {f=1; next} /DLL Name:/ {f=0} f && /^\t[0-9a-f]+[ \t]+[^ \t]+[ \t]+[0-9a-f]+[ \t]+[^ \t]+[ \t]*$/ {print $NF}' | sort -u; } +exports_of() { "$OBJDUMP" -p "$1" | awk '/^\t\[ *[0-9]+\] \+base\[ *[0-9]+\][ \t]+[0-9a-f]+[ \t]+[^ \t]+[ \t]*$/ {print $NF}' | sort -u; } +bad=0; checked=0 +for exe in "$DIR"/igneumd.exe "$DIR"/igneum-miner.exe; do + [ -f "$exe" ] || continue + for dll in $("$OBJDUMP" -p "$exe" | awk '/DLL Name:/ {print $3}' | sort -u); do + case "$dll" in lib*.dll) ;; *) continue ;; esac + if [ ! -f "$DIR/$dll" ]; then echo "runtime DLL check: $(basename "$exe") imports $dll and the payload has no $dll"; bad=1; continue; fi + missing="$(comm -23 <(imports_from "$exe" "$dll") <(exports_of "$DIR/$dll") || true)" + n_imp="$(imports_from "$exe" "$dll" | wc -l | tr -d ' ')"; n_exp="$(exports_of "$DIR/$dll" | wc -l | tr -d ' ')" + checked=$((checked+1)) + if [ -n "$missing" ]; then + echo "runtime DLL check: $(basename "$exe") imports $(echo "$missing" | wc -l | tr -d ' ') symbol(s) from $dll that this $dll does not export ($n_imp imported, $n_exp exported):" + echo "$missing" | head -5 | sed 's/^/ /'; bad=1 + else + echo "runtime DLL check: $(basename "$exe") <- $dll: all $n_imp imported symbols exported ($n_exp exports)" + fi + done +done +[ "$checked" -gt 0 ] || echo "runtime DLL check: no lib*.dll import in the exes (statically linked)" +[ "$bad" = 0 ] || { echo "runtime DLL check FAILED: ship the DLLs of the toolchain that linked the exes" >&2; exit 1; } diff --git a/packaging/windows/make-payload.sh b/packaging/windows/make-payload.sh index f2f55091..ff1c8400 100755 --- a/packaging/windows/make-payload.sh +++ b/packaging/windows/make-payload.sh @@ -55,6 +55,7 @@ for dll in lib/libstdc++-6.dll lib/libgcc_s_seh-1.dll bin/libwinpthread-1.dll; d elif [ -f "$MINGW/$dll" ]; then cp "$MINGW/$dll" "$STAGE/"; x86_64-w64-mingw32-strip "$STAGE/$name" 2>/dev/null || true else echo "note: $name not found in the mingw toolchain (the node exe is linked -static, so it may not need it)"; fi done +"$(dirname "$0")/check-runtime-dlls.sh" "$STAGE" # every imported libstdc++ symbol exported by the shipped DLL (5 October 2026) # the prebuilt one-click workers, from where proto-cuda/windows-app/make-package.sh takes them: the NVRTC CUDA worker # with NVIDIA's redistributable DLLs and licence texts, and the OpenCL worker (IGNEUM_WORKERS_DIR overrides with a flat folder) diff --git a/packaging/windows/push-inputs.sh b/packaging/windows/push-inputs.sh index 3676044a..05f515dc 100755 --- a/packaging/windows/push-inputs.sh +++ b/packaging/windows/push-inputs.sh @@ -43,11 +43,16 @@ TOKEN="$(tr -d '[:space:]' < "$TOKEN_FILE")" STAGE="$(mktemp -d)/payload-inputs" mkdir -p "$STAGE" cp "$REL/igneumd.exe" "$REL/igneum-miner.exe" "$STAGE/" +# the runtime DLLs: the ones next to the exes first (a PC build job ships its own toolchain's, 0.3.7), else this Mac's +# toolchain (which linked a Mac cross-build); then the gate: every symbol the exes import must be exported by the DLL +# (5 October 2026, 0.3.6: GCC 13 exes with the GCC 16 DLL, "Entry Point Not Found" on both PCs) for dll in lib/libstdc++-6.dll lib/libgcc_s_seh-1.dll bin/libwinpthread-1.dll; do name="$(basename "$dll")" - if [ -f "$MINGW/$dll" ]; then cp "$MINGW/$dll" "$STAGE/"; x86_64-w64-mingw32-strip "$STAGE/$name" 2>/dev/null || true + if [ -f "$REL/$name" ]; then cp "$REL/$name" "$STAGE/"; echo "$name: from next to the exes ($REL)" + elif [ -f "$MINGW/$dll" ]; then cp "$MINGW/$dll" "$STAGE/"; x86_64-w64-mingw32-strip "$STAGE/$name" 2>/dev/null || true; echo "$name: from the Mac toolchain ($MINGW)" else echo "note: $name not in the mingw toolchain (the node exe is linked -static, so it may not need it)"; fi done +"$(dirname "$0")/check-runtime-dlls.sh" "$STAGE" if [ -f "$NVRTC_DIR/igneum-worker-cuda.exe" ]; then cp "$NVRTC_DIR/igneum-worker-cuda.exe" "$STAGE/" for f in "$NVRTC_DIR"/redist/bin/nvrtc*.dll "$NVRTC_DIR"/redist/LICENSE-*.txt "$NVRTC_DIR/THIRD-PARTY.md"; do [ -f "$f" ] && cp "$f" "$STAGE/"; done diff --git a/proto-cuda/windows-node/cross-build.sh b/proto-cuda/windows-node/cross-build.sh index f08328f2..fcd1d9c1 100755 --- a/proto-cuda/windows-node/cross-build.sh +++ b/proto-cuda/windows-node/cross-build.sh @@ -27,7 +27,11 @@ export CXX_x86_64_pc_windows_gnu=x86_64-w64-mingw32-g++ export AR_x86_64_pc_windows_gnu=x86_64-w64-mingw32-ar export LIBCLANG_PATH=/opt/homebrew/opt/llvm/lib export BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_gnu="--target=x86_64-w64-mingw32 --sysroot=$MINGW -I$MINGW/include" -export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS="-C link-arg=-static -C link-arg=-static-libgcc" +# 5 October 2026 (0.3.7): -static-libstdc++ added as a try; -static alone left libstdc++-6.dll in the import table of +# every exe so far (0.3.5's and the PC's), which is why the payload must ship the DLLs of the toolchain that linked +# the exes (push-inputs.sh takes them from next to the exes first, then from this toolchain) and why push-inputs.sh +# refuses an exe that imports a symbol the shipped DLL does not export. +export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS="-C link-arg=-static -C link-arg=-static-libgcc -C link-arg=-static-libstdc++" cd "$NODE" nice -n 19 cargo build --release -j "$JOBS" -p kaspad -p igneum-miner --features igneum-pow --target x86_64-pc-windows-gnu for exe in igneumd igneum-miner; do diff --git a/tools/build-job.mjs b/tools/build-job.mjs index 58c0953a..32ce14c0 100755 --- a/tools/build-job.mjs +++ b/tools/build-job.mjs @@ -157,7 +157,7 @@ async function fetchOutputs(id, finals) { if (o.target === 'linux') { try { chmodSync(plain, 0o755); } catch {} } let note = ''; if (o.target === 'windows') { - const pe = peCheck(plain); + const pe = peCheck(plain, o.name.endsWith('.dll') ? 16 * 1024 : undefined); // the runtime DLLs the PC ships next to the exes are small if (!pe.ok) { console.log(`${o.name}: PE check FAILED: ${pe.problems.join('; ')}`); bad++; continue; } note = ` PE ok (${pe.sections.join(' ')}, subsystem ${pe.subsystem})`; if (o.name === 'igneum-app.exe') {