Files
nt-lite/code/win32/kernel32.c
T

1600 lines
61 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// kernel32.c - Win32 personality layer
//
// 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 kernel user-heap. This is fine: we don't expose
// multiple heaps, and NULL is a legal (no-op) heap argument under Win32.
// - GetLastError / SetLastError are per-thread on Windows. We keep a single
// global for now; enough for Bink and similar single-threaded guests.
// - File I/O uses SYS_OPEN_FILE / SYS_READ_FILE / SYS_SEEK_FILE /
// SYS_GET_FILE_SIZE / SYS_CLOSE_HANDLE (unified close path - no separate
// SysCloseFile).
// - WriteFile / CreateFileA in write-mode / SetEndOfFile are stubs because
// the VFS has no write path yet.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <intrin.h>
// ---------------------------------------------------------------------------
// Syscall stubs (from syscall.dll, assembled from syscall_stubs.asm)
// ---------------------------------------------------------------------------
// Memory
extern void* SysAlloc(size_t size);
extern void SysFree(void* ptr, size_t size);
extern void* SysRealloc(void* ptr, size_t newSize);
// Process & Thread
extern void SysExitProcess(int exitCode);
extern void SysExitThread(int exitCode);
extern uint32_t SysCreateProcess(const char* path, uint32_t* handleOut, const char* cmdline);
extern uint32_t SysCreateThread(uint64_t entry, uint64_t arg, uint32_t priority, uint32_t* tidOut);
extern uint32_t SysGetCurrentPid(void);
extern uint32_t SysGetCurrentTid(void);
extern uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut);
extern uint32_t SysWaitForProcess(uint32_t handle);
// File I/O
extern void SysCloseHandle(uint32_t handle);
extern uint32_t SysCreateFile(const char* path, uint32_t* handleOut);
extern uint32_t SysDeleteFile(const char* path);
extern uint64_t SysGetFilePos(uint32_t handle);
extern uint64_t SysGetFileSize(uint32_t handle);
extern uint32_t SysOpenFile(const char* path, uint32_t* handleOut);
extern uint32_t SysReadFile(uint32_t handle, void* buf, size_t size, size_t* bytesReadOut);
extern uint32_t SysSeekFile(uint32_t handle, uint64_t offset);
extern uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize);
extern uint32_t SysWriteFile(uint32_t handle, const void* buf, size_t size, size_t* bytesWrittenOut);
// Console & directory & CWD stubs
extern uint32_t SysGetCwd(char* buf, size_t size);
extern uint32_t SysOpenDir(const char* dirPath, uint32_t* handleOut);
extern uint32_t SysReadDir(uint32_t handle, void* entryOut);
extern void SysScreenClear(uint32_t color);
extern uint32_t SysSetCwd(const char* path);
extern void SysWriteConsole(const char* buf, size_t len);
// Keyboard syscalls
extern uint32_t SysKbGetModifiers(void);
extern uint64_t SysKbIsKeyDown(uint8_t scancode, bool extended);
extern uint32_t SysKbPoll(void);
extern char SysKbReadChar(void);
extern unsigned char SysKbReadScancode(void);
// Mouse syscalls
extern void SysMouseGetPos(int* x, int* y);
extern uint32_t SysMousePoll(void);
extern uint32_t SysMouseRead(int* dx, int* dy, unsigned char* buttons);
// Misc
extern void SysDebugPrint(const char* str);
extern void SysYield(void);
extern void SysSleep(uint32_t ms);
extern uint64_t SysGetTick(void);
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
typedef void* HANDLE;
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned char BYTE;
typedef short SHORT;
typedef long LONG;
typedef struct { LONG x; LONG y; } POINT;
#define MAX_PATH 260
#define TRUE 1
#define FALSE 0
#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1)
// File-find structures
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME;
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
// HeapAlloc flags
#define HEAP_ZERO_MEMORY 0x00000008UL
// ---------------------------------------------------------------------------
// 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; }
// ---------------------------------------------------------------------------
// GetLastError / SetLastError (single-threaded stub)
// ---------------------------------------------------------------------------
static DWORD gLastError = 0;
__declspec(dllexport) DWORD GetLastError(void) {
return gLastError;
}
__declspec(dllexport) void SetLastError(DWORD err) {
gLastError = err;
}
// ---------------------------------------------------------------------------
// Process termination
// ---------------------------------------------------------------------------
__declspec(dllexport) _Noreturn void ExitProcess(unsigned int code) {
SysExitProcess((int)code);
while (true) ; // unreachable
}
// ---------------------------------------------------------------------------
// Debug output
// ---------------------------------------------------------------------------
__declspec(dllexport) void OutputDebugStringA(const char* str) {
if (str) SysDebugPrint(str);
}
// ---------------------------------------------------------------------------
// Timer
// ---------------------------------------------------------------------------
__declspec(dllexport) DWORD GetTickCount(void) {
return (DWORD)(SysGetTick() / 1000);
}
__declspec(dllexport) uint64_t GetTickCount64(void) {
return SysGetTick() / 1000;
}
// ---------------------------------------------------------------------------
// Memory (heap argument is ignored - single kernel user-heap)
// ---------------------------------------------------------------------------
__declspec(dllexport) void* HeapAlloc(void* heap, DWORD flags, size_t size) {
(void)heap;
if (!size) return NULL;
void* ptr = SysAlloc(size);
if (ptr && (flags & HEAP_ZERO_MEMORY))
memset(ptr, 0, size);
if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return ptr;
}
__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 kernel's
// allocator handles it (the block header carries the size).
if (ptr) SysFree(ptr, 0);
return TRUE;
}
__declspec(dllexport) void* HeapReAlloc(void* heap, DWORD flags,
void* ptr, size_t size) {
(void)heap; (void)flags;
void* newptr = SysRealloc(ptr, size);
if (!newptr && size) gLastError = 8;
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;
}
// --- Win32 API ---
// Updated GetAsyncKeyState implementation
__declspec(dllexport) SHORT GetAsyncKeyState(int vKey) {
static const struct { int vk; uint8_t sc; bool ext; } vk_to_sc[] = {
// Letters
{ 0x41, 0x1E, false }, { 0x42, 0x30, false }, { 0x43, 0x2E, false },
{ 0x44, 0x20, false }, { 0x45, 0x12, false }, { 0x46, 0x21, false },
{ 0x47, 0x22, false }, { 0x48, 0x23, false }, { 0x49, 0x17, false },
{ 0x4A, 0x24, false }, { 0x4B, 0x25, false }, { 0x4C, 0x26, false },
{ 0x4D, 0x32, false }, { 0x4E, 0x31, false }, { 0x4F, 0x18, false },
{ 0x50, 0x19, false }, { 0x51, 0x10, false }, { 0x52, 0x13, false },
{ 0x53, 0x1F, false }, { 0x54, 0x14, false }, { 0x55, 0x16, false },
{ 0x56, 0x2F, false }, { 0x57, 0x11, false }, { 0x58, 0x2D, false },
{ 0x59, 0x15, false }, { 0x5A, 0x2C, false },
// Numbers
{ 0x31, 0x02, false }, { 0x32, 0x03, false }, { 0x33, 0x04, false },
{ 0x34, 0x05, false }, { 0x35, 0x06, false }, { 0x36, 0x07, false },
{ 0x37, 0x08, false }, { 0x38, 0x09, false }, { 0x39, 0x0A, false },
{ 0x30, 0x0B, false },
// Arrows (Extended)
{ 0x26, 0x48, true }, // VK_UP
{ 0x28, 0x50, true }, // VK_DOWN
{ 0x25, 0x4B, true }, // VK_LEFT
{ 0x27, 0x4D, true }, // VK_RIGHT
// Controls
{ 0x1B, 0x01, false }, // ESC
{ 0x0D, 0x1C, false }, // ENTER
{ 0x20, 0x39, false }, // SPACE
{ 0x11, 0x1D, false }, // CTRL (Generic)
{ 0x10, 0x2A, false }, // SHIFT (Generic)
{ 0x12, 0x38, false }, // ALT (Generic)
{ 0x08, 0x0E, false }, // BACKSPACE
{ 0x09, 0x0F, false }, // TAB
// Function Keys
{ 0x70, 0x3B, false }, { 0x71, 0x3C, false }, { 0x72, 0x3D, false },
{ 0x73, 0x3E, false }, { 0x74, 0x3F, false }, { 0x75, 0x40, false },
{ 0x76, 0x41, false }, { 0x77, 0x42, false }, { 0x78, 0x43, false },
{ 0x79, 0x44, false }, { 0x7A, 0x57, false }, { 0x7B, 0x58, false },
// Symbols
{ 0xBD, 0x0C, false }, // MINUS '-'
{ 0xBB, 0x0D, false }, // EQUALS '='
{ 0xBC, 0x33, false }, // COMMA ','
{ 0xBE, 0x34, false }, // PERIOD '.'
};
for (size_t i = 0; i < sizeof(vk_to_sc)/sizeof(vk_to_sc[0]); ++i) {
if (vk_to_sc[i].vk == vKey) {
// Check the hardware state array via our HAL helper
if (SysKbIsKeyDown(vk_to_sc[i].sc, vk_to_sc[i].ext)) {
return (SHORT)0x8000;
}
break;
}
}
return 0;
}
// FlushConsoleInputBuffer: clear the keyboard input buffer
__declspec(dllexport) BOOL FlushConsoleInputBuffer(HANDLE hConsoleInput) {
(void)hConsoleInput;
// Drain the keyboard buffer by reading until empty
while (SysKbPoll()) {
(void)SysKbReadChar();
}
return TRUE;
}
// !Made-up! clear console api.
__declspec(dllexport) BOOL ClearConsoleScreenBuffer(HANDLE hConsole) {
SysScreenClear(0x00000000);
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;
}
__declspec(dllexport) DWORD GetACP(void) { return 1252; } // Windows-1252
__declspec(dllexport) DWORD GetOEMCP(void) { return 437; } // DOS Latin US
// ---------------------------------------------------------------------------
// 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;
int wantWrite = (access & GENERIC_WRITE) != 0;
if (disposition == OPEN_EXISTING || disposition == TRUNCATE_EXISTING) {
// Open the file via the read path (it must already exist).
uint32_t kh = 0;
uint32_t s = SysOpenFile(name, &kh);
if (s != 0) { gLastError = 2; return INVALID_HANDLE_VALUE; }
if (disposition == TRUNCATE_EXISTING && wantWrite) {
// Immediately truncate to zero.
uint32_t ts = SysTruncateFile(kh, 0);
if (ts != 0) {
SysCloseHandle(kh);
gLastError = 5;
return INVALID_HANDLE_VALUE;
}
}
return KH_TO_HANDLE(kh);
}
if (disposition == CREATE_ALWAYS && wantWrite) {
// Create-or-replace: try to create; if the file already exists,
// open it and truncate it to zero instead.
uint32_t kh = 0;
uint32_t s = SysCreateFile(name, &kh);
if (s != 0) {
// File may already exist - open and truncate.
s = SysOpenFile(name, &kh);
if (s != 0) { gLastError = 5; return INVALID_HANDLE_VALUE; }
SysTruncateFile(kh, 0);
}
return KH_TO_HANDLE(kh);
}
if (disposition == OPEN_ALWAYS && wantWrite) {
// Open if exists, create if not.
uint32_t kh = 0;
if (SysOpenFile(name, &kh) != 0) {
if (SysCreateFile(name, &kh) != 0) {
gLastError = 5;
return INVALID_HANDLE_VALUE;
}
}
return KH_TO_HANDLE(kh);
}
if (disposition == CREATE_NEW && wantWrite) {
// Fail if the file already exists (we can't check atomically,
// but SysCreateFile returns NAME_COLLISION for existing files).
uint32_t kh = 0;
uint32_t s = SysCreateFile(name, &kh);
if (s != 0) { gLastError = 80; return INVALID_HANDLE_VALUE; } // ERROR_FILE_EXISTS
return KH_TO_HANDLE(kh);
}
// Any other combination (e.g. read-only CREATE_ALWAYS) is not supported.
gLastError = 87; // ERROR_INVALID_PARAMETER
return INVALID_HANDLE_VALUE;
}
// 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) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile));
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 (!hFile || !buf) { gLastError = 6; return FALSE; }
size_t got = 0;
uint32_t s = SysReadFile(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
if (bytesRead) *bytesRead = (DWORD)got;
if (s != 0) { gLastError = 1; return FALSE; }
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 || !buf) { gLastError = 6; return FALSE; }
size_t written = 0;
uint32_t s = SysWriteFile(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
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) { gLastError = 6; return 0xFFFFFFFFUL; }
uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t offset = 0;
int64_t delta = distHigh
? ((int64_t)(uint64_t)(uint32_t)*distHigh << 32) | (uint32_t)dist
: (int64_t)dist;
if (method == FILE_BEGIN) {
offset = (uint64_t)delta;
} else if (method == FILE_CURRENT) {
uint64_t cur = SysGetFilePos(kh);
if (cur == (uint64_t)-1) { gLastError = 6; return 0xFFFFFFFFUL; }
offset = (uint64_t)((int64_t)cur + delta);
} else if (method == FILE_END) {
uint64_t sz = SysGetFileSize(kh);
offset = (uint64_t)((int64_t)sz + delta);
} else {
gLastError = 87; return 0xFFFFFFFFUL;
}
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) { gLastError = 6; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t pos = SysGetFilePos(kh);
if (pos == (uint64_t)-1) { gLastError = 6; return FALSE; }
uint32_t 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; }
uint32_t s = SysDeleteFile(lpFileName);
if (s != 0) { gLastError = 2; return FALSE; } // ERROR_FILE_NOT_FOUND
return TRUE;
}
// CloseHandle - unified close for files, mutexes, semaphores, events, queues.
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
if (!hObject) { gLastError = 6; return FALSE; }
SysCloseHandle(HANDLE_TO_KH(hObject));
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
}
__declspec(dllexport) BOOL HeapDestroy(HANDLE hHeap) {
(void)hHeap;
return TRUE;
}
__declspec(dllexport) size_t HeapSize(HANDLE heap, DWORD flags, const void* ptr) {
(void)heap; (void)flags; (void)ptr;
return (size_t)-1; // Win32 returns (SIZE_T)-1 on error / unsupported
}
__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);
}
__declspec(dllexport) HANDLE GetCurrentThread(void) {
return (HANDLE)(uintptr_t)(~(uintptr_t)1);
}
__declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); }
__declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); }
// GetModuleHandleA/W - no PE map; return NULL so callers that check can fail
// gracefully. GetModuleHandleA(NULL) on Windows returns the exe base; we
// return a recognisable non-NULL sentinel so code that just passes it to
// GetProcAddress doesn't immediately crash.
__declspec(dllexport) HANDLE GetModuleHandleA(const char* lpModuleName) {
return lpModuleName ? NULL : (HANDLE)(uintptr_t)0x00400000UL;
}
__declspec(dllexport) HANDLE GetModuleHandleW(const unsigned short* lpModuleName) {
return lpModuleName ? NULL : (HANDLE)(uintptr_t)0x00400000UL;
}
// GetProcAddress - no dynamic linker; always fail.
__declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcName) {
(void)hModule; (void)lpProcName;
gLastError = 127; // ERROR_PROC_NOT_FOUND
return NULL;
}
// LoadLibraryA - no dynamic loader; always fail.
__declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) {
(void)lpLibFileName;
gLastError = 2; // ERROR_FILE_NOT_FOUND
return NULL;
}
__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) {
(void)hLibModule;
return TRUE;
}
__declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
char* lpFilename, DWORD nSize) {
(void)hModule;
if (lpFilename && nSize) lpFilename[0] = '\0';
return 0;
}
__declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) {
(void)hProcess;
SysExitProcess((int)uExitCode);
while (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;
__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);
}
__declspec(dllexport) char* GetCommandLineA(void) {
// Return a valid but empty-ish command line.
static char cmdline[4] = "";
return cmdline;
}
// ---------------------------------------------------------------------------
// Threading
// ---------------------------------------------------------------------------
#define WAIT_OBJECT_0 0x00000000UL
#define WAIT_TIMEOUT 0x00000102UL
#define INFINITE 0xFFFFFFFFUL
__declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
size_t dwStackSize,
void* lpStartAddress,
void* lpParameter,
DWORD dwCreationFlags,
DWORD* lpThreadId) {
(void)lpThreadAttributes; (void)dwStackSize; (void)dwCreationFlags;
uint32_t tid = 0;
uint32_t s = SysCreateThread((uint64_t)(uintptr_t)lpStartAddress,
(uint64_t)(uintptr_t)lpParameter,
0 /* normal priority */, &tid);
if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; }
if (lpThreadId) *lpThreadId = (DWORD)tid;
return KH_TO_HANDLE(tid);
}
// WaitForSingleObject - blocks on process handles via SysWaitForProcess;
// thread and other handle types are not yet waitable so return immediately.
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
(void)dwMilliseconds;
uint32_t kh = HANDLE_TO_KH(hHandle);
if (kh) SysWaitForProcess(kh); // no-op KSTATUS if not a process handle
return WAIT_OBJECT_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)lpCommandLine; (void)lpProcessAttributes; (void)lpThreadAttributes;
(void)bInheritHandles; (void)dwCreationFlags; (void)lpEnvironment;
(void)lpCurrentDirectory; (void)lpStartupInfo;
// lpProcessInformation is mandatory
if (!lpProcessInformation) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return FALSE;
}
// Standard Win32 behavior: use lpCommandLine if lpApplicationName is NULL
const char* targetPath = lpApplicationName;
if (!targetPath) {
targetPath = lpCommandLine;
}
// If both are NULL, then it's truly an invalid parameter
if (!targetPath) {
gLastError = 87;
return FALSE;
}
// Build the cmdline: prefer lpCommandLine if given, otherwise fall back to path
const char* cmdline = lpCommandLine ? lpCommandLine : targetPath;
uint32_t kh = 0;
// Call the syscall.dll stub
uint32_t s = SysCreateProcess(targetPath, &kh, cmdline);
if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND
return FALSE;
}
lpProcessInformation->hProcess = KH_TO_HANDLE(kh);
lpProcessInformation->hThread = INVALID_HANDLE_VALUE;
lpProcessInformation->dwProcessId = 0;
lpProcessInformation->dwThreadId = 0;
return TRUE;
}
__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; } // ERROR_INVALID_HANDLE
int code = 0;
uint32_t s = SysGetProcessExitCode(kh, &code);
if (s != 0) { gLastError = 6; return FALSE; }
*lpExitCode = (DWORD)code;
return TRUE;
}
__declspec(dllexport) DWORD ResumeThread(HANDLE hThread) {
(void)hThread;
return 0; // previous suspend count
}
#define THREAD_PRIORITY_NORMAL 0
__declspec(dllexport) BOOL SetThreadPriority(HANDLE hThread, int nPriority) {
(void)hThread; (void)nPriority;
return TRUE;
}
__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
}
__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)
// 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 SysKbReadChar 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 = SysKbReadChar();
// 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;
}
SysWriteConsole((const char*)lpBuffer, (size_t)nNumberOfCharsToWrite);
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nNumberOfCharsToWrite;
return TRUE;
}
// ---------------------------------------------------------------------------
// Directory enumeration (FindFirstFileA / FindNextFileA / FindClose)
// ---------------------------------------------------------------------------
// KDIR_ENTRY layout must exactly match the definition in io_file.h.
#define KDIR_NAME_MAX 260
typedef struct {
char Name[KDIR_NAME_MAX];
uint8_t Attr;
uint32_t FileSize;
} 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
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;
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;
}
// Split path into directory and wildcard pattern
char dir[MAX_PATH];
char pat[MAX_PATH];
SplitFindPath(lpFileName, dir, sizeof(dir), pat, sizeof(pat));
// Open the kernel directory iterator
uint32_t kh = 0;
uint32_t s = SysOpenDir(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;
for (;;) {
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;
for (;;) {
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;
}
}
}
__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; }
uint32_t s = SysSetCwd(lpPathName);
if (s != 0) { gLastError = 2; return FALSE; }
return TRUE;
}
__declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBuffer) {
if (!lpBuffer || nBufferLength == 0) {
// Return required size (including NUL). Max CWD is 512 bytes + "C:\".
return 515;
}
// Retrieve the raw relative CWD from the kernel (e.g. "" or "apps\\data")
char raw[512];
uint32_t s = SysGetCwd(raw, sizeof(raw));
if (s != 0) { gLastError = 1; return 0; }
// Prepend the Windows-style drive letter so callers see "C:\apps\data"
// rather than just "apps\data".
char full[515];
if (raw[0] == '\0') {
// Root
full[0] = 'C'; full[1] = ':'; full[2] = '\\'; full[3] = '\0';
} else {
// "C:\<raw>"
full[0] = 'C'; full[1] = ':'; full[2] = '\\';
size_t rawLen = strlen(raw);
if (rawLen + 4 > sizeof(full)) rawLen = sizeof(full) - 4;
memcpy(full + 3, raw, rawLen);
full[3 + rawLen] = '\0';
}
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
}
// ---------------------------------------------------------------------------
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
const char* lpName) {
(void)lpMutexAttributes; (void)bInitialOwner; (void)lpName;
return (HANDLE)(uintptr_t)0x4D555458UL; // 'MUTX' sentinel
}
__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) {
(void)hMutex;
return TRUE;
}
__declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes,
long lInitialCount, long lMaximumCount,
const char* lpName) {
(void)lpSemaphoreAttributes; (void)lInitialCount;
(void)lMaximumCount; (void)lpName;
return (HANDLE)(uintptr_t)0x53454D41UL; // 'SEMA' sentinel
}
__declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount,
long* lpPreviousCount) {
(void)hSemaphore; (void)lReleaseCount;
if (lpPreviousCount) *lpPreviousCount = 0;
return TRUE;
}
// ---------------------------------------------------------------------------
// Critical Sections & Condition Variables
// Single-threaded: all locks are no-ops.
// ---------------------------------------------------------------------------
// Minimal opaque types; callers allocate these from their Windows headers -
// we just need the pointer to be accepted by our stubs.
typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION;
typedef struct { void* _ptr; } CONDITION_VARIABLE;
__declspec(dllexport) void InitializeCriticalSection(CRITICAL_SECTION* lpCS) {
(void)lpCS;
}
__declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION* lpCS,
DWORD dwSpinCount) {
(void)lpCS; (void)dwSpinCount;
return TRUE;
}
__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCS) {
(void)lpCS; return TRUE;
}
__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
__declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE* cv,
CRITICAL_SECTION* cs,
DWORD dwMilliseconds) {
(void)cv; (void)cs; (void)dwMilliseconds;
return TRUE;
}
__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
// ---------------------------------------------------------------------------
// Timing & Performance Counters
//
// We use the CPU's Time Stamp Counter (TSC) for QueryPerformanceCounter so
// that callers like Bink get a truly high-resolution, continuously-advancing
// clock. The TSC frequency is measured once on first use.
//
// Calibration strategy:
// 1. Try CPUID leaf 0x15 (TSC / Crystal-clock ratio). If the crystal
// frequency (ECX) is non-zero, compute TSC Hz = crystal_hz * EBX / EAX
// with no blocking at all.
// 2. Otherwise fall back to measuring TSC ticks over a known wall-clock
// interval supplied by SysGetTick(). SysGetTick() returns raw LAPIC
// interrupt counts; each unit represents QUANTUM_MS = 10 ms. We wait
// for two consecutive tick-boundary crossings and divide.
// ---------------------------------------------------------------------------
#define QUANTUM_MS 10 // must match hal_timer.c QUANTUM_MS
typedef union {
struct { DWORD LowPart; long HighPart; } u;
long long QuadPart;
} LARGE_INTEGER;
static volatile long long gTscFreq = 0; // TSC ticks per second; 0 = uninitialised
// Attempt to read TSC frequency from CPUID leaf 0x15.
// Returns 0 if the leaf is absent or the crystal frequency is not enumerated.
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]; // EAX – TSC/crystal denominator
unsigned numer = (unsigned)info[1]; // EBX – TSC/crystal numerator
unsigned crystal_hz = (unsigned)info[2]; // ECX – crystal clock in Hz
if (!denom || !numer || !crystal_hz) return 0;
return (long long)crystal_hz * numer / denom;
}
// Calibrate TSC frequency by timing over two LAPIC-tick boundaries.
// Each SysGetTick() unit = QUANTUM_MS ms; we measure across N_TICKS units
// for a total gate time of N_TICKS * QUANTUM_MS ms.
static long long TscFreqCalibrate(void) {
const uint64_t N_TICKS = 10; // 10 * 10 ms = 100 ms gate time
// Synchronise to the leading edge of a tick.
uint64_t t = SysGetTick();
while (SysGetTick() == t) { _mm_pause(); }
uint64_t tsc0 = __rdtsc();
uint64_t t0 = SysGetTick();
// Wait for N_TICKS more ticks.
while ((SysGetTick() - t0) < N_TICKS) { _mm_pause(); }
uint64_t tsc1 = __rdtsc();
uint64_t ticks_got = SysGetTick() - t0; // actual ticks elapsed (>= N_TICKS)
// ticks_got LAPIC ticks = ticks_got * QUANTUM_MS ms = ticks_got * QUANTUM_MS / 1000 s
// TSC Hz = (tsc1-tsc0) / (ticks_got * QUANTUM_MS / 1000)
// = (tsc1-tsc0) * 1000 / (ticks_got * QUANTUM_MS)
return (long long)((tsc1 - tsc0) * 1000ULL / (ticks_got * (uint64_t)QUANTUM_MS));
}
static long long TscFreqGet(void) {
if (gTscFreq) return gTscFreq;
long long f = TscFreqFromCpuid();
if (!f) f = TscFreqCalibrate();
gTscFreq = f;
return f;
}
__declspec(dllexport) BOOL QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency) {
if (lpFrequency) lpFrequency->QuadPart = TscFreqGet();
return TRUE;
}
__declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount) {
// Ensure frequency is measured before the first counter read so that the
// baseline TSC and the frequency come from the same calibration window.
TscFreqGet();
if (lpPerformanceCount)
lpPerformanceCount->QuadPart = (long long)__rdtsc();
return TRUE;
}
__declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime) {
// Return a fixed FILETIME representing 2024-01-01 00:00:00 UTC.
// FILETIME = 100-nanosecond intervals since 1601-01-01.
// 2024-01-01 offset: 133186080000000000 * 100 ns
if (!lpSystemTimeAsFileTime) return;
// SysGetTick() units = QUANTUM_MS ms = QUANTUM_MS * 10000 * 100-ns intervals.
unsigned long long ft = 133186080000000000ULL
+ SysGetTick() * ((unsigned long long)QUANTUM_MS * 10000ULL);
unsigned char* p = (unsigned char*)lpSystemTimeAsFileTime;
for (int i = 0; i < 8; i++) p[i] = (unsigned char)(ft >> (i * 8));
}
// ---------------------------------------------------------------------------
// 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;
__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
__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
}
__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;
}
__declspec(dllexport) DWORD GetEnvironmentVariableA(const char* lpName, char* lpBuffer,
DWORD nSize) {
(void)lpName; (void)lpBuffer; (void)nSize;
gLastError = 203; // ERROR_ENVVAR_NOT_FOUND
return 0;
}
__declspec(dllexport) char* GetEnvironmentStrings(void) {
// Return a double-NUL-terminated empty environment block.
static char emptyEnv[2] = { '\0', '\0' };
return emptyEnv;
}
__declspec(dllexport) BOOL FreeEnvironmentStringsA(char* penv) {
(void)penv; return TRUE;
}
__declspec(dllexport) unsigned short* GetEnvironmentStringsW(void) {
static unsigned short emptyEnvW[2] = { 0, 0 };
return emptyEnvW;
}
__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
}
__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)
__declspec(dllexport) DWORD GetFileType(HANDLE hFile) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) return FILE_TYPE_UNKNOWN;
return FILE_TYPE_DISK;
}
// ---------------------------------------------------------------------------
// Exception Handling & Debugging
// ---------------------------------------------------------------------------
#define EXCEPTION_CONTINUE_SEARCH 0
#define EXCEPTION_EXECUTE_HANDLER 1
__declspec(dllexport) void* AddVectoredExceptionHandler(unsigned long First, void* Handler) {
(void)First; (void)Handler;
// Return a non-NULL sentinel so callers can pass it to RemoveVectoredExceptionHandler.
return (void*)(uintptr_t)0x56455843UL; // 'VEXC'
}
__declspec(dllexport) unsigned long RemoveVectoredExceptionHandler(void* Handle) {
(void)Handle;
return 1; // success
}
__declspec(dllexport) void RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags,
DWORD nNumberOfArguments,
const uintptr_t* lpArguments) {
(void)dwExceptionCode; (void)dwExceptionFlags;
(void)nNumberOfArguments; (void)lpArguments;
// Best-effort: nothing to unwind; just halt.
while (true) ;
}
typedef struct _EXCEPTION_POINTERS { void* ExceptionRecord; void* ContextRecord; } EXCEPTION_POINTERS;
__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
SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpFilter) {
(void)lpFilter;
return NULL; // previous filter: none
}
__declspec(dllexport) BOOL IsDebuggerPresent(void) {
return FALSE;
}
// ---------------------------------------------------------------------------
// RTL Exception / Unwind (SEH infrastructure - all stubs)
// ---------------------------------------------------------------------------
typedef struct _RUNTIME_FUNCTION {
unsigned int BeginAddress;
unsigned int EndAddress;
unsigned int UnwindInfoAddress;
} RUNTIME_FUNCTION;
// 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;
}
__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;
__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;
}
__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)
// We implement a minimal 64-slot static table so callers that use FLS for
// thread-local-ish state don't immediately fail.
// ---------------------------------------------------------------------------
#define FLS_MAX_SLOTS 64
#define FLS_OUT_OF_INDEXES 0xFFFFFFFFUL
static void* gFlsSlots[FLS_MAX_SLOTS];
static int gFlsUsed[FLS_MAX_SLOTS]; // 0 = free, 1 = allocated
__declspec(dllexport) DWORD FlsAlloc(void* lpCallback) {
(void)lpCallback;
for (int i = 0; i < FLS_MAX_SLOTS; i++) {
if (!gFlsUsed[i]) {
gFlsUsed[i] = 1;
gFlsSlots[i] = NULL;
return (DWORD)i;
}
}
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return (DWORD)FLS_OUT_OF_INDEXES;
}
__declspec(dllexport) void* FlsGetValue(DWORD dwFlsIndex) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return NULL;
}
return gFlsSlots[dwFlsIndex];
}
__declspec(dllexport) BOOL FlsSetValue(DWORD dwFlsIndex, void* lpFlsData) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87;
return FALSE;
}
gFlsSlots[dwFlsIndex] = lpFlsData;
return TRUE;
}
__declspec(dllexport) BOOL FlsFree(DWORD dwFlsIndex) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87;
return FALSE;
}
gFlsUsed[dwFlsIndex] = 0;
gFlsSlots[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; }
__declspec(dllexport) void* DecodePointer(void* Ptr) { return Ptr; }