From 7a4ca7eca76892e2762c78b660f154a51e60a3cc Mon Sep 17 00:00:00 2001 From: Owen Rummage Date: Mon, 20 Jul 2026 13:19:11 -0500 Subject: [PATCH] fixes --- .github/workflows/release.yml | 10 ++++++++ Makefile | 8 ++++++- README.md | 7 +++--- src/app/benchmark.c | 44 ++++++++++++++++++++++++++++------- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1aa5327..595e3f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -111,6 +111,16 @@ jobs: run: | make "${{ matrix.target }}" file "dist/fossbench-${{ matrix.target }}.exe" + if [ "${{ matrix.target }}" = windows-i386 ]; then + pe="dist/fossbench-windows-i386.exe" + i686-w64-mingw32-objdump -p "$pe" > "$RUNNER_TEMP/windows-i386-pe.txt" + grep -Eq 'MajorSubsystemVersion[[:space:]]+5' "$RUNNER_TEMP/windows-i386-pe.txt" + grep -Eq 'MinorSubsystemVersion[[:space:]]+1' "$RUNNER_TEMP/windows-i386-pe.txt" + if grep -Eq 'GetTickCount64|InitializeCriticalSectionEx|CreateThreadpool|WaitOnAddress' "$RUNNER_TEMP/windows-i386-pe.txt"; then + echo 'Windows i386 release imports an API newer than Windows XP' >&2 + exit 1 + fi + fi - name: Package artifact run: | diff --git a/Makefile b/Makefile index de46b42..70eaef7 100644 --- a/Makefile +++ b/Makefile @@ -118,6 +118,12 @@ endif # Windows uses MinGW. CC_WINDOWS_AMD64 ?= x86_64-w64-mingw32-gcc CC_WINDOWS_I386 ?= i686-w64-mingw32-gcc +# Keep the 32-bit executable loadable by the Windows XP loader and prevent the +# SDK headers from exposing APIs newer than XP. The code uses native Win32 +# threads on Windows, so this target does not acquire a libwinpthread runtime +# dependency with a newer OS baseline. +WINDOWS_I386_XP_CFLAGS := -D_WIN32_WINNT=0x0501 -DWINVER=0x0501 -DNTDDI_VERSION=0x05010000 +WINDOWS_I386_XP_LDFLAGS := -Wl,--major-subsystem-version,5,--minor-subsystem-version,1 NATIVE_BIN := $(DIST)/fossbench-$(OSNAME)-$(HOST_ARCHNAME) @@ -185,7 +191,7 @@ $(DIST)/fossbench-windows-amd64.exe: $(DRIVER) $(DRIVER_DEPS) $(ASM_AMD64) | $(D @echo "built $@" $(DIST)/fossbench-windows-i386.exe: $(DRIVER) $(DRIVER_DEPS) $(ASM_I386) | $(DIST) - $(CC_WINDOWS_I386) -march=pentium4 $(CFLAGS) $(PTHREAD) -static -o $@ $(DRIVER) $(ASM_I386) -lm -lwinhttp -ladvapi32 + $(CC_WINDOWS_I386) -march=pentium4 $(WINDOWS_I386_XP_CFLAGS) $(CFLAGS) -static $(WINDOWS_I386_XP_LDFLAGS) -o $@ $(DRIVER) $(ASM_I386) -lm -lwinhttp -ladvapi32 @echo "built $@" # Add a native rule if one was not already made above. diff --git a/README.md b/README.md index 3ab6c85..8d153ad 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,13 @@ score weights. Version 0.1 and 0.2 scores are not comparable. | PowerPC 32-bit, little-endian | scalar fallback with runtime-selected extensions | | PowerPC 64-bit, little-endian | POWER8 with AltiVec/VSX | -Linux, macOS, and Windows are supported. Send a patch if a supported target -fails. +Linux, macOS, and Windows are supported. The Windows i386 release has a Windows +XP (NT 5.1) compatibility baseline. Send a patch if a supported target fails. ## Build -GNU Make, a C compiler, pthreads, and the system math library are required. +GNU Make, a C compiler, and the system math library are required. POSIX builds +also require pthreads; Windows builds use native Win32 threads. Uploads use plain HTTP and require no TLS library. Build for the current machine: diff --git a/src/app/benchmark.c b/src/app/benchmark.c index 729dac4..3a00361 100644 --- a/src/app/benchmark.c +++ b/src/app/benchmark.c @@ -4,10 +4,19 @@ #include #include #include -#include -#include #include +#if defined(_WIN32) +# define WIN32_LEAN_AND_MEAN +# include +# include +# include +# include +#else +# include +# include +#endif + #include "benchmark.h" #include "hw_detect.h" #if defined(__linux__) @@ -60,10 +69,6 @@ /* Get the current time. */ #if defined(_WIN32) -# define WIN32_LEAN_AND_MEAN -# include -# include -# include static double now_seconds(void) { LARGE_INTEGER f, t; @@ -574,19 +579,28 @@ struct job { uint64_t result; }; +#if defined(_WIN32) +static unsigned WINAPI job_entry(void *arg) +#else static void *job_entry(void *arg) +#endif { struct job *j = arg; j->result = j->run(j->n, j->ws); - return NULL; + return 0; } /* Run a kernel on all requested threads. */ static uint64_t dispatch(run_fn run, uint64_t n, int threads) { struct job *jobs = xalloc((size_t)threads * sizeof *jobs); +#if defined(_WIN32) + HANDLE *tids = threads > 1 + ? xalloc((size_t)(threads - 1) * sizeof *tids) : NULL; +#else pthread_t *tids = threads > 1 ? xalloc((size_t)(threads - 1) * sizeof *tids) : NULL; +#endif int i, spawned = 0; uint64_t agg = 0; @@ -596,16 +610,30 @@ static uint64_t dispatch(run_fn run, uint64_t n, int threads) jobs[i].ws = &g_ws[i]; } for (i = 1; i < threads; i++) { +#if defined(_WIN32) + tids[spawned] = (HANDLE)_beginthreadex(NULL, 0, job_entry, &jobs[i], 0, NULL); + if (tids[spawned] != NULL) + spawned++; + else + job_entry(&jobs[i]); /* Run it here if the thread fails. */ +#else if (pthread_create(&tids[spawned], NULL, job_entry, &jobs[i]) == 0) spawned++; else job_entry(&jobs[i]); /* Run it here if the thread fails. */ +#endif } job_entry(&jobs[0]); /* The main thread does the first job. */ - for (i = 0; i < spawned; i++) + for (i = 0; i < spawned; i++) { +#if defined(_WIN32) + WaitForSingleObject(tids[i], INFINITE); + CloseHandle(tids[i]); +#else pthread_join(tids[i], NULL); +#endif + } for (i = 0; i < threads; i++) agg += jobs[i].result;