Removes dynamic loading on Windows

All required functions are available on all supported Windows versions.
This commit is contained in:
Vladislav Belov committed 2026-10-17 01:50:46 +02:00
1 parent 862bdd5349
commit 8bda1d8642
6 files changed
+42 -137

No files matched your search

+6 -30
View File
@@ -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));
}
+5 -15
View File
@@ -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;
+11 -35
View File
@@ -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 <bit>
@@ -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<std::uint8_t*>(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++)
+12 -20
View File
@@ -42,11 +42,10 @@
#include <atomic>
#include <ctime>
#include <tuple>
// (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);
}
+7 -4
View File
@@ -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<RtlGetVersionFunction>(
GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion"))};
if (!pRtlGetVersion)
return -1;
+1 -33
View File
@@ -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