From 8bda1d8642764b68f26c45787ed1479a20f1a2dd Mon Sep 17 00:00:00 2001 From: Vladislav Belov Date: Sat, 17 Oct 2026 01:27:35 +0200 Subject: [PATCH] Removes dynamic loading on Windows All required functions are available on all supported Windows versions. --- source/lib/sysdep/os/win/wcpu.cpp | 36 +++------------ source/lib/sysdep/os/win/wfirmware.cpp | 20 +++------ source/lib/sysdep/os/win/wnuma.cpp | 46 +++++--------------- source/lib/sysdep/os/win/wposix/waio.cpp | 32 +++++--------- source/lib/sysdep/os/win/wposix/wutsname.cpp | 11 +++-- source/lib/sysdep/os/win/wutil.h | 34 +-------------- 6 files changed, 42 insertions(+), 137 deletions(-) diff --git a/source/lib/sysdep/os/win/wcpu.cpp b/source/lib/sysdep/os/win/wcpu.cpp index 22bae047d2..5c5e16c419 100644 --- a/source/lib/sysdep/os/win/wcpu.cpp +++ b/source/lib/sysdep/os/win/wcpu.cpp @@ -115,19 +115,11 @@ size_t os_cpu_LargePageSize() if(largePageSize == ~(size_t)0) { - WUTIL_FUNC(pGetLargePageMinimum, SIZE_T, (void)); - WUTIL_IMPORT_KERNEL32(GetLargePageMinimum, pGetLargePageMinimum); - if(pGetLargePageMinimum) - { - largePageSize = pGetLargePageMinimum(); - // Note: checks disabled due to failing on Vista SP2 with old Xeon CPU - // see https://gitea.wildfiregames.com/0ad/0ad/issues/2346 - //ENSURE(largePageSize != 0); // IA-32 and AMD64 definitely support large pages - //ENSURE(largePageSize > os_cpu_PageSize()); - } - // no OS support for large pages - else - largePageSize = 0; + largePageSize = GetLargePageMinimum(); + // Note: checks disabled due to failing on Vista SP2 with old Xeon CPU + // see https://gitea.wildfiregames.com/0ad/0ad/issues/2346 + //ENSURE(largePageSize != 0); // IA-32 and AMD64 definitely support large pages + //ENSURE(largePageSize > os_cpu_PageSize()); } return largePageSize; @@ -214,23 +206,7 @@ uintptr_t wcpu_ProcessorMaskFromAffinity(DWORD_PTR processAffinity, DWORD_PTR af static void VerifyRunningOnCorrectProcessors(DWORD_PTR affinity) { - DWORD currentProcessor; - - // note: NtGetCurrentProcessorNumber and RtlGetCurrentProcessorNumber aren't - // implemented on WinXP SP2. - WUTIL_FUNC(pGetCurrentProcessorNumber, DWORD, (void)); - WUTIL_IMPORT_KERNEL32(GetCurrentProcessorNumber, pGetCurrentProcessorNumber); - if(pGetCurrentProcessorNumber) - currentProcessor = pGetCurrentProcessorNumber(); - else - { - // note: searching for the current APIC ID or IDT address in a - // table won't work because initializing the table also requires - // this function. LSL only works on Vista (which already - // has GetCurrentProcessorNumber). - return; - } - + const DWORD currentProcessor{GetCurrentProcessorNumber()}; ENSURE(IsBitSet(affinity, currentProcessor)); } diff --git a/source/lib/sysdep/os/win/wfirmware.cpp b/source/lib/sysdep/os/win/wfirmware.cpp index b1ebcde578..05bac7fc7d 100644 --- a/source/lib/sysdep/os/win/wfirmware.cpp +++ b/source/lib/sysdep/os/win/wfirmware.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -29,17 +29,12 @@ namespace wfirmware { TableIds GetTableIDs(Provider provider) { - WUTIL_FUNC(pEnumSystemFirmwareTables, UINT, (DWORD, PVOID, DWORD)); - WUTIL_IMPORT_KERNEL32(EnumSystemFirmwareTables, pEnumSystemFirmwareTables); - if(!pEnumSystemFirmwareTables) - return TableIds(); - - const size_t tableIdsSize = pEnumSystemFirmwareTables(provider, 0, 0); + const size_t tableIdsSize = EnumSystemFirmwareTables(provider, 0, 0); ENSURE(tableIdsSize != 0); ENSURE(tableIdsSize % sizeof(TableId) == 0); TableIds tableIDs(DivideRoundUp(tableIdsSize, sizeof(TableId)), 0); - const size_t bytesWritten = pEnumSystemFirmwareTables(provider, &tableIDs[0], (DWORD)tableIdsSize); + const size_t bytesWritten = EnumSystemFirmwareTables(provider, &tableIDs[0], (DWORD)tableIdsSize); ENSURE(bytesWritten == tableIdsSize); return tableIDs; @@ -48,12 +43,7 @@ TableIds GetTableIDs(Provider provider) Table GetTable(Provider provider, TableId tableId) { - WUTIL_FUNC(pGetSystemFirmwareTable, UINT, (DWORD, DWORD, PVOID, DWORD)); - WUTIL_IMPORT_KERNEL32(GetSystemFirmwareTable, pGetSystemFirmwareTable); - if(!pGetSystemFirmwareTable) - return Table(); - - const size_t tableSize = pGetSystemFirmwareTable(provider, tableId, 0, 0); + const size_t tableSize = GetSystemFirmwareTable(provider, tableId, 0, 0); if(tableSize == 0) { DEBUG_WARN_ERR(ERR::LOGIC); @@ -61,7 +51,7 @@ Table GetTable(Provider provider, TableId tableId) } Table table(tableSize, 0); - const size_t bytesWritten = pGetSystemFirmwareTable(provider, tableId, &table[0], (DWORD)tableSize); + const size_t bytesWritten = GetSystemFirmwareTable(provider, tableId, &table[0], (DWORD)tableSize); ENSURE(bytesWritten == tableSize); return table; diff --git a/source/lib/sysdep/os/win/wnuma.cpp b/source/lib/sysdep/os/win/wnuma.cpp index 1289be1f1b..6a4365d2bc 100644 --- a/source/lib/sysdep/os/win/wnuma.cpp +++ b/source/lib/sysdep/os/win/wnuma.cpp @@ -32,7 +32,6 @@ #include "lib/sysdep/os_cpu.h" #include "lib/sysdep/os/win/acpi.h" #include "lib/sysdep/os/win/win.h" -#include "lib/sysdep/os/win/wutil.h" #include "lib/sysdep/os/win/wcpu.h" #include @@ -95,24 +94,14 @@ static Node* FindNodeWithProcessor(size_t processor) static UCHAR HighestNodeNumber() { - WUTIL_FUNC(pGetNumaHighestNodeNumber, BOOL, (PULONG)); - WUTIL_IMPORT_KERNEL32(GetNumaHighestNodeNumber, pGetNumaHighestNodeNumber); - if(!pGetNumaHighestNodeNumber) - return 0; // NUMA not supported => only one node - - ULONG highestNodeNumber; - const BOOL ok = pGetNumaHighestNodeNumber(&highestNodeNumber); + ULONG highestNodeNumber{0}; + const BOOL ok = GetNumaHighestNodeNumber(&highestNodeNumber); WARN_IF_FALSE(ok); return (UCHAR)highestNodeNumber; } static void PopulateNodes() { - WUTIL_FUNC(pGetNumaNodeProcessorMask, BOOL, (UCHAR, PULONGLONG)); - WUTIL_IMPORT_KERNEL32(GetNumaNodeProcessorMask, pGetNumaNodeProcessorMask); - if(!pGetNumaNodeProcessorMask) - return; - DWORD_PTR processAffinity, systemAffinity; { const BOOL ok = GetProcessAffinityMask(GetCurrentProcess(), &processAffinity, &systemAffinity); @@ -122,9 +111,9 @@ static void PopulateNodes() for(UCHAR nodeNumber = 0; nodeNumber <= HighestNodeNumber(); nodeNumber++) { - ULONGLONG affinity; + ULONGLONG affinity{0}; { - const BOOL ok = pGetNumaNodeProcessorMask(nodeNumber, &affinity); + const BOOL ok = GetNumaNodeProcessorMask(nodeNumber, &affinity); WARN_IF_FALSE(ok); } if(!affinity) @@ -339,20 +328,12 @@ size_t numa_AvailableMemory(size_t node) // note: it is said that GetNumaAvailableMemoryNode sometimes incorrectly // reports zero bytes. the actual cause may however be unexpected // RAM configuration, e.g. not all slots filled. - WUTIL_FUNC(pGetNumaAvailableMemoryNode, BOOL, (UCHAR, PULONGLONG)); - WUTIL_IMPORT_KERNEL32(GetNumaAvailableMemoryNode, pGetNumaAvailableMemoryNode); - if(pGetNumaAvailableMemoryNode) - { - const UCHAR nodeNumber = NodeNumberFromNode(node); - ULONGLONG availableBytes; - const BOOL ok = pGetNumaAvailableMemoryNode(nodeNumber, &availableBytes); - WARN_IF_FALSE(ok); - const size_t availableMiB = size_t(availableBytes / MiB); - return availableMiB; - } - // NUMA not supported - return available system memory - else - return os_cpu_MemoryAvailable(); + const UCHAR nodeNumber = NodeNumberFromNode(node); + ULONGLONG availableBytes{0}; + const BOOL ok = GetNumaAvailableMemoryNode(nodeNumber, &availableBytes); + WARN_IF_FALSE(ok); + const size_t availableMiB = size_t(availableBytes / MiB); + return availableMiB; } @@ -478,11 +459,6 @@ bool numa_IsMemoryInterleaved() static bool VerifyPages([[maybe_unused]] void* mem, [[maybe_unused]] size_t size, [[maybe_unused]] size_t pageSize, [[maybe_unused]] size_t node) { - WUTIL_FUNC(pQueryWorkingSetEx, BOOL, (HANDLE, PVOID, DWORD)); - WUTIL_IMPORT_KERNEL32(QueryWorkingSetEx, pQueryWorkingSetEx); - if(!pQueryWorkingSetEx) - return true; // can't do anything - #if WINVER >= 0x600 size_t largePageSize = os_cpu_LargePageSize(); ENSURE(largePageSize != 0); // this value is needed for later @@ -492,7 +468,7 @@ static bool VerifyPages([[maybe_unused]] void* mem, [[maybe_unused]] size_t size PSAPI_WORKING_SET_EX_INFORMATION* wsi = new PSAPI_WORKING_SET_EX_INFORMATION[numPages]; for(size_t i = 0; i < numPages; i++) wsi[i].VirtualAddress = static_cast(mem) + i*pageSize; - pQueryWorkingSetEx(GetCurrentProcess(), wsi, DWORD(sizeof(PSAPI_WORKING_SET_EX_INFORMATION)*numPages)); + QueryWorkingSetEx(GetCurrentProcess(), wsi, DWORD(sizeof(PSAPI_WORKING_SET_EX_INFORMATION)*numPages)); // ensure each is valid and allocated on the correct node for(size_t i = 0; i < numPages; i++) diff --git a/source/lib/sysdep/os/win/wposix/waio.cpp b/source/lib/sysdep/os/win/wposix/waio.cpp index b410866728..dadfb9cd3e 100644 --- a/source/lib/sysdep/os/win/wposix/waio.cpp +++ b/source/lib/sysdep/os/win/wposix/waio.cpp @@ -42,11 +42,10 @@ #include #include +#include -// (dynamic linking preserves compatibility with previous Windows versions) -static WUTIL_FUNC(pSetFileCompletionNotificationModes, BOOL, (HANDLE, UCHAR)); -static WUTIL_FUNC(pSetFileIoOverlappedRange, BOOL, (HANDLE, PUCHAR, ULONG)); -static WUTIL_FUNC(pSetFileValidData, BOOL, (HANDLE, LONGLONG)); +static bool useSetFileIoOverlappedRange{false}; +static bool useSetFileValidData{false}; // (there must be one global IOCP because aio_suspend might be called for // requests from different files) @@ -189,8 +188,8 @@ struct OvlAllocator // POD // pin the page in kernel address space, which means our thread // won't have to be the one to service the I/O, thus avoiding an APC. // ("thread agnostic I/O") - if(pSetFileIoOverlappedRange) - WARN_IF_FALSE(pSetFileIoOverlappedRange(hFile, (PUCHAR)storage, storageSize)); + if(useSetFileIoOverlappedRange) + WARN_IF_FALSE(SetFileIoOverlappedRange(hFile, (PUCHAR)storage, storageSize)); } // @return OVERLAPPED initialized for I/O starting at offset, @@ -270,14 +269,9 @@ struct FileControlBlock // POD AttachToCompletionPort(hFile, hIOCP, (ULONG_PTR)this); - // minor optimization: avoid posting to IOCP in rare cases - // where the I/O completes synchronously - if(pSetFileCompletionNotificationModes) - { - // (for reasons as yet unknown, this fails when the file is - // opened for read-only access) - (void)pSetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS); - } + // (for reasons as yet unknown, this fails when the file is + // opened for read-only access) + std::ignore = SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS); return INFO::OK; } @@ -374,17 +368,15 @@ static Status waio_Init() { fileControlBlocks.Init(); - WUTIL_IMPORT_KERNEL32(SetFileCompletionNotificationModes, pSetFileCompletionNotificationModes); - // NB: using these functions when the privileges are not available would // trigger warnings. since callers have to check the function pointers // anyway, just refrain from setting them in such cases. if(wutil_SetPrivilege(L"SeLockMemoryPrivilege", true) == INFO::OK) - WUTIL_IMPORT_KERNEL32(SetFileIoOverlappedRange, pSetFileIoOverlappedRange); + useSetFileIoOverlappedRange = true; if(wutil_SetPrivilege(L"SeManageVolumePrivilege", true) == INFO::OK) - WUTIL_IMPORT_KERNEL32(SetFileValidData, pSetFileValidData); + useSetFileValidData = true; return INFO::OK; } @@ -467,7 +459,7 @@ Status waio_Preallocate(int fd, off_t size) } // avoid synchronous zero-fill (see discussion in header) - if(pSetFileValidData) + if(useSetFileValidData) { // this has been reported (#849) to fail with GetLastError() == 0 for // both tiny and medium alignedSize, despite Administrator privileges. @@ -477,7 +469,7 @@ Status waio_Preallocate(int fd, off_t size) // verifying the filesystem is indeed FAT would be overkill; we'll just // ignore the return code. however, GetLastError can be used to check // whether other problems arose. - (void)pSetFileValidData(hFile, alignedSize); + std::ignore = SetFileValidData(hFile, alignedSize); ENSURE(GetLastError() == 0); } diff --git a/source/lib/sysdep/os/win/wposix/wutsname.cpp b/source/lib/sysdep/os/win/wposix/wutsname.cpp index ff3b5e5b2d..5eb1f1b942 100644 --- a/source/lib/sysdep/os/win/wposix/wutsname.cpp +++ b/source/lib/sysdep/os/win/wposix/wutsname.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -71,9 +71,12 @@ static DWORD GetNtdllVersion() */ int uname(struct utsname* un) { - WUTIL_FUNC(pRtlGetVersion, NTSTATUS, (LPOSVERSIONINFOEXW)); - WUTIL_IMPORT_NTDLL(RtlGetVersion, pRtlGetVersion); - + // note: ntdll is guaranteed to be loaded, so we don't + // need to LoadLibrary and FreeLibrary. + using RtlGetVersionFunction = NTSTATUS (WINAPI*) (LPOSVERSIONINFOEXW); + RtlGetVersionFunction pRtlGetVersion{ + reinterpret_cast( + GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion"))}; if (!pRtlGetVersion) return -1; diff --git a/source/lib/sysdep/os/win/wutil.h b/source/lib/sysdep/os/win/wutil.h index 27309245e1..261ef1cb96 100644 --- a/source/lib/sysdep/os/win/wutil.h +++ b/source/lib/sysdep/os/win/wutil.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -40,38 +40,6 @@ bool wutil_IsValidHandle(H h) return h != 0 && h != INVALID_HANDLE_VALUE; } - -//----------------------------------------------------------------------------- -// dynamic linking - -// define a function pointer (optionally prepend 'static') -#define WUTIL_FUNC(varName, ret, params)\ - ret (WINAPI* varName) params - -// rationale: -// - splitting up WUTIL_FUNC and WUTIL_IMPORT is a bit verbose in -// the common case of a local function pointer definition, -// but allows one-time initialization of static variables. -// - differentiating between procName and varName allows searching -// for the actual definition of the function pointer in the code. -// - a cast would require passing in ret/params. -// - writing a type-punned pointer breaks strict-aliasing rules. -#define WUTIL_IMPORT(hModule, procName, varName)\ - STMT(\ - const FARPROC f = GetProcAddress(hModule, #procName);\ - memcpy(&varName, &f, sizeof(FARPROC));\ - ) - -// note: Kernel32 is guaranteed to be loaded, so we don't -// need to LoadLibrary and FreeLibrary. -#define WUTIL_IMPORT_KERNEL32(procName, varName)\ - WUTIL_IMPORT(GetModuleHandleW(L"kernel32.dll"), procName, varName) - -// note: ntdll is guaranteed to be loaded, so we don't -// need to LoadLibrary and FreeLibrary. -#define WUTIL_IMPORT_NTDLL(procName, varName)\ - WUTIL_IMPORT(GetModuleHandleW(L"ntdll.dll"), procName, varName) - //----------------------------------------------------------------------------- // safe allocator