Igneum Miner 0.3.7: the Windows runtime DLLs come from the toolchain that linked the exes, and a gate refuses a payload whose exe imports a symbol the shipped DLL lacks

0.3.6 on both PCs: igneumd.exe (built on PC 1 with GCC 13's mingw) shipped with the Mac toolchain's GCC 16
libstdc++-6.dll, which no longer exports seven symbols the exe imports (std::codecvt_utf8_utf16 and a
stringbuf::seekpos); Windows refused the node with Entry Point Not Found. -C link-arg=-static had never removed
the libstdc++ import (0.3.5's Mac-built exe carries it too).
- packaging/windows/check-runtime-dlls.sh: objdump imports per DLL against the DLL's exports; shown to fail
  the 0.3.6 pairing (7 missing) and pass 0.3.5's; run by push-inputs.sh before signing and by make-payload.sh
- push-inputs.sh: DLLs next to the exes first, then the Mac toolchain
- jobbuild.rs: the PC's windows stage copies its own toolchain's three DLLs into the pack (unit test);
  build-job.mjs accepts the small DLL PE files and places them next to the exes
- cross-build.sh: -static-libstdc++ added as a try (measured on the 0.3.7 build)
- the six version files: 0.3.7
This commit is contained in:
igneum-labs 2026-10-05 09:46:49 +00:00
parent ea8061ab69
commit a0e092d109
12 changed files with 77 additions and 13 deletions

View file

@ -219,7 +219,7 @@ dependencies = [
[[package]]
name = "igneum-app"
version = "0.3.6"
version = "0.3.7"
dependencies = [
"ed25519-dalek",
"getrandom",

View file

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

View file

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

View file

@ -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"));

View file

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

View file

@ -11,7 +11,7 @@
<key>CFBundleVersion</key>
<string>VERSION_STAMP</string>
<key>CFBundleShortVersionString</key>
<string>0.3.6</string>
<string>0.3.7</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleExecutable</key>

View file

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

View file

@ -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 <folder with igneumd.exe, igneum-miner.exe and lib*.dll>
#
# 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: "<vma> <ordinal or <none>> <hint> <name>"; and under
# "[Ordinal/Name Pointer] Table" one export per line: "[ n] +base[ m] <hint> <name>"
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; }

View file

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

View file

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

View file

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

View file

@ -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') {