// 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 #include #include #include #include #include // --------------------------------------------------------------------------- // 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; } // Path translator static void TranslatePath(const char* input, char* output) { if (input && input[0] != '\0' && input[1] == ':' && (input[2] == '\\' || input[2] == '/')) { // Translate "C:\" -> "0:/" (assuming 'C' maps to '0', etc.) // Map drive letter to index: 'C' or 'c' -> '0' char drive = input[0]; if (drive >= 'a') drive -= ('a' - 'A'); // To uppercase output[0] = (char)('0' + (drive - 'C')); output[1] = ':'; output[2] = '/'; // Copy the rest, swapping backslashes to forward slashes const char* src = input + 3; char* dst = output + 3; while (*src) { *dst = (*src == '\\') ? '/' : *src; src++; dst++; } *dst = '\0'; } else { // Fallback for relative paths: just swap slashes while (*input) { *output = (*input == '\\') ? '/' : *input; input++; output++; } *output = '\0'; } } // --------------------------------------------------------------------------- // 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; // Translate the path from Windows format to custom 0:/ format char translatedName[260]; TranslatePath(name, translatedName); 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(translatedName, &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(translatedName, &kh); if (s != 0) { // File may already exist - open and truncate. s = SysOpenFile(translatedName, &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(translatedName, &kh) != 0) { if (SysCreateFile(translatedName, &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(translatedName, &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 || hFile == INVALID_HANDLE_VALUE) { 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 || hFile == INVALID_HANDLE_VALUE || !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 || hFile == INVALID_HANDLE_VALUE) { 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 || hFile == INVALID_HANDLE_VALUE) { 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; } char translatedPath[MAX_PATH]; TranslatePath(lpFileName, translatedPath); uint32_t s = SysDeleteFile(translatedPath); if (s != 0) { gLastError = 2; return FALSE; } 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; char translatedPath[MAX_PATH]; TranslatePath(targetPath, translatedPath); uint32_t kh = 0; // Call the syscall.dll stub uint32_t s = SysCreateProcess(translatedPath, &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; } // 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 = 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; } char translatedPath[MAX_PATH]; TranslatePath(lpPathName, translatedPath); uint32_t s = SysSetCwd(translatedPath); 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:\" 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 // --------------------------------------------------------------------------- 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; } // Calibrate TSC against SysGetTick (1 unit = 1 microsecond). static long long TscFreqCalibrate(void) { // We'll wait for 100ms (100,000 microseconds) to get a stable sample. const uint64_t CALIBRATION_US = 100000; uint64_t t0 = SysGetTick(); uint64_t tsc0 = __rdtsc(); // Simple busy wait for the duration while ((SysGetTick() - t0) < CALIBRATION_US) { _mm_pause(); } uint64_t t1 = SysGetTick(); uint64_t tsc1 = __rdtsc(); uint64_t delta_tsc = tsc1 - tsc0; uint64_t delta_us = t1 - t0; // Hz = (Delta TSC / Delta Microseconds) * 1,000,000 return (long long)(delta_tsc * 1000000ULL / delta_us); } 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 calibration happens before the first read. TscFreqGet(); if (lpPerformanceCount) lpPerformanceCount->QuadPart = (long long)__rdtsc(); return TRUE; } __declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime) { if (!lpSystemTimeAsFileTime) return; // Baseline: 2024-01-01 00:00:00 UTC in 100-ns intervals const unsigned long long BASELINE_FT = 133485408000000000ULL; // SysGetTick() is in microseconds. // 1 microsecond = 10 units of 100-nanoseconds. unsigned long long now_us = SysGetTick(); unsigned long long ft = BASELINE_FT + (now_us * 10ULL); // Copy the 64-bit value into the output buffer. // We use memcpy to safely handle potential alignment issues with the void pointer. 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; __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; }