Files
nt-lite/code/user/personalities/win32/kernel32.c
T
maksw 73abd6f90d isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
2026-09-16 10:33:20 +02:00

3862 lines
136 KiB
C

// Win32 kernel32 compatibility layer. This file maps common Win32 process, file, console, heap, and timing APIs onto native syscalls.
// Thin wrappers around syscall.dll stubs that expose a familiar Win32 API
// surface to user-mode code originally written for Windows.
// Design notes:
// - HANDLE is void*; we store the uint32_t kernel handle as a pointer value
// so Win32 code can pass it around opaquely without knowing its size.
// HANDLE_TO_KH / KH_TO_HANDLE do that conversion in one place.
// - INVALID_HANDLE_VALUE maps to kernel handle 0 (the always-invalid slot).
// - HeapAlloc / HeapFree / HeapReAlloc ignore the HANDLE heap argument and
// always use the single user-mode heap. This is fine: we don't expose
// multiple heaps, and NULL is a legal (no-op) heap argument under Win32.
// - Win32 per-thread state lives behind SYS_TEB::SubsystemData so the native
// TEB ABI stays subsystem-neutral.
// - File I/O resolves Win32 paths in userspace, then uses capability-relative
// opens plus the common read/write/seek/query/close syscalls.
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/syscall.h>
#include <ntlite/input_service.h>
#include <ntlite/console_service.h>
#include <ntlite/exception.h>
#include <ntlite/process_loader.h>
#include <ntlite/path.h>
#include <ntlite/module_loader.h>
#include <ntlite/resource.h>
extern size_t _malloc_usable_size(const void* ptr);
// Types.
typedef void* HANDLE;
typedef unsigned long DWORD;
typedef unsigned long long DWORD64;
typedef unsigned short WORD;
typedef int BOOL;
typedef unsigned char BYTE;
typedef short SHORT;
typedef long LONG;
typedef struct { LONG x; LONG y; } POINT;
typedef struct { SHORT X; SHORT Y; } COORD;
typedef struct { SHORT Left; SHORT Top; SHORT Right; SHORT Bottom; } SMALL_RECT;
typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION;
typedef struct { void* _ptr; } CONDITION_VARIABLE;
// Forward declarations used by the process-local GetProcAddress surface.
__declspec(dllexport) HANDLE CreateEventA(void*, BOOL, BOOL, const char*);
__declspec(dllexport) HANDLE CreateEventW(void*, BOOL, BOOL, const unsigned short*);
__declspec(dllexport) BOOL SetEvent(HANDLE);
__declspec(dllexport) BOOL ResetEvent(HANDLE);
__declspec(dllexport) _Noreturn void ExitThread(DWORD);
__declspec(dllexport) HANDLE CreateThread(void*, size_t, void*, void*, DWORD, DWORD*);
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE, DWORD);
__declspec(dllexport) HANDLE CreateMutexA(void*, BOOL, const char*);
__declspec(dllexport) HANDLE CreateMutexW(void*, BOOL, const unsigned short*);
__declspec(dllexport) BOOL ReleaseMutex(HANDLE);
__declspec(dllexport) BOOL CloseHandle(HANDLE);
__declspec(dllexport) void InitializeCriticalSection(CRITICAL_SECTION*);
__declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION*, DWORD);
__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION*);
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION*);
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION*);
__declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION*);
__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE*);
__declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE*, CRITICAL_SECTION*, DWORD);
__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE*);
__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE*);
__declspec(dllexport) BOOL IsDebuggerPresent(void);
extern void waveOutGetNumDevs(void);
extern void waveOutOpen(void);
extern void waveOutClose(void);
extern void waveOutPrepareHeader(void);
extern void waveOutUnprepareHeader(void);
extern void waveOutWrite(void);
extern void waveOutPause(void);
extern void waveOutRestart(void);
extern void waveOutReset(void);
extern void waveOutSetVolume(void);
extern void waveOutGetVolume(void);
extern void waveOutGetPosition(void);
extern void waveOutGetID(void);
extern void waveOutGetDevCapsA(void);
extern void waveOutGetDevCapsW(void);
extern void waveOutGetErrorTextA(void);
extern void waveOutGetErrorTextW(void);
extern void waveOutBreakLoop(void);
extern void waveOutMessage(void);
extern void timeGetTime(void);
extern void timeBeginPeriod(void);
extern void timeEndPeriod(void);
typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
COORD dwSize;
COORD dwCursorPosition;
WORD wAttributes;
SMALL_RECT srWindow;
COORD dwMaximumWindowSize;
} CONSOLE_SCREEN_BUFFER_INFO;
#define MAX_PATH 260
#define TRUE 1
#define FALSE 0
#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1)
#define MEM_COMMIT 0x00001000UL
#define MEM_RESERVE 0x00002000UL
#define MEM_RELEASE 0x00008000UL
#define MEM_FREE 0x00010000UL
#define MEM_PRIVATE 0x00020000UL
#define MEM_MAPPED 0x00040000UL
#define PAGE_NOACCESS 0x01UL
#define PAGE_READONLY 0x02UL
#define PAGE_READWRITE 0x04UL
#define PAGE_WRITECOPY 0x08UL
#define PAGE_EXECUTE 0x10UL
#define PAGE_EXECUTE_READ 0x20UL
#define PAGE_EXECUTE_READWRITE 0x40UL
#define SEC_COMMIT 0x08000000UL
#define FILE_MAP_COPY 0x00000001UL
#define FILE_MAP_WRITE 0x00000002UL
#define FILE_MAP_READ 0x00000004UL
#define FILE_MAP_EXECUTE 0x00000020UL
#define FILE_MAP_ALL_ACCESS 0x000F001FUL
typedef struct _MEMORY_BASIC_INFORMATION {
void* BaseAddress;
void* AllocationBase;
DWORD AllocationProtect;
size_t RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION;
// Implements the win Protect To Sys Vm operation.
static uint32_t WinProtectToSysVm(DWORD protect) {
switch (protect) {
case PAGE_NOACCESS: return SYS_VM_NOACCESS;
case PAGE_READONLY: return SYS_VM_READ;
case PAGE_READWRITE:
case PAGE_WRITECOPY: return SYS_VM_READWRITE;
case PAGE_EXECUTE: return SYS_VM_EXECUTE;
case PAGE_EXECUTE_READ: return SYS_VM_EXECUTE_READ;
case PAGE_EXECUTE_READWRITE: return SYS_VM_EXECUTE_READWRITE;
default: return SYS_VM_READWRITE;
}
}
static uint32_t WinFileMappingProtectToSysVm(DWORD protect) {
// Strip section attributes; the low byte carries PAGE_* protection.
DWORD pageProtect = protect & 0xFFu;
if (pageProtect == PAGE_WRITECOPY)
return SYS_VM_READ; // MapViewOfFile(FILE_MAP_COPY) asks for the private writable view.
return WinProtectToSysVm(pageProtect ? pageProtect : PAGE_READONLY);
}
static uint32_t WinFileMapAccessToSys(DWORD desiredAccess) {
uint32_t access = 0;
if (desiredAccess & FILE_MAP_EXECUTE) access |= SYS_MAP_ACCESS_EXECUTE;
if (desiredAccess & FILE_MAP_COPY) access |= SYS_MAP_ACCESS_COPY;
if (desiredAccess & FILE_MAP_WRITE) access |= SYS_MAP_ACCESS_WRITE;
if (desiredAccess & FILE_MAP_READ) access |= SYS_MAP_ACCESS_READ;
if ((desiredAccess & FILE_MAP_ALL_ACCESS) == FILE_MAP_ALL_ACCESS)
access |= SYS_MAP_ACCESS_READ | SYS_MAP_ACCESS_COPY;
if (!access) access = SYS_MAP_ACCESS_READ;
return access;
}
// Implements the protect Vm To Win operation.
static DWORD SysProtectVmToWin(uint32_t protect) {
switch (protect) {
case SYS_VM_NOACCESS: return PAGE_NOACCESS;
case SYS_VM_READ: return PAGE_READONLY;
case SYS_VM_READWRITE: return PAGE_READWRITE;
case SYS_VM_EXECUTE: return PAGE_EXECUTE;
case SYS_VM_EXECUTE_READ: return PAGE_EXECUTE_READ;
case SYS_VM_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE;
default: return PAGE_NOACCESS;
}
}
// File-find structures.
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME;
typedef struct _SYSTEMTIME {
unsigned short wYear;
unsigned short wMonth;
unsigned short wDayOfWeek;
unsigned short wDay;
unsigned short wHour;
unsigned short wMinute;
unsigned short wSecond;
unsigned short wMilliseconds;
} SYSTEMTIME;
typedef struct _WIN32_FIND_DATAA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
char cFileName[260]; // MAX_PATH
char cAlternateFileName[14];
} WIN32_FIND_DATAA;
// CreateFileA disposition / access flags (subset used by Bink and similar).
#define GENERIC_READ 0x80000000UL
#define GENERIC_WRITE 0x40000000UL
#define GENERIC_READ 0x80000000UL
#define GENERIC_WRITE 0x40000000UL
#define OPEN_EXISTING 3
#define OPEN_ALWAYS 4
#define CREATE_ALWAYS 2
#define CREATE_NEW 1
#define TRUNCATE_EXISTING 5
#define FILE_BEGIN 0
#define FILE_CURRENT 1
#define FILE_END 2
// Handle conversion helpers
// uint32_t kernel handle <-> opaque Win32 HANDLE
// Kernel handle 0 == invalid; we map that to INVALID_HANDLE_VALUE (NULL).
static inline HANDLE KH_TO_HANDLE(uint32_t kh) { return (HANDLE)(uintptr_t)kh; }
static inline uint32_t HANDLE_TO_KH(HANDLE h) { return (uint32_t)(uintptr_t)h; }
static inline BOOL IS_VALID_KERNEL_HANDLE(HANDLE h) {
uintptr_t v = (uintptr_t)h;
return v != 0 && v != (uintptr_t)INVALID_HANDLE_VALUE;
}
// Translates Win32 path syntax into native resource/CWD syntax.
static void TranslatePath(const char* input, char* output) {
if (!output) return;
if (!input) { output[0] = '\0'; return; }
const char* src = input;
char* dst = output;
if (src[0] != '\0' && src[1] == ':' && (src[2] == '\\' || src[2] == '/')) {
char drive = src[0];
if (drive >= 'a' && drive <= 'z') drive = (char)(drive - ('a' - 'A'));
if (drive != 'C') { output[0] = '\0'; return; }
memcpy(dst, "/SystemRoot", 11u);
dst += 11u;
src += 3;
if (*src) *dst++ = '/';
} else if (*src == '\\' || *src == '/') {
memcpy(dst, "/SystemRoot", 11u);
dst += 11u;
while (*src == '\\' || *src == '/') src++;
if (*src) *dst++ = '/';
}
while (*src) {
*dst++ = (*src == '\\') ? '/' : *src;
src++;
}
*dst = '\0';
}
// Implements the k32 Open File Native operation.
static uint32_t K32OpenFileNative(const char* path,
uint32_t access,
uint32_t disposition,
uint32_t* handleOut) {
return NtlOpenFile(SysSvFromCstr(path), access, disposition, SYS_FILE_FLAG_NONE, handleOut);
}
// Implements the k32 Delete File Native operation.
static uint32_t K32DeleteFileNative(const char* path) {
return NtlDeleteFile(SysSvFromCstr(path));
}
// Implements the k32 Open Dir Native operation.
static uint32_t K32OpenDirNative(const char* path, uint32_t* handleOut) {
return NtlOpenDir(SysSvFromCstr(path), SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN,
SYS_DIR_FLAG_NONE, handleOut);
}
// Implements the k32 Set Cwd Native operation.
static uint32_t K32SetCwdNative(const char* path) {
return NtlSetCurrentDirectory(SysSvFromCstr(path));
}
// Implements the k32 Get Cwd Native operation.
static uint32_t K32GetCwdNative(char* buffer, size_t capacity, size_t* lengthOut) {
size_t required = 0;
uint32_t status = NtlGetCurrentDirectory(buffer, capacity, &required);
if (lengthOut) *lengthOut = required ? required - 1u : 0;
return status;
}
// Resolves packaged personality import aliases entirely in userspace.
static KSTATUS K32ResolveModuleName(void* context, SYS_STRING_VIEW requested, SYS_STRING_VIEW* canonicalOut) {
return NtlResolveSystemModuleName(context, requested, canonicalOut);
}
// Translates Win32 process syntax into native resource/CWD syntax.
static uint32_t K32ProcessPath(const char* input, char* output, size_t outputSize) {
if (!input || !output || outputSize == 0) return NTLSTATUS_INVALID_PARAMETER;
char translated[MAX_PATH];
TranslatePath(input, translated);
if (!translated[0]) return NTLSTATUS_NOT_FOUND;
size_t length = strlen(translated);
if (length + 1u > outputSize) return NTLSTATUS_BUFFER_TOO_SMALL;
memcpy(output, translated, length + 1u);
return NTLSTATUS_SUCCESS;
}
// Creates one Win32 child through RSSRV lookup plus the userspace PE loader.
static uint32_t K32CreateProcessNative(const char* path, uint32_t imageFile, uint32_t* handleOut, const char* commandLine, const char* currentDirectory) {
char relative[MAX_PATH];
uint32_t status = K32ProcessPath(path, relative, sizeof(relative));
if (status != NTLSTATUS_SUCCESS) return status;
NTL_CREATE_SYSTEM_PROCESS_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
req.ImageFile = imageFile;
req.Path = SysSvFromCstr(relative);
req.CommandLine = SysSvFromCstr(commandLine);
req.CurrentDirectory = currentDirectory ? SysSvFromCstr(currentDirectory) : SysSvFromParts((const char*)0, 0);
req.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT;
req.ResolveModuleName = K32ResolveModuleName;
req.ProcessHandleOut = handleOut;
return NtlCreateSystemProcess(&req);
}
// Implements the k32 Write Console Span operation.
static void K32WriteConsoleSpan(const char* data, size_t length) {
(void)NtlConsoleWrite(SYS_HANDLE_INVALID, data, length, 0);
}
// Implements the k32 Debug Print A operation.
static void K32DebugPrintA(const char* s) {
if (!s) return;
SYS_PRINT_DEBUG_REQUEST req = { sizeof(SYS_PRINT_DEBUG_REQUEST), SysSvFromCstr(s) };
SysPrintDebug(&req);
}
#define WIN32_TLS_SLOTS 64
#define FLS_MAX_SLOTS 64
#define FLS_OUT_OF_INDEXES 0xFFFFFFFFUL
typedef struct WIN32_THREAD_ENV {
DWORD LastError;
DWORD Reserved0;
void* TlsSlots[WIN32_TLS_SLOTS];
void* FlsSlots[FLS_MAX_SLOTS];
void* ExceptionFrameHead;
} WIN32_THREAD_ENV;
static WIN32_THREAD_ENV gFallbackThreadEnv;
// Implements the k32 Thread Env operation.
static WIN32_THREAD_ENV* K32ThreadEnv(void) {
SYS_TEB* teb = SysGetTeb();
if (!teb || teb->Magic != SYS_TEB_MAGIC) return &gFallbackThreadEnv;
WIN32_THREAD_ENV* env = (WIN32_THREAD_ENV*)teb->SubsystemData;
if (!env) {
env = (WIN32_THREAD_ENV*)malloc(sizeof(WIN32_THREAD_ENV));
if (!env) return &gFallbackThreadEnv;
memset(env, 0, sizeof(*env));
teb->SubsystemData = env;
}
return env;
}
// Implements the k32 Last Error Slot operation.
static DWORD* K32LastErrorSlot(void) {
return &K32ThreadEnv()->LastError;
}
#define gLastError (*K32LastErrorSlot())
// Returns a stable nonzero identifier for the calling thread.
static DWORD K32CurrentTidOrOne(void);
// Implements the file Exists A operation.
static BOOL KiFileExistsA(const char* path) {
if (!path || !*path) return FALSE;
char native[MAX_PATH];
TranslatePath(path, native);
uint32_t h = 0;
if (K32OpenFileNative(native,
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
&h) != 0) return FALSE;
SysCloseHandle(h);
return TRUE;
}
// Copies search Path Result.
static DWORD KiCopySearchPathResult(char* out, DWORD outChars, const char* text, char** filePart) {
DWORD len = (DWORD)strlen(text);
if (out && outChars) {
DWORD copy = (len < outChars - 1) ? len : outChars - 1;
memcpy(out, text, copy);
out[copy] = '\0';
if (filePart) {
char* slash1 = strrchr(out, '/');
char* slash2 = strrchr(out, '\\');
char* slash = slash1 > slash2 ? slash1 : slash2;
*filePart = slash ? slash + 1 : out;
}
}
return len;
}
__declspec(dllexport) DWORD SearchPathA(const char* lpPath, const char* lpFileName,
const char* lpExtension, DWORD nBufferLength,
char* lpBuffer, char** lpFilePart) {
if (!lpFileName || !*lpFileName) { gLastError = 87; return 0; }
char candidate[MAX_PATH];
size_t nameLen = strlen(lpFileName);
bool hasExt = (strrchr(lpFileName, '.') != NULL);
bool rooted = (lpFileName[0] == '/' || lpFileName[0] == '\\' ||
(lpFileName[0] && lpFileName[1] == ':'));
if (rooted || strchr(lpFileName, '/') || strchr(lpFileName, '\\')) {
strncpy(candidate, lpFileName, sizeof(candidate) - 1);
candidate[sizeof(candidate) - 1] = '\0';
if (!hasExt && lpExtension && nameLen + strlen(lpExtension) + 1 < sizeof(candidate))
strcat(candidate, lpExtension);
if (KiFileExistsA(candidate))
return KiCopySearchPathResult(lpBuffer, nBufferLength, candidate, lpFilePart);
}
const char* paths = lpPath && *lpPath ? lpPath : ".";
const char* p = paths;
while (*p) {
char dir[MAX_PATH];
size_t di = 0;
while (*p && *p != ';' && di + 1 < sizeof(dir)) dir[di++] = *p++;
while (*p == ';') p++;
dir[di] = '\0';
size_t pos = 0;
if (strcmp(dir, ".") != 0) {
while (dir[pos] && pos + 1 < sizeof(candidate)) { candidate[pos] = dir[pos]; pos++; }
if (pos && candidate[pos - 1] != '/' && candidate[pos - 1] != '\\' && pos + 1 < sizeof(candidate))
candidate[pos++] = '/';
}
for (size_t i = 0; lpFileName[i] && pos + 1 < sizeof(candidate); i++) candidate[pos++] = lpFileName[i];
if (!hasExt && lpExtension) {
for (size_t i = 0; lpExtension[i] && pos + 1 < sizeof(candidate); i++) candidate[pos++] = lpExtension[i];
}
candidate[pos] = '\0';
if (KiFileExistsA(candidate))
return KiCopySearchPathResult(lpBuffer, nBufferLength, candidate, lpFilePart);
}
gLastError = 2; // ERROR_FILE_NOT_FOUND
if (lpBuffer && nBufferLength) lpBuffer[0] = '\0';
if (lpFilePart) *lpFilePart = NULL;
return 0;
}
// GetLastError / SetLastError (single-threaded stub).
static SYS_PEB* gProcessPeb = NULL;
// Implements the peb operation.
static SYS_PEB* Win32Peb(void) {
if (!gProcessPeb) gProcessPeb = SysGetPeb();
return gProcessPeb;
}
// Returns last Error.
__declspec(dllexport) DWORD GetLastError(void) {
return gLastError;
}
// Updates last Error.
__declspec(dllexport) void SetLastError(DWORD err) {
gLastError = err;
}
// Process termination.
__declspec(dllexport) _Noreturn void ExitProcess(unsigned int code) {
SysExitProcess((int32_t)code);
while (true) ; // unreachable
}
// Debug output.
__declspec(dllexport) void OutputDebugStringA(const char* str) {
if (str) K32DebugPrintA(str);
}
// Queries the native monotonic tick frequency.
static uint64_t K32TickFrequency(void) {
uint64_t frequency = 0;
if (SysGetTickFrequency(&frequency) != NTLSTATUS_SUCCESS) return 0;
return frequency;
}
// Returns monotonic milliseconds since boot.
static uint64_t K32TickMilliseconds(void) {
uint64_t frequency = K32TickFrequency();
if (!frequency) return 0;
return SysTicksToMilliseconds(SysGetTick(), frequency);
}
// Timer.
__declspec(dllexport) DWORD GetTickCount(void) {
return (DWORD)K32TickMilliseconds();
}
// Returns tick Count64.
__declspec(dllexport) uint64_t GetTickCount64(void) {
return K32TickMilliseconds();
}
// Memory (heap argument is ignored - single user-mode heap).
#define HEAP_ZERO_MEMORY 0x00000008UL
#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010UL
__declspec(dllexport) void* HeapAlloc(void* heap, DWORD flags, size_t size) {
(void)heap;
if (!size) return NULL;
void* ptr = malloc(size);
if (!ptr) {
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return NULL;
}
// Bink and the Microsoft CRT routinely allocate internal state with
// HEAP_ZERO_MEMORY. Ignoring this flag only works accidentally while the
// heap keeps receiving fresh demand-zero pages; under low memory / swap the
// allocator reuses dirty freed blocks and stale pitch/pointer fields can
// corrupt the decoder.
if (flags & HEAP_ZERO_MEMORY)
memset(ptr, 0, size);
return ptr;
}
// Implements the heap Free operation.
__declspec(dllexport) BOOL HeapFree(void* heap, DWORD flags, void* ptr) {
(void)heap; (void)flags;
// Win32's HeapFree doesn't need the size; we pass 0 and the user-mode heap carries the block size.
if (ptr) free(ptr);
return TRUE;
}
__declspec(dllexport) void* HeapReAlloc(void* heap, DWORD flags,
void* ptr, size_t size) {
(void)heap;
if (flags & HEAP_REALLOC_IN_PLACE_ONLY) {
// The user heap does not expose an in-place grow primitive. Preserve
// Windows semantics by failing instead of silently moving the block.
size_t oldSize = _malloc_usable_size(ptr);
if (oldSize != (size_t)-1 && oldSize >= size)
return ptr;
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return NULL;
}
size_t oldSize = ptr ? _malloc_usable_size(ptr) : 0;
void* newptr = realloc(ptr, size);
if (!newptr && size) {
gLastError = 8;
return NULL;
}
if (newptr && (flags & HEAP_ZERO_MEMORY) && oldSize != (size_t)-1 && size > oldSize)
memset((unsigned char*)newptr + oldSize, 0, size - oldSize);
return newptr;
}
// GetProcessHeap - return a sentinel non-NULL value so callers can pass it
// back into HeapAlloc/HeapFree without crashing (we ignore it anyway).
__declspec(dllexport) void* GetProcessHeap(void) {
return (void*)1;
}
// Virtual memory compatibility.
__declspec(dllexport) void* VirtualAlloc(void* lpAddress, size_t dwSize,
DWORD flAllocationType, DWORD flProtect) {
(void)flAllocationType;
if (!dwSize) return NULL;
size_t pages = (dwSize + 4095u) / 4096u;
void* base = NULL;
uint32_t flags = lpAddress ? SYS_VM_FLAG_FIXED : SYS_VM_FLAG_NONE;
SYS_ALLOC_VM_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.PreferredBase = lpAddress;
req.PageCount = pages;
req.Protect = WinProtectToSysVm(flProtect);
req.Flags = flags;
req.BaseOut = &base;
KSTATUS s = SysAllocVm(&req);
if (s != NTLSTATUS_SUCCESS) {
gLastError = (s == NTLSTATUS_NOT_FOUND || s == NTLSTATUS_INVALID_PARAMETER) ? 87 : 8;
return NULL;
}
return base;
}
// Implements the virtual Free operation.
__declspec(dllexport) BOOL VirtualFree(void* lpAddress, size_t dwSize, DWORD dwFreeType) {
(void)dwSize; (void)dwFreeType;
if (!lpAddress) return TRUE;
uint32_t s = SysFreeVm(lpAddress);
if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; }
return TRUE;
}
__declspec(dllexport) BOOL VirtualProtect(void* lpAddress, size_t dwSize,
DWORD flNewProtect, DWORD* lpflOldProtect) {
if (!lpAddress || !dwSize) { gLastError = 87; return FALSE; }
size_t pages = (dwSize + 4095u) / 4096u;
uint32_t oldSysProtect = 0;
uint32_t s = SysProtectVm(lpAddress, pages, WinProtectToSysVm(flNewProtect),
lpflOldProtect ? &oldSysProtect : NULL);
if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (lpflOldProtect) *lpflOldProtect = SysProtectVmToWin(oldSysProtect);
return TRUE;
}
__declspec(dllexport) size_t VirtualQuery(const void* lpAddress,
MEMORY_BASIC_INFORMATION* lpBuffer,
size_t dwLength) {
if (!lpAddress || !lpBuffer || dwLength < sizeof(MEMORY_BASIC_INFORMATION)) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return 0;
}
SYS_VM_REGION_INFO info;
uint32_t s = SysQueryVm((void*)lpAddress, &info);
if (s != NTLSTATUS_SUCCESS) {
gLastError = 87;
return 0;
}
DWORD protect = SysProtectVmToWin(info.Protect);
lpBuffer->BaseAddress = info.Base;
lpBuffer->AllocationBase = info.Base;
lpBuffer->AllocationProtect = protect;
lpBuffer->RegionSize = info.PageCount * 4096u;
lpBuffer->State = MEM_COMMIT;
lpBuffer->Protect = protect;
lpBuffer->Type = (info.Type == 7u) ? MEM_MAPPED : MEM_PRIVATE; // 7 == MmVadMappedFile
return sizeof(MEMORY_BASIC_INFORMATION);
}
__declspec(dllexport) HANDLE CreateFileMappingA(HANDLE hFile,
void* lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
const char* lpName) {
(void)lpFileMappingAttributes;
(void)lpName;
if (!hFile || hFile == INVALID_HANDLE_VALUE) {
gLastError = 6; // ERROR_INVALID_HANDLE
return NULL;
}
uint64_t maximumSize = ((uint64_t)dwMaximumSizeHigh << 32) | (uint64_t)dwMaximumSizeLow;
uint32_t kh = 0;
SYS_CREATE_FILE_MAPPING_REQUEST createMapping = {
sizeof(SYS_CREATE_FILE_MAPPING_REQUEST), HANDLE_TO_KH(hFile),
WinFileMappingProtectToSysVm(flProtect), SYS_MAP_FLAG_NONE,
maximumSize, &kh
};
uint32_t s = SysCreateFileMapping(&createMapping);
if (s != NTLSTATUS_SUCCESS) {
gLastError = (s == NTLSTATUS_NOT_SUPPORTED) ? 120 : ((s == NTLSTATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return KH_TO_HANDLE(kh);
}
__declspec(dllexport) HANDLE CreateFileMappingW(HANDLE hFile,
void* lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
const unsigned short* lpName) {
(void)lpName;
return CreateFileMappingA(hFile, lpFileMappingAttributes, flProtect,
dwMaximumSizeHigh, dwMaximumSizeLow, NULL);
}
__declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
size_t dwNumberOfBytesToMap) {
if (!hFileMappingObject || hFileMappingObject == INVALID_HANDLE_VALUE) {
gLastError = 6;
return NULL;
}
uint64_t offset = ((uint64_t)dwFileOffsetHigh << 32) | (uint64_t)dwFileOffsetLow;
void* base = NULL;
SYS_MAP_FILE_REQUEST mapFile = {
sizeof(SYS_MAP_FILE_REQUEST), HANDLE_TO_KH(hFileMappingObject),
WinFileMapAccessToSys(dwDesiredAccess), SYS_MAP_FLAG_NONE,
offset, dwNumberOfBytesToMap, &base
};
uint32_t s = SysMapFile(&mapFile);
if (s != NTLSTATUS_SUCCESS) {
gLastError = (s == NTLSTATUS_NOT_SUPPORTED) ? 120 : ((s == NTLSTATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return base;
}
__declspec(dllexport) BOOL UnmapViewOfFile(const void* lpBaseAddress) {
if (!lpBaseAddress) { gLastError = 87; return FALSE; }
uint32_t s = SysFreeVm((void*)lpBaseAddress);
if (s != NTLSTATUS_SUCCESS) { gLastError = 87; return FALSE; }
return TRUE;
}
__declspec(dllexport) BOOL FlushViewOfFile(const void* lpBaseAddress, size_t dwNumberOfBytesToFlush) {
(void)dwNumberOfBytesToFlush;
if (!lpBaseAddress) { gLastError = 87; return FALSE; }
// Native flush is file-handle based for now; unmap/close also harvests dirty
// PTEs, so FlushViewOfFile remains a successful cache-coherency hint.
return TRUE;
}
// --- Win32 API ---.
// Returns whether one input key is currently held for Win32 adapters.
static bool Win32IsInputKeyDown(INPUT_KEY key) {
bool down = false;
return NtlInputIsKeyDown(key, &down) == NTLSTATUS_SUCCESS && down;
}
// GetAsyncKeyState implementation over the canonical input key namespace.
__declspec(dllexport) SHORT GetAsyncKeyState(int vKey) {
if (vKey == 0x11) { // VK_CONTROL
return (Win32IsInputKeyDown(INPUT_KEY_LEFT_CTRL) || Win32IsInputKeyDown(INPUT_KEY_RIGHT_CTRL)) ? (SHORT)0x8000 : 0;
}
if (vKey == 0x10) { // VK_SHIFT
return (Win32IsInputKeyDown(INPUT_KEY_LEFT_SHIFT) || Win32IsInputKeyDown(INPUT_KEY_RIGHT_SHIFT)) ? (SHORT)0x8000 : 0;
}
if (vKey == 0x12) { // VK_MENU / ALT
return (Win32IsInputKeyDown(INPUT_KEY_LEFT_ALT) || Win32IsInputKeyDown(INPUT_KEY_RIGHT_ALT)) ? (SHORT)0x8000 : 0;
}
static const struct { int vk; INPUT_KEY key; } vk_to_key[] = {
// Letters.
{ 0x41, INPUT_KEY_A }, { 0x42, INPUT_KEY_B }, { 0x43, INPUT_KEY_C },
{ 0x44, INPUT_KEY_D }, { 0x45, INPUT_KEY_E }, { 0x46, INPUT_KEY_F },
{ 0x47, INPUT_KEY_G }, { 0x48, INPUT_KEY_H }, { 0x49, INPUT_KEY_I },
{ 0x4A, INPUT_KEY_J }, { 0x4B, INPUT_KEY_K }, { 0x4C, INPUT_KEY_L },
{ 0x4D, INPUT_KEY_M }, { 0x4E, INPUT_KEY_N }, { 0x4F, INPUT_KEY_O },
{ 0x50, INPUT_KEY_P }, { 0x51, INPUT_KEY_Q }, { 0x52, INPUT_KEY_R },
{ 0x53, INPUT_KEY_S }, { 0x54, INPUT_KEY_T }, { 0x55, INPUT_KEY_U },
{ 0x56, INPUT_KEY_V }, { 0x57, INPUT_KEY_W }, { 0x58, INPUT_KEY_X },
{ 0x59, INPUT_KEY_Y }, { 0x5A, INPUT_KEY_Z },
// Numbers.
{ 0x31, INPUT_KEY_1 }, { 0x32, INPUT_KEY_2 }, { 0x33, INPUT_KEY_3 },
{ 0x34, INPUT_KEY_4 }, { 0x35, INPUT_KEY_5 }, { 0x36, INPUT_KEY_6 },
{ 0x37, INPUT_KEY_7 }, { 0x38, INPUT_KEY_8 }, { 0x39, INPUT_KEY_9 },
{ 0x30, INPUT_KEY_0 },
// Arrows.
{ 0x26, INPUT_KEY_UP }, { 0x28, INPUT_KEY_DOWN },
{ 0x25, INPUT_KEY_LEFT }, { 0x27, INPUT_KEY_RIGHT },
// Controls.
{ 0x1B, INPUT_KEY_ESCAPE }, { 0x0D, INPUT_KEY_ENTER },
{ 0x20, INPUT_KEY_SPACE },
{ 0x08, INPUT_KEY_BACKSPACE }, { 0x09, INPUT_KEY_TAB },
// Function Keys.
{ 0x70, INPUT_KEY_F1 }, { 0x71, INPUT_KEY_F2 }, { 0x72, INPUT_KEY_F3 },
{ 0x73, INPUT_KEY_F4 }, { 0x74, INPUT_KEY_F5 }, { 0x75, INPUT_KEY_F6 },
{ 0x76, INPUT_KEY_F7 }, { 0x77, INPUT_KEY_F8 }, { 0x78, INPUT_KEY_F9 },
{ 0x79, INPUT_KEY_F10 }, { 0x7A, INPUT_KEY_F11 }, { 0x7B, INPUT_KEY_F12 },
// Symbols.
{ 0xBD, INPUT_KEY_MINUS }, { 0xBB, INPUT_KEY_EQUAL },
{ 0xBC, INPUT_KEY_COMMA }, { 0xBE, INPUT_KEY_PERIOD },
};
for (size_t i = 0; i < sizeof(vk_to_key) / sizeof(vk_to_key[0]); ++i) {
if (vk_to_key[i].vk == vKey) {
if (Win32IsInputKeyDown(vk_to_key[i].key))
return (SHORT)0x8000;
break;
}
}
return 0;
}
// FlushConsoleInputBuffer: clear the keyboard input buffer.
__declspec(dllexport) BOOL FlushConsoleInputBuffer(HANDLE hConsoleInput) {
(void)hConsoleInput;
(void)NtlConsoleFlushInput(SYS_HANDLE_INVALID);
return TRUE;
}
// !Made-up! clear console api.
__declspec(dllexport) BOOL ClearConsoleScreenBuffer(HANDLE hConsole) {
(void)hConsole;
(void)NtlConsoleClear(SYS_HANDLE_INVALID, 0x012456);
return TRUE;
}
// Locale / code-page stubs.
__declspec(dllexport) int MultiByteToWideChar(unsigned int cp, DWORD flags,
const char* src, int srcLen,
unsigned short* dst, int dstLen) {
(void)cp; (void)flags;
// Trivial ASCII -> UTF-16LE expansion, good enough for English strings.
if (!src || !dstLen) return 0;
if (srcLen < 0) srcLen = (int)strlen(src) + 1;
int out = 0;
while (out < dstLen && out < srcLen) {
if (dst) dst[out] = (unsigned short)(unsigned char)src[out];
out++;
}
return out;
}
__declspec(dllexport) int WideCharToMultiByte(unsigned int cp, DWORD flags,
const unsigned short* src, int srcLen,
char* dst, int dstLen,
const char* defChar, int* usedDefault) {
(void)cp; (void)flags; (void)defChar;
if (usedDefault) *usedDefault = FALSE;
if (!src || !dstLen) return 0;
if (srcLen < 0) {
srcLen = 0;
while (src[srcLen]) srcLen++;
srcLen++;
}
int out = 0;
while (out < dstLen && out < srcLen) {
if (dst) dst[out] = (char)(src[out] < 128 ? src[out] : '?');
out++;
}
return out;
}
// Returns aCP.
__declspec(dllexport) DWORD GetACP(void) { return 1252; } // Windows-1252
// Returns oEMCP.
__declspec(dllexport) DWORD GetOEMCP(void) { return 437; } // DOS Latin US
// Checks whether dBCS Lead Byte Ex.
__declspec(dllexport) BOOL IsDBCSLeadByteEx(unsigned int CodePage, BYTE TestChar) {
(void)CodePage; (void)TestChar;
return FALSE;
}
// File I/O.
// CreateFileA - opens or creates a file.
// Supported modes:
// OPEN_EXISTING + GENERIC_READ -> open existing, read-only
// OPEN_EXISTING + GENERIC_WRITE -> open existing, read-write
// OPEN_EXISTING + GENERIC_READ|GENERIC_WRITE -> open existing, read-write
// CREATE_ALWAYS + GENERIC_WRITE -> create/truncate, write
// OPEN_ALWAYS + GENERIC_WRITE -> create if absent, write
// TRUNCATE_EXISTING + GENERIC_WRITE -> open & truncate to zero.
__declspec(dllexport) HANDLE CreateFileA(const char* name,
DWORD access, DWORD share,
void* sa, DWORD disposition,
DWORD flags, void* templ) {
(void)share; (void)sa; (void)flags; (void)templ;
// Translate Win32 path syntax into the native resource/CWD form.
char translatedName[260];
TranslatePath(name, translatedName);
uint32_t sysAccess = SYS_FILE_ACCESS_QUERY;
if (access & GENERIC_READ)
sysAccess |= SYS_FILE_ACCESS_READ;
if (access & GENERIC_WRITE)
sysAccess |= SYS_FILE_ACCESS_WRITE;
uint32_t sysDisposition;
switch (disposition) {
case OPEN_EXISTING: sysDisposition = SYS_FILE_OPEN_EXISTING; break;
case OPEN_ALWAYS: sysDisposition = SYS_FILE_OPEN_ALWAYS; break;
case CREATE_ALWAYS: sysDisposition = SYS_FILE_CREATE_ALWAYS; break;
case CREATE_NEW: sysDisposition = SYS_FILE_CREATE_NEW; break;
case TRUNCATE_EXISTING: sysDisposition = SYS_FILE_TRUNCATE_EXISTING; break;
default:
gLastError = 87;
return INVALID_HANDLE_VALUE;
}
uint32_t kh = 0;
uint32_t s = K32OpenFileNative(translatedName, sysAccess, sysDisposition, &kh);
if (s != 0) {
gLastError = (s == NTLSTATUS_OBJECT_NAME_COLLISION) ? 80 :
(s == NTLSTATUS_NOT_FOUND) ? 2 : 5;
return INVALID_HANDLE_VALUE;
}
return KH_TO_HANDLE(kh);
}
// GetFileSize - fills optional high-DWORD pointer, returns low DWORD.
// Returns 0xFFFFFFFF on error, matching Win32 behaviour.
__declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t sz = 0;
uint32_t s = SysGetFileSize(HANDLE_TO_KH(hFile), &sz);
if (s != 0) { gLastError = 6; return 0xFFFFFFFFUL; }
if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32);
return (DWORD)(sz & 0xFFFFFFFFULL);
}
// ReadFile - sequential read at the file's current position.
__declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
DWORD* bytesRead, void* overlapped) {
(void)overlapped;
if (bytesRead) *bytesRead = 0;
if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) {
gLastError = 6;
return FALSE;
}
size_t got = 0;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
uint32_t s = SysReadFile(&req);
if (bytesRead) *bytesRead = (DWORD)got;
if (s != 0) {
gLastError = 1;
return FALSE;
}
// Harden callers that accidentally look past *bytesRead. EOF/short reads
// are legal for files, but do not touch the caller's remaining buffer after
// a failed read because that can fault in megabytes under low memory.
if (got < (size_t)toRead)
memset((unsigned char*)buf + got, 0, (size_t)toRead - got);
return TRUE;
}
// WriteFile - sequential write at the file's current position.
// Extends the file if writing past EOF. Overlapped I/O is not supported.
__declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrite,
DWORD* bytesWritten, void* overlapped) {
(void)overlapped;
if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) { gLastError = 6; return FALSE; }
size_t written = 0;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
uint32_t s = SysWriteFile(&req);
if (bytesWritten) *bytesWritten = (DWORD)written;
if (s != 0) { gLastError = 29; return FALSE; } // ERROR_WRITE_FAULT
return TRUE;
}
// SetFilePointer - seek to an absolute, relative, or end-relative position.
// Returns new position low DWORD, or 0xFFFFFFFF on error.
__declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
long* distHigh, DWORD method) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) {
gLastError = 6;
return 0xFFFFFFFFUL;
}
uint32_t kh = HANDLE_TO_KH(hFile);
int64_t delta;
if (distHigh) {
uint64_t combined = ((uint64_t)(uint32_t)(*distHigh) << 32) | (uint32_t)dist;
delta = (int64_t)combined;
} else {
delta = (int64_t)dist;
}
int64_t target = 0;
if (method == FILE_BEGIN) {
target = delta;
} else if (method == FILE_CURRENT) {
uint64_t cur = 0;
uint32_t s = SysGetFilePos(kh, &cur);
if (s != 0 || cur > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
target = (int64_t)cur + delta;
} else if (method == FILE_END) {
uint64_t sz = 0;
uint32_t s = SysGetFileSize(kh, &sz);
if (s != 0 || sz > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
target = (int64_t)sz + delta;
} else {
gLastError = 87; return 0xFFFFFFFFUL;
}
if (target < 0) {
gLastError = 131; // ERROR_NEGATIVE_SEEK
return 0xFFFFFFFFUL;
}
uint64_t offset = (uint64_t)target;
uint32_t s = SysSeekFile(kh, offset);
if (s != 0) { gLastError = 1; return 0xFFFFFFFFUL; }
if (distHigh) *distHigh = (long)(int32_t)(offset >> 32);
return (DWORD)(offset & 0xFFFFFFFFULL);
}
// SetEndOfFile - truncate or extend the file to the current seek position.
__declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t pos = 0;
uint32_t s = SysGetFilePos(kh, &pos);
if (s != 0) { gLastError = 6; return FALSE; }
s = SysTruncateFile(kh, pos);
if (s != 0) { gLastError = 1; return FALSE; }
return TRUE;
}
// DeleteFileA - delete a file by name; no open handle required.
__declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
if (!lpFileName) { gLastError = 87; return FALSE; }
char translatedPath[MAX_PATH];
TranslatePath(lpFileName, translatedPath);
uint32_t s = K32DeleteFileNative(translatedPath);
if (s != 0) { gLastError = 2; return FALSE; }
return TRUE;
}
#define K32_MUTEX_MAGIC 0x58544D4Bu
typedef struct K32_MUTEX_HANDLE {
struct K32_MUTEX_HANDLE* Next;
uint32_t Magic;
volatile long StateLock;
volatile long ApiUsers;
uint32_t NativeHandle;
DWORD OwnerTid;
uint32_t Recursion;
uint32_t Waiters;
volatile long Closing;
} K32_MUTEX_HANDLE;
static K32_MUTEX_HANDLE* gK32MutexRegistry;
static volatile long gK32MutexRegistryLock;
// Acquires one short process-local mutex registry lock.
static void K32MutexRegistryLock(void) {
while (_InterlockedCompareExchange(&gK32MutexRegistryLock, 1, 0) != 0)
(void)SysYield();
}
// Releases the process-local mutex registry lock.
static void K32MutexRegistryUnlock(void) {
_InterlockedExchange(&gK32MutexRegistryLock, 0);
}
// Acquires one short mutex-wrapper state lock.
static void K32MutexStateLock(K32_MUTEX_HANDLE* mutex) {
while (_InterlockedCompareExchange(&mutex->StateLock, 1, 0) != 0)
(void)SysYield();
}
// Releases one mutex-wrapper state lock.
static void K32MutexStateUnlock(K32_MUTEX_HANDLE* mutex) {
_InterlockedExchange(&mutex->StateLock, 0);
}
// Registers one process-local Win32 mutex wrapper.
static void K32RegisterMutex(K32_MUTEX_HANDLE* mutex) {
K32MutexRegistryLock();
mutex->Next = gK32MutexRegistry;
gK32MutexRegistry = mutex;
K32MutexRegistryUnlock();
}
// References one registered Win32 mutex without dereferencing unknown handles.
static K32_MUTEX_HANDLE* K32ReferenceMutex(HANDLE handle) {
K32_MUTEX_HANDLE* found = NULL;
K32MutexRegistryLock();
for (K32_MUTEX_HANDLE* current = gK32MutexRegistry;
current;
current = current->Next) {
if ((HANDLE)current == handle &&
current->Magic == K32_MUTEX_MAGIC &&
_InterlockedCompareExchange(&current->Closing, 0, 0) == 0) {
_InterlockedIncrement(&current->ApiUsers);
found = current;
break;
}
}
K32MutexRegistryUnlock();
return found;
}
// Releases one mutex-wrapper API reference.
static void K32DereferenceMutex(K32_MUTEX_HANDLE* mutex) {
if (mutex)
_InterlockedDecrement(&mutex->ApiUsers);
}
// Removes one mutex wrapper so no new API references can be acquired.
static BOOL K32RemoveMutex(K32_MUTEX_HANDLE* mutex) {
BOOL removed = FALSE;
K32MutexRegistryLock();
K32_MUTEX_HANDLE** link = &gK32MutexRegistry;
while (*link) {
if (*link == mutex) {
*link = mutex->Next;
mutex->Next = NULL;
removed = TRUE;
break;
}
link = &(*link)->Next;
}
K32MutexRegistryUnlock();
return removed;
}
// Waits on one recursive Win32 mutex wrapper.
static DWORD K32WaitMutex(K32_MUTEX_HANDLE* mutex, DWORD milliseconds) {
DWORD tid = K32CurrentTidOrOne();
K32MutexStateLock(mutex);
if (_InterlockedCompareExchange(&mutex->Closing, 0, 0) != 0) {
K32MutexStateUnlock(mutex);
gLastError = 6;
return 0xFFFFFFFFUL;
}
if (mutex->OwnerTid == tid) {
if (mutex->Recursion == UINT32_MAX) {
K32MutexStateUnlock(mutex);
gLastError = 8;
return 0xFFFFFFFFUL;
}
mutex->Recursion++;
K32MutexStateUnlock(mutex);
return 0;
}
mutex->Waiters++;
K32MutexStateUnlock(mutex);
uint64_t timeout = milliseconds == 0xFFFFFFFFUL
? SYS_WAIT_TIMEOUT_INFINITE
: (uint64_t)milliseconds;
uint32_t status = SysWaitSingle(mutex->NativeHandle, SYS_WAIT_FLAG_NONE, timeout);
K32MutexStateLock(mutex);
if (mutex->Waiters)
mutex->Waiters--;
if (status == NTLSTATUS_SUCCESS || status == NTLSTATUS_ABANDONED) {
mutex->OwnerTid = tid;
mutex->Recursion = 1;
}
K32MutexStateUnlock(mutex);
if (status == NTLSTATUS_SUCCESS)
return 0;
if (status == NTLSTATUS_ABANDONED)
return 0x00000080UL;
if (status == NTLSTATUS_TIMEOUT)
return 0x00000102UL;
gLastError = 6;
return 0xFFFFFFFFUL;
}
// Closes one idle Win32 mutex wrapper.
static BOOL K32CloseMutex(K32_MUTEX_HANDLE* mutex) {
K32MutexStateLock(mutex);
BOOL busy = mutex->OwnerTid != 0 || mutex->Waiters != 0;
if (!busy && _InterlockedCompareExchange(&mutex->Closing, 1, 0) != 0)
busy = TRUE;
K32MutexStateUnlock(mutex);
if (busy) {
gLastError = 170;
K32DereferenceMutex(mutex);
return FALSE;
}
if (!K32RemoveMutex(mutex)) {
gLastError = 6;
K32DereferenceMutex(mutex);
return FALSE;
}
while (mutex->ApiUsers > 1)
(void)SysYield();
uint32_t status = SysCloseHandle(mutex->NativeHandle);
mutex->Magic = 0;
K32DereferenceMutex(mutex);
free(mutex);
if (status != NTLSTATUS_SUCCESS) {
gLastError = 6;
return FALSE;
}
return TRUE;
}
// Closes one Win32 or native kernel handle.
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
if (!hObject || hObject == INVALID_HANDLE_VALUE) {
gLastError = 6;
return FALSE;
}
K32_MUTEX_HANDLE* mutex = K32ReferenceMutex(hObject);
if (mutex)
return K32CloseMutex(mutex);
uint32_t status = SysCloseHandle(HANDLE_TO_KH(hObject));
if (status != NTLSTATUS_SUCCESS) {
gLastError = 6;
return FALSE;
}
return TRUE;
}
// Extended shims - funcs.txt additions
// All functions here return plausible values; no real implementation exists
// unless a matching syscall stub is available.
// Additional Heap APIs
// We have one kernel heap; the HANDLE is always ignored.
typedef int HEAP_INFORMATION_CLASS; // opaque
__declspec(dllexport) HANDLE HeapCreate(DWORD flOptions,
size_t dwInitialSize,
size_t dwMaximumSize) {
(void)flOptions; (void)dwInitialSize; (void)dwMaximumSize;
return (HANDLE)(uintptr_t)1; // non-NULL sentinel
}
// Implements the heap Destroy operation.
__declspec(dllexport) BOOL HeapDestroy(HANDLE hHeap) {
(void)hHeap;
return TRUE;
}
// Implements the heap Size operation.
__declspec(dllexport) size_t HeapSize(HANDLE heap, DWORD flags, const void* ptr) {
(void)heap; (void)flags;
size_t size = _malloc_usable_size(ptr);
if (size == (size_t)-1)
gLastError = 87; // ERROR_INVALID_PARAMETER
return size;
}
__declspec(dllexport) BOOL HeapSetInformation(HANDLE HeapHandle,
HEAP_INFORMATION_CLASS cls,
void* info, size_t infoLen) {
(void)HeapHandle; (void)cls; (void)info; (void)infoLen;
return TRUE;
}
// Process & Module pseudo-handles / queries.
// Pseudo-handles matching Windows convention: -1 = process, -2 = thread.
__declspec(dllexport) HANDLE GetCurrentProcess(void) {
return (HANDLE)(uintptr_t)(~(uintptr_t)0);
}
// Returns current Thread.
__declspec(dllexport) HANDLE GetCurrentThread(void) {
return (HANDLE)(uintptr_t)(~(uintptr_t)1);
}
// Returns current Thread Id.
__declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); }
// Returns current Process Id.
__declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); }
// Tiny TLS index allocator; slot values are stored per thread in WIN32_THREAD_ENV.
static uint64_t gTlsBitmap = 1; // reserve slot 0
// Implements the tls Alloc operation.
__declspec(dllexport) DWORD TlsAlloc(void) {
for (DWORD i = 1; i < WIN32_TLS_SLOTS; i++) {
uint64_t bit = 1ULL << i;
if (!(gTlsBitmap & bit)) { gTlsBitmap |= bit; return i; }
}
gLastError = 8;
return 0xFFFFFFFFUL;
}
// Implements the tls Free operation.
__declspec(dllexport) BOOL TlsFree(DWORD dwTlsIndex) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
WIN32_THREAD_ENV* env = K32ThreadEnv();
if (env) env->TlsSlots[dwTlsIndex] = NULL;
gTlsBitmap &= ~(1ULL << dwTlsIndex);
return TRUE;
}
// Implements the tls Get Value operation.
__declspec(dllexport) void* TlsGetValue(DWORD dwTlsIndex) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return NULL; }
WIN32_THREAD_ENV* env = K32ThreadEnv();
gLastError = 0;
return env->TlsSlots[dwTlsIndex];
}
// Implements the tls Set Value operation.
__declspec(dllexport) BOOL TlsSetValue(DWORD dwTlsIndex, void* lpTlsValue) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
WIN32_THREAD_ENV* env = K32ThreadEnv();
env->TlsSlots[dwTlsIndex] = lpTlsValue;
return TRUE;
}
// Pseudo HMODULEs for callers that probe already-linked personality DLLs.
#define WIN32_HMODULE_EXE ((HANDLE)(uintptr_t)0x00400000UL)
#define WIN32_HMODULE_WIN32 ((HANDLE)(uintptr_t)0x57333232UL) // W322
// Implements the stricmp A operation.
static int KiStricmpA(const char* a, const char* b) {
while (*a && (((unsigned char)*a | 0x20) == ((unsigned char)*b | 0x20))) { a++; b++; }
return ((unsigned char)*a | 0x20) - ((unsigned char)*b | 0x20);
}
// Matches one Win32 module basename with its optional DLL suffix.
static BOOL KiMatchesWin32DllNameA(const char* name, const char* baseName) {
if (!name || !baseName)
return FALSE;
while (*name && *baseName &&
(((unsigned char)*name | 0x20u) == ((unsigned char)*baseName | 0x20u))) {
name++;
baseName++;
}
if (*baseName)
return FALSE;
if (!*name)
return TRUE;
return KiStricmpA(name, ".dll") == 0;
}
// Checks whether one name identifies a process-local Win32 personality module.
static BOOL KiIsWin32DllNameA(const char* name) {
return KiMatchesWin32DllNameA(name, "kernel32") ||
KiMatchesWin32DllNameA(name, "user32") ||
KiMatchesWin32DllNameA(name, "gdi32") ||
KiMatchesWin32DllNameA(name, "ole32") ||
KiMatchesWin32DllNameA(name, "winmm") ||
KiMatchesWin32DllNameA(name, "win32");
}
// Implements the dbg Write A operation.
static void KiDbgWriteA(const char* s) {
if (s) K32WriteConsoleSpan(s, strlen(s));
}
// Returns the process-local module handle for one ANSI module name.
__declspec(dllexport) HANDLE GetModuleHandleA(const char* lpModuleName) {
if (!lpModuleName)
return WIN32_HMODULE_EXE;
if (KiIsWin32DllNameA(lpModuleName))
return WIN32_HMODULE_WIN32;
gLastError = 126; // ERROR_MOD_NOT_FOUND.
return NULL;
}
// Returns module Handle W.
__declspec(dllexport) HANDLE GetModuleHandleW(const unsigned short* lpModuleName) {
return lpModuleName ? NULL : WIN32_HMODULE_EXE;
}
// Loads one DLL entirely through the process-local userspace module loader.
__declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) {
if (KiIsWin32DllNameA(lpLibFileName)) return WIN32_HMODULE_WIN32;
if (!lpLibFileName || !lpLibFileName[0]) { gLastError = 87; return NULL; }
SYS_HANDLE root = SYS_HANDLE_INVALID;
uint32_t status = NtlResourceOpenSystemRoot(SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &root);
if (status != NTLSTATUS_SUCCESS) { gLastError = 126; return NULL; }
char relative[MAX_PATH];
status = K32ProcessPath(lpLibFileName, relative, sizeof(relative));
if (status == NTLSTATUS_SUCCESS && !strchr(relative, '/')) {
size_t length = strlen(relative);
if (length + sizeof("system/") > sizeof(relative)) status = NTLSTATUS_BUFFER_TOO_SMALL;
else {
memmove(relative + 7, relative, length + 1u);
memcpy(relative, "system/", 7);
}
}
NTL_MODULE_HANDLE module = NTL_MODULE_HANDLE_INVALID;
if (status == NTLSTATUS_SUCCESS) {
NTL_LOAD_MODULE_AT_REQUEST load;
memset(&load, 0, sizeof(load));
load.Size = sizeof(load);
load.Flags = NTL_MODULE_LOAD_FLAG_NONE;
load.RootHandle = root;
load.Path = SysSvFromCstr(relative);
load.ModuleRootHandle = root;
load.ModuleDirectory = SYS_SV_LITERAL("system");
load.ResolveModuleName = K32ResolveModuleName;
load.ModuleHandleOut = &module;
status = NtlLoadModuleAt(&load);
}
(void)SysCloseHandle(root);
if (status != NTLSTATUS_SUCCESS || module == NTL_MODULE_HANDLE_INVALID) {
gLastError = 126;
return NULL;
}
return (HANDLE)(uintptr_t)module;
}
// GetProcAddress handles both built-in win32 exports and native module handles.
__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule);
__declspec(dllexport) void Sleep(DWORD dwMilliseconds);
__declspec(dllexport) BOOL GetConsoleScreenBufferInfo(HANDLE hConsoleOutput,
CONSOLE_SCREEN_BUFFER_INFO* lpConsoleScreenBufferInfo);
__declspec(dllexport) BOOL FillConsoleOutputCharacterA(HANDLE hConsoleOutput,
char cCharacter,
DWORD nLength,
COORD dwWriteCoord,
DWORD* lpNumberOfCharsWritten);
__declspec(dllexport) BOOL FillConsoleOutputAttribute(HANDLE hConsoleOutput,
WORD wAttribute,
DWORD nLength,
COORD dwWriteCoord,
DWORD* lpNumberOfAttrsWritten);
__declspec(dllexport) BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput,
COORD dwCursorPosition);
// Returns proc Address.
__declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcName) {
if (!hModule || !lpProcName) { gLastError = 127; return NULL; }
// Ordinal probes are represented as small integer pointers. Do not treat
// them as strings while debugging misses.
if ((uintptr_t)lpProcName < 0x10000) {
KiDbgWriteA("GetProcAddress ordinal miss\n");
gLastError = 127;
return NULL;
}
if (hModule != WIN32_HMODULE_WIN32) {
uintptr_t address = 0;
uint32_t status = NtlGetModuleExport((NTL_MODULE_HANDLE)(uintptr_t)hModule,
SysSvFromCstr(lpProcName), &address);
if (status != NTLSTATUS_SUCCESS || !address) { gLastError = 127; return NULL; }
return (void*)address;
}
#define GP(name) do { \
if (strcmp(lpProcName, #name) == 0) return (void*)&name; \
} while (0)
GP(GetLastError); GP(SetLastError); GP(LoadLibraryA); GP(GetProcAddress);
GP(IsDebuggerPresent);
GP(FreeLibrary); GP(VirtualAlloc); GP(VirtualFree); GP(VirtualProtect);
GP(VirtualQuery); GP(GetTickCount); GP(GetTickCount64); GP(Sleep);
GP(CreateEventA); GP(CreateEventW); GP(SetEvent); GP(ResetEvent);
GP(CreateThread); GP(ExitThread); GP(WaitForSingleObject);
GP(CreateMutexA); GP(CreateMutexW); GP(ReleaseMutex); GP(CloseHandle);
GP(InitializeCriticalSection); GP(InitializeCriticalSectionAndSpinCount);
GP(DeleteCriticalSection); GP(EnterCriticalSection); GP(LeaveCriticalSection);
GP(TryEnterCriticalSection); GP(InitializeConditionVariable);
GP(SleepConditionVariableCS); GP(WakeConditionVariable); GP(WakeAllConditionVariable);
GP(TlsAlloc); GP(TlsFree); GP(TlsGetValue); GP(TlsSetValue);
GP(GetConsoleScreenBufferInfo); GP(FillConsoleOutputCharacterA);
GP(FillConsoleOutputAttribute); GP(SetConsoleCursorPosition);
GP(waveOutGetNumDevs); GP(waveOutOpen); GP(waveOutClose);
GP(waveOutPrepareHeader); GP(waveOutUnprepareHeader); GP(waveOutWrite);
GP(waveOutPause); GP(waveOutRestart); GP(waveOutReset);
GP(waveOutSetVolume); GP(waveOutGetVolume); GP(waveOutGetPosition); GP(waveOutGetID);
GP(waveOutGetDevCapsA); GP(waveOutGetDevCapsW);
GP(waveOutGetErrorTextA); GP(waveOutGetErrorTextW);
GP(waveOutBreakLoop); GP(waveOutMessage);
GP(timeGetTime); GP(timeBeginPeriod); GP(timeEndPeriod);
#undef GP
KiDbgWriteA("GetProcAddress miss: ");
KiDbgWriteA(lpProcName);
KiDbgWriteA("\n");
gLastError = 127; // ERROR_PROC_NOT_FOUND
return NULL;
}
// Releases one native module handle returned by LoadLibraryA.
__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) {
if (!hLibModule || hLibModule == WIN32_HMODULE_WIN32 || hLibModule == WIN32_HMODULE_EXE)
return TRUE;
uint32_t status = NtlUnloadModule((NTL_MODULE_HANDLE)(uintptr_t)hLibModule);
if (status != NTLSTATUS_SUCCESS) {
gLastError = 126;
return FALSE;
}
return TRUE;
}
__declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
char* lpFilename, DWORD nSize) {
(void)hModule;
if (!lpFilename || !nSize) return 0;
SYS_PEB* peb = Win32Peb();
const char* src = (peb && peb->Magic == SYS_PEB_MAGIC && peb->ImagePath.Data)
? peb->ImagePath.Data
: "";
DWORD len = (DWORD)strlen(src);
DWORD copy = (len < nSize - 1) ? len : nSize - 1;
memcpy(lpFilename, src, copy);
lpFilename[copy] = '\0';
if (copy < len) gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return copy;
}
// Implements the terminate Process operation.
__declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) {
if (hProcess == GetCurrentProcess()) {
SysExitProcess((int32_t)uExitCode);
while (true) ;
}
if (!hProcess) {
gLastError = 6; // ERROR_INVALID_HANDLE
return FALSE;
}
uint32_t s = SysKillProcess(HANDLE_TO_KH(hProcess), (int32_t)uExitCode);
if (s != 0) {
gLastError = (s == NTLSTATUS_INVALID_HANDLE) ? 6 : 5;
return FALSE;
}
return TRUE;
}
// STARTUPINFOA - only cb matters for our stub.
typedef struct {
DWORD cb;
char* lpReserved;
char* lpDesktop;
char* lpTitle;
DWORD dwX, dwY, dwXSize, dwYSize;
DWORD dwXCountChars, dwYCountChars;
DWORD dwFillAttribute, dwFlags;
unsigned short wShowWindow, cbReserved2;
unsigned char* lpReserved2;
HANDLE hStdInput, hStdOutput, hStdError;
} STARTUPINFOA;
// Returns startup Info A.
__declspec(dllexport) void GetStartupInfoA(STARTUPINFOA* lpStartupInfo) {
if (!lpStartupInfo) return;
// Zero every field, then set the mandatory cb.
unsigned char* p = (unsigned char*)lpStartupInfo;
for (size_t i = 0; i < sizeof(*lpStartupInfo); i++) p[i] = 0;
lpStartupInfo->cb = (DWORD)sizeof(*lpStartupInfo);
}
// Returns command Line A.
__declspec(dllexport) char* GetCommandLineA(void) {
SYS_PEB* peb = Win32Peb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return "";
return (char*)peb->CommandLine.Data;
}
// Threading.
#define WAIT_OBJECT_0 0x00000000UL
#define WAIT_TIMEOUT 0x00000102UL
#define INFINITE 0xFFFFFFFFUL
#define CREATE_SUSPENDED 0x00000004UL
typedef DWORD (__stdcall *K32_THREAD_START_ROUTINE)(void* parameter);
typedef struct K32_THREAD_START_CONTEXT {
K32_THREAD_START_ROUTINE StartRoutine;
void* Parameter;
} K32_THREAD_START_CONTEXT;
// Invokes one Win32 thread procedure and terminates the native thread on return.
static void K32ThreadStart(uintptr_t argument) {
K32_THREAD_START_CONTEXT* context = (K32_THREAD_START_CONTEXT*)argument;
K32_THREAD_START_ROUTINE startRoutine = context->StartRoutine;
void* parameter = context->Parameter;
free(context);
DWORD exitCode = startRoutine(parameter);
SysExitThread((int32_t)exitCode);
while (true) {
(void)SysYield();
}
}
// Creates one Win32 thread with return-to-ExitThread semantics.
__declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
size_t dwStackSize,
void* lpStartAddress,
void* lpParameter,
DWORD dwCreationFlags,
DWORD* lpThreadId) {
(void)lpThreadAttributes;
(void)dwStackSize;
if (!lpStartAddress || (dwCreationFlags & ~CREATE_SUSPENDED)) {
gLastError = 87;
return NULL;
}
K32_THREAD_START_CONTEXT* context =
(K32_THREAD_START_CONTEXT*)malloc(sizeof(*context));
if (!context) {
gLastError = 8;
return NULL;
}
context->StartRoutine = (K32_THREAD_START_ROUTINE)lpStartAddress;
context->Parameter = lpParameter;
uint32_t tid = 0;
uint32_t kh = 0;
SYS_CREATE_THREAD_REQUEST req = {
sizeof(SYS_CREATE_THREAD_REQUEST), (uintptr_t)K32ThreadStart,
(uintptr_t)context,
0,
(dwCreationFlags & CREATE_SUSPENDED)
? SYS_CREATE_THREAD_FLAG_SUSPENDED
: SYS_CREATE_THREAD_FLAG_NONE,
&kh,
&tid
};
uint32_t status = SysCreateThread(&req);
if (status != NTLSTATUS_SUCCESS) {
free(context);
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 11;
return NULL;
}
if (lpThreadId)
*lpThreadId = (DWORD)tid;
return KH_TO_HANDLE(kh);
}
// Terminates the calling Win32 thread with the supplied exit code.
__declspec(dllexport) _Noreturn void ExitThread(DWORD exitCode) {
SysExitThread((int32_t)exitCode);
while (true) {
(void)SysYield();
}
}
// Waits for one Win32 mutex wrapper or native waitable handle.
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
if (!hHandle || hHandle == INVALID_HANDLE_VALUE) {
gLastError = 6;
return 0xFFFFFFFFUL;
}
K32_MUTEX_HANDLE* mutex = K32ReferenceMutex(hHandle);
if (mutex) {
DWORD result = K32WaitMutex(mutex, dwMilliseconds);
K32DereferenceMutex(mutex);
return result;
}
if (!IS_VALID_KERNEL_HANDLE(hHandle)) {
gLastError = 6;
return 0xFFFFFFFFUL;
}
uint64_t timeout = dwMilliseconds == INFINITE
? SYS_WAIT_TIMEOUT_INFINITE
: (uint64_t)dwMilliseconds;
uint32_t s = SysWaitSingle(HANDLE_TO_KH(hHandle), SYS_WAIT_FLAG_NONE, timeout);
if (s == NTLSTATUS_SUCCESS) return WAIT_OBJECT_0;
if (s == NTLSTATUS_ABANDONED) return 0x00000080UL;
if (s == NTLSTATUS_TIMEOUT) return WAIT_TIMEOUT;
gLastError = 6;
return 0xFFFFFFFFUL;
}
// Creates one unnamed native event with Win32 reset semantics.
__declspec(dllexport) HANDLE CreateEventA(void* lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
const char* lpName) {
(void)lpEventAttributes;
if (lpName && lpName[0]) {
gLastError = 120;
return NULL;
}
uint32_t handle = 0;
uint32_t status = SysCreateEvent(bManualReset ? 1u : 0u,
bInitialState ? 1u : 0u,
&handle);
if (status != NTLSTATUS_SUCCESS) {
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 87;
return NULL;
}
return KH_TO_HANDLE(handle);
}
// Creates one unnamed native event from the wide Win32 entry point.
__declspec(dllexport) HANDLE CreateEventW(void* lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
const unsigned short* lpName) {
if (lpName && lpName[0]) {
gLastError = 120;
return NULL;
}
return CreateEventA(lpEventAttributes, bManualReset, bInitialState, NULL);
}
// Signals one native event.
__declspec(dllexport) BOOL SetEvent(HANDLE hEvent) {
if (!IS_VALID_KERNEL_HANDLE(hEvent)) { gLastError = 6; return FALSE; }
uint32_t status = SysSignalEvent(HANDLE_TO_KH(hEvent));
if (status != NTLSTATUS_SUCCESS) { gLastError = 6; return FALSE; }
return TRUE;
}
// Clears one native event.
__declspec(dllexport) BOOL ResetEvent(HANDLE hEvent) {
if (!IS_VALID_KERNEL_HANDLE(hEvent)) { gLastError = 6; return FALSE; }
uint32_t status = SysClearEvent(HANDLE_TO_KH(hEvent));
if (status != NTLSTATUS_SUCCESS) { gLastError = 6; return FALSE; }
return TRUE;
}
// Implements the extract Application Path A operation.
static BOOL ExtractApplicationPathA(const char* commandLine, char* out, size_t outSize) {
if (!commandLine || !out || outSize == 0) return FALSE;
while (*commandLine == ' ' || *commandLine == '\t') commandLine++;
if (*commandLine == '\0') return FALSE;
size_t pos = 0;
if (*commandLine == '"') {
commandLine++;
while (*commandLine && *commandLine != '"' && pos + 1 < outSize)
out[pos++] = *commandLine++;
} else {
while (*commandLine && *commandLine != ' ' && *commandLine != '\t' && pos + 1 < outSize)
out[pos++] = *commandLine++;
}
out[pos] = '\0';
return pos != 0;
}
// Process creation.
typedef struct {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
#define STILL_ACTIVE 259UL
__declspec(dllexport) BOOL CreateProcessA(
const char* lpApplicationName,
char* lpCommandLine,
void* lpProcessAttributes,
void* lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
void* lpEnvironment,
const char* lpCurrentDirectory,
STARTUPINFOA* lpStartupInfo,
PROCESS_INFORMATION* lpProcessInformation) {
(void)lpProcessAttributes; (void)lpThreadAttributes;
(void)bInheritHandles; (void)dwCreationFlags; (void)lpEnvironment;
(void)lpStartupInfo;
// lpProcessInformation is mandatory.
if (!lpProcessInformation) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return FALSE;
}
char appPath[MAX_PATH];
const char* targetPath = lpApplicationName;
if (!targetPath) {
if (!ExtractApplicationPathA(lpCommandLine, appPath, sizeof(appPath))) {
gLastError = 87;
return FALSE;
}
targetPath = appPath;
}
const char* cmdline = (lpCommandLine && lpCommandLine[0]) ? lpCommandLine : targetPath;
char translatedPath[MAX_PATH];
uint32_t imageFile = SYS_HANDLE_INVALID;
bool relativeImage = targetPath[0] != '/' && targetPath[0] != '\\' &&
!(targetPath[0] && targetPath[1] == ':' &&
(targetPath[2] == '\\' || targetPath[2] == '/'));
if (relativeImage) {
TranslatePath(targetPath, translatedPath);
uint32_t openStatus = K32OpenFileNative(translatedPath,
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
&imageFile);
if (openStatus != NTLSTATUS_SUCCESS) {
gLastError = 2;
return FALSE;
}
} else if (K32ProcessPath(targetPath, translatedPath, sizeof(translatedPath)) != NTLSTATUS_SUCCESS) {
gLastError = 3;
return FALSE;
}
char translatedCwd[MAX_PATH];
const char* childCwd = (const char*)0;
if (lpCurrentDirectory && lpCurrentDirectory[0]) {
TranslatePath(lpCurrentDirectory, translatedCwd);
if (!translatedCwd[0]) {
if (imageFile != SYS_HANDLE_INVALID) (void)SysCloseHandle(imageFile);
gLastError = 3;
return FALSE;
}
childCwd = translatedCwd;
}
uint32_t kh = 0;
uint32_t s = K32CreateProcessNative(translatedPath, imageFile, &kh, cmdline, childCwd);
if (imageFile != SYS_HANDLE_INVALID) (void)SysCloseHandle(imageFile);
if (s != 0 || kh == 0) {
gLastError = 2;
return FALSE;
}
lpProcessInformation->hProcess = KH_TO_HANDLE(kh);
lpProcessInformation->hThread = INVALID_HANDLE_VALUE;
lpProcessInformation->dwProcessId = 0;
lpProcessInformation->dwThreadId = 0;
return TRUE;
}
// Returns exit Code Process.
__declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode) {
if (!lpExitCode) { gLastError = 87; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hProcess);
if (!kh) { gLastError = 6; return FALSE; }
int32_t code = 0;
uint32_t s = SysGetProcessExitCode(kh, &code);
if (s != 0) { gLastError = 6; return FALSE; }
*lpExitCode = (DWORD)code;
return TRUE;
}
// Decrements a thread's suspend count and makes it runnable at zero.
__declspec(dllexport) DWORD ResumeThread(HANDLE hThread) {
if (!hThread || hThread == INVALID_HANDLE_VALUE) {
gLastError = 6;
return 0xFFFFFFFFu;
}
uint32_t previousSuspendCount = 0;
uint32_t status = SysResumeThread(HANDLE_TO_KH(hThread),
&previousSuspendCount);
if (status != NTLSTATUS_SUCCESS) {
gLastError = status == NTLSTATUS_ACCESS_DENIED ? 5 : 6;
return 0xFFFFFFFFu;
}
return previousSuspendCount;
}
#define THREAD_PRIORITY_NORMAL 0
// Updates thread Priority.
__declspec(dllexport) BOOL SetThreadPriority(HANDLE hThread, int nPriority) {
(void)hThread; (void)nPriority;
return TRUE;
}
// Returns thread Priority.
__declspec(dllexport) int GetThreadPriority(HANDLE hThread) {
(void)hThread;
return THREAD_PRIORITY_NORMAL;
}
__declspec(dllexport) uintptr_t SetThreadAffinityMask(HANDLE hThread,
uintptr_t dwThreadAffinityMask) {
(void)hThread; (void)dwThreadAffinityMask;
return 1; // previous mask: CPU 0 only
}
// Implements the sleep operation.
__declspec(dllexport) void Sleep(DWORD dwMilliseconds) {
SysSleep(dwMilliseconds);
}
// Console I/O.
#define STD_INPUT_HANDLE ((DWORD)-10)
#define STD_OUTPUT_HANDLE ((DWORD)-11)
#define STD_ERROR_HANDLE ((DWORD)-12)
// Implements the k32 Is Std Handle Value operation.
static BOOL K32IsStdHandleValue(HANDLE h, DWORD stdHandle) {
uintptr_t value = (uintptr_t)h;
return value == (uintptr_t)stdHandle || value == (uintptr_t)(intptr_t)(int32_t)stdHandle;
}
// Implements the k32 Is Console Output Handle operation.
static BOOL K32IsConsoleOutputHandle(HANDLE h) {
return h && h != INVALID_HANDLE_VALUE &&
(K32IsStdHandleValue(h, STD_OUTPUT_HANDLE) ||
K32IsStdHandleValue(h, STD_ERROR_HANDLE));
}
// Implements the k32 Set Console Last Error operation.
static void K32SetConsoleLastError(uint32_t status) {
switch (status) {
case NTLSTATUS_SUCCESS:
gLastError = 0;
break;
case NTLSTATUS_INVALID_PARAMETER:
gLastError = 87;
break;
case NTLSTATUS_NO_MEMORY:
gLastError = 8;
break;
default:
gLastError = 1;
break;
}
}
// We have no real console handles; return a sentinel the other APIs recognise.
__declspec(dllexport) HANDLE GetStdHandle(DWORD nStdHandle) {
return (HANDLE)(uintptr_t)nStdHandle;
}
// ReadConsoleA: blocking read of up to nNumberOfCharsToRead characters.
// Each character is read individually via NtlInputReadKeyboardChar so the caller can
// do its own line-editing (echo, backspace, etc.) one char at a time.
__declspec(dllexport) BOOL ReadConsoleA(HANDLE hConsoleInput,
void* lpBuffer,
DWORD nNumberOfCharsToRead,
DWORD* lpNumberOfCharsRead,
void* pInputControl) {
(void)hConsoleInput; (void)pInputControl;
if (!lpBuffer || nNumberOfCharsToRead == 0) return FALSE;
char* buf = (char*)lpBuffer;
DWORD i = 0; // Notice we switched to a while loop
while (i < nNumberOfCharsToRead) {
char c = 0;
if (NtlConsoleReadCharacter(SYS_HANDLE_INVALID, &c) != NTLSTATUS_SUCCESS) continue;
// Ignore unmapped keys (Shift, Ctrl, etc.).
if (c == 0) continue;
// Deliver the raw character; the caller handles echo and line editing.
buf[i] = c;
i++;
// Stop reading when the user presses Enter.
if (c == '\n' || c == '\r') {
// If your shell expects strings to be \n terminated, leave this.
// If your shell expects \0 termination without a newline,
// you might want to replace buf[i-1] with \0 here!
break;
}
}
if (lpNumberOfCharsRead) *lpNumberOfCharsRead = i;
return TRUE;
}
// WriteConsoleA: write nNumberOfCharsToWrite characters from lpBuffer to the
// console as white-on-black text via SYS_WRITE_CONSOLE.
__declspec(dllexport) BOOL WriteConsoleA(HANDLE hConsoleOutput,
const void* lpBuffer,
DWORD nNumberOfCharsToWrite,
DWORD* lpNumberOfCharsWritten,
void* lpReserved) {
(void)hConsoleOutput; (void)lpReserved;
if (!lpBuffer || nNumberOfCharsToWrite == 0) {
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0;
return TRUE;
}
K32WriteConsoleSpan((const char*)lpBuffer, (size_t)nNumberOfCharsToWrite);
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nNumberOfCharsToWrite;
return TRUE;
}
__declspec(dllexport) BOOL GetConsoleScreenBufferInfo(HANDLE hConsoleOutput,
CONSOLE_SCREEN_BUFFER_INFO* lpConsoleScreenBufferInfo) {
if (!K32IsConsoleOutputHandle(hConsoleOutput)) {
gLastError = 6;
return FALSE;
}
if (!lpConsoleScreenBufferInfo) {
gLastError = 87;
return FALSE;
}
NTL_CONSOLE_INFO info;
uint32_t s = NtlConsoleQueryInfo(SYS_HANDLE_INVALID, &info);
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
memset(lpConsoleScreenBufferInfo, 0, sizeof(*lpConsoleScreenBufferInfo));
lpConsoleScreenBufferInfo->dwSize.X = (SHORT)info.Columns;
lpConsoleScreenBufferInfo->dwSize.Y = (SHORT)info.Rows;
lpConsoleScreenBufferInfo->dwCursorPosition.X = (SHORT)info.CursorX;
lpConsoleScreenBufferInfo->dwCursorPosition.Y = (SHORT)info.CursorY;
lpConsoleScreenBufferInfo->wAttributes = (WORD)info.Attribute;
lpConsoleScreenBufferInfo->srWindow.Left = 0;
lpConsoleScreenBufferInfo->srWindow.Top = 0;
lpConsoleScreenBufferInfo->srWindow.Right = info.Columns ? (SHORT)(info.Columns - 1u) : 0;
lpConsoleScreenBufferInfo->srWindow.Bottom = info.Rows ? (SHORT)(info.Rows - 1u) : 0;
lpConsoleScreenBufferInfo->dwMaximumWindowSize = lpConsoleScreenBufferInfo->dwSize;
return TRUE;
}
__declspec(dllexport) BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput,
COORD dwCursorPosition) {
if (!K32IsConsoleOutputHandle(hConsoleOutput)) {
gLastError = 6;
return FALSE;
}
if (dwCursorPosition.X < 0 || dwCursorPosition.Y < 0) {
gLastError = 87;
return FALSE;
}
uint32_t s = NtlConsoleSetCursor(SYS_HANDLE_INVALID, (int)dwCursorPosition.X, (int)dwCursorPosition.Y);
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
return TRUE;
}
__declspec(dllexport) BOOL FillConsoleOutputCharacterA(HANDLE hConsoleOutput,
char cCharacter,
DWORD nLength,
COORD dwWriteCoord,
DWORD* lpNumberOfCharsWritten) {
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0;
if (!K32IsConsoleOutputHandle(hConsoleOutput)) {
gLastError = 6;
return FALSE;
}
if (dwWriteCoord.X < 0 || dwWriteCoord.Y < 0) {
gLastError = 87;
return FALSE;
}
if (nLength == 0)
return TRUE;
size_t written = 0;
uint32_t s = NtlConsoleFillCharacter(SYS_HANDLE_INVALID, cCharacter,
(int)dwWriteCoord.X, (int)dwWriteCoord.Y,
(size_t)nLength, &written);
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = (DWORD)written;
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
return TRUE;
}
__declspec(dllexport) BOOL FillConsoleOutputAttribute(HANDLE hConsoleOutput,
WORD wAttribute,
DWORD nLength,
COORD dwWriteCoord,
DWORD* lpNumberOfAttrsWritten) {
if (lpNumberOfAttrsWritten) *lpNumberOfAttrsWritten = 0;
if (!K32IsConsoleOutputHandle(hConsoleOutput)) {
gLastError = 6;
return FALSE;
}
if (dwWriteCoord.X < 0 || dwWriteCoord.Y < 0) {
gLastError = 87;
return FALSE;
}
if (nLength == 0)
return TRUE;
size_t written = 0;
uint32_t s = NtlConsoleFillAttribute(SYS_HANDLE_INVALID, wAttribute,
(int)dwWriteCoord.X, (int)dwWriteCoord.Y,
(size_t)nLength, &written);
if (lpNumberOfAttrsWritten) *lpNumberOfAttrsWritten = (DWORD)written;
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
return TRUE;
}
// Directory enumeration (FindFirstFileA / FindNextFileA / FindClose).
typedef SYS_DIR_ENTRY KDIR_ENTRY;
// FIND_HANDLE is a user-side wrapper: it stores the kernel dir handle and
// the wildcard pattern so FindNextFileA can filter subsequent entries.
// We return a pointer to this struct cast to HANDLE; it is NOT a kernel
// handle, so FindClose must NOT go through CloseHandle/SysCloseHandle.
typedef struct {
uint32_t kh; // kernel DIR_OBJECT handle (raw pointer)
char pattern[MAX_PATH]; // wildcard pattern, e.g. "*" or "*.txt"
} FIND_HANDLE;
// Minimal wildcard match: supports '*' (any sequence) and '?' (any char).
// Case-insensitive to match FAT32/Windows behaviour.
static int WildMatch(const char* pat, const char* name) {
while (*pat && *name) {
if (*pat == '*') {
// Skip consecutive stars.
while (*pat == '*') pat++;
if (!*pat) return 1; // trailing * matches everything
// Try matching the rest of the pattern at every position.
while (*name) {
if (WildMatch(pat, name)) return 1;
name++;
}
return 0;
}
char p = (*pat >= 'A' && *pat <= 'Z') ? *pat + 32 : *pat;
char n = (*name >= 'A' && *name <= 'Z') ? *name + 32 : *name;
if (*pat != '?' && p != n) return 0;
pat++; name++;
}
// Consume trailing stars in the pattern.
while (*pat == '*') pat++;
return (*pat == '\0' && *name == '\0');
}
// Split a FindFirstFileA-style search path into directory and pattern parts.
// "apps\\*" -> dir="apps" pat="*"
// "*.txt" -> dir="" pat="*.txt"
// "*" -> dir="" pat="*"
// "apps\\" -> dir="apps" pat="*".
static void SplitFindPath(const char* searchPath,
char* dirOut, size_t dirSize,
char* patOut, size_t patSize) {
// Find last backslash or forward-slash.
const char* last = NULL;
for (const char* p = searchPath; *p; p++)
if (*p == '\\' || *p == '/') last = p;
if (!last) {
// No separator: whole string is the pattern, dir is CWD.
if (dirOut && dirSize) dirOut[0] = '\0';
if (patOut && patSize) {
size_t len = strlen(searchPath);
if (len + 1 > patSize) len = patSize - 1;
memcpy(patOut, searchPath, len);
patOut[len] = '\0';
}
} else {
// Everything before the last separator is the directory.
if (dirOut) {
size_t len = (size_t)(last - searchPath);
if (len + 1 > dirSize) len = dirSize - 1;
memcpy(dirOut, searchPath, len);
dirOut[len] = '\0';
}
// Everything after the last separator is the pattern.
if (patOut) {
const char* p = last + 1;
size_t len = strlen(p);
if (len + 1 > patSize) len = patSize - 1;
memcpy(patOut, p, len);
patOut[len] = '\0';
}
}
// Empty pattern defaults to "*".
if (patOut && patOut[0] == '\0') {
patOut[0] = '*'; patOut[1] = '\0';
}
}
// Fill a WIN32_FIND_DATAA from a KDIR_ENTRY.
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010UL
#define FILE_ATTRIBUTE_ARCHIVE 0x00000020UL
#define FILE_ATTRIBUTE_NORMAL 0x00000080UL
// Implements the k32 Dir Is Leap Year operation.
static int K32DirIsLeapYear(unsigned int year) {
return ((year % 4u) == 0u && (year % 100u) != 0u) || ((year % 400u) == 0u);
}
// Implements the k32 Dir Days In Month operation.
static unsigned int K32DirDaysInMonth(unsigned int year, unsigned int month) {
static const unsigned char days[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
if (month < 1u || month > 12u) return 31u;
if (month == 2u && K32DirIsLeapYear(year)) return 29u;
return days[month - 1u];
}
// Implements the k32 Dir Calendar To Epoch Seconds operation.
static uint64_t K32DirCalendarToEpochSeconds(const SYS_SYSTEM_TIME* st) {
uint64_t days = 0;
for (unsigned int y = 1970u; y < st->Year; y++)
days += K32DirIsLeapYear(y) ? 366u : 365u;
for (unsigned int m = 1u; m < st->Month; m++)
days += K32DirDaysInMonth(st->Year, m);
days += (uint64_t)(st->Day - 1u);
return (((days * 24u) + st->Hour) * 60u + st->Minute) * 60u + st->Second;
}
// Implements the k32 Dir Store File Time operation.
static void K32DirStoreFileTime(FILETIME* ft, const SYS_SYSTEM_TIME* st) {
if (!ft || !st || !(st->Flags & SYS_TIME_FLAG_VALID) || st->Year < 1970u) {
if (ft) { ft->dwLowDateTime = 0; ft->dwHighDateTime = 0; }
return;
}
const uint64_t WINDOWS_EPOCH_BIAS_SECONDS = 11644473600ULL;
uint64_t epoch = st->EpochSeconds ? st->EpochSeconds : K32DirCalendarToEpochSeconds(st);
uint64_t value = (epoch + WINDOWS_EPOCH_BIAS_SECONDS) * 10000000ULL +
((uint64_t)st->Millisecond * 10000ULL) +
((uint64_t)st->Nanosecond / 100ULL);
ft->dwLowDateTime = (DWORD)(value & 0xFFFFFFFFULL);
ft->dwHighDateTime = (DWORD)(value >> 32);
}
// Implements the kdir To Find Data operation.
static void KdirToFindData(const KDIR_ENTRY* ke, WIN32_FIND_DATAA* fd) {
// Zero the entire struct (covers all FILETIME and reserved fields).
unsigned char* p = (unsigned char*)fd;
for (size_t i = 0; i < sizeof(*fd); i++) p[i] = 0;
fd->dwFileAttributes = ke->Attr;
if (!(fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
!(fd->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE))
fd->dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
fd->nFileSizeLow = ke->FileSize;
fd->nFileSizeHigh = 0;
K32DirStoreFileTime(&fd->ftCreationTime, &ke->CreateTime);
K32DirStoreFileTime(&fd->ftLastAccessTime, &ke->LastAccessTime);
K32DirStoreFileTime(&fd->ftLastWriteTime, &ke->LastWriteTime);
size_t nameLen = strlen(ke->Name);
if (nameLen >= MAX_PATH) nameLen = MAX_PATH - 1;
memcpy(fd->cFileName, ke->Name, nameLen);
fd->cFileName[nameLen] = '\0';
}
__declspec(dllexport) HANDLE FindFirstFileA(const char* lpFileName,
WIN32_FIND_DATAA* lpFindFileData) {
if (!lpFileName || !lpFindFileData) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return INVALID_HANDLE_VALUE;
}
// Translate the path.
char translatedInput[MAX_PATH];
TranslatePath(lpFileName, translatedInput);
// Split path into directory and wildcard pattern.
char dir[MAX_PATH];
char pat[MAX_PATH];
SplitFindPath(translatedInput, dir, sizeof(dir), pat, sizeof(pat));
// Open the kernel directory iterator.
uint32_t kh = 0;
uint32_t s = K32OpenDirNative(dir[0] ? dir : NULL, &kh);
if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND
return INVALID_HANDLE_VALUE;
}
// Allocate our user-side FIND_HANDLE wrapper.
FIND_HANDLE* fh = (FIND_HANDLE*)HeapAlloc(NULL, 0, sizeof(FIND_HANDLE));
if (!fh) {
SysCloseHandle(kh);
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return INVALID_HANDLE_VALUE;
}
fh->kh = kh;
size_t patLen = strlen(pat);
if (patLen >= MAX_PATH) patLen = MAX_PATH - 1;
memcpy(fh->pattern, pat, patLen);
fh->pattern[patLen] = '\0';
// Read the first entry that matches the pattern.
KDIR_ENTRY ke;
while (true) {
uint32_t rs = SysReadDir(kh, &ke);
if (rs != 0) {
// Directory exhausted or error.
SysCloseHandle(kh);
HeapFree(NULL, 0, fh);
gLastError = 18; // ERROR_NO_MORE_FILES
return INVALID_HANDLE_VALUE;
}
if (WildMatch(fh->pattern, ke.Name)) {
KdirToFindData(&ke, lpFindFileData);
return (HANDLE)fh;
}
}
}
__declspec(dllexport) BOOL FindNextFileA(HANDLE hFindFile,
WIN32_FIND_DATAA* lpFindFileData) {
if (hFindFile == INVALID_HANDLE_VALUE || !lpFindFileData) {
gLastError = 6; // ERROR_INVALID_HANDLE
return FALSE;
}
FIND_HANDLE* fh = (FIND_HANDLE*)hFindFile;
KDIR_ENTRY ke;
while (true) {
uint32_t rs = SysReadDir(fh->kh, &ke);
if (rs != 0) {
gLastError = 18; // ERROR_NO_MORE_FILES
return FALSE;
}
if (WildMatch(fh->pattern, ke.Name)) {
KdirToFindData(&ke, lpFindFileData);
return TRUE;
}
}
}
// Returns close.
__declspec(dllexport) BOOL FindClose(HANDLE hFindFile) {
if (hFindFile == INVALID_HANDLE_VALUE) {
gLastError = 6;
return FALSE;
}
FIND_HANDLE* fh = (FIND_HANDLE*)hFindFile;
SysCloseHandle(fh->kh);
HeapFree(NULL, 0, fh);
return TRUE;
}
// Current Working Directory.
__declspec(dllexport) BOOL SetCurrentDirectoryA(const char* lpPathName) {
if (!lpPathName) { gLastError = 87; return FALSE; }
char translatedPath[MAX_PATH];
TranslatePath(lpPathName, translatedPath);
uint32_t s = K32SetCwdNative(translatedPath);
if (s != 0) { gLastError = 2; return FALSE; }
return TRUE;
}
// Returns current Directory A.
__declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBuffer) {
if (!lpBuffer || nBufferLength == 0) {
// Return a conservative required size including NUL.
return SYS_PATH_MAX + 1u;
}
char raw[SYS_PATH_MAX + 1u];
uint32_t s = K32GetCwdNative(raw, sizeof(raw), NULL);
if (s != 0) { gLastError = 1; return 0; }
static const char prefix[] = "/SystemRoot";
size_t prefixLength = sizeof(prefix) - 1u;
if (strncmp(raw, prefix, prefixLength) != 0 ||
(raw[prefixLength] != '\0' && raw[prefixLength] != '/')) {
gLastError = 3;
return 0;
}
const char* rel = raw + prefixLength;
if (*rel == '/') rel++;
char full[SYS_PATH_MAX + 1u];
full[0] = 'C';
full[1] = ':';
full[2] = '\\';
size_t rawLen = strlen(rel);
if (rawLen + 4 > sizeof(full)) rawLen = sizeof(full) - 4;
memcpy(full + 3, rel, rawLen);
full[3 + rawLen] = '\0';
for (char* p = full + 3; *p; p++) if (*p == '/') *p = '\\';
size_t needed = strlen(full) + 1;
if ((DWORD)needed > nBufferLength) {
gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return (DWORD)needed;
}
memcpy(lpBuffer, full, needed);
return (DWORD)(needed - 1); // length without NUL, matching Win32
}
// Creates one unnamed recursive Win32 mutex over a native kernel mutex.
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
const char* lpName) {
(void)lpMutexAttributes;
if (lpName && lpName[0]) {
gLastError = 120;
return NULL;
}
K32_MUTEX_HANDLE* mutex = (K32_MUTEX_HANDLE*)calloc(1, sizeof(*mutex));
if (!mutex) {
gLastError = 8;
return NULL;
}
uint32_t status = SysCreateMutex(&mutex->NativeHandle);
if (status != NTLSTATUS_SUCCESS || !mutex->NativeHandle) {
free(mutex);
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 1;
return NULL;
}
mutex->Magic = K32_MUTEX_MAGIC;
if (bInitialOwner) {
status = SysWait(mutex->NativeHandle, SYS_WAIT_FLAG_NONE);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_ABANDONED) {
(void)SysCloseHandle(mutex->NativeHandle);
free(mutex);
gLastError = 1;
return NULL;
}
mutex->OwnerTid = K32CurrentTidOrOne();
mutex->Recursion = 1;
}
K32RegisterMutex(mutex);
return (HANDLE)mutex;
}
// Creates one unnamed recursive Win32 mutex from a wide-name request.
__declspec(dllexport) HANDLE CreateMutexW(void* lpMutexAttributes, BOOL bInitialOwner,
const unsigned short* lpName) {
if (lpName && lpName[0]) {
gLastError = 120;
return NULL;
}
return CreateMutexA(lpMutexAttributes, bInitialOwner, NULL);
}
// Releases one recursion level from a Win32 mutex wrapper.
__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) {
K32_MUTEX_HANDLE* mutex = K32ReferenceMutex(hMutex);
if (!mutex) {
gLastError = 6;
return FALSE;
}
DWORD tid = K32CurrentTidOrOne();
K32MutexStateLock(mutex);
if (mutex->OwnerTid != tid || mutex->Recursion == 0) {
K32MutexStateUnlock(mutex);
K32DereferenceMutex(mutex);
gLastError = 288;
return FALSE;
}
mutex->Recursion--;
if (mutex->Recursion != 0) {
K32MutexStateUnlock(mutex);
K32DereferenceMutex(mutex);
return TRUE;
}
mutex->OwnerTid = 0;
K32MutexStateUnlock(mutex);
uint32_t status = SysReleaseMutex(mutex->NativeHandle);
if (status != NTLSTATUS_SUCCESS) {
K32MutexStateLock(mutex);
mutex->OwnerTid = tid;
mutex->Recursion = 1;
K32MutexStateUnlock(mutex);
K32DereferenceMutex(mutex);
gLastError = 288;
return FALSE;
}
K32DereferenceMutex(mutex);
return TRUE;
}
__declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes,
long lInitialCount, long lMaximumCount,
const char* lpName) {
(void)lpSemaphoreAttributes; (void)lpName;
uint32_t kh = 0;
uint32_t s = SysCreateSem((int32_t)lInitialCount, (int32_t)lMaximumCount, &kh);
if (s != 0 || !kh) { gLastError = 8; return NULL; }
return KH_TO_HANDLE(kh);
}
__declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount,
long* lpPreviousCount) {
if (!IS_VALID_KERNEL_HANDLE(hSemaphore) || lReleaseCount < 0) { gLastError = 6; return FALSE; }
if (lpPreviousCount) *lpPreviousCount = 0;
for (long i = 0; i < lReleaseCount; ++i) {
uint32_t s = SysSignalSem(HANDLE_TO_KH(hSemaphore));
if (s != 0) { gLastError = 6; return FALSE; }
}
return TRUE;
}
// Critical Sections & Condition Variables.
// Match the size of Win64 RTL_CRITICAL_SECTION (5 pointer-sized fields) while
// using the storage as a small recursive spin lock. Bink uses these from its
// worker/copy paths, so the old no-op implementation allowed internal state
// races as soon as CreateThread started working.
typedef struct K32_CONDITION_WAITER K32_CONDITION_WAITER;
typedef struct K32_CONDITION_STATE {
volatile long Lock;
K32_CONDITION_WAITER* Head;
K32_CONDITION_WAITER* Tail;
} K32_CONDITION_STATE;
struct K32_CONDITION_WAITER {
K32_CONDITION_WAITER* Previous;
K32_CONDITION_WAITER* Next;
uint32_t Event;
BOOL Queued;
};
#define K32_CS_LOCK 0
#define K32_CS_OWNER 1
#define K32_CS_RECURSION 2
static volatile long* K32CsLockWord(CRITICAL_SECTION* cs) {
return (volatile long*)&cs->_opaque[K32_CS_LOCK];
}
static DWORD K32CurrentTidOrOne(void) {
DWORD tid = (DWORD)SysGetCurrentTid();
return tid ? tid : 1;
}
// Initializes critical Section.
__declspec(dllexport) void InitializeCriticalSection(CRITICAL_SECTION* lpCS) {
if (!lpCS) return;
memset(lpCS, 0, sizeof(*lpCS));
}
__declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION* lpCS,
DWORD dwSpinCount) {
(void)dwSpinCount;
InitializeCriticalSection(lpCS);
return TRUE;
}
// Releases critical Section.
__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
// Implements the enter Critical Section operation.
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) {
if (!lpCS) return;
DWORD tid = K32CurrentTidOrOne();
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] == tid) {
lpCS->_opaque[K32_CS_RECURSION]++;
return;
}
uint32_t spins = 0;
while (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0) {
_mm_pause();
if ((++spins & 0xFFFu) == 0)
(void)SysYield();
}
lpCS->_opaque[K32_CS_OWNER] = tid;
lpCS->_opaque[K32_CS_RECURSION] = 1;
}
// Implements the leave Critical Section operation.
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) {
if (!lpCS) return;
DWORD tid = K32CurrentTidOrOne();
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] != tid || lpCS->_opaque[K32_CS_RECURSION] == 0)
return;
lpCS->_opaque[K32_CS_RECURSION]--;
if (lpCS->_opaque[K32_CS_RECURSION] == 0) {
lpCS->_opaque[K32_CS_OWNER] = 0;
_ReadWriteBarrier();
_InterlockedExchange(K32CsLockWord(lpCS), 0);
}
}
// Implements the try Enter Critical Section operation.
__declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCS) {
if (!lpCS) return FALSE;
DWORD tid = K32CurrentTidOrOne();
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] == tid) {
lpCS->_opaque[K32_CS_RECURSION]++;
return TRUE;
}
if (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0)
return FALSE;
lpCS->_opaque[K32_CS_OWNER] = tid;
lpCS->_opaque[K32_CS_RECURSION] = 1;
return TRUE;
}
// Acquires one condition-variable queue lock.
static void K32ConditionLock(K32_CONDITION_STATE* state) {
uint32_t spins = 0;
while (_InterlockedCompareExchange(&state->Lock, 1, 0) != 0) {
_mm_pause();
if ((++spins & 0xFFFu) == 0)
(void)SysYield();
}
}
// Releases one condition-variable queue lock.
static void K32ConditionUnlock(K32_CONDITION_STATE* state) {
_ReadWriteBarrier();
_InterlockedExchange(&state->Lock, 0);
}
// Returns the lazily allocated process-local condition-variable state.
static K32_CONDITION_STATE* K32ConditionState(CONDITION_VARIABLE* cv, BOOL create) {
if (!cv)
return NULL;
K32_CONDITION_STATE* state = (K32_CONDITION_STATE*)cv->_ptr;
if (state || !create)
return state;
K32_CONDITION_STATE* allocated = (K32_CONDITION_STATE*)calloc(1, sizeof(*allocated));
if (!allocated)
return NULL;
void* previous = _InterlockedCompareExchangePointer((void* volatile*)&cv->_ptr,
allocated,
NULL);
if (previous) {
free(allocated);
state = (K32_CONDITION_STATE*)previous;
} else {
state = allocated;
}
return state;
}
// Removes one waiter while the condition-variable queue lock is held.
static void K32ConditionRemoveWaiter(K32_CONDITION_STATE* state,
K32_CONDITION_WAITER* waiter) {
if (!waiter->Queued)
return;
if (waiter->Previous)
waiter->Previous->Next = waiter->Next;
else
state->Head = waiter->Next;
if (waiter->Next)
waiter->Next->Previous = waiter->Previous;
else
state->Tail = waiter->Previous;
waiter->Previous = NULL;
waiter->Next = NULL;
waiter->Queued = FALSE;
}
// Initializes one process-local condition variable.
__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE* cv) {
if (cv)
cv->_ptr = NULL;
}
// Atomically releases a critical section and waits for one condition wake.
__declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE* cv,
CRITICAL_SECTION* cs,
DWORD dwMilliseconds) {
if (!cv || !cs) {
gLastError = 87;
return FALSE;
}
DWORD tid = K32CurrentTidOrOne();
uintptr_t recursion = cs->_opaque[K32_CS_RECURSION];
if ((DWORD)cs->_opaque[K32_CS_OWNER] != tid || recursion == 0) {
gLastError = 87;
return FALSE;
}
K32_CONDITION_STATE* state = K32ConditionState(cv, TRUE);
if (!state) {
gLastError = 8;
return FALSE;
}
uint32_t event = 0;
KSTATUS status = SysCreateEvent(0, 0, &event);
if (status != NTLSTATUS_SUCCESS) {
gLastError = status == NTLSTATUS_NO_MEMORY ? 8 : 87;
return FALSE;
}
// The waiter is heap-backed because process termination can stop a user
// thread at syscall return before it can remove a stack-backed queue node.
K32_CONDITION_WAITER* waiter = (K32_CONDITION_WAITER*)calloc(1, sizeof(*waiter));
if (!waiter) {
(void)SysCloseHandle(event);
gLastError = 8;
return FALSE;
}
waiter->Event = event;
waiter->Queued = TRUE;
K32ConditionLock(state);
waiter->Previous = state->Tail;
if (state->Tail)
state->Tail->Next = waiter;
else
state->Head = waiter;
state->Tail = waiter;
K32ConditionUnlock(state);
// The waiter is visible before the lock is released, so a wake that races
// this transition is retained by the private auto-reset event.
cs->_opaque[K32_CS_RECURSION] = 0;
cs->_opaque[K32_CS_OWNER] = 0;
_ReadWriteBarrier();
_InterlockedExchange(K32CsLockWord(cs), 0);
uint64_t timeout = dwMilliseconds == INFINITE
? SYS_WAIT_TIMEOUT_INFINITE
: (uint64_t)dwMilliseconds;
status = SysWaitSingle(event, SYS_WAIT_FLAG_NONE, timeout);
K32ConditionLock(state);
BOOL wakeWon = waiter->Queued ? FALSE : TRUE;
K32ConditionRemoveWaiter(state, waiter);
K32ConditionUnlock(state);
EnterCriticalSection(cs);
cs->_opaque[K32_CS_RECURSION] = recursion;
(void)SysCloseHandle(event);
free(waiter);
if (status == NTLSTATUS_SUCCESS || wakeWon)
return TRUE;
if (status == NTLSTATUS_TIMEOUT)
gLastError = 1460;
else
gLastError = 6;
return FALSE;
}
// Wakes the oldest waiter on one condition variable.
__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE* cv) {
K32_CONDITION_STATE* state = K32ConditionState(cv, FALSE);
if (!state)
return;
K32ConditionLock(state);
K32_CONDITION_WAITER* waiter = state->Head;
if (waiter) {
K32ConditionRemoveWaiter(state, waiter);
(void)SysSignalEvent(waiter->Event);
}
K32ConditionUnlock(state);
}
// Wakes every waiter currently queued on one condition variable.
__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) {
K32_CONDITION_STATE* state = K32ConditionState(cv, FALSE);
if (!state)
return;
K32ConditionLock(state);
while (state->Head) {
K32_CONDITION_WAITER* waiter = state->Head;
K32ConditionRemoveWaiter(state, waiter);
(void)SysSignalEvent(waiter->Event);
}
K32ConditionUnlock(state);
}
// Timing & Performance Counters.
typedef union {
struct { DWORD LowPart; long HighPart; } u;
long long QuadPart;
} LARGE_INTEGER;
static volatile long long gTscFreq = 0;
// Attempt to read TSC frequency from CPUID leaf 0x15 (Modern Intel/AMD).
static long long TscFreqFromCpuid(void) {
int info[4];
__cpuid(info, 0);
if ((unsigned)info[0] < 0x15) return 0;
__cpuid(info, 0x15);
unsigned denom = (unsigned)info[0];
unsigned numer = (unsigned)info[1];
unsigned crystal_hz = (unsigned)info[2];
if (!denom || !numer || !crystal_hz) return 0;
return (long long)crystal_hz * numer / denom;
}
// Calibrates TSC against the native monotonic tick source.
static long long TscFreqCalibrate(void) {
uint64_t frequency = K32TickFrequency();
if (!frequency) return 0;
uint64_t calibrationTicks = frequency / 10ull;
if (!calibrationTicks) calibrationTicks = 1;
uint64_t t0 = SysGetTick();
uint64_t tsc0 = __rdtsc();
while ((SysGetTick() - t0) < calibrationTicks) {
_mm_pause();
}
uint64_t t1 = SysGetTick();
uint64_t tsc1 = __rdtsc();
uint64_t delta_tsc = tsc1 - tsc0;
uint64_t delta_us = SysTicksToMicroseconds(t1 - t0, frequency);
if (!delta_us) return 0;
return (long long)(delta_tsc * 1000000ULL / delta_us);
}
// Implements the tsc Freq Get operation.
static long long TscFreqGet(void) {
if (gTscFreq) return gTscFreq;
long long f = TscFreqFromCpuid();
if (!f) f = TscFreqCalibrate();
gTscFreq = f;
return f;
}
// Returns performance Frequency.
__declspec(dllexport) BOOL QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency) {
if (lpFrequency) lpFrequency->QuadPart = TscFreqGet();
return TRUE;
}
// Returns performance Counter.
__declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount) {
// Ensure calibration happens before the first read.
TscFreqGet();
if (lpPerformanceCount)
lpPerformanceCount->QuadPart = (long long)__rdtsc();
return TRUE;
}
// Implements the k32 Is Leap Year operation.
static BOOL K32IsLeapYear(unsigned int year) {
return ((year % 4u) == 0u && (year % 100u) != 0u) || ((year % 400u) == 0u);
}
// Implements the k32 Days In Month operation.
static unsigned int K32DaysInMonth(unsigned int year, unsigned int month) {
static const unsigned char days[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2u && K32IsLeapYear(year)) return 29u;
if (month < 1u || month > 12u) return 0u;
return days[month - 1u];
}
// Implements the k32 Calendar To Epoch Seconds operation.
static uint64_t K32CalendarToEpochSeconds(const SYSTEMTIME* st) {
uint64_t days = 0;
for (unsigned int y = 1970u; y < st->wYear; y++)
days += K32IsLeapYear(y) ? 366u : 365u;
for (unsigned int m = 1u; m < st->wMonth; m++)
days += K32DaysInMonth(st->wYear, m);
days += (uint64_t)(st->wDay - 1u);
return (((days * 24u) + st->wHour) * 60u + st->wMinute) * 60u + st->wSecond;
}
// Implements the k32 Epoch To File Time operation.
static uint64_t K32EpochToFileTime(uint64_t epochSeconds, uint32_t nanosecond) {
const uint64_t WINDOWS_EPOCH_BIAS_SECONDS = 11644473600ULL;
return (epochSeconds + WINDOWS_EPOCH_BIAS_SECONDS) * 10000000ULL +
((uint64_t)nanosecond / 100ULL);
}
// Implements the k32 Store File Time operation.
static void K32StoreFileTime(FILETIME* ft, uint64_t value) {
ft->dwLowDateTime = (DWORD)(value & 0xFFFFFFFFULL);
ft->dwHighDateTime = (DWORD)(value >> 32);
}
// Returns system Time.
__declspec(dllexport) void GetSystemTime(SYSTEMTIME* lpSystemTime) {
if (!lpSystemTime) return;
SYS_SYSTEM_TIME st;
if (SysGetSystemTime(&st) != NTLSTATUS_SUCCESS) {
memset(lpSystemTime, 0, sizeof(*lpSystemTime));
return;
}
lpSystemTime->wYear = st.Year;
lpSystemTime->wMonth = st.Month;
lpSystemTime->wDayOfWeek = st.DayOfWeek;
lpSystemTime->wDay = st.Day;
lpSystemTime->wHour = st.Hour;
lpSystemTime->wMinute = st.Minute;
lpSystemTime->wSecond = st.Second;
lpSystemTime->wMilliseconds = st.Millisecond;
}
__declspec(dllexport) BOOL SystemTimeToFileTime(const SYSTEMTIME* lpSystemTime,
FILETIME* lpFileTime) {
if (!lpSystemTime || !lpFileTime) return FALSE;
if (lpSystemTime->wYear < 1970 || lpSystemTime->wMonth < 1 ||
lpSystemTime->wMonth > 12 || lpSystemTime->wDay < 1 ||
lpSystemTime->wDay > K32DaysInMonth(lpSystemTime->wYear, lpSystemTime->wMonth) ||
lpSystemTime->wHour > 23 || lpSystemTime->wMinute > 59 ||
lpSystemTime->wSecond > 59 || lpSystemTime->wMilliseconds > 999) {
return FALSE;
}
uint64_t ft = K32EpochToFileTime(K32CalendarToEpochSeconds(lpSystemTime),
(uint32_t)lpSystemTime->wMilliseconds * 1000000u);
K32StoreFileTime(lpFileTime, ft);
return TRUE;
}
// Returns system Time As File Time.
__declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime) {
if (!lpSystemTimeAsFileTime) return;
SYS_SYSTEM_TIME st;
uint64_t ft;
if (SysGetSystemTime(&st) == NTLSTATUS_SUCCESS) {
ft = K32EpochToFileTime(st.EpochSeconds, st.Nanosecond);
} else {
uint64_t frequency = K32TickFrequency();
const uint64_t FALLBACK_2024_01_01_FT = 133485408000000000ULL;
ft = FALLBACK_2024_01_01_FT + SysTicksToMicroseconds(SysGetTick(), frequency) * 10ULL;
}
memcpy(lpSystemTimeAsFileTime, &ft, sizeof(ft));
}
// System Information.
typedef struct {
union {
DWORD dwOemId;
struct { unsigned short wProcessorArchitecture;
unsigned short wReserved; };
};
DWORD dwPageSize;
void* lpMinimumApplicationAddress;
void* lpMaximumApplicationAddress;
uintptr_t dwActiveProcessorMask;
DWORD dwNumberOfProcessors;
DWORD dwProcessorType;
DWORD dwAllocationGranularity;
unsigned short wProcessorLevel;
unsigned short wProcessorRevision;
} SYSTEM_INFO;
// Returns system Info.
__declspec(dllexport) void GetSystemInfo(SYSTEM_INFO* lpSystemInfo) {
if (!lpSystemInfo) return;
unsigned char* p = (unsigned char*)lpSystemInfo;
for (size_t i = 0; i < sizeof(*lpSystemInfo); i++) p[i] = 0;
lpSystemInfo->wProcessorArchitecture = 9; // PROCESSOR_ARCHITECTURE_AMD64
lpSystemInfo->dwPageSize = 4096;
lpSystemInfo->lpMinimumApplicationAddress = (void*)0x10000;
lpSystemInfo->lpMaximumApplicationAddress = (void*)0x7FFFFFFEFFFFull;
lpSystemInfo->dwActiveProcessorMask = 1;
lpSystemInfo->dwNumberOfProcessors = 1;
lpSystemInfo->dwProcessorType = 8664; // PROCESSOR_AMD_X8664
lpSystemInfo->dwAllocationGranularity= 65536;
lpSystemInfo->wProcessorLevel = 6;
lpSystemInfo->wProcessorRevision = 0;
}
typedef struct {
DWORD dwLength;
DWORD dwMemoryLoad;
unsigned long long ullTotalPhys;
unsigned long long ullAvailPhys;
unsigned long long ullTotalPageFile;
unsigned long long ullAvailPageFile;
unsigned long long ullTotalVirtual;
unsigned long long ullAvailVirtual;
unsigned long long ullAvailExtendedVirtual;
} MEMORYSTATUSEX;
__declspec(dllexport) BOOL GetLogicalProcessorInformation(void* Buffer,
DWORD* ReturnedLength) {
(void)Buffer;
// Signal that the buffer is too small so callers know to handle failure.
if (ReturnedLength) *ReturnedLength = 0;
gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return FALSE;
}
#define MAX_PATH 260
// Returns system Directory A.
__declspec(dllexport) unsigned int GetSystemDirectoryA(char* lpBuffer, unsigned int uSize) {
static const char sysdir[] = "C:\\Windows\\System32";
unsigned int needed = sizeof(sysdir); // includes NUL
if (lpBuffer && uSize >= needed) {
for (unsigned int i = 0; i < needed; i++) lpBuffer[i] = sysdir[i];
}
return needed - 1; // return length without NUL, matching Win32
}
// Returns windows Directory A.
__declspec(dllexport) unsigned int GetWindowsDirectoryA(char* lpBuffer, unsigned int uSize) {
static const char windir[] = "C:\\Windows";
unsigned int needed = sizeof(windir);
if (lpBuffer && uSize >= needed) {
for (unsigned int i = 0; i < needed; i++) lpBuffer[i] = windir[i];
}
return needed - 1;
}
// Returns static Env A.
static const char* LookupStaticEnvA(const char* name) {
if (!name) return NULL;
if (strcmp(name, "HOME") == 0) return ".";
if (strcmp(name, "USERPROFILE") == 0) return ".";
if (strcmp(name, "HOMEDRIVE") == 0) return "C:";
if (strcmp(name, "HOMEPATH") == 0) return "\\";
return NULL;
}
__declspec(dllexport) DWORD GetEnvironmentVariableA(const char* lpName, char* lpBuffer,
DWORD nSize) {
const char* value = LookupStaticEnvA(lpName);
if (!value) {
if (lpBuffer && nSize) lpBuffer[0] = '\0';
gLastError = 203; // ERROR_ENVVAR_NOT_FOUND
return 0;
}
DWORD len = (DWORD)strlen(value);
if (!lpBuffer || nSize == 0) return len;
if (len + 1 > nSize) {
if (nSize) lpBuffer[0] = '\0';
gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return len + 1;
}
memcpy(lpBuffer, value, len + 1);
return len;
}
// Returns environment Strings.
__declspec(dllexport) char* GetEnvironmentStrings(void) {
// Double-NUL-terminated environment block.
static char env[] = "HOME=.\0USERPROFILE=.\0HOMEDRIVE=C:\0HOMEPATH=\\\0\0";
return env;
}
// Releases environment Strings A.
__declspec(dllexport) BOOL FreeEnvironmentStringsA(char* penv) {
(void)penv; return TRUE;
}
// Returns environment Strings W.
__declspec(dllexport) unsigned short* GetEnvironmentStringsW(void) {
static unsigned short emptyEnvW[2] = { 0, 0 };
return emptyEnvW;
}
// Releases environment Strings W.
__declspec(dllexport) BOOL FreeEnvironmentStringsW(unsigned short* penv) {
(void)penv; return TRUE;
}
// Handle / Error helpers.
__declspec(dllexport) unsigned int SetErrorMode(unsigned int uMode) {
(void)uMode;
return 0; // previous mode
}
// Updates handle Count.
__declspec(dllexport) unsigned int SetHandleCount(unsigned int uNumber) {
return uNumber < 256 ? 256 : uNumber;
}
// File helpers.
#define FILE_TYPE_UNKNOWN 0x0000
#define FILE_TYPE_DISK 0x0001
#define FILE_TYPE_CHAR 0x0002
#define FILE_TYPE_PIPE 0x0003
#define STD_INPUT_HANDLE ((DWORD)-10)
#define STD_OUTPUT_HANDLE ((DWORD)-11)
#define STD_ERROR_HANDLE ((DWORD)-12)
// Returns file Type.
__declspec(dllexport) DWORD GetFileType(HANDLE hFile) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) return FILE_TYPE_UNKNOWN;
if (K32IsStdHandleValue(hFile, STD_INPUT_HANDLE) ||
K32IsStdHandleValue(hFile, STD_OUTPUT_HANDLE) ||
K32IsStdHandleValue(hFile, STD_ERROR_HANDLE))
return FILE_TYPE_CHAR;
return FILE_TYPE_DISK;
}
// Exception Handling & Debugging.
#define EXCEPTION_CONTINUE_EXECUTION ((LONG)-1)
#define EXCEPTION_CONTINUE_SEARCH 0
#define EXCEPTION_EXECUTE_HANDLER 1
#define EXCEPTION_NONCONTINUABLE 0x1UL
#define EXCEPTION_MAXIMUM_PARAMETERS 15u
#define WIN_VECTORED_MAX_HANDLERS 16u
#define WIN_SEH_MAX_FRAMES 16u
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD* ExceptionRecord;
void* ExceptionAddress;
DWORD NumberParameters;
uintptr_t ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
typedef struct _CONTEXT {
DWORD64 P1Home;
DWORD64 P2Home;
DWORD64 P3Home;
DWORD64 P4Home;
DWORD64 P5Home;
DWORD64 P6Home;
DWORD ContextFlags;
DWORD MxCsr;
WORD SegCs;
WORD SegDs;
WORD SegEs;
WORD SegFs;
WORD SegGs;
WORD SegSs;
DWORD EFlags;
DWORD64 Dr0;
DWORD64 Dr1;
DWORD64 Dr2;
DWORD64 Dr3;
DWORD64 Dr6;
DWORD64 Dr7;
DWORD64 Rax;
DWORD64 Rcx;
DWORD64 Rdx;
DWORD64 Rbx;
DWORD64 Rsp;
DWORD64 Rbp;
DWORD64 Rsi;
DWORD64 Rdi;
DWORD64 R8;
DWORD64 R9;
DWORD64 R10;
DWORD64 R11;
DWORD64 R12;
DWORD64 R13;
DWORD64 R14;
DWORD64 R15;
DWORD64 Rip;
} CONTEXT;
typedef struct _EXCEPTION_POINTERS {
EXCEPTION_RECORD* ExceptionRecord;
CONTEXT* ContextRecord;
} EXCEPTION_POINTERS;
typedef LONG (*PVECTORED_EXCEPTION_HANDLER)(EXCEPTION_POINTERS* ExceptionInfo);
typedef LONG (*PRTL_EXCEPTION_FRAME_HANDLER)(EXCEPTION_POINTERS* ExceptionInfo, void* UserContext);
typedef struct _RTL_EXCEPTION_FRAME {
DWORD Size;
DWORD Flags;
struct _RTL_EXCEPTION_FRAME* Previous;
PRTL_EXCEPTION_FRAME_HANDLER Handler;
void* UserContext;
} RTL_EXCEPTION_FRAME;
typedef struct WIN_VECTORED_HANDLER_ENTRY {
uint32_t Active;
uint32_t IsContinue;
uint32_t Previous;
uint32_t Next;
PVECTORED_EXCEPTION_HANDLER Handler;
} WIN_VECTORED_HANDLER_ENTRY;
typedef struct WIN_VECTORED_HANDLER_SNAPSHOT {
PVECTORED_EXCEPTION_HANDLER Handler;
} WIN_VECTORED_HANDLER_SNAPSHOT;
typedef struct WIN_SEH_FRAME_SNAPSHOT {
PRTL_EXCEPTION_FRAME_HANDLER Handler;
void* UserContext;
} WIN_SEH_FRAME_SNAPSHOT;
static volatile long gWinVectoredLock;
static uint32_t gWinVectoredExceptionHead = UINT32_MAX;
static uint32_t gWinVectoredExceptionTail = UINT32_MAX;
static uint32_t gWinVectoredContinueHead = UINT32_MAX;
static uint32_t gWinVectoredContinueTail = UINT32_MAX;
static WIN_VECTORED_HANDLER_ENTRY gWinVectoredHandlers[WIN_VECTORED_MAX_HANDLERS];
static void* gWinExceptionNativeHandle;
// Acquires the Win32 vectored-handler list lock.
static void WinpVectoredLock(void) {
while (_InterlockedCompareExchange(&gWinVectoredLock, 1, 0) != 0)
_mm_pause();
_ReadWriteBarrier();
}
// Releases the Win32 vectored-handler list lock.
static void WinpVectoredUnlock(void) {
_ReadWriteBarrier();
_InterlockedExchange(&gWinVectoredLock, 0);
}
// Returns the list head slot for one vectored-handler kind.
static uint32_t* WinpVectoredHeadSlot(uint32_t isContinue) {
return isContinue ? &gWinVectoredContinueHead : &gWinVectoredExceptionHead;
}
// Returns the list tail slot for one vectored-handler kind.
static uint32_t* WinpVectoredTailSlot(uint32_t isContinue) {
return isContinue ? &gWinVectoredContinueTail : &gWinVectoredExceptionTail;
}
// Windows exception values remain a Win32 personality ABI and are translated
// explicitly from the native nt-lite exception classes.
#define WIN_EXCEPTION_GUARD_PAGE_VIOLATION 0x80000001u
#define WIN_EXCEPTION_DATATYPE_MISALIGNMENT 0x80000002u
#define WIN_EXCEPTION_BREAKPOINT 0x80000003u
#define WIN_EXCEPTION_SINGLE_STEP 0x80000004u
#define WIN_EXCEPTION_ACCESS_VIOLATION 0xC0000005u
#define WIN_EXCEPTION_ILLEGAL_INSTRUCTION 0xC000001Du
#define WIN_EXCEPTION_ARRAY_BOUNDS_EXCEEDED 0xC000008Cu
#define WIN_EXCEPTION_FLT_DENORMAL_OPERAND 0xC000008Du
#define WIN_EXCEPTION_FLT_DIVIDE_BY_ZERO 0xC000008Eu
#define WIN_EXCEPTION_FLT_INEXACT_RESULT 0xC000008Fu
#define WIN_EXCEPTION_FLT_INVALID_OPERATION 0xC0000090u
#define WIN_EXCEPTION_FLT_OVERFLOW 0xC0000091u
#define WIN_EXCEPTION_FLT_STACK_CHECK 0xC0000092u
#define WIN_EXCEPTION_FLT_UNDERFLOW 0xC0000093u
#define WIN_EXCEPTION_INT_DIVIDE_BY_ZERO 0xC0000094u
#define WIN_EXCEPTION_INT_OVERFLOW 0xC0000095u
#define WIN_EXCEPTION_PRIVILEGED_INSTRUCTION 0xC0000096u
#define WIN_EXCEPTION_STACK_OVERFLOW 0xC00000FDu
#define WIN_EXCEPTION_NTLITE_UNKNOWN 0xE04E544Cu
// Maps one native exception class to the Win32 exception namespace.
static DWORD WinpExceptionCodeFromNative(const SYS_EXCEPTION_RECORD* source) {
if (!source) return WIN_EXCEPTION_NTLITE_UNKNOWN;
if (source->Code == SYS_EXCEPTION_CODE_USER_RAISED)
return source->UserCode;
switch (source->Code) {
case SYS_EXCEPTION_CODE_GUARD_PAGE_VIOLATION: return WIN_EXCEPTION_GUARD_PAGE_VIOLATION;
case SYS_EXCEPTION_CODE_DATATYPE_MISALIGNMENT: return WIN_EXCEPTION_DATATYPE_MISALIGNMENT;
case SYS_EXCEPTION_CODE_BREAKPOINT: return WIN_EXCEPTION_BREAKPOINT;
case SYS_EXCEPTION_CODE_SINGLE_STEP: return WIN_EXCEPTION_SINGLE_STEP;
case SYS_EXCEPTION_CODE_ACCESS_VIOLATION: return WIN_EXCEPTION_ACCESS_VIOLATION;
case SYS_EXCEPTION_CODE_ILLEGAL_INSTRUCTION: return WIN_EXCEPTION_ILLEGAL_INSTRUCTION;
case SYS_EXCEPTION_CODE_ARRAY_BOUNDS_EXCEEDED: return WIN_EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
case SYS_EXCEPTION_CODE_FLT_DENORMAL_OPERAND: return WIN_EXCEPTION_FLT_DENORMAL_OPERAND;
case SYS_EXCEPTION_CODE_FLT_DIVIDE_BY_ZERO: return WIN_EXCEPTION_FLT_DIVIDE_BY_ZERO;
case SYS_EXCEPTION_CODE_FLT_INEXACT_RESULT: return WIN_EXCEPTION_FLT_INEXACT_RESULT;
case SYS_EXCEPTION_CODE_FLT_INVALID_OPERATION: return WIN_EXCEPTION_FLT_INVALID_OPERATION;
case SYS_EXCEPTION_CODE_FLT_OVERFLOW: return WIN_EXCEPTION_FLT_OVERFLOW;
case SYS_EXCEPTION_CODE_FLT_STACK_CHECK: return WIN_EXCEPTION_FLT_STACK_CHECK;
case SYS_EXCEPTION_CODE_FLT_UNDERFLOW: return WIN_EXCEPTION_FLT_UNDERFLOW;
case SYS_EXCEPTION_CODE_INT_DIVIDE_BY_ZERO: return WIN_EXCEPTION_INT_DIVIDE_BY_ZERO;
case SYS_EXCEPTION_CODE_INT_OVERFLOW: return WIN_EXCEPTION_INT_OVERFLOW;
case SYS_EXCEPTION_CODE_PRIVILEGED_INSTRUCTION: return WIN_EXCEPTION_PRIVILEGED_INSTRUCTION;
case SYS_EXCEPTION_CODE_STACK_OVERFLOW: return WIN_EXCEPTION_STACK_OVERFLOW;
default: return WIN_EXCEPTION_NTLITE_UNKNOWN;
}
}
// Copies native exception state into the Win32-facing exception record.
static void WinpBuildExceptionRecord(const SYS_EXCEPTION_RECORD* source, EXCEPTION_RECORD* target) {
memset(target, 0, sizeof(*target));
target->ExceptionCode = WinpExceptionCodeFromNative(source);
if (source->Flags & SYS_EXCEPTION_FLAG_NONCONTINUABLE)
target->ExceptionFlags |= EXCEPTION_NONCONTINUABLE;
target->ExceptionAddress = (void*)(uintptr_t)source->ExceptionAddress;
target->NumberParameters = source->ParameterCount;
if (target->NumberParameters > EXCEPTION_MAXIMUM_PARAMETERS)
target->NumberParameters = EXCEPTION_MAXIMUM_PARAMETERS;
for (DWORD i = 0; i < target->NumberParameters; i++)
target->ExceptionInformation[i] = (uintptr_t)source->Parameters[i];
}
// Copies native user context into the subset of x64 CONTEXT currently exposed by Win32.
static void WinpBuildContext(const SYS_USER_CONTEXT* source, CONTEXT* target) {
memset(target, 0, sizeof(*target));
target->SegCs = (WORD)source->SegCs;
target->SegSs = (WORD)source->SegSs;
target->EFlags = (DWORD)source->Rflags;
target->Rax = source->Rax;
target->Rcx = source->Rcx;
target->Rdx = source->Rdx;
target->Rbx = source->Rbx;
target->Rsp = source->Rsp;
target->Rbp = source->Rbp;
target->Rsi = source->Rsi;
target->Rdi = source->Rdi;
target->R8 = source->R8;
target->R9 = source->R9;
target->R10 = source->R10;
target->R11 = source->R11;
target->R12 = source->R12;
target->R13 = source->R13;
target->R14 = source->R14;
target->R15 = source->R15;
target->Rip = source->Rip;
}
// Copies handler-edited Win32 context back to the native resume context.
static void WinpStoreContext(SYS_USER_CONTEXT* target, const CONTEXT* source) {
target->Rflags = source->EFlags;
target->Rax = source->Rax;
target->Rcx = source->Rcx;
target->Rdx = source->Rdx;
target->Rbx = source->Rbx;
target->Rsp = source->Rsp;
target->Rbp = source->Rbp;
target->Rsi = source->Rsi;
target->Rdi = source->Rdi;
target->R8 = source->R8;
target->R9 = source->R9;
target->R10 = source->R10;
target->R11 = source->R11;
target->R12 = source->R12;
target->R13 = source->R13;
target->R14 = source->R14;
target->R15 = source->R15;
target->Rip = source->Rip;
}
// Copies one vectored-handler list so callbacks can run without the list lock.
static size_t WinpSnapshotVectoredHandlers(uint32_t isContinue,
WIN_VECTORED_HANDLER_SNAPSHOT* snapshot,
size_t capacity) {
size_t count = 0;
WinpVectoredLock();
uint32_t head = *WinpVectoredHeadSlot(isContinue);
for (uint32_t index = head;
index != UINT32_MAX && count < capacity;
index = gWinVectoredHandlers[index].Next) {
WIN_VECTORED_HANDLER_ENTRY* entry = &gWinVectoredHandlers[index];
if (!entry->Active || entry->IsContinue != isContinue || !entry->Handler)
continue;
snapshot[count++].Handler = entry->Handler;
}
WinpVectoredUnlock();
return count;
}
// Runs one Win32 vectored-handler list against the current exception state.
static int WinpRunVectoredList(uint32_t isContinue,
EXCEPTION_POINTERS* pointers,
SYS_USER_CONTEXT* nativeContext) {
WIN_VECTORED_HANDLER_SNAPSHOT snapshot[WIN_VECTORED_MAX_HANDLERS];
size_t count = WinpSnapshotVectoredHandlers(isContinue, snapshot, WIN_VECTORED_MAX_HANDLERS);
for (size_t i = 0; i < count; i++) {
LONG disposition = snapshot[i].Handler(pointers);
if (disposition == EXCEPTION_CONTINUE_SEARCH)
continue;
if (disposition == EXCEPTION_CONTINUE_EXECUTION) {
if (pointers->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
return NTL_EXCEPTION_TERMINATE_PROCESS;
WinpStoreContext(nativeContext, pointers->ContextRecord);
return NTL_EXCEPTION_CONTINUE_EXECUTION;
}
}
return NTL_EXCEPTION_CONTINUE_SEARCH;
}
// Returns the current thread's top runtime-registered SEH frame.
static RTL_EXCEPTION_FRAME* WinpSehFrameHead(void) {
return (RTL_EXCEPTION_FRAME*)K32ThreadEnv()->ExceptionFrameHead;
}
// Updates the current thread's top runtime-registered SEH frame.
static void WinpSetSehFrameHead(RTL_EXCEPTION_FRAME* frame) {
K32ThreadEnv()->ExceptionFrameHead = frame;
}
// Copies the current SEH frame chain so callbacks can mutate registration safely.
static size_t WinpSnapshotSehFrames(WIN_SEH_FRAME_SNAPSHOT* snapshot, size_t capacity) {
size_t count = 0;
for (RTL_EXCEPTION_FRAME* frame = WinpSehFrameHead();
frame && count < capacity;
frame = frame->Previous) {
if (frame->Size != sizeof(*frame) || !frame->Handler)
break;
snapshot[count].Handler = frame->Handler;
snapshot[count].UserContext = frame->UserContext;
count++;
}
return count;
}
// Runs runtime-registered SEH frames after VEH has declined the exception.
static int WinpRunSehFrames(EXCEPTION_POINTERS* pointers, SYS_USER_CONTEXT* nativeContext) {
WIN_SEH_FRAME_SNAPSHOT snapshot[WIN_SEH_MAX_FRAMES];
size_t count = WinpSnapshotSehFrames(snapshot, WIN_SEH_MAX_FRAMES);
for (size_t i = 0; i < count; i++) {
LONG disposition = snapshot[i].Handler(pointers, snapshot[i].UserContext);
if (disposition == EXCEPTION_CONTINUE_SEARCH)
continue;
if (disposition == EXCEPTION_CONTINUE_EXECUTION) {
if (pointers->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
return NTL_EXCEPTION_TERMINATE_PROCESS;
WinpStoreContext(nativeContext, pointers->ContextRecord);
return NTL_EXCEPTION_CONTINUE_EXECUTION;
}
if (disposition == EXCEPTION_EXECUTE_HANDLER) {
// Runtime frames do not have a compiler-generated landing pad yet.
return NTL_EXCEPTION_TERMINATE_PROCESS;
}
return NTL_EXCEPTION_TERMINATE_PROCESS;
}
return NTL_EXCEPTION_CONTINUE_SEARCH;
}
// Bridges the native exception runtime into Win32 exception policy.
static int WinpExceptionNativeDispatch(SYS_EXCEPTION_RECORD* record, SYS_USER_CONTEXT* context, void* userContext) {
(void)userContext;
EXCEPTION_RECORD winRecord;
CONTEXT winContext;
EXCEPTION_POINTERS pointers;
WinpBuildExceptionRecord(record, &winRecord);
WinpBuildContext(context, &winContext);
pointers.ExceptionRecord = &winRecord;
pointers.ContextRecord = &winContext;
int disposition = WinpRunVectoredList(0, &pointers, context);
if (disposition != NTL_EXCEPTION_CONTINUE_SEARCH)
return disposition;
disposition = WinpRunSehFrames(&pointers, context);
if (disposition != NTL_EXCEPTION_CONTINUE_SEARCH)
return disposition;
return WinpRunVectoredList(1, &pointers, context);
}
// Ensures the Win32 exception bridge is registered with the native exception runtime.
static BOOL WinpEnsureExceptionBridge(void) {
if (gWinExceptionNativeHandle)
return TRUE;
WinpVectoredLock();
if (gWinExceptionNativeHandle) {
WinpVectoredUnlock();
return TRUE;
}
void* handle = NtlAddExceptionHandler(1, WinpExceptionNativeDispatch, NULL);
if (!handle) {
WinpVectoredUnlock();
gLastError = 8;
return FALSE;
}
gWinExceptionNativeHandle = handle;
WinpVectoredUnlock();
return TRUE;
}
// Adds one Win32 vectored exception or continue handler.
static void* WinpAddVectoredHandler(uint32_t isContinue, unsigned long first, void* handler) {
if (!handler) {
gLastError = 87;
return NULL;
}
if (!WinpEnsureExceptionBridge())
return NULL;
WinpVectoredLock();
uint32_t slot = UINT32_MAX;
for (uint32_t i = 0; i < WIN_VECTORED_MAX_HANDLERS; i++) {
if (!gWinVectoredHandlers[i].Active) {
slot = i;
break;
}
}
if (slot == UINT32_MAX) {
WinpVectoredUnlock();
gLastError = 8;
return NULL;
}
uint32_t* head = WinpVectoredHeadSlot(isContinue);
uint32_t* tail = WinpVectoredTailSlot(isContinue);
WIN_VECTORED_HANDLER_ENTRY* entry = &gWinVectoredHandlers[slot];
entry->Active = 1;
entry->IsContinue = isContinue;
entry->Handler = (PVECTORED_EXCEPTION_HANDLER)handler;
entry->Previous = UINT32_MAX;
entry->Next = UINT32_MAX;
if (*head == UINT32_MAX) {
*head = slot;
*tail = slot;
} else if (first) {
entry->Next = *head;
gWinVectoredHandlers[*head].Previous = slot;
*head = slot;
} else {
entry->Previous = *tail;
gWinVectoredHandlers[*tail].Next = slot;
*tail = slot;
}
WinpVectoredUnlock();
return entry;
}
// Removes one Win32 vectored exception or continue handler.
static unsigned long WinpRemoveVectoredHandler(uint32_t isContinue, void* handle) {
if (!handle) {
gLastError = 6;
return 0;
}
WinpVectoredLock();
uint32_t slot = UINT32_MAX;
for (uint32_t i = 0; i < WIN_VECTORED_MAX_HANDLERS; i++) {
if (handle == &gWinVectoredHandlers[i]) {
slot = i;
break;
}
}
if (slot == UINT32_MAX || !gWinVectoredHandlers[slot].Active ||
gWinVectoredHandlers[slot].IsContinue != isContinue) {
WinpVectoredUnlock();
gLastError = 6;
return 0;
}
uint32_t* head = WinpVectoredHeadSlot(isContinue);
uint32_t* tail = WinpVectoredTailSlot(isContinue);
WIN_VECTORED_HANDLER_ENTRY* entry = &gWinVectoredHandlers[slot];
if (entry->Previous != UINT32_MAX)
gWinVectoredHandlers[entry->Previous].Next = entry->Next;
else
*head = entry->Next;
if (entry->Next != UINT32_MAX)
gWinVectoredHandlers[entry->Next].Previous = entry->Previous;
else
*tail = entry->Previous;
memset(entry, 0, sizeof(*entry));
entry->Previous = UINT32_MAX;
entry->Next = UINT32_MAX;
WinpVectoredUnlock();
return 1;
}
// Registers one Win32 vectored exception handler.
__declspec(dllexport) void* AddVectoredExceptionHandler(unsigned long First, void* Handler) {
return WinpAddVectoredHandler(0, First, Handler);
}
// Removes one Win32 vectored exception handler.
__declspec(dllexport) unsigned long RemoveVectoredExceptionHandler(void* Handle) {
return WinpRemoveVectoredHandler(0, Handle);
}
// Registers one Win32 vectored continue handler.
__declspec(dllexport) void* AddVectoredContinueHandler(unsigned long First, void* Handler) {
return WinpAddVectoredHandler(1, First, Handler);
}
// Removes one Win32 vectored continue handler.
__declspec(dllexport) unsigned long RemoveVectoredContinueHandler(void* Handle) {
return WinpRemoveVectoredHandler(1, Handle);
}
// Pushes one explicit runtime SEH frame for tests and hand-written personality code.
__declspec(dllexport) void* RtlPushExceptionFrame(RTL_EXCEPTION_FRAME* Frame,
PRTL_EXCEPTION_FRAME_HANDLER Handler,
void* UserContext) {
if (!Frame || !Handler) {
gLastError = 87;
return NULL;
}
if (!WinpEnsureExceptionBridge())
return NULL;
memset(Frame, 0, sizeof(*Frame));
Frame->Size = sizeof(*Frame);
Frame->Previous = WinpSehFrameHead();
Frame->Handler = Handler;
Frame->UserContext = UserContext;
WinpSetSehFrameHead(Frame);
return Frame;
}
// Pops the current explicit runtime SEH frame and enforces stack discipline.
__declspec(dllexport) BOOL RtlPopExceptionFrame(RTL_EXCEPTION_FRAME* Frame) {
if (!Frame || WinpSehFrameHead() != Frame) {
gLastError = 87;
return FALSE;
}
WinpSetSehFrameHead(Frame->Previous);
memset(Frame, 0, sizeof(*Frame));
return TRUE;
}
__declspec(dllexport) void RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags,
DWORD nNumberOfArguments,
const uintptr_t* lpArguments) {
uint64_t arguments[EXCEPTION_MAXIMUM_PARAMETERS];
DWORD count = nNumberOfArguments;
if (count > EXCEPTION_MAXIMUM_PARAMETERS)
count = EXCEPTION_MAXIMUM_PARAMETERS;
for (DWORD i = 0; i < count; i++)
arguments[i] = lpArguments ? (uint64_t)lpArguments[i] : 0;
SYS_RAISE_EXCEPTION_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.UserCode = dwExceptionCode;
request.Flags = (dwExceptionFlags & EXCEPTION_NONCONTINUABLE) ?
SYS_RAISE_EXCEPTION_FLAG_NONCONTINUABLE :
SYS_RAISE_EXCEPTION_FLAG_NONE;
request.ParameterCount = count;
request.Parameters = count ? arguments : NULL;
uint32_t status = SysRaiseException(&request);
if (status != NTLSTATUS_SUCCESS)
SysExitProcess((int32_t)status);
}
// Implements the unhandled Exception Filter operation.
__declspec(dllexport) long UnhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) {
(void)ExceptionInfo;
return EXCEPTION_CONTINUE_SEARCH;
}
typedef long (*LPTOP_LEVEL_EXCEPTION_FILTER)(EXCEPTION_POINTERS*);
__declspec(dllexport) LPTOP_LEVEL_EXCEPTION_FILTER
// Updates unhandled Exception Filter.
SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpFilter) {
(void)lpFilter;
return NULL; // previous filter: none
}
// Reports that no user-mode debugger is attached.
__declspec(dllexport) BOOL IsDebuggerPresent(void) {
return FALSE;
}
// RTL Exception / Unwind metadata helpers.
typedef struct _RUNTIME_FUNCTION {
unsigned int BeginAddress;
unsigned int EndAddress;
unsigned int UnwindInfoAddress;
} RUNTIME_FUNCTION;
__declspec(dllexport) BOOL RtlAddFunctionTable(RUNTIME_FUNCTION* FunctionTable,
DWORD EntryCount,
unsigned long long BaseAddress) {
(void)FunctionTable; (void)EntryCount; (void)BaseAddress;
return TRUE;
}
// Implements the rtl Delete Function Table operation.
__declspec(dllexport) BOOL RtlDeleteFunctionTable(RUNTIME_FUNCTION* FunctionTable) {
(void)FunctionTable;
return TRUE;
}
// We have no runtime function table, so RtlLookupFunctionEntry always fails.
__declspec(dllexport) RUNTIME_FUNCTION*
RtlLookupFunctionEntry(unsigned long long ControlPc,
unsigned long long* ImageBase,
void* HistoryTable) {
(void)ControlPc; (void)HistoryTable;
if (ImageBase) *ImageBase = 0;
return NULL;
}
__declspec(dllexport) void* RtlVirtualUnwind(unsigned long HandlerType,
unsigned long long ImageBase,
unsigned long long ControlPc,
RUNTIME_FUNCTION* FunctionEntry,
void* ContextRecord,
void** HandlerData,
unsigned long long* EstablisherFrame,
void* ContextPointers) {
(void)HandlerType; (void)ImageBase; (void)ControlPc; (void)FunctionEntry;
(void)ContextRecord; (void)ContextPointers;
if (HandlerData) *HandlerData = NULL;
if (EstablisherFrame)*EstablisherFrame = 0;
return NULL;
}
// Implements the rtl Capture Context operation.
__declspec(dllexport) void RtlCaptureContext(void* ContextRecord) {
// Zero out the context record so callers don't read garbage.
if (!ContextRecord) return;
// CONTEXT is ~1232 bytes on x64; zero a conservative upper bound.
unsigned char* p = (unsigned char*)ContextRecord;
for (int i = 0; i < 1232; i++) p[i] = 0;
}
__declspec(dllexport) void RtlUnwindEx(void* TargetFrame, void* TargetIp,
void* ExceptionRecord, void* ReturnValue,
void* ContextRecord, void* HistoryTable) {
(void)TargetFrame; (void)TargetIp; (void)ExceptionRecord;
(void)ReturnValue; (void)ContextRecord; (void)HistoryTable;
// No real unwind possible; halt rather than corrupt the stack silently.
while (true) ;
}
// Code Pages & Locale helpers.
typedef struct { unsigned int MaxCharSize; unsigned char DefaultChar[2]; unsigned char LeadByte[12]; } CPINFO;
// Returns cP Info.
__declspec(dllexport) BOOL GetCPInfo(unsigned int CodePage, CPINFO* lpCPInfo) {
(void)CodePage;
if (lpCPInfo) {
lpCPInfo->MaxCharSize = 1;
lpCPInfo->DefaultChar[0] = '?';
lpCPInfo->DefaultChar[1] = 0;
for (int i = 0; i < 12; i++) lpCPInfo->LeadByte[i] = 0;
}
return TRUE;
}
// Checks whether valid Code Page.
__declspec(dllexport) BOOL IsValidCodePage(unsigned int CodePage) {
// Acknowledge the two code pages we claim to support.
return (CodePage == 1252 || CodePage == 437) ? TRUE : FALSE;
}
__declspec(dllexport) int LCMapStringA(unsigned int Locale, DWORD dwMapFlags,
const char* lpSrcStr, int cchSrc,
char* lpDestStr, int cchDest) {
(void)Locale; (void)dwMapFlags; (void)lpSrcStr; (void)cchSrc;
(void)lpDestStr; (void)cchDest;
return 0;
}
__declspec(dllexport) int LCMapStringW(unsigned int Locale, DWORD dwMapFlags,
const unsigned short* lpSrcStr, int cchSrc,
unsigned short* lpDestStr, int cchDest) {
(void)Locale; (void)dwMapFlags; (void)lpSrcStr; (void)cchSrc;
(void)lpDestStr; (void)cchDest;
return 0;
}
__declspec(dllexport) BOOL GetStringTypeA(unsigned int Locale, DWORD dwInfoType,
const char* lpSrcStr, int cchSrc,
unsigned short* lpCharType) {
(void)Locale; (void)dwInfoType; (void)lpSrcStr; (void)cchSrc; (void)lpCharType;
return FALSE;
}
__declspec(dllexport) BOOL GetStringTypeW(DWORD dwInfoType,
const unsigned short* lpSrcStr, int cchSrc,
unsigned short* lpCharType) {
(void)dwInfoType; (void)lpSrcStr; (void)cchSrc; (void)lpCharType;
return FALSE;
}
__declspec(dllexport) int GetLocaleInfoA(unsigned int Locale, DWORD LCType,
char* lpLCData, int cchData) {
(void)Locale; (void)LCType; (void)lpLCData; (void)cchData;
return 0;
}
// Fiber Local Storage (FLS).
// Allocation state is process-global, but the values themselves are per-thread,
// matching the contract that Bink and the CRT expect when worker threads are
// enabled. The old single global value array let unrelated threads overwrite
// each other's decoder-local state.
static int gFlsUsed[FLS_MAX_SLOTS]; // 0 = free, 1 = allocated
// Implements the fls Alloc operation.
__declspec(dllexport) DWORD FlsAlloc(void* lpCallback) {
(void)lpCallback;
for (int i = 0; i < FLS_MAX_SLOTS; i++) {
if (!gFlsUsed[i]) {
gFlsUsed[i] = 1;
K32ThreadEnv()->FlsSlots[i] = NULL;
return (DWORD)i;
}
}
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return (DWORD)FLS_OUT_OF_INDEXES;
}
// Implements the fls Get Value operation.
__declspec(dllexport) void* FlsGetValue(DWORD dwFlsIndex) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return NULL;
}
return K32ThreadEnv()->FlsSlots[dwFlsIndex];
}
// Implements the fls Set Value operation.
__declspec(dllexport) BOOL FlsSetValue(DWORD dwFlsIndex, void* lpFlsData) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87;
return FALSE;
}
K32ThreadEnv()->FlsSlots[dwFlsIndex] = lpFlsData;
return TRUE;
}
// Implements the fls Free operation.
__declspec(dllexport) BOOL FlsFree(DWORD dwFlsIndex) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87;
return FALSE;
}
gFlsUsed[dwFlsIndex] = 0;
K32ThreadEnv()->FlsSlots[dwFlsIndex] = NULL;
return TRUE;
}
// Pointer encoding (security cookie obfuscation)
// We have no security cookie; return the pointer unchanged.
__declspec(dllexport) void* EncodePointer(void* Ptr) { return Ptr; }
// Implements the decode Pointer operation.
__declspec(dllexport) void* DecodePointer(void* Ptr) { return Ptr; }