diff --git a/emp-tool/cmake/emp-base.cmake b/emp-tool/cmake/emp-base.cmake index 8e77d76..ae35cf9 100644 --- a/emp-tool/cmake/emp-base.cmake +++ b/emp-tool/cmake/emp-base.cmake @@ -59,6 +59,14 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -Wall -funroll-loops -Wno-ignored-a message("${Blue}-- Platform: ${CMAKE_SYSTEM_PROCESSOR}${ColourReset}") IF(${CMAKE_SYSTEM_PROCESSOR} MATCHES "(aarch64)|(arm64)") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a+simd+crypto+crc") +ELSEIF(WIN32) +# Windows (MinGW-w64): pin a distributable ISA baseline instead of -march=native, +# so the artifact runs on any x86-64-v2-class CPU (AES-NI, SSE4.2, PCLMUL). +# NOTE: -mrdseed is intentionally omitted. emp's PRG only uses _rdseed64_step under +# the ENABLE_RDSEED macro, which the build never defines (enable_rdseed.cmake defines +# the differently-named EMP_ENABLE_RDSEED), so PRG always seeds via std::random_device. +# Keeping RDSEED out of the baseline avoids implying a dependency on that instruction. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes -msse4.2 -mpclmul") ELSE(${CMAKE_SYSTEM_PROCESSOR} MATCHES "(aarch64)|(arm64)") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native -maes -mrdseed") ENDIF(${CMAKE_SYSTEM_PROCESSOR} MATCHES "(aarch64)|(arm64)" ) diff --git a/emp-tool/emp-tool/circuits/circuit_file.h b/emp-tool/emp-tool/circuits/circuit_file.h index 4b6f5e0..b0c5e4f 100644 --- a/emp-tool/emp-tool/circuits/circuit_file.h +++ b/emp-tool/emp-tool/circuits/circuit_file.h @@ -5,6 +5,7 @@ #include "emp-tool/execution/protocol_execution.h" #include "emp-tool/utils/block.h" #include "emp-tool/circuits/bit.h" +#include "emp-tool/utils/win_compat.h" // fmemopen shim on Windows (no-op elsewhere) #include #include diff --git a/emp-tool/emp-tool/io/highspeed_net_io_channel.h b/emp-tool/emp-tool/io/highspeed_net_io_channel.h index 6673907..6dcf0fe 100644 --- a/emp-tool/emp-tool/io/highspeed_net_io_channel.h +++ b/emp-tool/emp-tool/io/highspeed_net_io_channel.h @@ -11,12 +11,7 @@ #include "emp-tool/io/io_channel.h" using std::string; -#include -#include -#include -#include -#include -#include +#include "emp-tool/utils/win_compat.h" namespace emp { @@ -212,12 +207,13 @@ class HighSpeedNetIO : public IOChannel { public: void sync() {} void set_delay_opt(int sock, bool enable_nodelay) { + // cast to const char* for Winsock compatibility (no-op on POSIX) if (enable_nodelay) { const int one = 1; - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&one, sizeof(one)); } else { const int zero = 0; - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &zero, sizeof(zero)); + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&zero, sizeof(zero)); } } diff --git a/emp-tool/emp-tool/io/net_io_channel.h b/emp-tool/emp-tool/io/net_io_channel.h index a5d6ab9..d62e269 100644 --- a/emp-tool/emp-tool/io/net_io_channel.h +++ b/emp-tool/emp-tool/io/net_io_channel.h @@ -10,12 +10,7 @@ using std::string; -#include -#include -#include -#include -#include -#include +#include "emp-tool/utils/win_compat.h" namespace emp { @@ -111,12 +106,14 @@ class NetIO: public IOChannel { public: void set_nodelay() { const int one=1; - setsockopt(consocket,IPPROTO_TCP,TCP_NODELAY,&one,sizeof(one)); + // cast to const char* so it also compiles under Winsock (setsockopt takes + // const char* on Windows vs const void* on POSIX); no-op on POSIX. + setsockopt(consocket,IPPROTO_TCP,TCP_NODELAY,(const char*)&one,sizeof(one)); } void set_delay() { const int zero = 0; - setsockopt(consocket,IPPROTO_TCP,TCP_NODELAY,&zero,sizeof(zero)); + setsockopt(consocket,IPPROTO_TCP,TCP_NODELAY,(const char*)&zero,sizeof(zero)); } void flush() { diff --git a/emp-tool/emp-tool/utils/block.h b/emp-tool/emp-tool/utils/block.h index 6f77881..ac05df7 100644 --- a/emp-tool/emp-tool/utils/block.h +++ b/emp-tool/emp-tool/utils/block.h @@ -20,6 +20,7 @@ inline __m128i _mm_aesdeclast_si128 (__m128i a, __m128i RoundKey) #endif #include +#include #include #include #include diff --git a/emp-tool/emp-tool/utils/constants.h b/emp-tool/emp-tool/utils/constants.h index 9980693..5dae493 100644 --- a/emp-tool/emp-tool/utils/constants.h +++ b/emp-tool/emp-tool/utils/constants.h @@ -1,5 +1,6 @@ #ifndef EMP_CONFIG_H__ #define EMP_CONFIG_H__ +#include namespace emp { const static int AES_BATCH_SIZE = 8; const static int HASH_BUFFER_SIZE = 1024*8; diff --git a/emp-tool/emp-tool/utils/prg.h b/emp-tool/emp-tool/utils/prg.h index 83f136e..ef1779a 100644 --- a/emp-tool/emp-tool/utils/prg.h +++ b/emp-tool/emp-tool/utils/prg.h @@ -26,7 +26,12 @@ class PRG { public: block v; #ifndef ENABLE_RDSEED uint32_t * data = (uint32_t *)(&v); +#ifdef _WIN32 + // Windows has no /dev/urandom; the default token uses the OS CSPRNG (rand_s). + std::random_device rand_div; +#else std::random_device rand_div("/dev/urandom"); +#endif for (size_t i = 0; i < sizeof(block) / sizeof(uint32_t); ++i) data[i] = rand_div(); #else diff --git a/emp-tool/emp-tool/utils/win_compat.h b/emp-tool/emp-tool/utils/win_compat.h new file mode 100644 index 0000000..9eedd2a --- /dev/null +++ b/emp-tool/emp-tool/utils/win_compat.h @@ -0,0 +1,104 @@ +#ifndef EMP_UTIL_WIN_COMPAT_H__ +#define EMP_UTIL_WIN_COMPAT_H__ + +// Windows (MinGW-w64) portability shim for emp-toolkit. +// +// MinGW already provides and (winpthread), so only the +// BSD-socket headers and a couple of POSIX helpers are missing on Windows. +// This header centralizes those differences so the network I/O channels COMPILE +// under MinGW. +// +// SCOPE (P1): compile-only. emp's own NetIO/HighSpeedNetIO socket *runtime* path +// is intentionally NOT ported here — it still uses close() (not closesocket), +// never calls WSAStartup, and does not translate Winsock error codes. That is +// deferred to P2. In pado, real traffic goes through pado's own proxy channel, +// so emp's NetIO is only used as a template type and is not exercised at runtime. +// emp_wsa_init() below is the hook P2 will wire into the NetIO constructor. + +#ifdef _WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include + +// The NetIO/HighSpeedNetIO inline method bodies name ssize_t and close(); their +// declarations must be visible even though the socket runtime is not exercised in +// P1 (pado uses its own proxy channel). MinGW-w64 provides these headers. +// NOTE (P1 compile-only): close() here is the CRT file-descriptor close(), NOT a +// socket close — real socket cleanup (closesocket) is deferred to P2. +#include // ssize_t +#include // close() + +#if defined(_MSC_VER) +#pragma comment(lib, "ws2_32.lib") +#endif + +#include +#include +#include +#include // _open_osfhandle, _close +#include // _O_RDWR, _O_BINARY + +// POSIX usleep() shim (argument is microseconds). Windows Sleep() is ms-granular. +#ifndef usleep +static inline void emp_usleep(uint64_t usec) { Sleep((DWORD)((usec + 999) / 1000)); } +#define usleep(us) emp_usleep((uint64_t)(us)) +#endif + +// POSIX fmemopen() shim. MinGW-w64's CRT has no fmemopen, so BristolFormat's +// "load circuit from an in-memory string" ctor won't compile/link on Windows. +// Back it with a Win32 temp file opened FILE_FLAG_DELETE_ON_CLOSE: the file is +// removed automatically when the handle closes (or at process exit), so nothing +// is left on disk. Binary mode is used deliberately — fmemopen does no newline +// translation either, and the Bristol parser reads with fscanf on whitespace. +#ifndef fmemopen +static inline FILE* emp_fmemopen(const void* buf, size_t size, const char* mode) { + (void)mode; + char dir[MAX_PATH]; + DWORD n = GetTempPathA(MAX_PATH, dir); + if (n == 0 || n > MAX_PATH) return NULL; + char path[MAX_PATH]; + if (GetTempFileNameA(dir, "emp", 0, path) == 0) return NULL; + HANDLE h = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, + NULL); + if (h == INVALID_HANDLE_VALUE) return NULL; + int fd = _open_osfhandle((intptr_t)h, _O_RDWR | _O_BINARY); + if (fd == -1) { CloseHandle(h); return NULL; } + FILE* f = _fdopen(fd, "wb+"); + if (!f) { _close(fd); return NULL; } + if (size) fwrite(buf, 1, size, f); + fflush(f); + fseek(f, 0, SEEK_SET); + return f; +} +#define fmemopen(buf, size, mode) emp_fmemopen((buf), (size), (mode)) +#endif + +// One-time Winsock initialization; safe to call repeatedly. +static inline void emp_wsa_init() { + static std::once_flag once; + std::call_once(once, []() { + WSADATA data; + WSAStartup(MAKEWORD(2, 2), &data); + }); +} + +#else // !_WIN32 (POSIX) + +#include +#include +#include +#include +#include +#include + +static inline void emp_wsa_init() {} + +#endif // _WIN32 + +#endif // EMP_UTIL_WIN_COMPAT_H__